¿Cómo eliminas de manera segura un código que parece que nunca se ingresó?

125

Has encontrado un código que parece superfluo, y el compilador no lo nota. ¿Qué hacer para estar seguro (o lo más seguro posible) de que eliminar este código no causará regresión?

Dos ideas me vienen a la mente.

  1. "Simplemente" use la deducción en función de si el código parece o no debería ejecutarse. Sin embargo, a veces esto puede ser un trabajo complejo, que requiere mucho tiempo, un poco arriesgado (su evaluación es propensa a errores) sin un retorno comercial sustancial.

  2. Coloque el registro en esa sección de código y vea con qué frecuencia se ingresa en la práctica. Después de suficientes ejecuciones, debe tener la confianza razonable de que eliminar el código es seguro.

¿Hay alguna idea mejor o algo así como un enfoque canónico?

Brad Thomas
fuente
55
Puede ser útil mirar el historial de control de versiones: ¿Cuándo se creó el "código muerto"? ¿Parecía muerto cuando fue creado? ¿Se verificó otro código (que desde entonces ha sido modificado o eliminado) en el que lo usó, al mismo tiempo que se creó?
ChrisW
99
No se puede confiar en el compilador si hay alguna forma de metaprogramación como la reflexión en juego. Un grep adecuado del directorio base es una práctica esencial. También es común tener múltiples aplicaciones que comparten código, así que asegúrese de grep en la capa de directorio correcta.
Adam Caviness
16
Yo preguntaría: "¿Por qué molestarse en eliminarlo?". Si se usa en un caso de borde que se llama una vez en una luna azul, has roto cosas. Si nunca se usa y lo deja allí, el daño es que el archivo ejecutable es un poco más grande. A menos que esto sea para algún dispositivo integrado, ¿es un gran problema? Pegue un comentario al respecto y continúe usando su tiempo productivamente.
mickeyf
40
@mickeyf El daño es que alguien tiene que mantener ese código. Eso significa que un desarrollador tiene que entender lo que hace. El desarrollador tiene que modificarlo si las cosas cambian y el código que lo rodea ahora tiene que hacer algo diferente. Créeme, cuando dejas la migaja por ahí, es un gran dolor de cabeza para cualquiera que intente resolver las cosas más tarde.
jpmc26
99
@MichaelJ. Mi punto es que no debería haber estado allí en primer lugar. Y si lo dejo, la situación es peor: ahora algún otro desarrollador en el futuro también tiene que investigarlo. Cuanto más tiempo permanezca, más tiempo pasará entendiéndolo. Este es el costo del código superfluo.
jpmc26

Respuestas:

112

En mi mundo de fantasía perfecto, donde tengo un 100% de cobertura de prueba unitaria, simplemente lo eliminaría, ejecutaría mis pruebas unitarias y, cuando ninguna prueba se vuelva roja, lo comprometo.

Pero desafortunadamente tengo que levantarme todas las mañanas y enfrentar la dura realidad en la que gran cantidad de código no tiene pruebas unitarias o cuando están allí no se puede confiar para realmente cubrir todos los casos extremos posibles. Por lo tanto, consideraría el riesgo / recompensa y llegaría a la conclusión de que simplemente no vale la pena:

  • Recompensa: el código es un poco más fácil de mantener en el futuro.
  • Riesgo: rompa el código en un caso oscuro y oscuro en el que no estaba pensando, provoque un incidente cuando menos lo espere y tenga la culpa porque tuve que satisfacer la calidad del código OCD y hacer un cambio sin valor comercial perceptible a cualquier parte interesada que no sea codificadora.
Philipp
fuente
249
Habiendo observado el estado de los proyectos de software de gran tamaño en los que los ingenieros, durante diez a doce años, siguieron principalmente el enfoque de "no vale la pena" para eliminar el probable código superfluo, no puedo recomendar este enfoque.
njuffa
125
Además, las pruebas unitarias no le dicen nada sobre si este código se usa realmente en producción. El código podría probarse perfectamente, pero aún no se usa en producción, por lo tanto, superfluo.
nudo
8
En mi experiencia, el estado de tales proyectos nunca se debe a un enfoque de "no vale la pena" para arreglar el código después del hecho, siempre se debe a que se escribió un código incorrecto en primer lugar. Es un error escribir código incorrecto, pero también es un error (un error más avanzado) pensar que el código de limpieza siempre vale la pena.
Weyland Yutani
42
@Weyland Yutani Eso no coincide con mi experiencia en absoluto. La mayoría del código superfluo probable que he visto estaba lo suficientemente bien escrito y era perfectamente razonable dadas las especificaciones de software, plataformas de hardware o versiones del sistema operativo existentes una década antes, necesario para evitar errores o limitaciones en la cadena de herramientas o hardware disponible anteriormente, etc. .
njuffa
20
Answer pierde uno de los beneficios más críticos e importantes de seguir adelante y hacer el cambio: aprender algo. Después de haberlo aprendido, puede agregar estructura (comentarios, pruebas) para ayudar a los desarrolladores de mantenimiento más adelante. Elegir no cambiar algo porque no sabes las consecuencias es la programación de culto de carga, incluso si esa opción es no eliminar algo.
kojiro
84

