Ir al contenido
SMART-AI

Modernizar un backend .NET heredado sin reescribirlo

·9 min de lectura

Un sistema .NET heredado que todavía sostiene el negocio no es candidato a una reescritura. Este es el orden de trabajo que lo vuelve seguro de cambiar: primero cobertura, luego comprobaciones, después fronteras y por último migración.

01La reescritura es la opción más cara

Un backend .NET grande que lleva diez años funcionando no está mal escrito. Está escrito para requisitos que nadie documentó, con plazos que nadie recuerda, por gente que ya no está. Cada rama rara que contiene es una incidencia de 2017 que alguien arregló a las dos de la mañana. Eso no es deuda técnica en sentido contable: es lógica de negocio sin documentar.

Por eso la reescritura falla con tanta fiabilidad. La propuesta siempre es la misma: congelar el sistema viejo, construir uno limpio al lado y cambiar. Lo que ocurre de verdad es que el viejo sigue cambiando porque el negocio sigue operando, el nuevo persigue un objetivo móvil, y dieciocho meses después hay dos sistemas que mantener en lugar de uno. La reescritura no falla por ingeniería: falla porque los requisitos solo existen dentro de aquello que se quiere sustituir.

02Cómo se ve la situación de verdad

Los síntomas son lo bastante constantes como para servir de diagnóstico. Se despliega de noche porque nadie se fía de hacerlo de día. Hay un desarrollador, a menudo externo, que es la única persona que entiende el módulo de pagos. Nadie refactoriza, porque el último que lo intentó dejó la facturación rota dos días. La batería de pruebas o no existe o lleva tanto tiempo en rojo que el rojo ya no significa nada.

Por debajo, el código suele ser .NET Framework y no .NET moderno, atado a Windows, IIS y un SQL Server con su propia década de procedimientos almacenados acumulados. La lista de dependencias contiene paquetes que dejaron de recibir actualizaciones hace años. Nada de eso es mortal. Todo se arregla, pero en un orden.

03Paso uno: hacer el sistema observable antes que mejorarlo

El primer trabajo en un sistema así no es código: es averiguar qué hace. Eso significa logging estructurado donde no lo hay, seguimiento de errores que llegue a alguien, y un mapa de qué endpoints y tareas se ejecutan realmente, porque en una base de código de diez años una parte sorprendente no se ejecuta.

El tráfico de producción responde preguntas que ninguna lectura del código responde. ¿Cuál de los cuatro formatos de exportación usa todavía alguien? ¿Ese proceso nocturno sigue disparándose? Los datos de uso suelen jubilar módulos enteros antes de refactorizar una sola línea, y cada módulo jubilado es uno que jamás habrá que migrar.

04Paso dos: cubrir lo que no puede romperse

La cobertura de pruebas sobre código heredado no es un ejercicio de calidad: es el permiso para cambiar cosas. Sin ella cada refactor es una apuesta; con ella, un refactor es el resultado de una build.

El error es intentar cubrirlo todo. El porcentaje de cobertura es el objetivo equivocado; el correcto es la lista de operaciones que el negocio no puede permitirse que fallen. Normalmente son menos de veinte flujos: autenticación, precios, checkout, facturación, lo que inspecte el regulador. Esos reciben pruebas de caracterización que fijan el comportamiento actual, errores incluidos, porque en esta fase el objetivo no es la corrección: es enterarse en el momento en que el comportamiento cambia.

  • Pruebas de caracterización primero sobre las rutas críticas, fijando lo que el sistema hace hoy y no lo que debería hacer.
  • Pruebas de integración antes que unitarias: el código heredado rara vez tiene costuras a las que llegue una prueba unitaria.
  • Un juego de datos de base reproducible y fijado: la mayoría de suites heredadas fallan por datos, no por lógica.
  • Cada error corregido a partir de aquí llega con la prueba que lo habría detectado.

05Paso tres: automatizar las comprobaciones y hacerlas obligatorias

Una suite que nadie ejecuta es decoración. Las pruebas entran en CI, la build se pone roja al fallar, y fusionar en rojo deja de ser posible. Ese único cambio hace más por un código heredado que cualquier refactor: convierte «creemos que esto sigue funcionando» en un hecho que se renueva en cada commit.

Junto a las pruebas: análisis de dependencias, para que los paquetes abandonados en silencio se vuelvan visibles; análisis estático con un presupuesto de avisos que solo puede bajar; y un despliegue que se ejecuta igual siempre. El objetivo es que publicar un martes por la tarde deje de ser un acontecimiento.

06Paso cuatro: trazar fronteras antes de mover nada

Solo ahora empieza la modernización propiamente dicha, y empieza con líneas y no con código. El patrón strangler funciona porque cambia una cosa cada vez: se pone una capa de enrutado delante del sistema viejo, se lleva una capacidad acotada detrás de un servicio nuevo y se apunta el router al nuevo. El código antiguo se queda hasta que nadie lo llama.

La elección de la primera capacidad decide si el programa sobrevive. Debe tener una frontera clara, poco acoplamiento y valor de negocio visible: notificaciones, informes, procesamiento de ficheros, búsqueda. Nunca empieces por el módulo al que todos temen. Empieza por el que demuestra que el mecanismo funciona, para que la segunda migración sea una discusión que ya nadie tenga que dar.

07Qué gana el negocio, y cuándo

La respuesta honesta es que los pasos uno a tres no compran ninguna funcionalidad nueva. Compran la capacidad de cambiar el sistema sin contener la respiración, y suelen llevar un par de meses en un sistema de tamaño real. Todo lo que viene después es más rápido que antes, y la diferencia se acumula.

El argumento comercial no es la modernidad. Es que el estado actual tiene un punto único de fallo que es una persona, que contratar para .NET Framework se complica cada año, y que una dependencia sin parchear en una ruta de pago es un riesgo de negocio y no una preferencia de ingeniería. Esas tres cosas son las que hacen que un consejo apruebe el trabajo.

08Cómo fue en la práctica

Un cliente llegó con exactamente esto: un backend .NET grande y antiguo con volumen serio, sin recursos internos para mantenerlo y sin ganas de la reescritura que le habían propuesto otras dos empresas. Colocamos a un ingeniero con el perfil adecuado dentro de su equipo, en lugar de llevarnos el sistema.

El orden fue el de arriba. Aprender el sistema, cubrir las rutas que no podían romperse, meter las comprobaciones en CI para que nada se degrade en silencio, limpiar lo que los datos de producción mostraron que nadie usaba. Solo entonces empezó la migración a .NET moderno, capacidad a capacidad, con el negocio funcionando todo el tiempo.

Es un trabajo lento y lo decimos por adelantado. También es la versión en la que el sistema sigue disponible todo el rato, que es la única versión que la mayoría de negocios puede permitirse de verdad.

Auditoría gratis en 48 h
Quedan 2 plazas esta semana