OK, entonces mucha revisión de código es bastante rutinaria. Pero ocasionalmente hay cambios que afectan ampliamente el código complejo y frágil existente. En esta situación, la cantidad de tiempo que llevaría verificar la seguridad de los cambios, la ausencia de regresión, etc. es excesiva. Quizás incluso excediendo el tiempo que llevó hacer el desarrollo en sí.
¿Qué hacer en esta situación? ¿Combinar y esperar que nada se escape? (¡No lo defiendo!) ¿Puede hacer lo mejor que pueda e intente solo detectar cualquier falla obvia (tal vez esta sea la mayor revisión de código a la que debe apuntar de todos modos?)
Esto no es específicamente una cuestión de si las pruebas deben realizarse como parte de una revisión de código. Esta es una pregunta que pregunta cuáles son las mejores opciones en la situación descrita, especialmente con una fecha límite apremiante, no hay un conjunto completo de pruebas unitarias disponibles o pruebas unitarias no viables para el código fragmentado que ha cambiado.
EDITAR: Tengo la impresión de que algunas de las respuestas / comentarios hasta ahora han captado mi frase "impacto general", y posiblemente consideró que el cambio implicó una gran cantidad de líneas de código. Puedo entender que esta sea la interpretación, pero esa no era realmente mi intención. Por "impacto general", quiero decir, por ejemplo, el potencial de regresión es alto debido a la interconexión de la base de código, o el alcance de los efectos secundarios, no necesariamente que el cambio en sí sea grande. Por ejemplo, un desarrollador puede encontrar una manera de corregir un error con una sola línea llamando a una rutina de alto nivel existente que conecta en cascada las llamadas a muchas rutinas de nivel inferior. Probar y verificar que la corrección de errores funcionó es fácil. Validar manualmente (mediante revisión de código) el impacto de todos los efectos secundarios es mucho más difícil.
fuente
what if there is no pre-existing test suite?
- ¿Qué tal escribir uno?Merge and hope nothing slips through?
Esa es una idea notoriamente mala.Respuestas:
La premisa de la pregunta es, francamente, asombrosa. Suponemos que hay un gran cambio en el código complejo y frágil, y que simplemente no hay tiempo suficiente para revisarlo correctamente . ¡Este es el último código que debería dedicar menos tiempo a revisar! Esta pregunta indica que tiene problemas estructurales no solo en su propio código, sino también en su metodología de gestión del cambio.
Entonces, ¿cómo lidiar con esta situación? Comience por no entrar en él en primer lugar:
Identifique las fuentes de complejidad y aplique refactorizaciones correctas, cuidadosamente revisadas y correctas para aumentar el nivel de abstracción. El código debe ser entendible por un nuevo empleado recién salido de la universidad que sepa algo sobre su dominio comercial.
Identificar fuentes de fragilidad; Esto podría ser mediante la revisión del código en sí, examinando el historial de correcciones de errores en el código, y así sucesivamente. Determine qué subsistemas son frágiles y haga que sean más robustos . Añadir lógica de depuración. Añadir afirmaciones. Cree una implementación lenta pero obviamente correcta del mismo algoritmo y en su compilación de depuración, ejecute ambos y verifique que estén de acuerdo. En su compilación de depuración, haga que ocurran situaciones raras con más frecuencia. (Por ejemplo, cree un asignador de memoria que siempre mueva un bloque en la reasignación, o siempre asigne un bloque al final de una página, o lo que sea). Haga que el código sea robusto ante los cambios en su contexto. Ahora ya no tienes código frágil; ahora tiene un código que encuentra los errores, en lugar de causarlos.
Escribe un conjunto de pruebas automatizadas. Obviamente.
No hagas grandes cambios. Realice una serie de cambios pequeños y específicos, cada uno de los cuales puede verse como correcto.
Pero fundamentalmente, su escenario es "nos hemos metido en un agujero de deuda técnica y cada cambio complejo y no revisado nos profundiza más, ¿qué debemos hacer?". ¿Qué haces cuando te encuentras en ese agujero? Deja de cavar . Si tiene tanta deuda que no puede realizar tareas básicas como revisar el código del otro, entonces debe dejar de endeudarse más y dedicar tiempo a pagarla.
fuente
Uno del principal objetivo de una revisión de código es aumentar la calidad y entregar código robusto . Robusto, porque 4 ojos generalmente detectan más problemas que 2. Y el revisor que no ha escrito el código adicional tiene más probabilidades de cuestionar suposiciones (potencialmente incorrectas).
Evitar las revisiones por pares en su caso solo contribuiría a aumentar la fragilidad de su código. Por supuesto, reforzar las pruebas con un conjunto de pruebas sólido y repetible sin duda podría mejorar la calidad. Pero debería ser complementario a la revisión por pares, no un reemplazo .
Creo que la complejidad debe entenderse y dominarse, y la revisión por pares completa es la ocasión para compartir conocimientos y lograr esto. La inversión que realice para que más personas comprendan la fortaleza y la debilidad del código frágil ayudará a mejorarlo con el tiempo.
Una cita para concluir:
fuente
Bienvenido al mundo del desarrollo de software heredado.
Tiene cientos de miles, millones, 10 millones de líneas de código.
Estas líneas de código son valiosas, ya que producen un flujo de ingresos y su reemplazo es inviable.
Su modelo de negocio se basa en aprovechar esa base de código. Entonces, su equipo es pequeño, la base del código es grande. Es necesario agregar funciones para que las personas compren una nueva versión de su código o para que los clientes existentes estén contentos.
En un mundo perfecto, su enorme base de código está probada en el wazoo. No vives en un mundo perfecto.
En un mundo menos perfecto, tiene el presupuesto para arreglar su deuda técnica: desglosar su código en partes comprobables por unidad, realizar pruebas de integración exhaustivas e iterar.
Esto, sin embargo, está pagando deudas sin producir nuevas características. Lo que no coincide con el caso de negocio de "cosechar beneficios del código existente, mientras lo modifica para generar incentivos para actualizar".
Puede tomar grandes porciones de código y reescribirlo utilizando técnicas más modernas. Pero donde quiera que interactúe con el código existente expondrá posibles puntos de ruptura. Ese truco en el sistema del que se libró en realidad compensó una peculiaridad en un subsistema que no reescribió. Siempre.
Lo que puedes hacer es actuar con cuidado. Puede encontrar alguna parte del código que realmente comprende y cuyo comportamiento e interacción con el resto del sistema se entiende bien. Puede modernizar eso, agregando pruebas unitarias y haciendo su comportamiento aún más claro.
Luego, busca las partes del resto de la aplicación que interactúan principalmente con ella y ataca una por una.
Al hacerlo, puede mejorar el subsistema, agregando características que los clientes están dispuestos a pagar.
En resumen, este es el arte de lo posible: hacer cambios sin romper las cosas que proporcionan un caso de negocios.
Pero esta no es tu pregunta. Su pregunta es: "Estoy haciendo algo que es enorme y es probable que rompa cosas, y ¿cómo sigo las mejores prácticas?"
Al hacer algo enorme, es cierto que si quieres hacerlo de manera confiable, terminarás gastando más esfuerzo en localizar errores y corregirlos de lo que lo haces escribiendo. Esta es la regla general del desarrollo de software: escribir cosas es fácil, hacer que funcione sin problemas es difícil.
Probablemente tenga un caso de negocios sobre su cabeza, en el que le prometió a algún interesado que este cambio masivo entrará en vigencia. Y está "hecho", por lo que se ve obligado a decir "no, esto no está hecho, solo parece gusta".
Si tiene el poder y el presupuesto, realmente dedique el esfuerzo a generar confianza en que el cambio funciona, o simplemente rechace el cambio. Esto va a ser una cuestión de grado, no amable.
Si no tiene tanta potencia, pero todavía tiene algo, intente insistir en que el nuevo sistema sea comprobable por unidades . Si reescribe algún subsistema, insiste en que el nuevo subsistema esté compuesto de partes pequeñas con un comportamiento bien especificado y pruebas unitarias a su alrededor.
Luego está el peor de los casos. Te profundizas en la deuda. Usted toma prestado contra el futuro del programa al tener más código que es frágil y más errores para poder sacar la función ahora , y malditas las consecuencias. Realizas un control de calidad basado en barrido para encontrar los peores problemas e ignoras el resto. En realidad, esta es a veces la respuesta correcta desde la perspectiva del negocio, ya que ahora es más barato. Endeudarse para generar ganancias es una estrategia comercial válida, especialmente si la liquidación de la deuda por bancarrota (abandono del código) está sobre la mesa.
Un gran problema es que rara vez los incentivos de los propietarios de la compañía están alineados con los tomadores de decisiones y los programadores. Suele haber mucha presión para 'cumplir', y hacerlo generando una deuda técnica casi invisible (para sus superiores) es una gran estrategia a corto y, a veces, a mediano plazo. Incluso si sus superiores / partes interesadas serían mejor atendidos al no crear toda esa deuda.
fuente
Resuelva los problemas más grandes que hacen que la revisión del código sea demasiado difícil.
Los que he visto hasta ahora:
fuente
Puede devolver la revisión de código y decirle al desarrollador que la divida en conjuntos de cambios más pequeños e incrementales, y que envíe una revisión de código más pequeña.
Todavía puede verificar si hay olores de código, patrones y antipatrones, estándares de formato de código, principios SÓLIDOS, etc., sin tener que pasar necesariamente por cada detalle del código.
Aún puede realizar inspecciones de código táctico para una validación de entrada adecuada, bloqueo / gestión de subprocesos, posibles excepciones no controladas, etc. a un nivel detallado, sin comprender necesariamente la intención general de todo el conjunto de cambios.
Puede proporcionar una evaluación de las áreas de riesgo generales que sabe que pueden verse afectadas por el código, y pedirle al desarrollador que confirme que estas áreas de riesgo han sido sometidas a pruebas unitarias (o solicite que escriba pruebas unitarias automatizadas y que también las envíe para su revisión) )
fuente
Las revisiones de código no deben estar dirigidas principalmente a la corrección. Están aquí para mejorar la legibilidad del código, la mantenibilidad y el cumplimiento de los estándares del equipo.
Encontrar errores de corrección durante una revisión de código es un buen subproducto de hacerlos, pero un desarrollador debe asegurarse de que su código funcione perfectamente (incluida la no regresión) antes de enviarlo para su revisión .
La corrección se debe incorporar desde el principio. Si un desarrollador no puede lograrlo, haga que emparejen el programa o elaboren un plan con todo el equipo, pero no lo trate como algo que pueda agregar como una ocurrencia tardía.
fuente
Si cree que la revisión del código es demasiado difícil, porque cambió el código frágil que es casi imposible de cambiar sin romperlo, entonces tiene un problema. Pero el problema no es con la revisión del código. ¡El problema tampoco está en las pruebas unitarias, porque el código frágil no se puede probar en unidades! Si su código fuera comprobable por unidad, entonces se habría dividido en unidades pequeñas e independientes, que cada una puede probarse, y que funcionan bien juntas, ¡y eso es exactamente lo que no tiene!
Entonces tiene un montón de código basura (también conocido como "deuda técnica"). Lo peor que puede hacer es comenzar a arreglar ese montón de código de basura y no terminar el trabajo porque de esa manera obtendrá un montón de código de basura aún más grande. Entonces, lo primero es lograr que su gerencia compre la solución y termine el trabajo. O no lo haces. En ese caso, simplemente no lo toques.
Cuando lo arregla, extrae una unidad del código, lo convierte en algo que tiene un comportamiento bien definido y bien documentado, escribe pruebas unitarias para esa unidad, revisa el código y reza para que nada se rompa. Y luego haces lo mismo con la siguiente unidad, y así sucesivamente.
La parte difícil llega cuando te encuentras con errores. Sus nidos de código de ratas harán las cosas mal en algunos casos porque las cosas son tan frágiles y complicadas que las cosas saldrán mal. A medida que extraiga unidades, el código restante se volverá más claro. (Tuve un caso en el que después de una refactorización, una función comenzó con "if (condition1 && condition2 && condition3) crash ();" que era exactamente el comportamiento antes de refactorizar, solo que más claro. Luego borré esa línea :-) Verá comportamiento extraño e indeseable claramente, para que pueda solucionarlo. Por otro lado, ahí es donde debe cambiar el comportamiento del código existente, por lo que debe hacerse con cuidado).
fuente
Desafortunadamente, no hay mucho que pueda hacer al respecto en el momento de la revisión del código, aparte de tomar otra taza de café. La solución real para este problema es abordar la deuda técnica que ha acumulado: diseño frágil, falta de pruebas. Con suerte, al menos tienes algún tipo de control de calidad funcional. Si no tienes eso, siempre hay oraciones por algunos huesos de pollo.
fuente
Si no está contento con enviar software defectuoso / que no funciona y solucionarlo más tarde, ¡entonces el esfuerzo de V&V DEBERÍA ser más largo que el esfuerzo de desarrollo!
Si el código existente es frágil, entonces una primera pregunta es "¿debería incluso cambiarlo?" La gerencia debe hacer una llamada para determinar si el costo / riesgo de rediseñar y reimplementar este código es mayor que el costo / riesgo de arreglar la pila tambaleante de basura. Si es algo único, puede ser más fácil simplemente parchearlo. Si es probable que se necesiten más cambios en el futuro, tomar el golpe ahora para evitar más dolor en el futuro puede ser una mejor decisión. Debe plantear esto con su gerencia, porque dar a sus gerentes buena información es parte de su trabajo. Deben tomar esa decisión, porque es una decisión estratégica que está por encima de su nivel de responsabilidad.
fuente
Desde mi experiencia, le recomendaría encarecidamente que cubra su código con una buena cantidad de pruebas, tanto de unidad como de integración, ANTES de que se realicen cambios en el sistema en cuestión. Es importante recordar que hoy en día hay una muy buena cantidad de herramientas para ese propósito, sin importar el lenguaje con el que se esté desarrollando.
Además, existe LA herramienta de todas las herramientas para que pueda crear sus pruebas de integración. Sí, estoy hablando de contenedores y especialmente de Docker y Docker Compose . Nos brinda una forma hermosa de configurar rápidamente un entorno de aplicación complejo, con infraestructura (base de datos, mongodb, servidores de cola, etc.) y aplicaciones.
Las herramientas están disponibles, ¡úsalas! :)
fuente
No sé por qué no se ha mencionado aún, pero estas 2 son las piezas más importantes:
* Ejemplo: reemplaza la biblioteca A con la biblioteca B. Una lista de cambios introduce la biblioteca B, varias listas de cambios diferentes reemplazan el uso de A con B pieza por pieza (por ejemplo, una lista de cambios por módulo), y la última lista de cambios elimina la biblioteca A.
fuente
No subestimes el valor potencial de las revisiones de código. Pueden ser buenos para detectar errores:
También son útiles por otros motivos:
En el caso mejor / ideal, pasar la inspección del código no solo significa "no hay errores obvios": significa "obviamente no hay errores" (aunque, por supuesto, también querrá probarlo).
Si no puede verificar la nueva base de código a través de la inspección de código, necesitará una prueba de "caja negra" más extensa. Es posible que esté acostumbrado a un ciclo de desarrollo en el que ponga el código en producción después de pasar la inspección, pero si no puede "pasar la inspección", entonces no puede "ponerlo en producción" y necesita un ciclo más largo: por ejemplo, pruebas de integración , pruebas del sistema, pruebas alfa, pruebas de aceptación, pruebas beta, etc.
¿Qué pasa con las pruebas de integración, sistema y aceptación?
De todos modos, probablemente debería decirle al gerente de proyecto y al gerente de producto que el código es casi seguro que tiene errores, con un número desconocido de errores; y que "obtendrán lo que inspeccionan" en lugar de simplemente obtener "lo que esperan", es decir, que la calidad del código no es mejor que sus pruebas (porque la calidad del código no ha sido y no puede garantizarse mediante la inspección del código) .
Posiblemente deberían transmitir ese mensaje al cliente o a los usuarios, para que realicen pruebas beta (si están dispuestos a ser los primeros en adoptar), o utilicen la versión anterior hasta que la nueva versión esté fuera de beta (si no lo están).
fuente
Mucho código está escrito y fusionado sin una revisión adecuada del código. Puede funcionar. Hay una razón por la cual se llama olor a código, no "código roto" o algo por el estilo. La falta de revisión de código es una señal de advertencia, no un presagio de fatalidad.
La solución a este problema es que no hay una solución única para todos los casos que podamos empaquetar en una respuesta de estilo StackExchange. Es un fuerte consenso de la comunidad de desarrollo de software que la revisión de código es una "mejor práctica" crucial, y en este caso se está omitiendo. Su desarrollo ya no se encuentra en ese estrecho canal de "seguir todas las mejores prácticas". Necesitarás encontrar tu propio camino.
¿Qué es una "mejor práctica" de todos modos? Cuando llega el momento, es un conjunto de prácticas que la gente generalmente piensa que mejora el código. ¿Hacen el código correcto? ¡Diablos no! Internet está plagado de historias de compañías que siguieron las "mejores prácticas" y se atascaron. Quizás un mejor punto de vista de las "mejores prácticas" es que son las soluciones de "disparar y olvidar" del mundo del software. No puedo saber nada acerca de su empresa, su proyecto, su equipo y ser capaz de descartar las "mejores prácticas" como cosas que lo ayudarán. Son el consejo general de "no hacer daño".
Claramente te has desviado de este plan. Afortunadamente, lo reconoces. ¡Buen trabajo! Dicen que el conocimiento es la mitad de la batalla; Si es así, ¡la conciencia es más de la mitad! Ahora se necesita una solución. Según su descripción, está claro que el entorno empresarial en el que se encuentra ha evolucionado hasta un punto en el que el consejo aburrido de "ir a hacer la revisión del código, es la mejor práctica" no va a cortarlo. Para esto, recomiendo una regla clave que uso cuando se trata de las mejores prácticas de software:
Francamente, están pagando su sueldo, y la supervivencia de la empresa suele ser mucho más importante que la calidad del software. No nos gusta admitirlo, pero un software perfectamente escrito es inútil si está atrapado en el cuerpo de una empresa que muere por sus esfuerzos para mantener ese software perfectamente escrito.
Entonces a donde vas? Sigue el rastro de la fuerza. Usted ha señalado que, por alguna razón no declarada, no es razonable someterse a una revisión de código para alguna tarea. En mi experiencia, esa razón es siempre temporal. Siempre es "no hay suficiente tiempo" o "no hay suficiente dinero para mantener los sueldos fluyendo mientras pasas el tiempo". Esto es un negocio; está bien. Si fuera fácil, todos lo harían. Siga el rastro de la fuerza hacia arriba y encuentre la administración que esté en condiciones de ayudarlo a comprender por qué una revisión de código no es una opción. El lenguaje es difícil, y muy a menudo un decreto se filtrará desde la alta dirección y se distorsionará. La solución a su problema puede estar oculta en esa distorsión.
La respuesta a esto es, necesariamente, un escenario de caso específico. Es similar a tratar de predecir si un lanzamiento de moneda será cara o cruz. Las mejores prácticas dicen voltearlo 100 veces y la expectativa será de aproximadamente 50 caras y 50 colas, pero no tienes tiempo para voltearlo 1 vez. Aquí es donde importan los detalles de su situación. ¿Sabía que una moneda generalmente caerá en la misma orientación que fue lanzada aproximadamente el 51% del tiempo? ¿Te tomaste el tiempo de observar en qué dirección estaba la moneda antes de lanzarla? Podría hacer la diferencia.
Una solución general que puede estar disponible para usted es tratar de encontrar una manera de extraer el proceso de revisión del código y hacer que sea un esfuerzo de muy bajo costo. Gran parte del costo de un proceso de revisión de código es que todos están 100% dedicados a la revisión de código mientras lo está haciendo. Este tiene que ser el caso porque, una vez que se realiza la revisión del código, el código es bendecido. Quizás pueda colocar el código en una rama diferente y hacer la revisión del código en paralelo con el desarrollo en el tronco principal. O tal vez incluso puede configurarlo para que el software haga las pruebas por usted. Tal vez se encuentre en un entorno empresarial donde sus clientes puedan ejecutar el "nuevo" código en paralelo con el anterior y hacer que comparen los resultados. Esto convierte a los clientes en un montón de dispositivos de creación de casos de uso.
Una clave para todos estos "maybes" en ejecución es que debe esforzarse para que su código se divida fácilmente en pedazos. Es posible que pueda "probar" partes del código sin depender de una revisión formal del código usándolas en proyectos menos críticos. Es más fácil hacer esto si los cambios son en partes más pequeñas, incluso si la suma total de ellos es demasiado grande para una revisión por pares.
En general, busque soluciones específicas para su proyecto, su empresa, su equipo. La respuesta de propósito general fue "mejores prácticas". No los está utilizando, por lo que esta vez debería buscar soluciones personalizadas más personalizadas para este problema. Esto es un negocio. Si todo fuera como esperábamos todo el tiempo, las OPI serían mucho más fáciles de asignar valores, ¿no es así?
Si reemplazar una revisión de código es difícil, recuerde que nunca ha habido una sola pieza de código que se haya comprobado que funciona en una revisión de código. * Lo único que hace una revisión de código es darle confianza en el código y la oportunidad de hacer correcciones antes de que se conviertan en un problema. Ambos productos valiosos de una revisión de código pueden adquirirse por otros medios. La revisión de código tiene un valor reconocido por ser particularmente bueno en eso.
* Bueno, casi: el microkernel L4 recibió una revisión de código hace un tiempo por un sistema de prueba automatizado que prueba que su código, si es compilado por un compilador C ++ conforme, hará exactamente lo que dice la documentación.
fuente
Como @EricLippert señala en su excelente respuesta, este tipo de cambio necesita más atención, no menos . Si se da cuenta de que un cambio en el que está trabajando se convertirá en tal cambio, existen algunas estrategias que podrían ayudarlo:
fuente
Más respuestas abordan cómo llegaste a este punto. Muchos de ellos dan algunas sugerencias para remediar la situación, pero me gustaría arrojar mi respuesta para dar la respuesta breve.
¿Qué hacer cuando las revisiones de código son "demasiado difíciles"?
si
Ustedes desarrolladores fueron geniales! ¡Gatos para todos!
(o para aquellos que no crecieron viendo " The Simpsons " en la televisión estadounidense: si las pruebas pasan, omita tratar de ver las diferencias y que el desarrollador lo guíe en un recorrido por los cambios)
No
Siga refactorizando y agregando cobertura de prueba hasta que pasen las pruebas.
fuente
Al igual que una multiplicación, la revisión del código da un resultado cero cuando se aplica a cero. No aumenta el valor en tal caso, mientras que en la mayoría de los otros casos lo haría.
El código con el que necesita trabajar está demasiado mal diseñado para beneficiarse del proceso de revisión del código durante el desarrollo posterior. Use el proceso de revisión de código para refactorizarlo o volver a desarrollarlo.
También puede ser que el código aún sea soportable, pero la tarea no es buena. Es demasiado amplio y debería haberse hecho en incrementos más pequeños.
fuente