Saltar al contenido principal
Jordi Izquierdo Montalbán
Lo que practico

Fundamentos

Mantenimiento

Versiono con changelog y pago la deuda técnica antes de que crezca.

El oficio: mantener es la mayor parte de la vida del software

Un sistema útil se mantiene mucho más tiempo del que se construye. Las leyes de evolución del software de Meir Lehman lo resumen en dos ideas: un sistema que se usa debe cambiar continuamente o se vuelve cada vez menos útil, y su complejidad crece salvo que se haga un trabajo explícito para reducirla. Mantener bien consiste en administrar ese cambio con método: saber qué se toca, con qué riesgo, con qué red de seguridad y con qué rastro para quien venga detrás.

La norma de referencia es ISO/IEC/IEEE 14764 (proceso de mantenimiento del software), que forma parte del ciclo de vida descrito en ISO/IEC/IEEE 12207.

Tipos de mantenimiento

La norma ISO/IEC 14764 distingue cuatro, según qué lo provoca y qué busca.
TipoQué lo provocaEjemplosSeñal de que falta
CorrectivoUn fallo detectadoArreglar un cálculo erróneo, un cierre inesperadoLos mismos errores reaparecen
AdaptativoUn cambio en el entornoNueva versión del sistema operativo, de una API externa, de una normativaLas actualizaciones se descubren cuando algo deja de funcionar
PerfectivoUna mejora pedidaMejorar el rendimiento, ampliar una funciónEl producto no evoluciona con sus usuarios
PreventivoUn riesgo futuroRefactorizar un módulo frágil, mejorar pruebas, reducir dependenciasCada cambio se vuelve más lento y arriesgado

En la práctica, la parte perfectiva suele ser la mayor y la preventiva la primera que se sacrifica, con un precio que se paga después. Una cartera sana reserva capacidad explícita para cada tipo.

Del síntoma a la causa: gestión de incidencias

Ciclo de una petición

Desde que llega hasta que se cierra, con una decisión de prioridad en medio.

Triaje

El triaje decide qué ocurre con cada entrada. Primero se comprueba si es reproducible, si está duplicada y si es un error, una petición de mejora o un malentendido de uso.

ConceptoQué mideQuién lo decide
SeveridadGravedad técnica del efecto (pérdida de datos, caída total, defecto cosmético)Quien conoce el sistema
PrioridadCuándo se arregla, según urgencia y contexto de negocioQuien responde del producto

Pueden no coincidir: un error cosmético en la página de la campaña de mañana tiene severidad baja y prioridad alta.

Un buen informe de error

Título:       El total del pedido no incluye los gastos de envío al elegir recogida en tienda
Entorno:      Producción, versión 3.8.2, navegador de escritorio
Pasos:        1. Añadir cualquier producto al carrito.
              2. Elegir "Recogida en tienda" y volver a "Envío a domicilio".
Resultado:    El total no vuelve a incluir los 4,90 € de envío.
Esperado:     El total incluye el envío al volver a seleccionarlo.
Frecuencia:   Siempre que se alterna entre las dos opciones.
Impacto:      Se están cobrando pedidos por debajo de su precio.
Adjuntos:     Captura y registro de la petición.

Si el informe no permite reproducir el fallo, el primer paso es pedir lo que falta, no adivinar.

Corregir con método

PasoQué se hace
ReproducirConseguir que falle de forma controlada, en local si es posible
AislarReducir el caso hasta encontrar la causa, no el síntoma; técnicas como bisección del historial de cambios
ProbarEscribir el test que falla por ese motivo y que fallará de nuevo si el error vuelve
CorregirHacer el cambio mínimo que lo arregla
PrevenirPreguntar qué habría detectado el error antes: ¿una validación? ¿una prueba? ¿una alerta?

Para la causa raíz sirven los cinco porqués y el diagrama de causa y efecto. El objetivo es encontrar el fallo de proceso o de diseño que permitió el error, no al responsable.

Incidencias en producción

Cuando el servicio está afectado, el orden cambia: primero se recupera, después se investiga.

  1. Detectar (alerta, informe de usuarios).
  2. Evaluar el impacto y asignar una persona que coordine.
  3. Mitigar: volver a la versión anterior, desactivar la función con un interruptor o desviar tráfico.
  4. Comunicar el estado a los afectados con regularidad.
  5. Resolver la causa con calma.
  6. Revisión posterior sin culpables (postmortem): cronología, causa raíz, qué funcionó, qué no, y acciones con responsable y fecha.

Métricas habituales: tiempo medio de detección y tiempo medio de recuperación. Las incidencias se estudian como tendencia, no como anécdota.

