El código muerto son fragmentos de programa que nunca se ejecutan ni afectan el resultado, pero físicamente permanecen en los archivos fuente del proyecto. A diferencia de las secciones comentadas, el código muerto se compila y termina en el binario, aumentando su tamaño y complicando la navegación. Según el TIOBE Index (2025), el proyecto comercial promedio contiene entre un 10 y un 25 por ciento de código que nunca se llama. Código zombie es un subtipo de código muerto que funcionaba en el pasado pero perdió relevancia tras una refactorización y ahora solo ocupa espacio. La limpieza regular de estos fragmentos reduce la carga cognitiva de los desarrolladores y disminuye el riesgo de errores al realizar cambios.
Puntos clave
El código muerto (dead code) es código fuente que está incluido en el programa pero nunca se ejecuta en ningún escenario de uso. El compilador o intérprete lo procesa, pero en tiempo de ejecución el control nunca llega a estas secciones.
Ejemplos clásicos de código muerto: variables a las que se asigna un valor pero nunca se leen; funciones o métodos que nunca se llaman; ramas condicionales que nunca se vuelven verdaderas (if(false)); bucles cuyo cuerpo nunca se ejecuta.
Según el informe SonarQube State of Code Quality (2025), alrededor del 15 por ciento de todas las advertencias en proyectos Java comerciales están relacionadas con métodos y campos privados no utilizados. En proyectos JavaScript, la proporción de código no utilizado puede alcanzar el 30 por ciento debido a la naturaleza dinámica del lenguaje y la abundancia de bibliotecas de terceros.
Revise regularmente su proyecto en busca de código muerto, especialmente después de grandes refactorizaciones y eliminaciones de funcionalidades. Un import olvidado o una función no utilizada hoy puede convertirse mañana en código zombie que confunda a los nuevos miembros del equipo.
El código zombie (zombie code) es un caso especial de código muerto que se distingue por su contexto histórico. El código zombie alguna vez funcionó, pero después de cambios en el sistema se volvió inalcanzable, aunque no se eliminó y se dejó “por si acaso.”
La diferencia entre el código muerto y el zombie radica en su origen. El código muerto pudo haber sido escrito erróneamente (nunca funcionó), mientras que el código zombie es código antes vivo que perdió relevancia durante una refactorización. Por ejemplo, una función de cálculo de descuento basada en lógica de negocio antigua que fue reemplazada por una nueva, pero el método antiguo no se eliminó — por si acaso necesita restaurarse.
El principal peligro del código zombie es la ilusión de funcionalidad en funcionamiento. Un nuevo desarrollador ve una función, lee su documentación, asume que se llama en algún lado — y pierde tiempo estudiando un artefacto. Al intentar llamarla directamente, puede resultar que dependa de entidades eliminadas o API obsoletas.
Rastree el código zombie a través del historial de git: si una función no se ha modificado en dos años y no se utiliza — es un zombie. Elimínela sin dudar porque git guarda el historial, y el código siempre se puede restaurar si es necesario.
La primera y más común causa es el desarrollo iterativo con refactorización incompleta. El equipo agrega nueva funcionalidad que reemplaza a la anterior pero no elimina los módulos reemplazados. Los sprints acumulan estas “colas,” y después de un año el proyecto se cubre con una capa de código muerto.
La segunda causa son las pruebas A/B y los feature toggles. Las condiciones para activar una nueva funcionalidad pueden volverse fijas con el tiempo (por ejemplo, siempre true), pero la rama else con la lógica alternativa permanece en el código. Los desarrolladores temen eliminarla por si acaso rompen el sistema si el toggle se vuelve a activar.
La tercera causa es la generación de código y copy-paste. Los generadores de código (IDE, motores de plantillas) crean esqueletos con métodos que el desarrollador no completa ni utiliza. El código copiado de otro proyecto a menudo contiene bloques enteros irrelevantes para el nuevo contexto.
La cuarta causa es el miedo a eliminar. En proyectos grandes, los desarrolladores temen eliminar código porque no están seguros de que realmente no se use. Este miedo se ve agravado por un sistema de pruebas débil: sin verificaciones automatizadas, la eliminación puede generar errores que solo se descubren en producción.
El código muerto afecta directamente cuatro aspectos de la calidad del proyecto: rendimiento de compilación, tamaño del artefacto, carga cognitiva del equipo y fiabilidad de la refactorización.
Aumento del tiempo de compilación: el compilador procesa archivos no utilizados, analiza dependencias y genera bytecode o código máquina para fragmentos que nunca se ejecutarán. En proyectos grandes, esto añade minutos a cada compilación. Para lenguajes interpretados (JavaScript, Python), aumenta el tiempo de carga del módulo y el consumo de memoria.
Riesgo de errores durante la modificación: un desarrollador que modifica código no sospecha que una función solo se usa en una rama muerta. Después de la refactorización, el código muerto deja de compilar o genera errores — el equipo pierde tiempo diagnosticando un problema que no afecta a la aplicación.
La carga cognitiva es el factor más costoso. Cada función no utilizada requiere atención al leer el código. Un desarrollador gasta energía mental entendiendo por qué existe este código y dónde se llama. Un estudio de Developer Productivity Lab (2025) mostró que eliminar el 20 por ciento del código muerto reduce el tiempo de incorporación (onboarding time) en un promedio del 18 por ciento.
Elimine el código muerto inmediatamente al detectarlo. Cada día de demora aumenta la probabilidad de que alguien del equipo pase horas estudiando un artefacto que debería haberse eliminado ayer.
La detección de código muerto se realiza mediante dos métodos principales: análisis estático (sin ejecutar el programa) y análisis dinámico (perfilado de cobertura en tiempo de ejecución). Cada enfoque es efectivo para diferentes tipos de código muerto.
Los analizadores estáticos soportan todos los lenguajes de programación populares. Para Java y Kotlin — SonarQube, IntelliJ IDEA Inspections, SpotBugs. Para JavaScript y TypeScript — ESLint con las reglas no-unused-vars y no-unused-modules. Para Swift — SwiftLint con la regla unused_declaration. Para Python — pylint con la opción unused-import y vulture para búsqueda profunda.
// build.gradle.kts - Configuración de ProGuard para Android
android {
buildTypes {
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
// proguard-rules.pro - conservar solo las clases necesarias
-keep class com.example.app.** { *; }
-assumenosideeffects class Timber {
static <methods>;
}
ProGuard no solo elimina clases y métodos no utilizados, sino que también minimifica los nombres en las compilaciones release. Una compilación con ProGuard habilitado muestra automáticamente qué clases y métodos se consideran no utilizados — el informe usage.txt lista todo el código eliminado.
Las herramientas de cobertura de código (JaCoCo para Java, XCTest coverage para Swift, Istanbul para JavaScript) muestran qué líneas y ramas se ejecutan durante las pruebas. Los métodos con cobertura cero son candidatos a código muerto. Sin embargo, la falta de cobertura no garantiza que el código no se llame en producción — para total confianza, use una combinación de análisis estático y dinámico.
Configure su pipeline de CI para que la compilación falle cuando se supere el umbral de declaraciones no utilizadas. Un Quality Gate de SonarQube con la regla “Proporción de código privado no utilizado no mayor al 3%” previene la acumulación de código muerto a nivel de proceso de desarrollo.
El proceso de eliminación de código muerto consta de cuatro pasos: encontrar, verificar, eliminar, verificar nuevamente. Saltarse cualquier paso aumenta el riesgo de regresión.
El primer paso — encontrar candidatos mediante un analizador estático. Obtenga un informe de declaraciones no utilizadas: funciones, clases, variables, imports. Filtre los falsos positivos — los analizadores a veces se equivocan con reflexión, carga dinámica de clases o llamadas ocultas mediante serialización.
El segundo paso — verificar mediante git blame e historial de cambios. Revise cuándo y por qué se escribió el código. Si el código era parte de una funcionalidad deshabilitada por un feature toggle — asegúrese de que el toggle esté fijo y no se volverá a activar. Comente el código del que no esté seguro de eliminar y deje un TODO con un ticket para revisarlo en un mes.
El tercer paso — eliminar en una rama separada y ejecutar el conjunto completo de pruebas. Si las pruebas pasan — la probabilidad de regresión es baja. Si las pruebas fallan — el código aún se usa y debe averiguar en qué escenario.
// antes - código muerto y código zombie en el mismo archivo
int calculateV1(int price) { // no se llama en ninguna parte
int tax = price * 0.18;
return price + tax;
}
int calculateV2(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
// después - código muerto eliminado, código zombie limpiado
int calculatePrice(int price, double rate) {
return static_cast<int>(price * (1 + rate));
}
El cuarto paso — revisión de código de los cambios. El revisor debe confirmar que el código está realmente muerto. Si el revisor no está seguro — deje un comentario en el código y posponga la eliminación hasta un análisis completo. Después de fusionar la rama — elimine la rama para no proliferar código zombie en el repositorio git.
Adopte una regla: ningún pull request debe introducir nuevo código muerto. Agregue un linter en los hooks de pre-commit que bloquee los commits con variables o imports no utilizados. La prevención siempre es más barata que la limpieza.
Preguntas frecuentes
Sí, si el código muerto contiene errores de sintaxis o referencia tipos eliminados. Los compiladores modernos igualmente revisan las ramas muertas, por lo que un error en un bloque if(false) causará un fallo de compilación. Es una medida de seguridad: el código no debe estar tan muerto que el compilador no lo revise.
El código zombie es engañoso: un nuevo desarrollador ve una función con documentación y asume que se utiliza. Pierde tiempo estudiando código que no funciona y puede vincular accidentalmente nueva lógica a una entidad obsoleta, creando un error difícil de encontrar.
Use ESLint con las reglas no-unused-vars y no-unused-modules, así como la utilidad knip — analiza exports e imports en todo el proyecto, encontrando archivos, funciones y dependencias no utilizados. Para monorepos grandes, knip proporciona la imagen más completa.
Es mejor eliminar el código muerto antes del lanzamiento, pero no en el último momento. Eliminar código muerto es trabajo técnico que debe planificarse por separado en un sprint. Inmediatamente antes de un lanzamiento, la eliminación puede introducir inestabilidad si el código resultó no estar tan muerto como parecía.
Sí, los compiladores y minificadores modernos (ProGuard, R8, Terser, Closure Compiler) eliminan código inalcanzable a nivel de Dead Code Elimination. Sin embargo, esto no elimina la necesidad de limpiar los archivos fuente: el compilador elimina el código del binario pero no del repositorio — los desarrolladores siguen tropezando con él al leer.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también