Hay dos mitades en este proceso. El primero es confirmar que el código está muerto. El segundo es comprender los costos de equivocarse y asegurarse de que se mitiguen adecuadamente.

Muchas respuestas aquí tienen excelentes soluciones para la mitad anterior. Las herramientas como los analizadores estáticos son excelentes para identificar el código muerto. greppuede ser tu amigo en algunos casos. Un paso inusual que a menudo tomo es tratar de identificar cuál era el propósito original del código. Es mucho más fácil argumentar "X ya no es una característica de nuestro producto, y el segmento de código Y fue diseñado para admitir la característica X" que decir "No veo ningún propósito para el segmento de código Y".

La segunda mitad es un paso clave para romper cualquier estancamiento sobre si debe eliminar el código. Debe comprender cuáles son las implicaciones de obtener una respuesta incorrecta. Si la gente va a morir si la respuesta es incorrecta, ¡preste atención! Tal vez sea un buen momento para aceptar que el código cruft se desarrolla con el tiempo y, en cambio, trate de no escribir más cruft usted mismo. Si las personas no van a morir, pregúntese qué tan indulgentes son sus usuarios. ¿Puedes enviarles un hotfix si rompiste algo y mantienes tus relaciones con los clientes? ¿Tiene un equipo de preguntas y respuestas que paga para encontrar problemas como este? Este tipo de preguntas son esenciales para comprender qué tan seguro debe estar antes de presionar la tecla Eliminar.

En los comentarios, rmun señaló una excelente redacción del concepto de comprender el propósito original del código antes de eliminarlo. La cita ahora se conoce como Chesterton's Fence . Si bien es demasiado grande para ser citado directamente en un comentario, creo que merece ser citado correctamente aquí:

En cuanto a la reforma de las cosas, a diferencia de deformarlas, hay un principio claro y simple; un principio que probablemente se llamará una paradoja. Existe en tal caso una determinada institución o ley; digamos, en aras de la simplicidad, una valla o puerta erigida a través de una carretera. El tipo más moderno de reformador se acerca alegremente y dice: “No veo el uso de esto; eliminémoslo ". A lo que el reformador más inteligente hará bien en responder:" Si no ve el uso de él, ciertamente no dejaré que lo elimine. Vete y piensa. Entonces, cuando puedas volver y decirme que sí ves el uso, puedo permitirte que lo destruyas.

Cort Ammon
fuente
14
La valla de Chesterton supone que el descubrimiento se produce únicamente por el pensamiento, y no ofrece ningún recurso, una filosofía griega más bien antigua. El método científico es más moderno: diseñe un experimento controlado para determinar las consecuencias de eliminar la estructura. Sin duda, no propondría con indiferencia que este experimento se haga en producción. Pero si no hay alternativa, y el costo de la prueba en producción es muy alto, bueno, preferiría abandonar rápidamente un lugar de trabajo que no me ofreció medios seguros para saber por qué existe algún código: el riesgo personal en ese trabajo es demasiado alto .
kojiro
15
@kojiro Eso es muy cierto. Me gusta pensar que Chesterton estaría bien si yo, como un "reformador moderno" viniera y dijera "No sé por qué esta cerca está aquí, lo admito. Pero me gustaría pintarla de rojo para ver si eso me proporciona información nueva ", Chesterton probablemente estaría contento con eso.
Cort Ammon
41
El método de decodificación del reloj en modo kernel del sistema operativo VMS SYS $ ASCTIM contiene una línea de código que ayuda a corregir la deriva sideral del equinoccio vernal. Ninguna de las pruebas unitarias de DEC ejercita este código. Se ejecutó una vez, correctamente, el 28 de febrero de 2000 a las 23: 59: 59: 999. No volverá a ejecutarse hasta el año 2400. No lo elimine.
AI Breveleri
13
@AIBreveleri Espero que haya un comentario en el código explicando esto, y no simplemente aquí en StackExchange: D
Kyle Strand
11
@KyleStrand ¿De qué estás hablando? Esta es la documentación. ¿Se te ocurre un lugar mejor para poner información crucial para que otras personas la encuentren que StackExchange? ;-)
Cort Ammon
43

También tiendo a greputilizar el nombre de la función / clase en el código, lo que puede dar algunos beneficios adicionales que un analizador de código podría no tener, como si el nombre se menciona en un comentario o en un archivo de documentación, o un script, por ejemplo. Ejecuto grep en los archivos en el árbol de origen y almaceno el resultado en un archivo; por lo general, el resultado proporciona información condensada: nombre / ruta del archivo, número de línea y la línea donde se encuentra el nombre, lo que puede dar pistas sobre dónde se llama o menciona la función / clase sin ningún significado semántico (en contraste con un analizador de código ), e independientemente de las extensiones de archivo. Definitivamente no es la solución definitiva, sino una buena adición a un análisis.