Deuda técnica

La metáfora, acuñada por Ward Cunningham, compara una solución rápida con una deuda económica: permite avanzar hoy y cobra intereses en cada cambio posterior, porque el código es más difícil de entender y de tocar.

Martin Fowler la clasifica por si fue deliberada o no, y por si fue prudente o imprudente.

El cuadrante de Martin Fowler recuerda que no toda la deuda es mala ni toda es consecuencia de la prisa: puede ser una decisión prudente (se entrega pronto para aprender, con un plan de pago) o imprudente (se recorta por desconocimiento o desidia).

Origen y tratamiento

OrigenEjemploCómo tratarla
DeliberadaAtajo conocido para llegar a una fechaAnotarla con fecha y motivo, y planificar su pago
AccidentalNo se conocía una forma mejorCorregirla al descubrirla
Por envejecimientoLa plataforma evolucionó y el código noMantenimiento adaptativo continuo
De conocimientoSolo una persona entiende el móduloDocumentar, programar en pareja, rotar
De pruebasZonas sin cobertura que nadie se atreve a tocarAñadir pruebas antes de cambiar

Cómo priorizar el pago

No toda la deuda merece pago. Una guía: pagar donde más se cambia y más duele.

  • Zonas calientes (hotspots): cruzar la frecuencia de cambios de cada fichero (rotación o churn) con su complejidad. Lo muy cambiado y muy complejo es donde el interés es mayor. Es una técnica popularizada por Adam Tornhill.
  • Riesgo: lo que, si falla, causa más daño (seguridad, dinero, datos).
  • Coste del retraso: lo que impide una funcionalidad ya prevista.
  • Regla del boy scout: dejar cada fichero algo mejor de como se encontró, en cambios pequeños y seguros.

Hacerla visible

Lo que no se ve, no se prioriza. Se registra en un inventario de deuda con descripción, ubicación, impacto, estimación de esfuerzo y fecha. Se reserva una parte fija de la capacidad de cada iteración, en lugar de esperar a «un parón para limpiar» que nunca llega.

Trabajar con código heredado

Michael Feathers define el código heredado de forma pragmática: código sin pruebas. El problema no es la edad, sino que cambiarlo da miedo.

El método

  1. Identificar el punto de cambio.
  2. Encontrar puntos de apoyo (seams): lugares donde se puede alterar el comportamiento sin editar el código, como una interfaz o un parámetro, para poder probar.
  3. Escribir pruebas de caracterización: pruebas que no dicen cómo debería comportarse el código, sino cómo se comporta hoy. Fijan el comportamiento actual, incluso el discutible.
  4. Refactorizar con esa red de seguridad.
  5. Hacer el cambio funcional.

Estrategias de modernización

EstrategiaIdeaCuándoRiesgo
Mejora continuaRefactorizar poco a poco según se tocaCasi siempreLentitud percibida
Estrangulador (strangler fig)Construir lo nuevo alrededor de lo viejo y migrar rutas una a unaSistemas grandes con fronteras clarasConvivencia prolongada de dos sistemas
Rama por abstracciónIntroducir una interfaz y sustituir la implementación bajo ellaCambiar un componente internoEsfuerzo de abstraer
Ejecución en paraleloEjecutar viejo y nuevo y comparar resultadosCálculos críticosCoste y complejidad
Reescritura completaSustituir todo de una vezCasi nuncaSe reproducen errores ya resueltos y se pierde conocimiento implícito

El patrón estrangulador (Martin Fowler) es la alternativa a la reescritura: el sistema nuevo crece mientras el viejo se vacía, y en todo momento hay algo funcionando.

Versionado y publicación

Versionado semántico

SemVer 2.0.0 comunica con el número de versión el riesgo de actualizar: MAYOR.MENOR.PARCHE.

El número de versión comunica el riesgo de actualizar.
SubeCuándoEjemplo
PARCHECorrección compatible1.4.2 pasa a 1.4.3
MENORFuncionalidad nueva compatible1.4.3 pasa a 1.5.0
MAYORCambio incompatible1.5.0 pasa a 2.0.0

Matices: la versión 0.x indica que la API todavía no es estable; los prelanzamientos se marcan con sufijo (2.0.0-beta.1); y SemVer solo funciona si hay una API pública declarada y se respeta. Para aplicaciones sin API, se usan también versionados por calendario (año y mes) o por compilación.

Qué cuenta como cambio incompatible

