¿Qué debo hacer como revisor cuando el código "tiene que ser" malo?


18

Trabajo en un proyecto mal diseñado y de ingeniería excesiva en el que se introdujeron revisiones obligatorias de códigos hace unos meses.

Me han pedido que revise un fragmento sustancial de código que implementa una nueva característica. Tiene los mismos defectos que el resto de nuestra base de código. Entiendo que, en gran medida, esos defectos se introducen en el nuevo código, por ejemplo. al tener que heredar de una clase mal diseñada o implementar una interfaz mal diseñada manteniendo la compatibilidad, y realmente no puedo ofrecer una solución mucho mejor que no implique reescribir la mitad de la base de código. Pero siento que desde el punto de vista de la ingeniería, no sirve de nada a una base de código ya rota que la rompamos aún más. El código que estoy revisando es definitivamente malo, pero tiene que serlo si la característica se implementará.

¿Cómo debo comportarme con respecto a esta revisión en particular? ¿Hay alguna manera de mantener la integridad y seguir siendo constructivo?

Tenga en cuenta que estoy preguntando sobre el límite de la revisión de código en este contexto. Me doy cuenta de que el problema es causado por otros factores, incluida la organización y la cultura laboral, pero lo que quiero saber es cómo manejar la revisión en sí.


1
¿El desarrollador documentó por qué se hace así y tal vez incluso escribió un problema para el problema central en el rastreador de problemas?
Luc Franken

Escasamente, y no.
Rojo

44
¿Hay algún apetito dentro de su organización para hacer algo con respecto a la deuda técnica?
Robert Harvey

55
Si el código no está mal teniendo en cuenta el entorno, no atacaría el código. Sobre todo desde que escribiste, no ves una mejora real posible en este momento. Crearía conflicto sin oportunidad de mejorar. Lo que haría es enseñar a documentar mejor como primer paso. En segundo lugar, cree un procedimiento para que escriban cuestiones claras. Eso dará como resultado 2 cosas: documentación sobre el problema para que el siguiente pueda funcionar más rápido. Y además de eso, obtienes una lista de problemas concretos que se pueden solucionar potencialmente. Me gustan las menciones de GitHub porque ves cuántas veces vuelve a ocurrir si lo etiquetan.
Luc Franken

1
relacionado (posiblemente un duplicado): ¿Cómo lidiar con demasiado pragmatismo en el proyecto?
mosquito

Respuestas:


25

Sencillo:

1. Documente su deuda técnica

Ha identificado un fragmento de código que funciona pero tiene algunos problemas técnicos. Esta es la deuda técnica . Al igual que otros tipos de deuda, empeora con el tiempo si no se trata.

El primer paso para lidiar con la deuda técnica es documentarla. Haga esto agregando elementos en su rastreador de problemas que describan la deuda. Esto lo ayudará a tener una idea más clara de la magnitud del problema y también ayudará a diseñar un plan para abordarlo.

2. Pague gradualmente su deuda

Modifique su proceso de desarrollo de software para tener en cuenta el pago de la deuda técnica. Esto podría involucrar el sprint de endurecimiento ocasional, o simplemente resolver ítems de deuda a intervalos regulares (por ej., 2 ítems por semana). La parte importante para asegurarse de que está reduciendo su deuda más rápido de lo que se está acumulando (la deuda, incluso la deuda técnica, tiene intereses).

Una vez que haya alcanzado un punto en el que ya no tenga un Déficit Técnico, estará en camino a una base de código más saludable :)


5

Como nota al margen: busque un nuevo trabajo. Este no mejoraría.

Los objetivos del código que está revisando son:

  • Para enviar una función, que debería funcionar de acuerdo con los requisitos.

  • Reducir el crecimiento de la deuda técnica.

El primer objetivo se revisa comprobando que la unidad, la integración, el sistema y las pruebas funcionales están aquí, que son relevantes y que cubren todas las situaciones que deben probarse. También debe verificar las creencias que el autor original puede tener sobre el lenguaje de programación, lo que podría conducir a errores sutiles o al código que pretende hacer algo diferente de lo que realmente hace.