piwi
fuente
11
No soy un votante, pero ¿qué pasa si la sección de código que sospechas que nunca se ejecuta no es una función o clase completa sino una sección de un método / función?
Tulains Córdova
99
Dependiendo del idioma, esto puede no ser siempre confiable; algunos idiomas permiten que las llamadas a métodos se realicen dinámicamente. Por ejemplo php:$object->$variableMethod($arg1, $arg2);
Dezza
99
@ TulainsCórdova Entonces, probablemente esta no sea una buena solución, obviamente. En cuanto a cada posible solución, úsela si tiene sentido en el contexto dado. Pero tal vez grepel eg. La función que abarca la sección de código puede dar pistas sobre cómo se utiliza la función y, por lo tanto, su contenido.
Piwi
77
"como si el nombre se menciona en un comentario o en un archivo de documentación, o un script" - O si alguien está usando la reflexión para llamar al método (si está usando un lenguaje de alto nivel / OO). Aunque todavía no es necesariamente 100% preciso, ya que algunos lenguajes admiten algunas formas muy obtusas de encontrar e invocar métodos.
Aroth
Esto es útil si tiene acceso a todos los códigos posibles que podrían usar la función. Sin embargo, si el código es parte de una biblioteca, ¿cómo va a saber que algo más que no puede buscar no se romperá?
Kevin Shea
30
  • Identifique el código que parece muerto (análisis estático, etc.).
  • Agregue un mensaje de registro para cada invocación del código presuntamente muerto. Es fácil con funciones / métodos; con miembros estáticos como constantes es más complicado. A veces, puede marcar el código como obsoleto y el tiempo de ejecución registrará un mensaje automáticamente. Deje el código de otra manera intacto.
    • Registre un mensaje cuando se cargue el módulo muerto: la mayoría de los idiomas tienen una forma de ejecutar la inicialización estática en el momento de la carga, que puede ser respaldada.
    • Asegúrese de que su mensaje de registro incluya un seguimiento de pila razonable, para que entienda lo que se llama el código supuestamente muerto.
  • Ejecute el código alterado en todas sus suites de prueba. Sus suites de prueba también deben evaluar momentos especiales, como cruzar una medianoche, un cuarto de vuelta, un año, etc. Mire los registros, actualice su comprensión de lo que está muerto. Tenga en cuenta que las pruebas unitarias pueden probar específicamente el código muerto, mientras que ninguna otra prueba unitaria y ninguna prueba de integración lo tocan.
  • Ejecute el código alterado en producción durante algunas semanas. Asegúrese de que todos los procesos periódicos, como los trabajos cron ETL de una vez al mes, se ejecuten durante este período.
  • Mira los registros. Todo lo que se registró no está realmente muerto. El cierre transitivo del gráfico de llamadas sobre el resto del código muerto también es potencialmente no muerto, incluso si no se invocó. Analízalo. Tal vez algunas sucursales estén muertas de forma segura (por ejemplo, trabajando con una API de un servicio ahora extinto), mientras que otras aún no lo están. Tal vez un módulo / clase completo solo se carga para una constante definida en él, y usted puede deshabilitarlo fácilmente.
  • Lo que queda está muerto de forma segura y se puede quitar.
9000
fuente
Hay una primera vez para todo, así que solo porque el código no se ejecutó antes no significa que nunca se ejecutará, especialmente si hay una ruta de ejecución. Dicho esto, decidir eliminar algo que puede ejecutarse es un proceso diferente de determinar si podría ejecutarse.
77
@nocomprende exactamente lo que sucede si este código solo se usa para una función de fin de año y ejecuta su prueba se ejecuta de febrero a noviembre. No encontrará el uso aunque lo haya perfilado durante casi todo un año.
Erik
1
@ 9000 Estoy de acuerdo en teoría, pero los sistemas heredados están plagados de dependencias ocultas que pueden frustrar este tipo de pruebas. Para que su ejemplo de prueba funcione, necesita saber que hay algún tipo de acoplamiento temporal. Si el acoplamiento temporal es externo al código que está probando, entonces no lo verá en el código y sabrá que el acoplamiento temporal es un factor. Por ejemplo, el código del silo A está instalado en el GAC . Hace años, el silo B le pidió al silo A que agregara una ruta de código para un informe que necesitan ejecutar contra los datos del silo A. cont.
Erik
1
@ 9000 cont. Ahora, el código del silo B tiene un acoplamiento temporal con una ruta de código "muerta" en el código del silo A que no es aparente simplemente mirando el código del silo A. ¿Cómo probaría la unidad para este acoplamiento temporal ya que el acoplamiento temporal está solo en el código del silo B sin saber hablar con el silo B? Incluso entonces, el tiempo no es un factor en su código (silo A), entonces, ¿cómo sería una prueba temporal?
Erik
2
¿Alguien recuerda el pánico de Y2k ? ¡Algún código era correcto en 2000 porque era incorrecto para 2100! Tuvimos un problema potencial que podría ocurrir una vez cada 100 años o incluso una vez cada 400 años.
Steve Barnes
16

Además de las respuestas existentes mencionadas, también puede eliminar iterativamente el código en varias versiones.

En la versión inicial, podría hacer una advertencia de desaprobación con el bloque de código aún funcionando. En la versión posterior, puede eliminar el bloque de código pero dejar un mensaje de error que permite a los usuarios que la función esté en desuso y ya no esté disponible. En la versión final, eliminaría el bloque de código y cualquier mensaje.

