Una lectora me escribió la semana pasada con una captura de su ejecución de CI: PHP-CS-Fixer, PHP_CodeSniffer, PHPStan en nivel 8 y Deptrac, todos en fila uno detrás de otro, once minutos antes de que nadie supiera si una errata en un docblock había roto la build. Su pregunta era corta. ¿Debería arrancarlo todo y poner Mago en su lugar? Mi respuesta es que no, no todo, y no este trimestre. Lo que debería hacer mañana por la mañana es meter Mago en su hook de pre-commit y dejar que se encargue por completo del formateo y del linting.

Un poco de contexto por si todavía no te lo has cruzado. Mago es un único binario estático escrito en Rust, de Carthage Software, que agrupa un formateador (mago fmt, PER-CS por defecto, con presets psr-12, laravel y drupal), un linter (mago lint, 190 reglas en 9 categorías a partir de la 1.51.2), un analizador estático (mago analyze, que lee anotaciones de PHPStan y Psalm, genéricos y tipos condicionales incluidos) y un guardián de arquitectura (mago guard, que cubre el terreno que hoy cubren Deptrac y PHPArkitect). Todo vive en un único mago.toml, está en 1.x estable desde diciembre de 2025 y tiene licencia MIT o Apache 2.0. Puedes instalarlo con composer require --dev carthage-software/mago, o saltarte por completo el runtime de PHP y usar el script de instalación.

La cifra que todo el mundo repite sale de los benchmarks del propio proyecto: un linting unas 29 veces más rápido que PHP-CS-Fixer, gracias al código nativo y a un pipeline repartido entre todos los núcleos. Los benchmarks del propio fabricante merecen una ceja levantada, claro, pero aunque tu código solo note un tercio de eso, el cambio es mayor de lo que parece. Una comprobación que tarda cuarenta segundos vive en el CI y se ignora hasta que el pipeline se pone en rojo. Una que tarda menos de dos segundos puede ejecutarse cada vez que guardas, y de repente la discusión sobre el formato en la revisión de código desaparece sin más, porque el diff nunca la contenía. La velocidad decide dónde puede vivir una comprobación, y ahí es donde Mago se gana su sitio primero.

Ahora, el mejor argumento para apostarlo todo, porque es bueno. Siete herramientas son siete configuraciones que se van distanciando, siete parsers que no se ponen de acuerdo en los casos límite de la sintaxis nueva y un composer.json lleno de dependencias de desarrollo que se pelean entre sí en cada actualización menor de PHP. Un solo parser compartido por todas las comprobaciones es, de verdad, mejor ingeniería. Y el guard es discretamente excelente: poder decir en la configuración que todo lo que cuelga de App\Controller debe ser final y llamarse *Controller, o que App\Domain solo puede depender de sí mismo y de PHP nativo, sin una herramienta aparte ni otro dialecto de YAML, es el tipo de cosa de la que los equipos hablan durante años y nunca llegan a montar. Si nunca has impuesto capas porque Deptrac te parecía una cosa más que vigilar, Mago te pone el listón mucho más bajo.

Este es el motivo por el que, de momento, sigo teniendo PHPStan en el pipeline. El valor de mi configuración de análisis no viene solo del motor principal; viene de la extensión de Symfony que sabe qué devuelve el contenedor, de la extensión de Doctrine que entiende mis repositorios y de tres años de baseline que he ido rebajando entrada a entrada. Los propios mantenedores de Mago reconocen a PHPStan y Psalm como inspiración y no dicen estar todavía a la altura de ese ecosistema de plugins. Si cambias de analizador sin comprobar esa cobertura, tendrás uno de dos resultados: una avalancha de falsos positivos que enseña a tu equipo a ignorar la herramienta, o silencio donde antes había un aviso real. Ninguno de los dos aparece en un gráfico de benchmarks.

Así que mi reparto es aburrido, y me parece bien. Formateador y linter: dáselos a Mago, ejecuta mago format --check y mago lint en el hook y en el CI, usa un baseline para los rincones legacy y tira de mago lint --explain cuando alguien pregunte por qué ha saltado una regla. Guard: adóptalo si no tienes nada, y si ya usas Deptrac, migra sin prisas. Analizador: ejecuta mago analyze junto a tu herramienta actual durante unos cuantos sprints, compara lo que reporta cada una y retira la antigua solo cuando la diferencia sea algo que puedas nombrar y aceptar. Un consejo práctico ya que estás en mago.toml: escribe php-version = "8.3" con comillas, porque TOML lee el 8.3 sin comillas como un float y Mago quiere un string. Si no, te costará diez minutos de desconcierto.

Debajo de todo esto hay una pregunta más de fondo a la que sigo dándole vueltas. Nuestras herramientas de calidad siempre se han escrito en PHP, lo que significa que cualquiera de nosotros podía abrir el código de un sniff o de una regla de PHPStan, entenderlo y mandar un arreglo un viernes por la tarde. Mago lleva el motor a Rust, y la gente de Go te dirá que hoy en día las herramientas rápidas se construyen así y punto. Me parece justo. Pero PHP ha llevado sus herramientas hasta aquí precisamente porque sus usuarios podían meterles mano, y me gustaría saber si eso sobrevive cuando la regla que quieres retocar vive en un crate.

Así que esto es lo que de verdad me gustaría leer en los comentarios: si ya has ejecutado mago analyze contra un código real de Symfony o Laravel junto a PHPStan, ¿qué detectó que PHPStan no vio, qué se le escapó que PHPStan sí detectó, y fue la diferencia lo bastante pequeña como para que te pasaras?