El segundo objetivo es aquel en el que se centra su pregunta. Por un lado, no se espera que el nuevo código aumente la deuda técnica. Por otro lado, el alcance de la revisión es el código en sí mismo, pero en el contexto de toda la base de código. A partir de ahí, usted, como revisor, puede esperar dos enfoques del autor original:

  • El código externo no es mi culpa. Solo implemento la función y no me importa toda la base de código.

    En esta perspectiva, el código copiará las fallas de la base de código, y así inevitablemente aumentará la deuda técnica: más código malo siempre es peor.

    Si bien este es un enfoque válido a corto plazo, a largo plazo, daría lugar a retrasos crecientes y baja productividad, y eventualmente llevaría a que el proceso de desarrollo fuera tan costoso y arriesgado que el producto dejaría de evolucionar.

  • Escribir un nuevo código es una oportunidad para refactorizar uno heredado.

    En esta perspectiva, el efecto de los defectos del código heredado en el nuevo podría ser limitado. Además, la deuda técnica podría reducirse, o al menos no aumentarse proporcionalmente al crecimiento del código.

    Si bien este es un enfoque válido a largo plazo, tiene sus riesgos a corto plazo. La principal es que, a corto plazo, a veces llevaría más tiempo enviar la característica específica. Otro aspecto importante es que si el código heredado no se prueba, la refactorización presenta un gran riesgo de introducir regresiones.

Dependiendo de la perspectiva que desee fomentar, puede inclinarse a aconsejar a los revisados ​​que refactoricen más o no. En todos los casos, no espere una pieza de código impecable y limpia con una arquitectura y un diseño agradables dentro de una base de código horrible. Lo que no debe alentar es el comportamiento en el que un desarrollador bien informado que tiene que trabajar en una base de código deficiente intenta hacer bien su parte . En lugar de simplificar las cosas, solo las hace más complicadas que antes. Ahora, en lugar de un código incorrecto uniforme, tiene una parte con patrones de diseño, otra parte con código limpio y claro, otra parte que se refactorizó ampliamente con el tiempo y sin unidad alguna.

Imagine, por ejemplo, que está descubriendo una base de código heredada de un sitio web de tamaño mediano. Le sorprende la falta de una estructura habitual y el hecho de que el registro, cuando se hace, se realiza agregando cosas a un archivo de texto a mano, en lugar de usar un marco de registro. Decide que la nueva característica use MVC y un marco de registro.

Su colega está implementando otra función y está muy sorprendido por la falta de un ORM en el que uno tenga el tamaño perfecto. Entonces él comienza a usar un ORM.

Ni usted ni su colega pueden pasar por cientos de miles de líneas de código para utilizar MVC, un marco de registro o un ORM en todas partes. En realidad, requeriría meses de trabajo: imagine presentar MVC; ¿Cuanto tiempo tardaría? ¿O qué pasa con un ORM en situaciones en las que las consultas SQL se generaron caóticamente a través de la concatenación (con lugares ocasionales para la inyección SQL) dentro del código que nadie podía entender?

Crees que hiciste un gran trabajo, pero ahora, un nuevo desarrollador que se une al proyecto tiene que enfrentar mucha más complejidad que antes:

  • La vieja forma de tratar las solicitudes,

  • La manera MVC

  • El viejo mecanismo de registro,

  • El marco de registro,

  • El acceso directo a la base de datos con consultas SQL creadas sobre la marcha,

  • El ORM

En un proyecto en el que estaba trabajando, había cuatro (!) Marcos de registro utilizados uno al lado del otro (más el registro manual). La razón es que cada vez que alguien quería registrar cosas no había un enfoque común para hacerlo, por lo que en lugar de aprender un nuevo marco (que en todos los casos se usó solo en el 5% de la base de código), uno simplemente agregaría otro. ya sabe. Imagina el desastre.

Un mejor enfoque sería refactorizar la base de código paso a paso. Tomando nuevamente el ejemplo del registro, la refactorización consistiría en los siguientes pequeños pasos:

  • Encuentre todos los lugares donde se realiza el registro heredado (es decir, cuando se accede directamente al archivo de registro) y asegúrese de que todos invoquen los mismos métodos.

  • Mueva este código a una biblioteca dedicada, si corresponde. No quiero registrar la lógica de almacenamiento en mi clase de carrito de compras.

  • Modifique, si es necesario, la interfaz de los métodos de registro. Por ejemplo, podemos agregar un nivel que indique si el mensaje es informal o si es una advertencia o un error.

  • Use los métodos recientemente refactorizados en la nueva función.

  • Migre al marco de registro: el único código afectado es el código dentro de la biblioteca dedicada.