Esto puede ayudar a identificar funciones imprevistas sin avisar a los usuarios finales. En el mejor de los casos, el código realmente no hace nada y todo lo que sucede es que el código innecesario se mantiene en varias versiones antes de ser eliminado.

usuario3196468
fuente
77
+1. He visto tantos errores y problemas relacionados con el cliente que podrían haberse evitado si los desarrolladores (o su administración) hubieran estado dispuestos a dividir un proyecto en dos o tres fases seguras en lugar de tratar de incluir todo de una vez para cumplir con un plazo arbitrario antes de pasar al siguiente proyecto.
ruakh
Esto responde a la pregunta formulada en el título, pero si me preguntas, la pregunta tiene el título incorrecto. El título pregunta cómo eliminar el código, pero el cuerpo trata sobre cómo identificar si es seguro eliminar el código en primer lugar. Definitivamente respondes lo primero, pero no estoy seguro de que eso es lo que realmente preguntaba el OP.
underscore_d
7

Mucha gente sugiere que lo "seguro" es dejar el código si no puede probar que no se utiliza.

Pero el código no es un activo, es un pasivo.

A menos que pueda explicar por qué es importante y señalar sus pruebas, la alternativa "más segura" podría ser eliminarlo.

Si todavía no está seguro, al menos asegúrese de agregar pruebas para ejercer el código zombie.

Adrian McCarthy
fuente
Paso 1: elimine todo el código. Paso 2: devuelva lo que se necesita. Paso 3: repite.
1
@ Adrian ¿Puedo llamar su atención sobre Knight Capital? en.wikipedia.org/wiki/… - en caso de que quiera reforzar su punto con una referencia a él. Básicamente implosionaron porque algunos códigos antiguos volvieron a la vida y amablemente perdieron casi $ 500 millones por ellos. La investigación subsiguiente se centró en sus procedimientos de lanzamiento, pero el problema central era "no se eliminó la funcionalidad muerta".
SusanW
6

Puede usar las funciones alternar para cambiar la ruta de ejecución de su software para ignorar por completo el código en cuestión.

De esta manera, puede implementar de manera segura su cambio con el código que no está en uso y desactivarlo. Si observa algunas fallas importantes relacionadas con el código, vuelva a activar el interruptor e investigue la posible ruta hacia él.

Este enfoque debería brindarle confianza si no ve problemas durante un período prolongado de tiempo, así como la capacidad de volverlo a activar sin implementación. Sin embargo, una forma aún mejor sería aplicar también un registro adicional y una cobertura de prueba alrededor del área en cuestión, lo que proporcionará más evidencia de si se usa o no.

Lea más sobre alternar aquí: https://martinfowler.com/articles/feature-toggles.html

Faja
fuente
6

Análisis estático, por supuesto ... y lo bueno es que no necesita ninguna herramienta nueva; Su compilador tiene todas las herramientas de análisis estático que necesita.

Simplemente cambie el nombre del método (por ejemplo, cambie DoSomethinga DoSomethingX) y luego ejecute una compilación.

Si su compilación tiene éxito, el método, obviamente, no está siendo utilizado por nada. Seguro para eliminar.

Si su compilación falla, aísle la línea de código que la llama y verifique qué ifdeclaraciones rodean la llamada para determinar cómo activarla. Si no puede encontrar un posible caso de uso de datos que lo active, puede eliminarlo de forma segura.

Si está realmente preocupado por eliminarlo, considere conservar el código pero márquelo con un ObsoleteAttribute (o el equivalente en su idioma). Libérelo así para una versión, luego elimine el código después de que no se hayan encontrado problemas en la naturaleza.

John Wu
fuente
8
Este es un buen consejo para los lenguajes de programación que no tienen un enlace dinámico / tardío completo . Sin embargo, para aquellos que tienen, es más complicado. Y, por supuesto, el código podría estar usando el método incluso si nunca se usa en producción.
EIS
La pregunta presupone una resolución de símbolo en tiempo de compilación ( Entonces, ha encontrado un código que parece superfluo. El compilador no lo nota ). E incluso la función u objetos vinculados tarde se definen típicamente en dos lugares, por ejemplo, una interfaz o un archivo de mapa, por lo que la compilación aún fallará.
John Wu
1
Hmm ... estoy intrigado, no creo que lo último que digas sea verdad. No es para JavaScript de vainilla (no precompilado), ruby, python o smalltalk, por ejemplo, ¿verdad? Si se refiere a cosas que no existen, esos errores solo en tiempo de ejecución.
EIS
Tal vez no estemos de acuerdo con lo que significa "vincular", lo que para mí indica la resolución de una representación lógica a una representación física, por ejemplo, un símbolo a una dirección. Las funciones de Javascript se almacenan como pares de etiqueta / valor dentro del objeto que lo contiene, por lo que cualquier noción de enlace parece no segura.
John Wu
1
Sí, realmente no estamos de acuerdo. El término tiene un significado diferente en los idiomas que no tienen un enlace tardío completo (como expliqué en el primer comentario). Cuando vincula métodos en tiempo de ejecución, en lenguajes que pueden agregar y eliminar métodos de tiempo de ejecución, sobre la marcha, está utilizando el enlace tardío de la forma en que entiendo que se haga. Esto es cierto en los lenguajes de estilo ruby ​​/ python / smalltalk, y allí no tiene archivos de mapas separados. Dado que especialmente Ruby y Python son muy utilizados, no estoy de acuerdo con su idea de lo que significaría "típicamente".
EIS
4
  • Comenzaría a usar un analizador de código para verificar si este es un código muerto
  • Comenzaría a revisar mis pruebas e intentar alcanzar el código. Si no puedo alcanzar el código dentro de un caso de prueba, podría ser un código muerto
  • Quitaría el código y lanzaría una excepción en su lugar. Para una versión, la excepción se activa, y en la segunda versión, la excepción se puede eliminar. Para estar seguro, coloque una bandera de emergencia (en Java, una propiedad del sistema) para poder activar el código original, cuando un cliente note la excepción. Por lo tanto, la excepción se puede desactivar y el código original se puede activar en un entorno de producción.
