Saltar al contenido

Hoy en día tenemos herramientas muy buenas para trabajar con Laravel de una forma mucho más profesional.

Podemos configurar análisis estático con PHPStan o Larastan, aplicar formateo automático con Laravel Pint, usar Rector para automatizar mejoras en el código, escribir tests con Pest, revisar calidad con SonarQube e integrar todo esto dentro de un flujo de CI/CD antes de desplegar a producción.

De hecho, este es prácticamente mi punto de partida ideal en un proyecto Laravel moderno.

En el curso Así arranco un proyecto Laravel en 2026 explico cómo configuro desde cero un proyecto con PHPStan, Rector, Pint, Pest Framework 4 y Laravel Boost.

Y en el curso Laravel 0 a producción: CI/CD multi-stage y multi-entorno llevo esa base un paso más allá, preparando un flujo real para automatizar validaciones, despliegues y entornos.

También he hablado de cómo añadir SonarQube a Laravel para analizar la calidad del código.

Pero hay una idea importante que conviene tener clara:

Que todas estas herramientas estén en verde no significa que tu aplicación no tenga deuda técnica.

Significa que tu código cumple una serie de reglas automatizables. Y eso está muy bien. Pero no todo lo importante en un proyecto se puede detectar con una regla.

Qué sí detectan estas herramientas

Herramientas como PHPStan, Rector, Pint o SonarQube nos ayudan muchísimo.

Pueden detectar errores de tipos, código muerto, problemas de estilo, complejidad excesiva, duplicación, posibles bugs, problemas básicos de seguridad o partes del código que se pueden modernizar.

También nos ayudan a mantener una base más consistente.

Un proyecto donde cada desarrollador formatea a su manera, donde no hay análisis estático, donde no se ejecutan tests y donde nadie mide nada, suele degradarse mucho más rápido.

Por eso creo que estas herramientas no son opcionales en proyectos serios.

El problema aparece cuando confundimos “el código pasa las herramientas” con “el código está bien diseñado”.

No es lo mismo.

La deuda técnica que no siempre se ve

Hay una deuda técnica más silenciosa.

No siempre rompe tests.
No siempre aparece en SonarQube.
No siempre hace fallar PHPStan.
No siempre puede arreglarla Rector.

Pero está ahí.

Y normalmente aparece en forma de malas decisiones de diseño.

Actions que hacen demasiado

A mí me gusta trabajar con Actions en Laravel.

Una acción debería representar una operación concreta del sistema. Por ejemplo:

CreateUserAction
ApproveDocumentAction

El problema aparece cuando una action empieza a crecer demasiado.

Por ejemplo, una CreateOrderAction que:

  • valida reglas de negocio

  • crea varios modelos

  • llama a una API externa

  • envía emails

  • lanza eventos

  • escribe logs

  • calcula permisos

  • decide el siguiente estado del pedido

Puede que PHPStan no se queje.
Puede que Pint la formatee perfectamente.
Puede que incluso tenga tests.

Pero esa action está acumulando demasiada responsabilidad.

La deuda técnica no está en que el código esté “mal escrito”.
Está en que esa clase se ha convertido en un punto donde todo acaba cayendo.

Jobs con lógica de negocio

Otro caso típico son los jobs.

Un job debería ejecutar una tarea en segundo plano. Pero muchas veces termina convirtiéndose en el caso de uso principal de la aplicación.

Por ejemplo:

ProcessPaymentJob

Y dentro encontramos validaciones, decisiones de negocio, cambios de estado, llamadas externas, notificaciones y persistencia.

El problema no es usar jobs.
El problema es esconder la lógica importante dentro de ellos.

Normalmente prefiero que el job delegue en una action o servicio de aplicación:

ProcessPaymentJob
    -> handle()
        -> ProcessPaymentAction

Así el job se encarga del contexto asíncrono, pero la lógica principal sigue estando en una pieza reutilizable, testeable y más fácil de razonar.

API Resources acoplados a demasiados contextos

Otro sitio donde se acumula deuda técnica en Laravel es en los API Resources.

Al principio solemos tener algo simple:

UserResource

Pero con el tiempo empiezan los condicionales:

<?php

'is_admin' => $request->user()->isAdmin(),

'mobile_data' => $this->when($request->from_mobile, [
    // ...
]),

'dashboard_info' => $this->when($request->for_dashboard, [
    // ...
]),

Y ese resource empieza a servir para todo:

  • panel de administración

  • SPA

  • app móvil

  • API pública

  • dashboard interno

