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.
"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.
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?
clean-code
Brad Thomas
fuente
fuente
Respuestas:
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:
fuente
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.
grep
puede 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í:
fuente
También tiendo a
grep
utilizar 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.fuente
$object->$variableMethod($arg1, $arg2);
grep
el 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.fuente
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.
fuente
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.
fuente
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
fuente
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
DoSomething
aDoSomethingX
) 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é
if
declaraciones 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.
fuente
fuente
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:
¿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.
fuente
[[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.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.
fuente
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!)
fuente
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:
¡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!
fuente
Use
grep
o 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:
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.
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.).
fuente
Dos posibilidades:
fuente