Markus Lausberg
fuente
66
-1. No es absolutamente recomendable hacer estos cambios en el código productivo, bandera de seguridad o no. Solo cuando uno esté seguro de que el código en cuestión no se usa, debe eliminarse en código productivo. Ese tipo de desorden es exactamente la razón por la que siempre debe haber diferentes entornos productivos y de prueba.
Johannes Hahn
3
Esto funcionará en la fantasía cuando la cobertura de prueba sea de casi el 90%, pero qué pasa si no y tiene un proyecto con 100.000 líneas de código. Es por eso que describí intentar probar el código y si ninguna prueba puede alcanzar el código ... podría estar muerto.
Markus Lausberg
En mi humilde opinión, si no está lo suficientemente seguro como para evitar eliminar el código por completo, no debería lanzar excepciones en la producción, el registro sugerido por el OP es una alternativa mucho mejor, logra lo mismo y puede obtener diagnósticos en lugar de confiar en que los clientes se quejen
Sebi
@JohannesHahn Creo que te refieres a "código de producción", que es un código que se ejecuta en un entorno de producción.
jpmc26
@ jpmc26 Sí, por supuesto.
Johannes Hahn
3

Eliminar código inalcanzable

En un lenguaje con principios de tipo estático, siempre debe saber si el código es realmente accesible o no: eliminarlo, compilar, si no hay error, no fue accesible.

Desafortunadamente, no todos los idiomas están estáticamente tipados, y no todos los lenguajes tipados estáticamente tienen principios. Las cosas que podrían salir mal incluyen (1) reflexión y (2) sobrecarga sin principios.

Si utiliza un lenguaje dinámico, o un lenguaje con una reflexión lo suficientemente potente como para que se pueda acceder al código bajo escrutinio en tiempo de ejecución a través de la reflexión, no puede confiar en el compilador. Tales lenguajes incluyen Python, Ruby o Java.

Si utiliza un lenguaje con sobrecarga sin principios, simplemente eliminar una sobrecarga podría simplemente cambiar la resolución de sobrecarga a otra sobrecarga en silencio. Algunos de estos lenguajes le permiten programar una advertencia / error en tiempo de compilación asociado con el uso del código; de lo contrario, no puede confiar en el compilador. Dichos lenguajes incluyen Java (uso @Deprecated) o C ++ (uso [[deprecated]]o = delete).

Entonces, a menos que sea muy afortunado de trabajar con lenguajes estrictos (viene a la mente Rust), realmente puede estar disparándose en el pie al confiar en el compilador. Y desafortunadamente, los conjuntos de pruebas generalmente están incompletos, por lo que tampoco hay mucha más ayuda.

Cue la siguiente sección ...


Eliminar código potencialmente no utilizado

Lo más probable es que se haga referencia al código, sin embargo, sospecha que en la práctica nunca se toman las ramas del código que hacen referencia a él.

En este caso, sin importar el idioma, el código es demostrablemente accesible y solo se puede utilizar la instrumentación en tiempo de ejecución.

En el pasado, he utilizado con éxito un enfoque de 3 fases para eliminar dicho código:

  1. En cada rama que se sospeche que NO se tomará, registre una advertencia.
  2. Después de un ciclo, arroje una excepción / devuelva un error al ingresar el fragmento de código específico.
  3. Después de otro ciclo, elimine el código.

¿Qué es un ciclo? Es el ciclo de uso del código. Por ejemplo, para una aplicación financiera, esperaría un ciclo mensual corto (con los salarios pagados al final del mes) y un ciclo anual largo. En este caso, debe esperar al menos un año para verificar que no se emita ninguna advertencia para que el inventario de fin de año pueda usar rutas de código que de otro modo nunca se usarían.

Con suerte, la mayoría de las aplicaciones tienen ciclos más cortos.

Aconsejo poner un comentario TODO, con una fecha, aconsejando cuándo pasar al siguiente paso. Y un recordatorio en tu calendario.

Matthieu M.
fuente
1
En realidad, en C ++ puede marcar su sobrecarga sospechosa [[deprecated]]para identificar sitios que llaman a esa versión; entonces puede inspeccionarlos para ver si su comportamiento cambiará. Alternativamente, defina su sobrecarga como = delete: en este caso, deberá emitir argumentos explícitamente para usar una de las otras versiones.
Toby Speight
@TobySpeight: Eso es cierto, y una buena alternativa también. Permítanme editar eso.
Matthieu M.
"Doctor, me duele cuando hago esto". "¡Bueno , no hagas eso! " No uses enfoques que hagan que el código sea inmanejable.
2