El código puede funcionar.
Los tests pueden pasar.
PHPStan puede estar contento.

Pero el diseño empieza a ser frágil.

Cada cambio para una plataforma puede afectar a otra. Cada nuevo campo necesita más condicionales. Cada consumidor de la API empieza a depender de una estructura que ya no tiene un propósito claro.

Muchas veces es mejor separar recursos por contexto:

AdminUserResource
MobileUserResource
PublicUserResource

No por capricho, sino porque cada consumidor tiene necesidades distintas.

Policies inconsistentes

Tener policies en Laravel no significa automáticamente tener bien resuelta la autorización.

La deuda aparece cuando las reglas están repartidas por toda la aplicación:

  • algo en una policy

  • algo en un controller

  • algo en una action

  • algo en un middleware

  • algo duplicado en frontend

El problema no es técnico en el sentido estricto.
El problema es de criterio.

Cuando alguien quiere saber si un usuario puede hacer algo, ¿dónde mira?

Si la respuesta no está clara, hay deuda técnica.

Tests que prueban implementación, no comportamiento

También podemos tener Pest perfectamente configurado y aun así escribir malos tests.

Un test puede estar en verde y no aportar demasiada confianza.

Por ejemplo, cuando testeamos demasiado la implementación:

$this->mock(CreateInvoiceAction::class)
    ->shouldReceive('execute')
    ->once();

Esto puede tener sentido en algunos casos, pero si abusamos de este enfoque, acabamos probando que una clase llama a otra clase, no que el sistema se comporta correctamente.

Muchas veces es más útil comprobar el resultado real:

  • se ha creado la factura

  • el usuario correcto puede verla

  • se ha enviado la notificación

  • el estado ha cambiado

  • el evento esperado se ha producido

La deuda técnica no está solo en no tener tests.
También puede estar en tener tests que dificultan cambiar el código sin aportar demasiada seguridad.

La falsa tranquilidad del pipeline en verde

Un pipeline en verde es una buena señal.

Pero no es una garantía absoluta.

Significa que el proyecto ha pasado las reglas que hemos automatizado:

  • formato correcto

  • análisis estático correcto

  • tests correctos

  • calidad mínima aceptable

  • build correcto

  • despliegue correcto

Todo eso es muy importante.

Pero el pipeline no sabe si tus módulos están bien separados.
No sabe si tus actions tienen demasiadas responsabilidades.
No sabe si tu API está acoplada a tres clientes distintos.
No sabe si tus nombres expresan bien el dominio.
No sabe si una decisión que hoy parece cómoda dentro de seis meses será un problema.

Las herramientas ayudan, pero no sustituyen el criterio técnico.

Cómo reducir esta deuda técnica invisible

No hay una herramienta mágica para esto, pero sí hay hábitos que ayudan.

Revisar responsabilidades.
Una clase debería tener un motivo claro para existir.

Mantener pequeñas las actions.
Si una action empieza a hacer demasiadas cosas, probablemente necesita delegar.

Separar resources por contexto cuando tenga sentido.
No todos los consumidores necesitan la misma representación de los datos.

Evitar meter lógica de negocio importante en jobs, listeners o controllers.
Estas piezas deberían coordinar, no absorber todo el comportamiento del sistema.

Hacer code reviews arquitectónicos.
No revisar solo si el código está bien formateado, sino si la decisión tiene sentido.

Escribir tests de comportamiento.
Tests que validen qué hace el sistema, no solo cómo está implementado.

Y, sobre todo, aceptar que la calidad de un proyecto no depende únicamente de sus herramientas.

Depende también de las decisiones que tomamos cada día.

Conclusión

PHPStan, Rector, Pint, Pest, Laravel Boost, SonarQube y un buen flujo de CI/CD son una base excelente para cualquier proyecto Laravel moderno.

Yo los uso y los recomiendo.

Pero no eliminan toda la deuda técnica.

Eliminan una parte.
Hacen visible otra.
Y nos ayudan a prevenir muchos problemas.

Pero hay una deuda técnica que sigue dependiendo del diseño, la arquitectura y la experiencia del equipo.

Por eso, para mí, el objetivo no es solo tener el pipeline en verde.

El objetivo es tener una aplicación que siga siendo fácil de entender, modificar y mantener cuando el proyecto crece.

Porque un proyecto Laravel puede estar perfectamente formateado, pasar PHPStan, tener tests en verde y aun así estar mal diseñado.

Y esa es la deuda técnica más peligrosa: la que no aparece en rojo.

school Curso completo

Curso Laravel 12
Completo 2026

El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje