2.19

Ver en inglés

2.19 Refactoring y deuda técnica

Visión general y motivación

El refactoring es modificar la estructura interna de un código sin alterar su comportamiento observable. Renombras una variable, divides una función larga, extraes una clase, reduces un enredo de condicionales a algo que un lector puede seguir, y el programa se comporta exactamente igual. Ese último matiz es la disciplina entera: el refactoring preserva el comportamiento por definición, y en el instante en que también alteras lo que el código hace, dejas de refactorizar; en ese momento estás haciendo dos cosas arriesgadas a la vez y escondiendo una detrás de la otra. Este capítulo las trata como actos deliberadamente separados, porque esa confusión es donde la mayoría de los refactorsings se desvían.

En un equipo grande esto importa mucho más que en desarrollo individual, porque el código que estás limpiando es código que cientos de personas leen, en el que dependen y al que le tienen miedo. El refactoring es lo que mantiene habitable una base de código compartida a lo largo de años y rotaciones de personal. Se conecta directamente con la construcción de software (capítulo 2.9), donde se establece la calidad del código día a día, y con la estrategia de pruebas (capítulo 2.4), que es la red de seguridad que hace posible refactorizar sin miedo. También se conecta con la cuestión más espinosa de la deuda técnica: el coste acumulado de atajos, diseños envejecidos y limpieza diferida que hacen que cada cambio futuro sea más lento. El refactoring es la vía principal para ir pagando esa deuda, por lo que ambos temas pertenecen al mismo capítulo.

En entornos de empresa y de gobierno, las apuestas suben. Estos sistemas son de vida larga, a menudo con décadas de antigüedad, y frecuentemente están sujetos a regímenes de auditoría y control de cambios que tratan cualquier modificación como un evento regulado. No puedes reescribir un sistema de prestaciones ciudadanas en un fin de semana largo; lo modernizas en pasos pequeños, reversibles y respaldados por evidencia, que es exactamente lo que el refactoring disciplinado te ofrece. Coordinar ese trabajo entre muchos equipos y sistemas de vida prolongada (capítulo 10.4) es uno de los desafíos definitorios de la ingeniería a gran escala, y hacerlo mal es como las organizaciones terminan paralizadas, incapaces de modificar software que ya no comprenden.

Principios clave

  • El refactoring preserva el comportamiento; si estás cambiando lo que el código hace, esa es una modificación aparte, que se hace aparte.
  • Un conjunto de pruebas fiable es la condición previa para refactorizar con seguridad, no un extra opcional.
  • Trabaja en pasos pequeños, con nombre y reversibles, y mantén el código funcionando tras cada uno.
  • Haz que la deuda técnica sea visible y trazable, y financia su amortización como capacidad constante, no como hazañas puntuales.
  • Refactoriza el código que ya estás tocando, donde la limpieza merece la inversión.
  • No toda la deuda merece pagarse; el código estable, poco alterado o a punto de retirarse puede dejarse en paz.
  • Mide la calidad interna para informar tu juicio, nunca como objetivo que pueda burlarse.

Recomendaciones

Mantén el refactoring y el cambio de comportamiento estrictamente separados

Decide antes de empezar cuál de las dos cosas estás haciendo y nunca las mezcles en un mismo compromiso. Cuando refactorizas, las pruebas que pasaban antes deben seguir pasando después, sin cambios, porque el comportamiento observable no se ha movido. Cuando cambias el comportamiento, hazlo en un compromiso propio, con sus propias pruebas. La razón es práctica: si un cambio mezclado rompe algo, no puedes saber si fue la reestructuración o el cambio de comportamiento quien introdujo el fallo, y en la revisión de código (capítulo 2.5) el revisor no puede razonar con claridad sobre ninguna de las dos mitades. El hábito que funciona es la regla de los dos sombreros de Martin Fowler: siempre llevas puesto el sombrero del refactoring o el sombrero de la funcionalidad, sabes cuál es, y lo cambias de forma deliberada. Separar los compromisos también hace que el historial de control de versiones sea legible, de modo que quien haga una bisect para localizar un fallo puede saltarse los compromisos puramente de refactoring con confianza.