Eliminar el código de producción es como limpiar la casa. En el momento en que arrojes un objeto del ático, tu esposa te matará al día siguiente por tirar el regalo de bienvenida de su vecino, el tercer sobrino de su bisabuela, de su vecino que murió en 1923.

En serio, después de un análisis superficial usando varias herramientas que todos ya han mencionado, y después de usar el enfoque de desaprobación escalonado ya mencionado, tenga en cuenta que es posible que se vaya de la empresa cuando se realice la eliminación real. Permitir que el código permanezca y que se registre su ejecución y se asegure de que cualquier alerta sobre su ejecución se le comunique definitivamente (o sus sucesores y cesionarios) es esencial.

Si no sigue este enfoque, es probable que su esposa lo mate. Si mantiene el código (como todo recuerdo), puede descansar su conciencia en el hecho de que la ley de Moore viene a su rescate y el costo del espacio en el disco basura utilizado por el código que se siente como una "roca en mi zapato", reduce cada año y no te arriesgas a convertirte en el centro de múltiples chismes de enfriadores de agua y miradas extrañas en los pasillos.

PD: Para aclarar en respuesta a un comentario ... La pregunta original es "¿Cómo se elimina con seguridad ...", por supuesto, se asume el Control de versiones en mi respuesta. Al igual que cavar en la basura para encontrar lo valioso se supone. Ningún cuerpo con ningún sentido desecha el código y el control de versiones debe ser parte del rigor de cada desarrollador.

El problema es sobre el código que puede ser superfluo. Nunca podremos saber que un código es superfluo a menos que podamos garantizar que el 100% de las rutas de ejecución no lo alcancen. Y se supone que este es un software lo suficientemente grande como para que esta garantía sea casi imposible. Y a menos que lea mal la pregunta, la única vez que todo este hilo de conversación es relevante es para los casos durante la producción en los que se puede llamar al código eliminado y, por lo tanto, tenemos un problema de tiempo de ejecución / producción. El control de versiones no ahorra a nadie debido a fallas de producción, por lo que el comentario sobre "Control de versiones" no es relevante para la pregunta o mi punto original, es decir, desaprobar adecuadamente durante un período de tiempo extremadamente largo si realmente tiene que hacerlo, de lo contrario no '

En mi humilde opinión, el comentario es superfluo y es un candidato para la eliminación.

LMSingh
fuente
12
Si tuviera el equivalente del control de versiones , la objeción de mi esposa se revertirá fácilmente. Lo mismo ocurre con la eliminación del código: no es un delito mortal. El repositorio nunca se tira.
JDługosz
Pensé que se suponía que la programación orientada a objetos reduciría el alcance en el que se podía llamar a un método. Por lo tanto, debería conducir a un código que sea más fácil de entender y administrar, ¿verdad? De lo contrario, ¿por qué lo hacemos así?
@nocomprende En ninguna parte de este hilo se indica que la discusión se limita solo al diseño / lenguajes OOP. No estoy seguro de por qué crees que es relevante.
underscore_d
1

Tal vez el compilador se da cuenta de eso, simplemente no hace un escándalo.

Ejecute una compilación con optimizaciones completas del compilador, orientadas al tamaño. Luego elimine el código sospechoso y vuelva a ejecutar la compilación.

Compara los binarios. Si son idénticos, entonces el compilador ha notado y silenciosamente borró el código. Puede eliminarlo de la fuente de forma segura.

Si los binarios son diferentes ... entonces no es concluyente. Podría ser algún otro cambio. Algunos compiladores incluso incluyen la fecha y la hora de la compilación en el binario (¡y tal vez se pueda configurar de forma remota!)

Emilio M Bumachar
fuente
1
No todos los idiomas están compilados, no todos los compiladores tienen optimización y no todas las optimizaciones son iguales, muchos no eliminan el código no invocado en caso de que ese código esté allí por alguna razón. Si el código vive en una biblioteca / DLL compartida, no se eliminará.
Steve Barnes
1
@SteveBarnes Bueno, sí, si no estás haciendo LTO y vinculando todo de forma estática, te dolerá un poco. Eso es un hecho.
Navin
1

En realidad, recientemente me he encontrado con esta situación exacta, con un método llamado "deleteFoo". En ninguna parte de la base de código completa se encontró esa cadena que no sea la declaración del método, pero sabiamente escribí una línea de registro en la parte superior del método.

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

¡Resulta que se utilizó el código! Algunos métodos AJAX llaman "método: eliminar, elem = Foo" que luego se concatena y se llama con call_user_func_array () .

En caso de duda, inicie sesión. Si ha pasado suficiente tiempo sin ver el registro lleno, considere eliminar el código. ¡Pero incluso si elimina el código, deje el registro en su lugar y un comentario con la fecha para facilitar la búsqueda del compromiso en Git para el tipo que tiene que mantenerlo!