Eliminar o renombrar algo público, cambiar el tipo de un parámetro, endurecer una validación, modificar el formato de una respuesta, cambiar un valor por defecto que alteraba el comportamiento, subir la versión mínima de una plataforma soportada.

Mensajes de commit y changelog

Con Conventional Commits el mensaje del commit lleva un prefijo (feat, fix, refactor, docs…) y una marca para los cambios incompatibles. Facilita automatizar el cálculo de la versión y el borrador del changelog.

fix(carrito): recalcular el envío al cambiar el método de entrega
 
feat(api)!: devolver las fechas en formato ISO 8601
 
BREAKING CHANGE: los campos de fecha ya no se devuelven como marca de tiempo numérica.

Sin embargo, el changelog definitivo se escribe para personas. Siguiendo Keep a Changelog:

## [3.8.3] - 2026-04-02
 
### Corregido
- El total del pedido vuelve a incluir los gastos de envío al alternar entre
  recogida en tienda y envío a domicilio.
 
### Seguridad
- Se actualiza la librería de análisis de ficheros por una vulnerabilidad
  de lectura fuera de rango.
 
## [3.8.0] - 2026-03-10
 
### Añadido
- Exportación de pedidos a hoja de cálculo.
 
### Obsoleto
- El parámetro `formato` del listado se retirará en la versión 4.0.

Reglas: la versión más reciente primero, fecha en formato ISO, categorías (Añadido, Cambiado, Obsoleto, Eliminado, Corregido, Seguridad), y una sección Sin publicar arriba. Un volcado del historial de commits no es un changelog.

Flujo de una versión

Cada versión deja rastro de qué cambió y por qué.

Además: una versión publicada es inmutable; si hay un error, se publica una nueva. Cada versión debe poder reproducirse a partir de su etiqueta.

Estrategias de ramas y soporte

ModeloRasgoEncaja con
Desarrollo sobre la rama principal (trunk-based)Cambios pequeños y frecuentes, interruptores de funcionesServicios web con despliegue continuo
Ramas de versiónCada versión mayor tiene su rama y recibe correccionesProductos que soportan varias versiones a la vez
Versiones de soporte a largo plazo (LTS)Algunas versiones reciben parches de seguridad durante añosLibrerías y plataformas con muchos usuarios

Una política de soporte escrita (qué versiones reciben correcciones y hasta cuándo) evita promesas implícitas imposibles de cumplir.

Retirar funcionalidades

Quitar algo también se diseña. Cada funcionalidad que se conserva cuesta código, pruebas y atención, pero retirarla sin cuidado rompe a quien dependía de ella.

Quitar algo sin romper a nadie exige avisar, ofrecer alternativa y dar plazo.
  • Aviso con plazo proporcional al impacto (semanas para algo interno, versiones para una API pública).
  • Aviso en tiempo de ejecución además de en la documentación: nadie lee las notas de versión.
  • Guía de migración con ejemplos de antes y después.
  • Medir el uso para saber si se puede retirar o si hace falta acompañar a alguien.
  • Eliminar en una versión mayor y dejarlo anotado.

Dependencias y entorno

Gran parte del mantenimiento adaptativo consiste en seguir el ritmo de lo que no se controla: lenguajes, librerías, sistemas operativos y servicios de terceros.

Cuanto más a menudo y en trozos más pequeños, menos duele.
PrácticaPara qué
Fichero de bloqueo (lockfile)Instalaciones reproducibles: mismas versiones en todas partes
Actualización automatizadaUna herramienta propone cambios pequeños y frecuentes
Pruebas automáticas fiablesSin ellas, cada actualización es un acto de fe
Inventario de componentes (SBOM)Saber qué hay dentro cuando se anuncia una vulnerabilidad
Calendario de fin de vidaConocer cuándo dejan de recibir parches el lenguaje, la plataforma o la base de datos
Minimizar dependenciasCada una es superficie de ataque y de mantenimiento

La regla práctica: actualizar a menudo y en trozos pequeños. Cada versión menor de diferencia añade incertidumbre; una migración de cinco versiones mayores es un proyecto, no una tarea.

Evaluar una dependencia antes de adoptarla

  • ¿Está mantenida (versiones recientes, incidencias atendidas)?
  • ¿Cuántas personas la mantienen? ¿Qué pasa si desaparecen?
  • ¿Cuál es su licencia y es compatible con la del proyecto?
  • ¿Cuánto código transitivo arrastra?
  • ¿Sería fácil sustituirla? Aislarla tras una interfaz propia reduce el coste de cambio.

Cambios seguros en producción

La mejor protección contra los errores en mantenimiento no es no cambiar, sino hacer que cambiar sea barato y reversible.