Asegúrate de tener una red de seguridad fiable antes de reestructurar

Refactorizar sin pruebas es editar y rezar. Antes de reestructurar algo de enjundia necesitas una suite que confíes capaz de detectar un cambio de comportamiento si lo introduces, que es el argumento central de la estrategia de pruebas (capítulo 2.4). Si el código ya tiene buena cobertura, ejecuta las pruebas, refactoriza en pasos pequeños y vuelve a ejecutarlas tras cada uno. Si te enfrentas a código heredado sin pruebas, el movimiento honesto es escribir pruebas de caracterización primero. Una prueba de caracterización no afirma qué debería hacer el código; capta lo que el código hace en este momento, incluyendo sus peculiaridades, para que cualquier cambio de comportamiento aparezca como una prueba que falla. Michael Feathers popularizó este enfoque para la situación exacta en la que viven las grandes organizaciones: código que funciona, importa y no tiene pruebas. Una vez que el comportamiento actual está fijado, puedes refactorizar debajo con seguridad, y solo entonces cambiar el comportamiento por encima.

Aprende a reconocer los code smells y aplica refactorsings pequeños con nombre

Un code smell es una señal de superficie de que algo por debajo puede necesitar atención: una función que se ha hecho demasiado larga, una clase que sabe de más, lógica duplicada, una lista de parámetros excesiva, nombres que mienten sobre lo que hacen. Un code smell es una pista, no una sentencia, así que investigates en lugar de obedecerlo a ciegas. La respuesta es un refactoring pequeño y con nombre del catálogo de Fowler: Extraer Función, Renombrar Variable, Mover Método, Reemplazar Condicional por Polimorfismo, y decenas más. El valor de usar movimientos con nombre es que cada uno es pequeño, comprensible, mecánicamente seguro y a menudo soportado directamente por tu IDE. Compones mejoras grandes a partir de muchos pasos diminutos y fiables, manteniendo el código en verde durante todo el proceso, en lugar de dar un salto enorme que no puedes verificar.

Prefiere el refactoring oportunista y reserva las campañas para necesidades estructurales reales

La mayor parte del refactoring debe ser oportunista, integrado en el trabajo que ya estás haciendo. La regla del boy scout lo resume: deja el código un poco más limpio de como lo encontraste. Cuando tocas un archivo para añadir una funcionalidad o arreglar un fallo, ya entiendes ese rincón, y las pequeñas limpiezas ahí se acumulan con el tiempo sin necesitar permiso de nadie ni un presupuesto aparte. Las campañas de refactoring planificado, donde un equipo deja el trabajo de funcionalidad para reestructurar una zona grande, a veces son necesarias, pero son caras, difíciles de programar frente a la presión de producto y arriesgadas si esa zona tiene poca prueba. Resérvalas para problemas estructurales que la limpieza oportunista no alcanza, y haz el caso con evidencia del coste de cambio que estás pagando. Prefiere el goteo constante de pequeñas mejoras; es más duradero que la reescritura heroica ocasional.

Usa el patrón del higuero estrangulador para cambios estructurales de gran escala

Cuando un subsistema entero necesita reemplazo, no intentes una reescritura de golpe que corra un año y se integre al final; esa es la forma en que mueren los proyectos de modernización. Usa el patrón del higuero estrangulador, llamado así por Martin Fowler en alusión a la hiedra que crece enredada en un árbol y va reemplazándolo poco a poco. Colocas una fachada delante del sistema antiguo, enrutas una porción de funcionalidad a la vez hacia código nuevo detrás de esa fachada, lo verificas en producción y repites hasta que el sistema antiguo quede por completo rodeado y pueda retirarse. Cada porción es pequeña, desplegable y reversible, así que el riesgo se mantiene acotado y el valor llega de forma continua. Un pariente cercano, la rama por abstracción, hace lo mismo dentro de un único código base: introduces una capa de abstracción sobre lo que quieres reemplazar, construyes la nueva implementación detrás mientras ambas coexisten, migras a los consumidores gradualmente y eliminas la implementación antigua cuando nada ya dependa de ella. Ambos permiten que un sistema heredado evolucione mientras sigue vivo, que es la única forma de modernización que la mayoría de las grandes organizaciones pueden permitirse realmente.

