design - printable - Cómo lidiar con el código malo
name tags templates (9)
Una de las situaciones más desagradables (y desafortunadamente más frecuentes) a las que me enfrento en mi vida cotidiana como desarrollador es que tengo que corregir errores o agregar funciones al código que está mal diseñado. Ahora, como buen artesano, me gustaría dejar el código en un mejor estado que el que encontré. A menudo, las nuevas características no se pueden implementar si no refactorizo el diseño. Bueno, podrían, pero eso empeoraría el código.
Desafortunadamente, esto es exactamente con lo que tiendo a tener dificultades. Siento que si hay algo que es difícil, es refactorizar el código incorrecto, especialmente cuando tienes fechas límite. Tocar el código malo y complejo que más o menos funciona es aterrador. Como resultado, introduzco aún más desorden cuando hackeo una nueva característica en el código sin modificar el código existente.
Ahora mi pregunta es ¿Cómo puedo aprender a lidiar con el código malo? ¿Cómo puedo aprender a entender grandes bases de código y luego refactorizar algunas partes sin romper cosas que ya funcionaron y sin exceder la fecha límite? ¿Hay literatura que puedas recomendar? ¿Tienes algún consejo general para mí?
Actualmente estoy en esta situación. Mi enfoque es responder algunas preguntas antes de tocar el código:
- Es el código realmente tan malo? En caso afirmativo, ¿cuáles son los errores comunes? ==> tal vez concentrarse en aquellos primero
- ¿Cuál es el flujo de tiempo de ejecución principal en el código? Quizás puedas descartar bastantes constructos de él.
- Intenta copiar / modularizar el código sin cambiarlo. Esto conduce a una cierta reducción de interdependencias
- Intenta introducir el código con las pruebas. Si la base del código se enreda más allá de la esperanza: use algo como PowerMock para simular objetos que no necesitan cambios (aún)
- Tenga un entorno de integración disponible, en el que pueda probar los cambios en un entorno cercano a la producción.
- No se rehúse a reescribir partes de la base de códigos. Pero trata de no implementar demasiadas cosas nuevas en él
- Intenta formar equipo, discutir diseños, principios, soluciones
Este es un trabajo difícil, y nadie se lo agradecerá. Siéntete orgulloso de pequeñas mejoras y disfruta de un buen trabajo hecho :)
Bueno, si va a refabricar grandes cantidades de código en un proyecto, le recomendaría usar algún control de versión decente, para que pueda ramificarse y retroceder fácilmente. Teniendo en cuenta, esta es probablemente una puerta abierta, pero crucial.
Además, antes de comenzar a entrar en OO complejo, intente dividir métodos y funciones en otros más pequeños. Asegurando un cierto nivel de atomicidad en cada una de las funciones, lo que hace que el código sea mucho más fácil de mantener, leer y administrar. Se trata de cosas pequeñas, dividirlas en unidades lógicas de operación, estoy haciendo una acción de refactorización en un método de 1k líneas. Lo hace todo tipo de cosas de lujo. Mi primer objetivo es obtener la mayor cantidad de material en piezas más pequeñas, cuando eso esté listo comenzaré a pensar en un mejor diseño de OO, que es mucho más fácil porque tengo una mejor comprensión de las cosas.
La aspirina también funciona bien.
Consejo general:
if (it is working)
Do (not touch it);
else
{
Make (as few modifications as possible)
Or (it will stop working at all);
}
Esta es la experiencia de generaciones.
Creo que siempre es bueno tener una idea general de cómo funciona todo en el software que está desarrollando / mejorando. Aquí es donde entran los documentos de diseño y otros documentos creados después o durante el proceso de desarrollo. Creo que si alguien antes no ha realizado la documentación adecuada, al menos debe escribir un par de líneas sobre lo que experimenta durante su proceso de desarrollo. . Usualmente uso OneNote u otras cosas para escribir notas sobre lo que encuentro, y generalmente sigo enumerando cosas que creo que requerirían refactorización. Y si hay algún tiempo de inactividad durante el proyecto, por lo general vuelvo a esa lista y trato de mejorar las cosas poco a poco.
Entonces, básicamente, si alguien antes que usted no lo ha hecho bien, sería bueno si al menos pudiera ayudar a reducir los problemas para cualquier otro desarrollador que se encuentre con el mismo código.
Cuando tengo que lidiar con la adición de funcionalidad al código incorrecto, mi enfoque habitual es:
- Escriba pruebas automatizadas para cada característica importante que debe funcionar (ya que la mayoría del código incorrecto no tiene ninguna prueba).
- Cambie el código
- Asegúrate de que las pruebas sigan funcionando.
Eso le da al menos algo de confianza de que no rompió todo. En cuanto a cómo aprender a lidiar con el mal código, supongo que se trata de experiencia.
Depende del número de factores, pero el más importante es si usted tiene la autoridad para modificarlo.
En caso de que lo hagas, refactorízala. Por ejemplo, cambie el nombre de clases / funciones / variables. Extrae y generaliza funcionalidades. Ver Refactorización: mejorar el diseño del código existente (Biblia para el tema). Antes de comenzar, asegúrese de que el código tenga el control de versión adecuado (VC) y tenga un buen conjunto de casos de prueba. VC le permite retroceder y los casos de prueba ayudan a detectar efectos secundarios inesperados.
Sugiero el Control de versiones distribuidas como Mercurial / Bazaar y Git porque es muy refactorizado y no está exactamente estructurado como agregar características.
Si no hubo pruebas (comunes), debe crearlas. Leer trabajando efectivamente con código heredado . Especialmente sobre "Punto de sello" (no sobre gato siamés: p).
En caso de que no crees una API de contenedor que sea más limpia.
Por ejemplo:
Old code ====================
const ACT_SHOW = ''show'';
const ACT_HIDE = ''hide'';
function int DoThing(Object $Obj, Stirng $Action, Object $Param1, Object $Param1) {
....;
}
Added code ==================
enum Actions {
show, hide;
};
class ActionDoer {
private obj;
ActionDoer($Object) {
this.obj = $Object;
}
function int act(Actions $Action, $Param1, $Param1) {
this.act($Action.toString(), $Param1, $Param1) ;
}
function int act(String $Action, $Param1, $Param1) {
DoThing(this.obj, $Action, $Param1, $Param1) ;
}
function int show() {
this.act(Actions.show, null, null);
}
function int hide(Color $ToBGColor, long $FadeTime) {
this.act(Actions.hide, $ToBGColor, $FadeTime);
}
}
De esta forma, el código anterior no se toca y la extensión se puede hacer usando el nuevo código. Un buen ejemplo de este método es jQuery, donde la forma antigua (por defecto) de acceder al DOM es dolorosa.
Espero que esto ayude.
Michael Feathers escribió un buen libro sobre este tema exactamente.
Trabajando Efectivamente con Legacy Code .
Otro gran libro es de Martin Fowler, Kent Beck y otros:
Refactoring necesita el arnés de seguridad de un conjunto de pruebas unitarias para eliminar ese "¿Lo he roto?" sensación. Cubrir el código incorrecto en una serie de pruebas te ayudará mientras luchas por un buen código de limpieza.
Pex es una herramienta que considero útil (si estás en el mundo de .NET) para crear pruebas para el código heredado.
Código heredado == código sin pruebas!
Amabilidad,
Dan