1
Gran respuesta hasta el último párrafo. Ya sea que lo haya querido o no, está insinuando que las buenas prácticas no deberían usarse para el nuevo código. -1
RubberDuck

2
@RubberDuck: no se trata de buenas prácticas, se trata de escribir código radicalmente diferente de la base de código restante. He sido ese desarrollador y he visto las consecuencias de lo que he hecho: tener un código drásticamente mejor entre los códigos malos solo empeora las cosas; lo que lo hace mejor es mejorar la base de código a través de pequeños pasos de refactorización. Agregaré un ejemplo en mi respuesta en unos minutos.
Arseni Mourzenko

Volvería a DV si pudiera. La edición lo empeora. Tener cuatro métodos diferentes para hacer algo de una manera nueva es malo, pero es culpa del tipo que agrega el segundo marco de registro. No es malo en sí mismo dibujar una línea en la arena y tener un código limpio al lado del código podrido.
RubberDuck

@RubberDuck: No se trata de agregar el segundo marco de registro, sino el primero. El tipo que agrega el segundo marco de registro lo hace solo porque el primero se usa en una sola característica pequeña; Si la base de código se refactorizara como aconsejo, esto no sucedería.
Arseni Mourzenko

Creo que usted y yo estamos de acuerdo, pero sus respuestas se leen de una manera que desalienta la mejora. Simplemente no puedo subir a bordo con eso.
RubberDuck

3

Si está haciendo revisiones de código como parte de su proceso de desarrollo; entonces debe establecer las reglas contra las cuales 'juzga' el código que está revisando.

Esto debería entrar en su 'definición de hecho' y podría ser una guía de estilo, documento de arquitectura para la base de código o análisis estático, verificar los requisitos legales, lo que la empresa decida es sus requisitos para la 'calidad del código'

Una vez que tenga esto en su lugar, las revisiones de códigos se convierten en una cuestión de rutina, se ha seguido la guía de estilo, ¿tenemos el porcentaje requerido de cobertura de prueba, etc.

Si no tiene esto en su lugar, las revisiones de código pueden convertirse en una batalla de quién tiene la mayor codificación o, como en su situación, cuestionamiento de juicios sobre la refactorización frente a las características realizadas antes de la fecha límite. Lo cual es solo una pérdida de tiempo para todos.


3

Su principal problema es que una revisión de código en una nueva característica importante es el momento equivocado para tener esta discusión. En ese momento, es demasiado tarde para hacer cualquier cosa menos cambios menores. El lugar correcto es en las etapas de planificación, o en una revisión de diseño preliminar a más tardar. Si su empresa no está haciendo esas primeras revisiones al menos de manera informal, primero debe trabajar para cambiar esa cultura.

El siguiente paso es ser invitado a esas reuniones y tener ideas productivas en esas reuniones. Principalmente eso significa no tratar de cambiar todo de la noche a la mañana, sino buscar trozos del tamaño de un bocado que pueda aislar y abordar. Esos trozos se sumarán significativamente con el tiempo.

En otras palabras, la clave es sugerir regularmente cambios más pequeños hacia el inicio de los proyectos, en lugar de ser derribado, lo que sugiere cambios más grandes hacia el final.


2

En los lugares donde he estado revisando el código, aceptaría y aprobaría el envío del código, si viene con el compromiso de hacer (al menos) algunas refactorizaciones. Ya sea como un error archivado, como una historia o una promesa de enviar otra revisión con (algunos) refactorización realizada.

En estos casos, si soy la persona que escribe el código para revisar, generalmente preparo dos cambios, uno con mis nuevas características o correcciones de errores, luego otro que tiene eso Y algo de limpieza. De esa manera, los cambios de limpieza no restan valor a las nuevas características o correcciones de errores, pero se señalan fácilmente como un token "sí, sé que esto no soluciona estos problemas, pero allá está" pura limpieza "que hace".

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.