Trata la deuda técnica como un portafolio y hazla visible

La metáfora de la deuda, acuñada por Ward Cunningham, separa dos cosas: el principal (el código desordenado o el atajo en sí) y el interés (el esfuerzo extra que cada cambio futuro paga por él). No toda la deuda es igual. El cuadrante de Fowler la clasifica en dos ejes: deliberada frente a inadvertida, y prudente frente a temeraria. La deuda prudente y deliberada («lanzamos ahora y limpiamos el próximo sprint, y sabemos el coste») es una decisión empresarial legítima. La deuda temeraria e inadvertida («¿qué es un patrón de diseño?») es solo daño. La labor de gestión, que se conecta con la toma de decisiones y la gobernanza (capítulo 1.5) y su tratamiento de la deuda como portafolio, consiste en hacerla visible para que pueda razonarse: registra los elementos significativos donde reside el trabajo, etiqueta el código y anota el interés que estás pagando, para que la amortización compita por capacidad con evidencia en lugar de con quien se queja más alto. La deuda que no puedes ver, no puedes gestionarla.

Financia la amortización como capacidad constante, no como hazañas

El modo de fallo es tratar la limpieza como algo que se hará «cuando las cosas se calmen», lo cual nunca ocurre. El patrón duradero es una capacidad fija y protegida para la amortización: una fracción explícita de cada ciclo, o un acuerdo estable de que la limpieza viaja junto con el trabajo de funcionalidad en la misma zona. Lo que no funciona es el sprint heroico periódico en que alguien se pasa un fin de semana arreglando todo, porque es insostenible, no se revisa y normalmente se deshace a sí mismo. La capacidad constante mantiene el interés bajo y evita el ciclo de auge y caída en que la deuda se acumula hasta que una crisis fuerza una reescritura cara. Esto es tanto un compromiso de gestión como una práctica de ingeniería, y pertenece a cómo planificas el mantenimiento del software (capítulo 3.7) a lo largo de la vida de un sistema.

Mide la calidad interna, pero no dejes que la medida se convierta en objetivo

Puedes medir la calidad interna con señales como la complejidad ciclomática (un recuento de caminos independientes a través de una función), la duplicación, la cobertura de pruebas, la tasa de fallo por cambio y cuánto tardan los cambios en las zonas que sospechas. Estos números son útiles para detectar dónde se concentra la deuda y para observar una tendencia en el tiempo. El peligro es la ley de Goodhart: cuando una medida se convierte en objetivo, deja de medir nada real. Exiges un número de cobertura y obtienes pruebas que no afirman nada; recompensas bajos índices de complejidad y obtienes lógica esparcida por más funciones para esquivar la métrica. Usa las métricas para iniciar conversaciones y localizar puntos calientes, y nunca conectes una métrica de calidad a una puerta que la gente tenga incentivos para burlar.

Saber cuándo no refactorizar

El refactoring es una inversión, y hay código que nunca la recuperará. Si un módulo es estable, se toca poco y se entiende lo suficiente para modificarlo en las raras ocasiones que hace falta, limpiarlo es esfuerzo gastado en un interés que no estabas pagando. Si el código está previsto para retirarse, refactorizarlo es pulir algo que estás a punto de tirar. La disciplina es gastar tu presupuesto de limpieza donde el cambio es frecuente y doloroso, que es donde reducir el interés realmente se multiplica, y dejar en paz los rincones tranquilos.

Trade-offs: ventajas e inconvenientes

