Letzte Woche hat mir eine Leserin einen Screenshot ihres CI-Laufs geschickt: PHP-CS-Fixer, PHP_CodeSniffer, PHPStan auf Level 8, Deptrac, alles brav hintereinander, elf Minuten, bevor überhaupt jemand erfuhr, ob ein Tippfehler in einem Docblock den Build kaputt gemacht hatte. Ihre Frage war kurz. Soll sie das alles rausreißen und Mago an die Stelle setzen? Meine Antwort: nein, nicht alles, und nicht in diesem Quartal. Was sie morgen früh tun sollte: Mago in ihren Pre-Commit-Hook packen und ihm Formatierung und Linting komplett überlassen.
Kurz zum Hintergrund, falls du noch nicht drübergestolpert bist. Mago ist ein einzelnes statisches Binary von Carthage Software, in Rust geschrieben, und bündelt einen Formatter (mago fmt, standardmäßig PER-CS, mit Presets für psr-12, laravel und drupal), einen Linter (mago lint, 190 Regeln in 9 Kategorien, Stand 1.51.2), einen statischen Analyzer (mago analyze, der PHPStan- und Psalm-Annotationen liest, inklusive Generics und Conditional Types) und einen Architektur-Wächter (mago guard, der das Terrain abdeckt, für das heute Deptrac und PHPArkitect zuständig sind). Alles steckt in einer einzigen mago.toml, seit Dezember 2025 ist das Projekt auf stabilem 1.x, und es steht unter MIT oder Apache 2.0. Du holst es dir mit composer require --dev carthage-software/mago, oder du lässt die PHP-Runtime ganz weg und nimmst das Install-Skript.
Die Zahl, die alle wiederholen, stammt aus den eigenen Benchmarks des Projekts: Linting etwa 29-mal schneller als PHP-CS-Fixer, dank nativem Code und einer Pipeline, die sich über alle Kerne verteilt. Herstellerbenchmarks verdienen eine hochgezogene Augenbraue, klar. Aber selbst wenn bei deiner Codebasis nur ein Drittel davon ankommt, ist der Unterschied größer, als er klingt. Ein Check, der vierzig Sekunden braucht, lebt in der CI und wird ignoriert, bis die Pipeline rot wird. Ein Check, der unter zwei Sekunden braucht, kann bei jedem Speichern laufen, und plötzlich hört die Formatierungsdiskussion im Code Review einfach auf, weil sie im Diff gar nicht mehr vorkommt. Tempo entscheidet, wo ein Check leben darf, und genau dort verdient sich Mago seinen Platz zuerst.
Jetzt das stärkste Argument dafür, voll einzusteigen, denn es ist ein gutes. Sieben Tools heißen sieben Configs, die auseinanderdriften, sieben Parser, die sich bei Randfällen neuer Syntax uneinig sind, und eine composer.json voller Dev-Dependencies, die sich bei jedem PHP-Minor-Upgrade gegenseitig in die Quere kommen. Ein einziger Parser für alle Checks ist schlicht saubereres Engineering. Und der Guard ist unauffällig richtig gut: In der Config festlegen zu können, dass alles unter App\Controller final sein und auf *Controller enden muss oder dass App\Domain nur von sich selbst und nativem PHP abhängen darf, ohne separates Tool und ohne eigenen YAML-Dialekt, ist genau die Sorte Ding, über die Teams jahrelang reden und die sie nie einrichten. Falls du Schichtung nie durchgesetzt hast, weil Deptrac sich wie noch ein Ding zum Babysitten anfühlte, senkt Mago diese Hürde gewaltig.
Und hier der Grund, warum PHPStan bei mir vorerst trotzdem in der Pipeline bleibt. Der Wert meines Analyse-Setups kommt nicht allein von der Core-Engine, sondern von der Symfony-Extension, die weiß, was der Container zurückgibt, von der Doctrine-Extension, die meine Repositories versteht, und von drei Jahren Baseline, die ich Eintrag für Eintrag runterverhandelt habe. Magos eigene Maintainer nennen PHPStan und Psalm als Inspiration und behaupten nicht, mit diesem Plugin-Ökosystem schon mithalten zu können. Tauschst du den Analyzer aus, ohne diese Abdeckung zu prüfen, bekommst du eins von zwei Ergebnissen: eine Flut von False Positives, die deinem Team beibringt, das Tool zu ignorieren, oder Stille, wo früher eine echte Warnung stand. Keins von beiden taucht in einem Benchmark-Chart auf.
Meine Aufteilung ist also langweilig, und damit kann ich gut leben. Formatter und Linter: gib sie an Mago ab, lass mago format --check und mago lint im Hook und in der CI laufen, nimm eine Baseline für die Legacy-Ecken und greif zu mago lint --explain, wenn jemand fragt, warum eine Regel angeschlagen hat. Guard: einführen, wenn du noch nichts hast, und in aller Ruhe von Deptrac wegmigrieren, wenn doch. Analyzer: lass mago analyze ein paar Sprints lang neben deinem bestehenden Tool laufen, vergleich, was jedes davon meldet, und schick das alte erst in Rente, wenn du die Lücke benennen und akzeptieren kannst. Ein praktischer Tipp, wenn du schon in der mago.toml bist: Schreib php-version = "8.3" mit Anführungszeichen, denn TOML liest die nackte 8.3 als Float, und Mago will einen String. Sonst kostet dich das zehn verwirrte Minuten.
Unter all dem liegt eine leisere Frage, auf der ich immer noch herumkaue. Unser Qualitäts-Tooling war schon immer in PHP geschrieben, und das hieß: Jede und jeder von uns konnte den Quellcode eines Sniffs oder einer PHPStan-Regel öffnen, ihn verstehen und am Freitagnachmittag einen Fix schicken. Mago verlegt die Engine nach Rust, und die Go-Fraktion wird dir sagen, dass schnelles Tooling heutzutage eben so gebaut wird. Geschenkt. Aber PHP hat sein eigenes Tooling nur so weit gebracht, weil seine Nutzer daran herumbasteln konnten, und ich wüsste gern, ob das überlebt, wenn die Regel, die du anpassen willst, in einem Crate wohnt.
Deshalb würde ich in den Kommentaren wirklich gern Folgendes von dir hören: Wenn du mago analyze schon neben PHPStan auf eine echte Symfony- oder Laravel-Codebasis losgelassen hast, was hat es gefunden, das PHPStan übersehen hat, was hat es übersehen, das PHPStan gefunden hat, und war der Unterschied klein genug, dass du gewechselt hast?




Kommentare
Noch keine Kommentare — schreib den ersten.
Starte die Diskussion
Kein Konto, kein Passwort nötig — gib einfach deine E-Mail-Adresse ein, wir senden dir einen einmaligen Anmelde-Link. Beim ersten Mal bist du damit automatisch angemeldet.
Deine Bewertung wird nach der Anmeldung automatisch übernommen.
Schau in dein Postfach
Wir haben einen Anmelde-Link an … gesendet. Öffne ihn auf diesem Gerät — dieser Tab meldet dich automatisch an.
Nichts angekommen? Wirf einen Blick in den Spam-Ordner — und markiere die Mail dort als „Kein Spam“, dann landet sie künftig direkt im Postfach.