TécnicaQué aporta
Integración y entrega continuasCada cambio se compila y prueba automáticamente
Interruptores de funciones (feature flags)Se despliega el código sin activarlo; se activa por tramos y se apaga sin redesplegar
Despliegue gradual (canario)Se prueba con una parte pequeña del tráfico antes de generalizar
Azul-verdeDos entornos idénticos; se conmuta el tráfico y se vuelve atrás al instante
Migraciones compatibles hacia atrásPrimero se amplía el esquema, luego se cambia el código, al final se limpia (patrón expandir y contraer)
Marcha atrás ensayadaSi volver atrás nunca se ha probado, no existe

Migraciones de datos

Son el cambio más delicado porque los datos no admiten «deshacer» limpio. Principios: copia de seguridad verificada antes; migraciones idempotentes y repetibles; separar el cambio de esquema del cambio de código en despliegues distintos; ensayo con datos de tamaño real.

Medir la salud del mantenimiento

Lo que se mide orienta decisiones. Las cuatro métricas de DORA (investigación sobre rendimiento de la entrega de software) son una referencia reconocida:

MétricaMide
Frecuencia de despliegueCada cuánto se publica
Tiempo de entrega de cambiosDesde el commit hasta producción
Tasa de fallos de cambiosQué proporción de despliegues causa un problema
Tiempo de recuperaciónCuánto se tarda en restablecer el servicio tras un fallo

Otras señales útiles: tiempo medio de resolución por tipo de incidencia, antigüedad de las incidencias abiertas, cobertura de pruebas en zonas calientes, proporción de tiempo en trabajo no planificado, versiones de dependencias por detrás de la última y número de componentes sin propietario.

Conocimiento y continuidad

Un sistema que solo entiende una persona tiene una fecha de caducidad desconocida.

  • Propiedad compartida del código; nadie es dueño de un módulo en exclusiva.
  • Revisión de código con intercambio de conocimiento real.
  • Guardias rotativas con runbooks actualizados.
  • Mapa de servicios con propietario, dependencias y nivel de criticidad.
  • Registro de decisiones (ADR) que cuente por qué el sistema es como es.
  • Traspasos ensayados cuando cambia el equipo.

Cuándo parar: fin de vida

Todo software llega al final. Decidirlo con tiempo es mejor que sufrirlo.

SeñalInterpretación
El coste de mantenimiento supera el valor que aportaCandidato a retirada o sustitución
La plataforma base ya no recibe parches de seguridadRiesgo creciente: migrar o aislar
Nadie conoce el sistema y no hay forma de recuperar el conocimientoRiesgo operativo alto
Los usuarios han migrado a otra soluciónPlanificar la retirada con aviso

Una retirada ordenada incluye comunicación, exportación de datos para los usuarios, plazo de convivencia, cumplimiento de requisitos legales de conservación y borrado, y apagado verificado.

Lista de comprobación

  • Cada incidencia se registra con pasos para reproducirla.
  • Hay un criterio escrito de severidad y prioridad.
  • Todo error corregido deja una prueba que lo demuestra.
  • Existe capacidad reservada para deuda y mantenimiento preventivo.
  • Las versiones siguen SemVer y cada una tiene su entrada de changelog.
  • La política de soporte está escrita.
  • Las dependencias se actualizan de forma regular y hay calendario de fin de vida.
  • Volver atrás a la versión anterior está probado.
  • Hay runbooks para los fallos más probables.
  • Todo componente crítico tiene al menos dos personas que lo entienden.

Errores y antipatrones

  • Arreglar el síntoma. Poner una condición para que el caso concreto no falle y dejar la causa intacta.
  • Aplazar las actualizaciones hasta que se convierten en una migración de varias versiones mayores.
  • Reescribir desde cero por frustración. Casi siempre se subestima el conocimiento escondido en el sistema antiguo.
  • Cambios sin registro. Nadie sabe qué versión contiene qué ni por qué.
  • Changelog generado y sin leer, con ruido de commits internos.
  • Parches directos en producción que no vuelven a la rama principal: el siguiente despliegue los borra.
  • Héroes. Una persona que lo arregla todo por la noche no es una estrategia; es un riesgo.
  • Deuda sin dueño ni fecha: una lista de «mejoras pendientes» que solo crece.
  • Culpar a personas en las revisiones posteriores: asegura que la próxima vez se oculte el problema.
  • Eliminar sin avisar una función porque «nadie la usa» sin haber medido el uso.
  • Congelar el sistema por miedo a romperlo. Un sistema sin cambios acumula riesgo adaptativo.