EnfoqueVentajasInconvenientes
Refactoring oportunista (regla del boy scout)Barato, continuo, sin presupuesto aparte, se acumula con el tiempoCobertura desigual; los archivos calientes mejoran mientras los fríos se pudren
Campaña de refactoring planificadaCorrige problemas estructurales que la limpieza no alcanzaCaro; compite con las funcionalidades; arriesgado sin buenas pruebas
Higuero estrangulador / rama por abstracciónIncremental, reversible, mantiene el sistema vivo, acota el riesgoMás lento que una reescritura sobre el papel; exige disciplina para terminar
Reescritura de golpePizarra limpia; sin restricciones de legadoTasa de fallo elevada; largo tiempo hasta el valor; brechas de comportamiento
Deuda deliberada y prudenteLanza valor ahora; pago explícito y planificadoSe vuelve temeraria si el pago nunca se programa
Calidad sujeta a métricasObjetiva, visible, detecta el desfase prontoInvita a la burla; castiga el matiz; puede degradar la calidad real

La tensión central es velocidad ahora frente a modificabilidad después, y es real. Publicar un atajo puede ser la decisión correcta cuando el plazo es genuino y la deuda es prudente y trazable. El error es fingir que la deuda es gratuita, o dejar que se acumule de forma invisible hasta que el sistema sea demasiado caro de modificar. Resuélvelo haciendo que el trade-off sea explícito cada vez: nombra la deuda, estima el interés, decide deliberadamente y registra la decisión para que la amortización pueda programarse en lugar de olvidarse. Un equipo que pide prestado con conocimiento y paga de forma constante se mantiene rápido durante años; un equipo que pide prestado a ciegas termina atascado.