dotancohen
fuente
Hay bibliotecas para PHP y Perl llamadas Tombstone (php: github.com/scheb/tombstone ) que pueden ser útiles aquí.
Alister Bulman
0

Use grepo cualquier herramienta que tenga disponible primero, puede encontrar referencias al código. (Originalmente no es mi instrucción / consejo).

Luego, decida si desea arriesgarse a eliminar el código, o si mi sugerencia podría ser útil:

Puede modificar la función / bloque de código para escribir que se ha utilizado en un archivo de registro. Si dicho mensaje no se registra "nunca" en el archivo de registro, entonces probablemente pueda eliminar el código de registro.

Dos problemas:

  1. Obteniendo el registro de otros usuarios. Quizás pueda establecer un indicador global que bloqueará el programa al salir y le pedirá al usuario que le envíe el registro de errores.

  2. Rastreando a la persona que llama. En algunos lenguajes de alto nivel (p. Ej., Python) puede obtener fácilmente un rastreo. Pero en los idiomas compilados, tendrá que guardar la dirección de retorno en el registro y forzar un volcado de núcleo al salir (punto 1).
    En C, esto debería ser bastante simple: use un bloque condicional (p. Ej. #Ifdef ... #endif) para usar esto solo cuando sepa que funcionará (y haya probado que funciona), y simplemente lea la dirección de retorno de la pila (puede o no requerir lenguaje ensamblador en línea).

Guardar los argumentos en el archivo de registro puede o no ser útil.

Oskar Skog
fuente
0

Si sabe qué hace el código que lo rodea, pruébelo para ver si se rompe. Esta es la forma más simple y rentable de refactorizar una pieza de código en la que está trabajando, y debe hacerse de todos modos como una cuestión de desarrollar cualquier cambio en el código en el que está trabajando en primer lugar.

Si, por otro lado, está refactorizando un fragmento de código donde no sabe lo que hace, no lo elimine . Comprenda primero, luego refactorícelo si determina que es seguro (y solo si puede determinar que la refactorización sería un uso adecuado de su tiempo).

Ahora, puede haber algunas circunstancias (sé que me he encontrado) en las que el código que está 'muerto' en realidad no está conectado a nada . Como las bibliotecas de código desarrolladas por un contratista que nunca se implementaron en ningún lugar porque quedaron obsoletas inmediatamente después de que se entregó la aplicación (¿por qué sí? Tengo un ejemplo específico en mente, ¿cómo lo sabía?).

Personalmente terminé eliminando todo este código, pero es un gran riesgo que no recomiendo tomarlo a la ligera, dependiendo de cuánto código pueda afectar potencialmente este cambio (porque siempre existe el potencial de que hayas pasado por alto algo) debe ser extraordinariamente cuidadoso y ejecutar pruebas unitarias agresivas para determinar si eliminar este antiguo código heredado dañará su aplicación.

Ahora que se dice todo eso, probablemente no valga la pena su tiempo o cordura para tratar de eliminar un código como ese. No, a menos que se esté convirtiendo en un problema grave con su aplicación (por ejemplo, no poder completar los análisis de seguridad debido a la hinchazón de la aplicación ... por qué sí, todavía tengo un ejemplo en mente), por lo que no lo recomendaría a menos que llegue a Una situación en la que el código realmente ha comenzado a pudrirse de una manera impactante.

En resumen, si sabe lo que hace el código, pruébelo primero. Si no lo hace, probablemente pueda dejarlo solo. Si sabe que no puede dejarlo solo, dedique su tiempo a probar el cambio agresivamente antes de implementarlo.

Zibbobz
fuente
0

Mi enfoque, que siempre asumí como estándar de la industria en este caso, extrañamente no se ha mencionado hasta ahora:

Consigue un segundo par de ojos.

Existen diferentes tipos de "código no utilizado" y, en algunos casos, eliminar es apropiado, en otros casos debe refactorizarlo, y en otros casos debe huir lo más lejos que pueda y nunca mirar hacia atrás. ¡Necesitas descubrir cuál es, y para hacerlo es mejor emparejarte con un segundo experimentado! - desarrollador, uno que está muy familiarizado con la base del código. Esto reduce significativamente el riesgo de un error innecesario y le permite realizar las mejoras necesarias para mantener la base del código en un estado mantenible. Y si no hace esto, en algún momento se quedará sin desarrolladores que estén muy familiarizados con la base del código.

También he visto el enfoque de registro: para ser más exactos, he visto el código de registro: aún más código muerto que se dejó allí durante 10 años. El enfoque de registro es bastante decente si está hablando de eliminar funciones completas, pero no si solo está eliminando un pequeño fragmento de código muerto.

Peter
fuente
0

La respuesta simple a su pregunta es que no puede eliminar con seguridad el código que no comprende, lo que incluye no entender cómo se llama. Habrá algún riesgo.

Tendrá que juzgar la mejor manera de mitigar ese riesgo inevitable. Otros han mencionado la tala. El registro, por supuesto, puede probar que se usa, pero no puede probar que no se use. Dado que el principal problema con el código muerto es que se mantiene, es posible que pueda evitar agregar un comentario que diga que se sospecha que es un código muerto, y no hacer ningún mantenimiento en él.

Si el código está bajo control de origen, siempre puede averiguar cuándo se agregó y determinar cómo se llamó entonces.

Finalmente, lo entiendes, lo dejas en paz o te arriesgas.

jmoreno
fuente
0

En caso de que el desarrollador del código en cuestión aún esté disponible, revise la eliminación del código con él . Además, lea los comentarios en el código .

Obtendrá la explicación adecuada, por qué se ha agregado este código y para qué función sirve. Puede ser fácilmente un soporte para el caso de uso específico, o incluso puede que no tenga la experiencia suficiente para juzgar si realmente no es necesario. En casos extremos, uno puede eliminar todas las declaraciones gratuitas de un programa en C y no notar nada incorrecto (puede tardar unos minutos en quedarse sin memoria).

Es realmente una mala cultura cuando el autor del código se sienta a tu lado, sin embargo, no ves ninguna razón para hablar porque estás seguro de que solo escribe un código incorrecto, y el tuyo es tan perfecto. Y, por supuesto, solo escribe palabras al azar en los comentarios, sin necesidad de perder el tiempo leyendo.

Una de las razones más importantes para escribir comentarios en el código es explicar POR QUÉ se ha implementado de esta manera. Puede no estar de acuerdo con la solución, pero debe tener en cuenta el problema.

La discusión con el autor también puede revelar que el código es el remanente histórico de algo eliminado o nunca terminado, por lo que es seguro eliminarlo.

h22
fuente
-1

Estoy totalmente de acuerdo con el primer punto de Markus Lausberg: el código definitivamente debe analizarse con la ayuda de herramientas. ¡No se puede confiar en los cerebros de los simios con este tipo de tarea!

La mayoría de los lenguajes de programación estándar se pueden usar en IDE y cada IDE que vale la pena llamar tiene una función de "buscar para todos los usos", que es exactamente la herramienta adecuada para este tipo de situación. (Ctrl + Shift + G en Eclipse, por ejemplo)

Por supuesto: las herramientas de análisis estático no son perfectas. Hay que estar al tanto de situaciones más complicadas en las que la decisión de que se llama al método en cuestión ocurre solo en tiempo de ejecución y no antes (llamadas a través de Reflection, llamadas a funciones "eval" en lenguajes de script, etc.).

Johannes Hahn
fuente
55
¿Quizás los cerebros de los simios no deberían escribir código? "No tocado por manos humanas " solía ser una frase de marketing. Siempre imaginé manos de gorila haciendo el trabajo.
3
(Cerebros Ape escribieron las herramientas también) (Shhh Arruinarás la historia!)
1
Creo que ambos están perdiendo mi punto. Dije claramente que no hay garantía de que el análisis de código estático encuentre cada invocación de un método o clase. Es, como dice en la lata, un análisis estático. No se puede y no se debe esperar que encuentre invocaciones dinámicas a través de la reflexión, eval (), etc. Mi punto es que el análisis de código estático debe considerarse un requisito previo necesario para eliminar un fragmento de código. Y cuanto más grande es el proyecto, menos probable es que un cerebro humano sea incluso capaz de hacer ese análisis y mucho menos hacerlo correctamente ...
Johannes Hahn
1
... el análisis de código estático es un trabajo tedioso y repetitivo que se realiza mejor con una computadora. Exactamente el tipo de tarea que las computadoras pueden ejecutar de manera confiable pero que es notoriamente poco confiable cuando la ejecuta un cerebro de simio. Este último solo debe usarse para cosas que una computadora no puede hacer, como encontrar esas reflexiones difíciles y llamadas de evaluación.
Johannes Hahn
1
De nuevo, perdiendo el punto. Incluso si las herramientas no son perfectas porque fueron escritas por cerebros simios (¡y ni siquiera estoy seguro de si debo conceder eso! Calcular las jerarquías de llamadas estáticas no es tan difícil), siguen siendo muy superiores a la ejecución manual de tareas. Esta no es una situación de todo o nada. Una herramienta confiable al 99% sigue siendo preferible a un cerebro que tal vez comience con un 99% por ciento, pero rápidamente cae a tasas mucho más bajas después de las primeras mil líneas de código.
Johannes Hahn
-1

Dos posibilidades:

  1. Tiene más del 99% de cobertura de prueba : elimine el código y termine con él.
  2. No tiene una cobertura de prueba confiable: entonces la respuesta es agregar una advertencia de desaprobación . Es decir, agrega un código a ese lugar que hace algo sensato (por ejemplo, registre una advertencia en algún lugar fácilmente visible que no entre en conflicto con el uso diario de su aplicación). Luego déjelo hervir a fuego lento durante unos meses / años. Si nunca encontró alguna ocurrencia, reemplace la desaprobación por algo que arroje una excepción. Deja que eso permanezca por un tiempo. Finalmente, elimine todo por completo.
AnoE
fuente
2
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las 17 respuestas anteriores. Tanto la cobertura como la advertencia de desaprobación ya se discutieron allí
mosquito el
@gnat, tienes razón. Cuando escaneé las respuestas por primera vez, por alguna razón pensé que había visto varias que en la parte superior no llegaron a estos dos puntos de manera breve y sucinta. En medio de las respuestas hay algunas como esas.
AnoE