¿Cómo medir código muerto sin borrar reglas de negocio?
Aprende a medir código muerto con alcance estático, cobertura en ejecución, repetición de tráfico y pruebas de reglas raras de fin de año.

El código muerto no es todo lo que parece viejo, torpe o silencioso en producción. Es código sobre cuyo alcance, observación y propiedad de negocio puedes formular una afirmación concreta y respaldarla con pruebas. Son afirmaciones distintas. Mezclarlas bajo una sola etiqueta hace que una limpieza o reescritura elimine la única implementación de un ajuste anual, un contrato inactivo o una ruta de recuperación.
He visto equipos borrar rutinas porque nadie reconocía sus nombres y descubrir después que un planificador las invocaba con una cuenta de servicio el último día laborable del año. No tenían llamadas entrantes desde la aplicación ni ejecuciones en una muestra de tráfico normal, pero sí un llamante real. Un método útil debe superar ese caso.
Trata el presunto código muerto como un problema de pruebas. El análisis estático muestra qué puede llamarse. La cobertura en ejecución registra qué se llamó bajo condiciones definidas. Catálogos de trabajos, calendarios, configuración, valores de datos, procedimientos operativos y archivos de tráfico revelan entradas que ninguno ve por separado. Solo se borra cuando esos registros coinciden, un responsable acepta la conclusión y una prueba de paridad no detecta pérdida de comportamiento.
Código muerto significa tres cosas distintas
Un informe debe separar rutas imposibles, rutas no observadas y comportamiento obsoleto. Cada categoría justifica una acción distinta. Un único porcentaje oculta la incertidumbre que el revisor necesita conocer.
El código estáticamente inalcanzable no tiene una ruta posible desde los puntos de entrada declarados según el modelo del analizador. Puede ser una función privada sin referencias en un módulo cerrado o una rama cuya condición el sistema de tipos demuestra falsa. Es la señal técnica más fuerte, pero depende de la lista de entradas y del modelo de despacho.
El código no observado en ejecución no recibió impactos durante una carga y un periodo concretos. Eso no dice nada sobre otras fechas, clientes, roles, rangos de datos, fallos o acciones del operador. El tráfico web habitual suele omitir procesos por lotes, recuperación, pantallas administrativas, migraciones y la rama para un saldo negativo tras una reversión. Llámalo no observado, nunca inalcanzable.
El código funcionalmente obsoleto aún se ejecuta o puede alcanzarse, pero el negocio ya no necesita su resultado. Una regla fiscal para un país abandonado podría serlo. Un cálculo de un producto retirado quizá siga corrigiendo cuentas antiguas. Solo un responsable de negocio, con los hechos contractuales y de conservación, puede decidirlo. Un compilador no puede.
Usa un campo de estado, no un booleano:
- inalcanzable bajo el modelo M
- no observado en la carga W durante el periodo T
- conservado para el evento raro E
- obsoleto por la decisión D
- desconocido, requiere investigación
Este vocabulario evita una sustitución habitual. Los ingenieros parten de cero impactos, hablan de código muerto y aprueban un borrado como si hubieran probado la imposibilidad. Las palabras cambiaron, las pruebas no.
El alcance estático prueba imposibilidad, no desuso
El análisis estático demuestra mejor que el flujo normal no puede alcanzar un símbolo desde raíces conocidas. Falla donde un sistema heredado convierte nombres y datos en flujo de control. El resultado siempre debe registrar raíces, reglas de resolución y puntos ciegos.
Enumera todos los puntos de entrada legítimos, no solo el ejecutable principal. En mainframe incluye programas de transacción, pasos JCL, módulos llamados, salidas, disparadores y utilidades operativas. En AS/400, programas CL y descripciones de trabajos pueden invocar RPG sin ruta interactiva. Los sistemas de escritorio añaden entradas COM, macros e indicaciones por asociación de archivos. Los monolitos web añaden scripts planificados, consumidores de colas, hooks y rutas creadas desde configuración.
Construye un grafo de llamadas con confianza etiquetada en cada arista. Una llamada directa resuelta por el compilador vale más que una cadena parecida a un nombre de procedimiento. Mantén visibles las llamadas indirectas sin resolver. Punteros, reflexión, inyección de dependencias, enlace tardío, SQL generado, procedimientos almacenados y registros de plugins crean aristas que una búsqueda de texto no ve.
La eliminación del enlazador y del compilador responde una pregunta más estrecha. Sus manuales describen secciones o instrucciones sin efecto en una compilación concreta. Ayuda al tamaño binario, pero no prueba que la fuente pueda borrarse en otras opciones, módulos cargados, scripts o entradas operativas. Un binario optimizado no es el modelo completo de la aplicación.
Un artefacto pequeño y revisable es más útil que un grafo de diez mil nodos. Exporta por candidato símbolo, ubicación, raíces buscadas, llamadas directas, referencias indirectas posibles, variantes y versión del analizador. Si una arista depende de una cadena, clave, fila de base de datos o nombre de trabajo, guarda esa prueba.
El hallazgo solo es fuerte tras responder: ¿qué raíces se incluyeron?, ¿qué lenguajes y artefactos generados se analizaron?, ¿cómo se resolvieron llamadas indirectas?, ¿qué componentes están fuera del repositorio? Cualquier incógnita mantiene el resultado provisional.
La cobertura prueba ejecución, no seguridad
La cobertura puede demostrar que un código se ejecutó con una carga registrada, pero cero impactos no demuestran que sea innecesario. Es testigo de presencia, no prueba de ausencia.
GNU gcov informa cuántas veces se ejecutaron líneas y ramas de un programa instrumentado. Coverage.py distingue algo parecido en Python y registra destinos de ramas. Ambos manuales vinculan el resultado a las ejecuciones proporcionadas. La condición importa más que el porcentaje: una ejecución solo cubre sus entradas, entorno, fechas, identidades y fallos.
La cobertura de líneas pierde detalles. Una condición compuesta puede ejecutarse sin que cambie un operando. Un switch puede pasar y dejar un caso intacto. La cobertura de ramas mejora la prueba, pero no confirma que la salida o el efecto fuera correcto. Para borrar, reúne ramas o aristas cuando sea posible y compara salidas.
Instrumenta cada superficie hallada estáticamente. El tráfico interactivo rara vez basta. Incluye lotes, workers, tareas programadas, informes, rutinas de base de datos, recuperación y administración. Antes de fusionar, conserva versión, entrada, trabajo, cliente o unidad, rol, fecha y fuente. Un mapa combinado oculta que una línea solo corrió en el cierre anual.
La ventana debe seguir el calendario del negocio, no un número cómodo de días. Incluye procesos diarios, semanales, mensuales, cierres, renovaciones, vencimientos, cambio horario, día bisiesto e informes normativos reales. Si no puedes esperar, repite una carga grabada o recrea el evento de forma aislada. Tres días intensos no representan un ejercicio.
La instrumentación puede cambiar tiempos, memoria y fallos. Mide su coste cerca de carreras, timeouts y límites de lotes. Si es insegura, usa trazas muestreadas, contadores, auditoría de base o registros existentes. Una prueba más débil sirve si se describe con honestidad y se combina con otras.
El tiempo forma parte de la entrada
El código de calendario poco frecuente está activo y recibe una fecha, un corte o un estado acumulado. Los equipos lo pierden porque tratan el tiempo como contexto y no como entrada.
Piensa en una asignación de fin de año. La aplicación registra operaciones todo el año. El último día laborable, un planificador inicia un lote tras cerrar el libro mayor. JCL pasa un modo, una tabla elige cuentas diferidas y el programa emite ajustes en el periodo siguiente. Ninguna petición web llama la rutina. Nadie reconoce su nombre abreviado. Once meses de cobertura dan cero.
La reescritura la borra y sustituye el servicio contable. Las pruebas pasan porque usan marzo y junio. Al cierre, los totales aún cuadran y también pasan las comprobaciones básicas. El defecto surge cuando los extractos incumplen la regla contractual. La regla perdida era una combinación programada de fecha, control del trabajo, estado de tabla y periodo de salida.
Crea un inventario de eventos junto al grafo. Pregunta a finanzas, operaciones, soporte y cumplimiento, pero revisa también planificadores, JCL, cron, historial, manuales, tablas de control, calendarios, llegada de archivos y marcas de archivo. Busca comparaciones de fecha, periodos, festivos, modos especiales y años de corte.
Registra la última ejecución y la siguiente oportunidad prevista. Una rutina vista el cierre anterior tiene explicación. Otra sin impactos durante cuatro años puede cubrir correcciones de cinco. Un trabajo nocturno quizá entre en una rama solo cuando existe una fila de control. La frecuencia del llamante no es la de cada regla.
La sustitución del reloj merece una interfaz de prueba. Encauza el tiempo por una fuente controlable y congela de forma coherente la hora de base de datos y planificador. Si un componente lee la fecha simulada y otro el reloj real, el test crea estados imposibles.
Crea un registro de pruebas antes de borrar
Un registro convierte un debate vago en afirmaciones reproducibles. Debe acompañar la migración, revisarse como código y conservar las referencias de cada conclusión.
Una fila por símbolo o función coherente basta. Registra ID, símbolo, estado estático, raíces, aristas sin resolver, impactos, ventana, cargas, evento raro, llamantes externos, responsable, decisión y pruebas. No conviertas desconocido en cero. Cero es medido y ausente; desconocido es no medido.
Un almacén de cobertura produce candidatos con una consulta normal. Ajusta nombres, pero conserva dimensiones:
SELECT s.symbol_id, s.qualified_name,
COALESCE(SUM(c.hit_count), 0) AS hits,
MIN(c.observed_at) AS first_seen,
MAX(c.observed_at) AS last_seen,
COUNT(DISTINCT c.workload_id) AS workloads
FROM symbols s
LEFT JOIN coverage_events c ON c.symbol_id = s.symbol_id
WHERE s.release_id = :release_id
GROUP BY s.symbol_id, s.qualified_name
ORDER BY hits, s.qualified_name;
La salida debe ser sencilla: símbolo, impactos, primera y última observación y número de cargas. Únela a una tabla que marque tráfico, fin de mes, fin de año, recuperación, administración o repetición. Un cero basado solo en tráfico web no condena un símbolo de lote.
Añade esta tabla de decisión:
| Resultado estático | Resultado en ejecución | Prueba de negocio | Acción |
|---|---|---|---|
| Inalcanzable | Sin impactos | Sin entrada externa | Aislar y probar el borrado |
| Alcanzable | Sin impactos | Existe evento raro | Conservar y probar |
| Alcanzable | Con impactos | Responsable dice obsoleto | Confirmar llamadas y retirar |
| Desconocido | Sin impactos | Desconocida | Investigar, no borrar |
La primera fila aún exige compilación y prueba de comportamiento. Código generado, variantes y empaquetado pueden abrir el grafo. La tercera también requiere cuidado: los llamantes pueden depender de efectos secundarios. Retira junta la ruta y sus obligaciones de datos.
Ejecuta a propósito las rutas raras
Una ruta rara merece una carga dirigida, no la esperanza de que producción la cubra. Deriva pruebas del inventario y define salidas capaces de detectar la regla ausente.
El tráfico grabado conserva combinaciones que los datos sintéticos no anticipan. Limpia o tokeniza campos sensibles, conserva orden si importa y captura tablas, reloj y archivos. Una petición sin estado de base no suele ser reproducible. Para lotes, archiva entradas, parámetros, códigos, archivos, cambios y mensajes del operador.
Añade límites sintéticos: el día anterior, el evento y el día siguiente; entrada vacía; cuenta mínima; reversión; cuenta creada tarde; y repetición tras un proceso parcial. Prueban por separado selección, cálculo, persistencia y recuperación.
Compara límites observables: HTTP, escrituras, archivos, mensajes, códigos, redondeo, orden y reintentos. Normaliza identificadores variables, no valores de negocio. Si el sistema escribe ancho fijo, compara posiciones y relleno porque el consumidor puede depender de los bytes.
No persigas un porcentaje. Una suite alta puede omitir la rama del periodo fiscal 13. Asocia cada candidato con una carga o prueba por qué ninguna puede alcanzarlo. La unidad útil es un símbolo explicado.
Si no puedes ejecutar una ruta con seguridad, crea un test de caracterización debajo. Llama al cálculo con entradas capturadas, ejecuta el procedimiento en una base restaurada o invoca el lote con parámetros reales. Documenta con precisión la frontera sin probar.
El borrado debe ser un experimento controlado
Borra en unidades pequeñas y reversibles y deja que el sistema refute tu conclusión. El mejor test elimina el candidato, recompila variantes, repite cargas y compara todos los efectos con la base.
Si hay dependencias enredadas, aísla primero. Pon un adaptador único, elimina entradas duplicadas y añade contadores en la frontera. La estructura cambia sin variar el comportamiento y mejora el punto de medida. Si no hay llamadas en todos los eventos, el borrado gana fuerza.
Conserva cuatro artefactos: fila del registro, diff exacto, salidas base y salidas tras borrar. Ejecuta pruebas, objetivos alternativos, lotes, administración y tráfico. Comprueba mutaciones y archivos, no solo el éxito. Un código cero puede ocultar una operación contable ausente.
La comparación en sombra sirve para cálculos sin efectos. Ejecuta original y reemplazo con la misma entrada, suprime escrituras de un lado y compara. No dupliques cargos, avisos, reservas ni estado compartido sin un destino seguro.
El consejo de borrar todo lo que permanezca en cero un periodo fijo es popular porque da una métrica simple. Es erróneo con calendario, contratos dormidos, recuperación manual o despacho por datos. Define requisitos por clase: una validación web necesita roles y clientes representativos; un cierre necesita una carga fiel.
Mantén una reversión real. Un revert no basta si cambias datos, quitas una columna, dejas de emitir un archivo o alteras contratos. Retrasa esquemas destructivos y conserva compatibilidad hasta obtener pruebas aguas abajo.
Una reescritura conserva comportamiento antes que diseño
La reescritura debe clasificar y probar el comportamiento antes de decidir qué implementar. Traducir toda rutina alcanzable conserva estructura accidental; borrar toda sospechosa pierde reglas. Primero llega la paridad de obligaciones, luego el cambio arquitectónico.
Construye el arnés con observaciones de producción y eventos raros. Da a ambos sistemas entradas ordenadas y estado inicial idénticos. Compara respuestas, estado, archivos, mensajes y fallos. Si cambia la interfaz, compara una representación canónica de negocio en lugar de copiar módulos internos.
La diferencia entre paridad de código y de comportamiento es decisiva. Un servicio Go no necesita párrafos COBOL ni variables globales VB6. Sí necesita importes, decisiones, redondeo y recuperación iguales hasta aprobar un cambio. CodeHero usa tráfico de producción grabado en un arnés de paridad mientras moderniza la arquitectura, que es el nivel correcto de comparación.
Da una aserción negativa al comportamiento retirado. Si desaparece un informe, prueba que ningún trabajo lo planifica ni sale un archivo. Si cesa una regla, incluye un registro antes elegible y comprueba el tratamiento aprobado. La ausencia se prueba en una frontera definida.
No borres la procedencia. Enlaza cada regla implementada, cambiada u omitida con el registro. El revisor debe encontrar carga, responsable y efecto que explican una rama, y pruebas que explican una desaparición.
Define una regla de borrado exigible
Una política funciona si especifica prueba, autoridad y condición de parada. Escríbela para que el revisor rechace un cambio sin discutir si el código parece viejo.
Una puerta defendible puede exigir:
- No hay ruta entrante sin explicar desde el inventario completo, o todos los llamantes se retiran.
- La ejecución cubre cargas y eventos pertinentes y conserva datos brutos.
- Se comprobaron llamantes externos, configuración, planificador, despacho por datos y procedimientos.
- Un responsable aprobó la obsolescencia, o la prueba técnica demostró imposibilidad.
- El borrado pasó compilaciones, pruebas, repetición y comparación de efectos, con plan de reversión.
La puerta debe admitir desconocido. Algunas rutinas esperan restaurar un archivo o reconstruir un cierre. Marcarlas identifica la prueba que falta. Borrarlas para mejorar un porcentaje no es progreso.
Mide candidatos investigados, rutas imposibles, rutas raras probadas, comportamiento retirado con aprobación, incógnitas y borrados con paridad. No premies líneas eliminadas. Una regla anual de diez líneas puede tener más obligaciones que diez mil líneas de pantallas abandonadas.
Toma una rutina supuestamente muerta y escribe la afirmación exacta. Si solo dice que nadie la vio, mediste familiaridad. Añade raíces, cargas, eventos, responsables y efectos hasta que otro ingeniero reproduzca la conclusión. Entonces el borrado pertenece al proceso.
Preguntas frecuentes
¿Qué diferencia hay entre código muerto y no usado?
El código muerto tiene pruebas de que ninguna ejecución requerida lo alcanza. No usado suele significar que una herramienta o periodo no encontró uso, aunque existan llamantes o eventos raros.
¿El análisis estático puede probar que un código está muerto?
Puede probar inaccesibilidad dentro de un modelo definido. Reflexión, llamadas generadas, configuración, planificadores, disparadores y componentes externos limitan la prueba.
¿Cero cobertura hace segura una eliminación?
No. Solo indica que la función no corrió en las cargas y fechas medidas. También debes revisar alcance, eventos raros, llamantes externos y decisión de negocio.
¿Cuánto tiempo debe medirse la cobertura?
Sigue el calendario del negocio, no un plazo fijo. El periodo o repetición debe incluir tareas, cierres, renovaciones, vencimientos, recuperación y procesos anuales.
¿Cómo se encuentra código que solo corre al cierre anual?
Revisa historial, JCL, trabajos, procedimientos, tablas, archivos y condiciones de fecha. Recrea el cierre con parámetros y estado reales, y mide ramas y salidas.
¿El código eliminado por el compilador cuenta como fuente muerta?
No necesariamente. El compilador prueba qué puede omitir una compilación. Otras opciones, módulos, scripts y entradas pueden seguir necesitando la fuente.
¿Qué contiene un registro de pruebas?
Registra símbolo, entradas, aristas pendientes, cargas, fechas, eventos, referencias, responsable, decisión y pruebas. Mantén desconocido separado de cero.
¿Cómo se prueba un borrado de código heredado?
Captura la base, elimina un candidato pequeño, recompila variantes y repite cargas. Compara estado, archivos, mensajes, códigos y fallos, no solo respuestas.
¿Basta una cobertura alta para una reescritura?
No. Un total alto puede omitir una rama fiscal o de recuperación. Asocia conductas a cargas y compara efectos antiguos y nuevos.
¿Quién aprueba retirar una regla antigua?
Un responsable de negocio debe confirmar que la obligación terminó con pruebas técnicas. Los ingenieros pueden probar imposibilidad, pero no deducir cambios contractuales del silencio.