Preguntas para debatir con tu equipo

  1. ¿Cómo mantenemos el refactoring y el cambio de comportamiento separados en el mismo compromiso, y nuestras revisiones lo aplican de verdad? Esta es la disciplina fundacional de todo el capítulo y la que más se viola bajo presión de plazo, porque parece eficiente «aprovechar que estoy aquí y limpiar esto» y mandarlo todo junto. El coste llega después: cuando un compromiso mezclado rompe producción, nadie sabe si fue la reestructuración o la funcionalidad quien lo causó, y una bisect por tu historial deja de ser confiable. Trae un puñado de pull requests recientes y revisa con honestidad cuántos mezclan los dos sombreros. La consideración contraria es la fricción, ya que dividir el trabajo en compromisos separados cuesta un poco más al principio. La respuesta debería plasmar en tus convenciones de compromiso y en tu checklist de revisión.

  2. ¿Dónde está nuestra deuda técnica, cuánto interés estamos pagando y quién decide qué se amortiza? La mayoría de los equipos no pueden responder, y eso es el verdadero problema, porque la deuda que no se ve se gestiona por quien se queja más alto y no por donde está el coste real. Hacerla visible significa registrar elementos significativos, etiquetar el código y recopilar evidencia de qué zonas hacen que los cambios sean lentos y propensos a fallar. El tirón contrario es que cada hora dedicada a amortizar es una hora que no va a funcionalidades, así que la decisión tiene que ser una decisión de portafolio tomada con la dirección, conectada con cómo gobernáis el trabajo de ingeniería (capítulo 1.5). Trae tus datos de tasa de fallo por cambio y la lista de archivos que a todos les da miedo tocar. La respuesta debería convertirse en una capacidad de amortización protegida y constante, no en una intención difusa de limpiar cuando las cosas se calmen.

  3. ¿Qué partes de nuestro código base conviene dejar sin refactorizar deliberadamente, y cómo lo sabríamos? Refactorizar todo es tan defectuoso como no refactorizar nada, porque el esfuerzo en limpiar código estable, poco alterado o a punto de retirarse es pagar interés por un préstamo que no debías. La decisión es real: un módulo puede parecer feo y aun así ser el lugar equivocado para invertir si nadie lo modifica. Trae tus datos de frecuencia de cambio junto con tus señales de complejidad, porque la intersección de alta rotación y alta complejidad es donde la limpieza se multiplica, mientras que el código de baja rotación suele estar mejor en paz. El riesgo contrario es que «lo dejamos así» se convierta en excusa para no tocar nada difícil. La respuesta debería darte una lista corta explícita de puntos calientes que merecen inversión y permiso para ignorar los rincones tranquilos.

  4. ¿Confiamos de verdad en nuestra suite de pruebas lo suficiente para refactorizar el código que más necesitamos cambiar, y dónde tendríamos que escribir pruebas de caracterización primero? Una red de seguridad en la que no confieres convierte el refactoring en editar y rezar, y en un equipo grande el código más aterrador suele ser el peor probado, que es exactamente donde la limpieza más rentaría. Trae los datos de cobertura y tasa de fallo de tus puntos calientes, y sé honesto sobre qué módulos críticos no te darían ninguna advertencia si una reestructuración cambiara el comportamiento. La consideración contraria es que escribir pruebas de caracterización para código heredado es un trabajo lento y poco glamoroso que no lanza funcionalidad, así que es fácil posponerlo indefinidamente. En sistemas empresariales y gubernamentales bajo auditoría y control de cambios, esas pruebas que fijan el comportamiento son también la evidencia de que un cambio lo preservó, así que financiarlas es a la vez una medida de seguridad y de cumplimiento; la respuesta debería nombrar qué zonas reciben un arnés de pruebas antes de que nadie las toque.

  5. Cuando un subsistema necesita reemplazo de verdad, ¿cómo decidimos entre un enfoque incremental de higuero estrangulador y una reescritura, y quién tiene autoridad para decir que no a la reescritura? La reescritura de golpe es la opción más seductora y más propensa al fallo, porque una pizarra limpia siempre parece más barata sobre el papel que vivir con las restricciones del legado. Para una organización grande, la vía incremental (una fachada, una porción a la vez, verificada en producción) mantiene el sistema vivo y acota el riesgo, pero es más lenta, exige disciplina para terminar y compite con el apetito de un comienzo fresco. Trae el mapa de frecuencia de cambio del subsistema, una estimación honesta de cuánto correría una reescritura antes de entregar valor y las brechas de comportamiento que una reescritura en paralelo tendría que cerrar. En entornos de gobierno y regulados, una reescritura de varios años que se integra al final rara vez es sobrevivible bajo auditoría, así que la respuesta debería defaultear al higuero estrangulador o a la rama por abstracción y tratar cualquier reescritura como una excepción que debe justificarse con evidencia.

  6. ¿Cómo usamos las métricas de calidad interna para localizar dónde se concentra la deuda sin dejar que un número se convierta en objetivo que la gente se salte? Métricas como complejidad, duplicación, cobertura y tasa de fallo por cambio son la única forma en que una organización grande puede ver a través de código que nadie lee en su totalidad, pero en el instante en que una se conecta a una puerta o a una revisión de desempeño, la ley de Goodhart toma el relevo y el número deja de medir nada real. Trae ejemplos de dónde una métrica ya está dirigiendo el comportamiento y pregunta si está iniciando conversaciones o recompensando en silencio pruebas que no afirman nada y lógica esparcida por funciones para esquivar un umbral. El tirón contrario es que la dirección quiere un número de panel simple, y «usa tu criterio» es un argumento más difícil que una barra verde. En contextos empresariales y gubernamentales donde las métricas alimentan informes de gobernanza, sé explícito de que las señales de calidad informan la inversión y localizan puntos calientes, pero nunca juzgan a individuos; la respuesta debería trazar una línea firme entre medir para aprender y medir para juzgar.

Lente por sector

Startup. Con unos pocos ingenieros y sin margen de maniobra que sobrar, refactoriza solo de forma oportunista: un sombrero por compromiso para que el historial siga siendo navegable con bisect, y mantén una lista corta y honesta de los atajos que tomaste a propósito. No lances campañas de limpieza ni pulas módulos estables; gasta tu atención escasa en el archivo que a todos da miedo y escribe pruebas de caracterización solo donde un cambio te da un susto de verdad. La deuda deliberada y visible está bien a esta altura; lo que te mata es la deuda temeraria e invisible.

Pyme. Sin especialista de plataforma ni de herramientas y con un presupuesto ajustado, apóyate en lo que tu IDE y el ecosistema del lenguaje te dan gratis: movimientos automáticos de renombrar y extraer, un linter y una señal básica de cobertura. Trata la mayor parte de la deuda como algo que gestionas en el curso del trabajo normal, no como algo que requiere contratar un consultor, y prefiere comprar librerías bien mantenidas a construir y luego tener que refactorizar lo tuyo. Reserva el esfuerzo pagado excepcional para el único sistema cuya lentitud te está costando clientes directamente.

Empresa. Entre muchos equipos, el problema es la gobernanza de portafolio: un registro de deuda compartido, etiquetado consistente de puntos calientes por frecuencia de cambio y complejidad, y una fracción protegida de la capacidad de cada equipo para la amortización, para que la limpieza deje de perder por defecto frente a las funcionalidades. Estandariza la disciplina de los dos sombreros y la práctica de pruebas de caracterización para que cualquier ingeniero que se mueva entre equipos encuentre las mismas reglas, y usa el higuero estrangulador y la rama por abstracción para el cambio estructural coordinado entre grupos. Mantén las métricas de calidad informativas para que localicen deuda sin que se burle de ellas en las revisiones de desempeño.

Gobierno. Los sistemas de vida prolongada bajo auditoría estricta y control de cambios convierten el refactoring disciplinado en un activo de cumplimiento, no solo de ingeniería: mantener la reestructuración estrictamente separada del cambio de comportamiento permite que los auditores vean exactamente qué compromisos alteraron el comportamiento y cuáles solo ordenaron. Las reglas de contratación y transparencia favorecen los pasos pequeños, reversibles y respaldados por evidencia frente a las reescrituras de golpe, así que defaultea al higuero estrangulador con pruebas de caracterización que documenten que el comportamiento se preservó. Haz que el registro de deuda y su plan de amortización formen parte del registro de mantenimiento del sistema para que los órganos de supervisión obtengan la trazabilidad que exigen.

Ejemplos

Startup. Una startup de seis personas lanza rápido y sabe que acumula deuda, así que hace dos cosas baratas bien. Cada pull request lleva un solo sombrero: los compromisos de refactoring son separados de los de funcionalidad, lo que mantiene su historial navegable con bisect incluso a alta velocidad. Y guardan una lista corta y honesta de los atajos que tomaron a propósito, con una línea que anota el interés que cada uno cuesta. Cuando el módulo de pagos se convierte en el archivo que a todos da miedo, esa lista junto con su historial de fallos por cambio hace el caso para gastar dos días extrayendo una frontera más limpia. Escriben pruebas de caracterización para fijar el comportamiento actual, refactorizan por debajo con los movimientos de renombrar y extraer del IDE y no tocan los módulos estables que nadie modifica. La deuda que cargan es deliberada y visible, así que nunca se convierte en la variedad temeraria.

Empresa. Una compañía logística global opera un sistema de pedidos de quince años que muchos equipos modifican cada semana. En lugar de una reescritura, adoptan el patrón del higuero estrangulador: una fachada se sitúa delante del monolito, y una capacidad acotada a la vez se enruta a servicios nuevos detrás de ella, verificada en producción antes de que comience la siguiente porción. Coordinar esto entre equipos y un sistema de vida prolongada (capítulo 10.4) es la parte difícil, así que mantienen un registro de deuda compartido, etiquetan los puntos calientes por frecuencia de cambio y complejidad y reservan una fracción fija de la capacidad de cada equipo para la amortización. Las métricas de calidad interna informan dónde mirar, pero nunca condicionan la revisión de desempeño de nadie, lo que mantiene los números honestos. En dos años el monolito encoge de forma constante y ningún cambio individual arriesga jamás el sistema entero.

Gobierno. Una agencia tributaria nacional debe modernizar una plataforma de tasación de varias décadas bajo reglas estrictas de auditoría y control de cambios, donde cada modificación de código es un evento regulado y respaldado por evidencia. Una reescritura de golpe es imposible, así que usan la rama por abstracción: se introduce una capa de abstracción sobre el motor de cálculo heredado, se construye una nueva implementación detrás y se migran los consumidores una regla fiscal a la vez, cada migración documentada como un cambio pequeño, reversible y con pruebas de caracterización que prueban que el comportamiento no cambió. Como el refactoring se mantiene estrictamente separado de cualquier cambio de comportamiento legislativo, los auditores pueden ver exactamente qué compromisos alteraron el comportamiento y cuáles meramente reestructuraron. El registro de deuda y su plan de amortización se incorporan al registro de mantenimiento del sistema (capítulo 3.7), otorgando a los órganos de supervisión la trazabilidad que requieren.

Caso de negocio: motivaciones, ROI y coste total de propiedad

El retorno del refactoring y la amortización de deuda es la capacidad sostenida de modificar software a bajo coste, y para la mayoría de los sistemas la mayor parte del coste a lo largo de su vida es el mantenimiento, así que es donde se decide en gran medida el coste total de propiedad. El interés de la deuda técnica se paga en la moneda que la dirección ya rastrea: entregas más lentas, mayor tasa de fallo por cambio, más tiempo para recuperarse de incidentes e ingenieros que evitan el código más aterrador. Cuando haces la deuda visible y financias su amortización de forma constante, reduces el coste de cada cambio futuro en las zonas que más importan y evitas el patrón de auge y caída en que la deuda negligente fuerza una reescritura de emergencia y cara.

El coste de adoptar es modesto y en su mayoría cultural: establece la disciplina de los dos sombreros, construye la red de seguridad donde necesites refactorizar, mantén un registro de deuda y protege una fracción constante de capacidad para la amortización. El coste de la negligencia se multiplica en silencio. El interés se acumula con cada cambio hasta que la velocidad colapsa y la organización se encuentra paralizada, incapaz de modificar con seguridad un sistema que ya no comprende, que es el resultado más caro de todos. Para hacer el caso ante la dirección, conecta la deuda directamente con las métricas de entrega que a ellos les importan y enmarca la amortización como una decisión de portafolio con un retorno medible, no como ingenieros pidiendo tiempo para ordenar.

Antipatrón y trampas

  • Mezclar refactoring con cambio de comportamiento: un solo compromiso hace las dos cosas, de modo que un fallo no puede atribuirse y el historial pierde fiabilidad.
  • Refactorizar sin red de seguridad: reestructurar código sin pruebas y esperar, que es editar por fe.
  • La reescritura de golpe: reemplazar un sistema funcional de una sola vez, un patrón con alta tasa de fallo y largo tiempo hasta el valor.
  • El refactoring heroico de fin de semana: limpieza no revisada, insostenible, que se deshace a sí misma en lugar de capacidad constante.
  • La deuda invisible: atajos que nadie registra, de modo que la amortización la dirige el volumen de quejas y no el coste real.
  • Burlar las métricas de calidad: alcanzar un objetivo de cobertura o complejidad mientras la calidad real cae, porque la medida se convirtió en objetivo.
  • Refactorizar el código equivocado: pulir módulos estables o a punto de retirarse mientras los verdaderos puntos calientes te siguen costando.
  • El refactoring perpetuo: reestructuración interminable que nunca lanza valor, el espejo de no limpiar nunca.

Modelo de madurez

  • Nivel 1, Iniciar: El refactoring es ad hoc y reactivo, a menudo mezclado con el cambio de comportamiento en el mismo compromiso. No hay red de seguridad fiable, la deuda técnica es invisible y sin registro, y la limpieza ocurre solo en estallidos heroicos ocasionales o no ocurre.
  • Nivel 2, Desarrollar: Algunos equipos separan el refactoring del cambio de comportamiento y se apoyan en las pruebas donde existen; los refactorsings con nombre y las pruebas de caracterización aparecen en bolsas. La práctica es inconsistente entre equipos, la deuda se discute y a veces se registra, y la amortización compite de forma ad hoc con las funcionalidades y casi siempre pierde.
  • Nivel 3, Estandarizar: La disciplina de los dos sombreros, las pruebas de caracterización para código heredado y los refactorsings pequeños con nombre están documentados y son esperados en toda la organización. La deuda se registra en un registro compartido que separa principal de interés, y una capacidad protegida para la amortización se planifica cada ciclo y se aplica en la revisión.
  • Nivel 4, Gestionar: La deuda y la limpieza se miden y controlan con datos contra líneas base. Rastreas frecuencia de cambio y complejidad para localizar puntos calientes, vigilas la tasa de fallo por cambio y el tiempo de respuesta en las áreas refactorizadas, y registras el interés que cada elemento significativo cuesta, de modo que las decisiones de amortización se basan en evidencia y las decisiones de mantener o invertir se toman con tendencias, no con el volumen de quejas. Las señales de calidad informan la inversión sin conectarse a puertas que la gente puede burlar.
  • Nivel 5, Orquestar: La deuda se gestiona como un portafolio que se reequilibra de forma continua, integrado con la planificación de producto y mantenimiento de toda la organización. El cambio estructural usa de forma rutinaria el higuero estrangulador y la rama por abstracción coordinados entre equipos, la amortización es continua y se ajusta a donde el cambio es frecuente y doloroso, y la práctica se adapta a medida que el sistema y su perfil de riesgo cambian, de modo que el código de vida prolongada sigue siendo modificable durante décadas.

Ideas para la discusión

  1. ¿Cuál es la regla real y aplicable de tu equipo para mantener el refactoring separado del cambio de comportamiento, y dónde se rompe bajo presión de plazo?
  2. ¿Cómo decides, con evidencia, qué código merece limpieza y cuál está mejor dejándose en paz?
  3. ¿Dónde te permitirían las pruebas de caracterización refactorizar con seguridad una zona heredada que hoy evitas?
  4. Para tu próxima modernización importante, ¿cómo sería un enfoque de higuero estrangulador y qué fachada o abstracción introducirías primero?
  5. ¿Quién es responsable del registro de deuda técnica y cómo gana la amortización capacidad frente al trabajo de funcionalidad de verdad?

Ideas clave

  • El refactoring preserva el comportamiento; mantenlo estrictamente separado del cambio de comportamiento, en compromisos distintos.
  • Una suite de pruebas fiable es la condición previa para refactorizar con seguridad, y las pruebas de caracterización le dan una al código heredado.
  • Trabaja en pasos pequeños, con nombre y reversibles, prefiere la limpieza oportunista y usa el higuero estrangulador o la rama por abstracción para cambios estructurales de gran escala.
  • Haz que la deuda técnica sea visible, separa el principal del interés y financia la amortización como capacidad constante, no como hazañas.
  • Mide la calidad interna para guiar el juicio, nunca como objetivo que se pueda burlar, y no refactorices código que sea estable o esté previsto para su retiro.

Referencias y lectura adicional

  • Martin Fowler, Refactoring: Improving the Design of Existing Code, segunda edición
  • Michael Feathers, Working Effectively with Legacy Code
  • Ward Cunningham, The WyCash Portfolio Management System (informe de experiencia OOPSLA 1992, origen de la metáfora de la deuda)
  • Martin Fowler, “TechnicalDebtQuadrant” y “StranglerFigApplication” (martinfowler.com)
  • Kent Beck, Tidy First? A Personal Exercise in Empirical Software Design
  • Steve McConnell, Code Complete: A Practical Handbook of Software Construction
  • Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship