c++ - tutorial - Nuestro código apesta y soy incapaz de arreglarlo. ¡Ayuda!
unit test in c# (10)
Nuestro código apesta. En realidad, déjame aclarar eso. Nuestro viejo código apesta. Es difícil de depurar y está lleno de abstracciones que pocas personas entienden o incluso recuerdan. Ayer mismo, pasé una hora depurando en un área en la que he trabajado durante más de un año y me encontré pensando: "Wow, esto es realmente doloroso". No es culpa de nadie, estoy seguro de que todo tenía sentido al principio. La peor parte suele ser It Just Works ... siempre que no le pida que haga nada fuera de su zona de confort.
Nuestro nuevo código es bastante bueno. Creo que estamos haciendo muchas cosas buenas allí. Es claro, consistente y (con suerte) mantenible. Tenemos un servidor Hudson en ejecución para una integración continua y tenemos los inicios de un conjunto de pruebas de unidad en su lugar. El problema es que nuestra administración está enfocada en el láser para escribir Nuevo Código. No hay tiempo para darle al Código Antiguo (o incluso al Nuevo Código Nuevo) el TLC que tan desesperadamente necesita. En cualquier momento dado, nuestra cartera de scrum (para seis desarrolladores) tiene alrededor de 140 artículos y alrededor de una docena de defectos. Y esos números no están cambiando mucho. Estamos agregando cosas tan rápido como podemos quemarlas.
Entonces, ¿qué puedo hacer para evitar los dolores de cabeza de las sesiones de depuración de maratón en las profundidades del Código Antiguo? Cada sprint se llena hasta el borde con nuevos desarrollos y defectos impecables. Específicamente...
- ¿Qué puedo hacer para ayudar a que las tareas de mantenimiento y refactorización obtengan la prioridad suficiente para poder trabajar?
- ¿Hay alguna estrategia específica de C ++ que emplee para ayudar a evitar que el Nuevo Código se pudra tan rápidamente?
Reuniones de Stand Up
Podría ir a mi mecánico, y tenemos una pequeña reunión en la mañana:
Le digo que quiero que mis ruedas estén alineadas, mis llantas rotadas y mi aceite cambiado. Menciono que "Oh, por cierto, mis frenos se sintieron un poco blandos al entrar. ¿Podría [él] echarles un vistazo? ¿Cuándo podré recuperar mi auto porque necesito volver al trabajo?"
Él hace estallar su cabeza debajo de mi auto, aparece de nuevo y dice que mis frenos están goteando aceite y comienzan a fallar. Necesitará una parte que llegará a las 10:30 am. Su hombre no terminará antes del almuerzo, pero debería recuperar mi auto a la 1:30 pm aproximadamente. Ha hecho una reserva sólida para que no pueda hacer ninguna de las otras cosas hoy, y tendré que reservar otra cita.
Le pregunto si él puede hacer las otras cosas y vuelvo por el freno. Me dice que realmente no puede dejarme salir de allí sin arreglar los frenos porque podrían causar un accidente, pero si quiero ir a otro mecánico, puede pedir un remolque.
Como el automóvil estará listo poco después del almuerzo, le pregunto si su hombre puede almorzar tarde para que pueda recuperar mi automóvil una hora antes.
Me dice que sus hombres llegan a las 8 am y con frecuencia trabajan durante la noche. Ellos ganan cada descanso que obtienen, y su hombre merece almorzar con todos los demás.
Nada de eso es lo que quería escuchar. Quería saber que saldría de allí en media hora con las ruedas, los neumáticos y el aceite listos.
Mi mecánico fue directo y honesto conmigo. ¿Eres directo y honesto con tu gestión? ¿O evitas decirles cosas que no quieren escuchar?
Examen de la unidad
No tocaría una línea de código que no entendía, y no registraría una nueva línea de código que no probé a fondo. (Al menos, no intencionalmente.)
Su pregunta parece implicar que, de alguna manera, un gran corpus de código mal documentado pasó a ser revisado sin ninguna prueba unitaria. Tal vez usted participó en eso, y tal vez no lo hizo. Todos los involucrados deben aceptar la responsabilidad de eso, incluida la administración. En cualquier caso, lo que está hecho está hecho. No puedes volver atrás y cambiarlo.
Sin embargo, en este momento, en el momento presente, es responsabilidad de todos detener el comportamiento que condujo al problema en primer lugar. Dice que pasó un año trabajando en un código que le resulta difícil de entender y que no tiene pruebas unitarias. Durante ese año, mientras trabajaba duro para mejorar su comprensión, ¿cuántas pruebas de unidad escribió para documentar y verificar esa comprensión?
A medida que avanzaba en el código poco a poco adquiriendo comprensión, ¿cuántos comentarios agregó para que no tuviera que luchar la próxima vez?
Scrum Backlog
Personalmente, creo que el término "atraso de Scrum" es un nombre inapropiado. Una lista de cosas que hacer es solo una lista, una lista de compras si lo desea. Tuve una lista cuando fui al mecánico. Mi reunión con el mecánico fue realmente una reunión de planificación de sprint.
Una reunión de planificación de sprint es una negociación. Si su administración es boxeo de tiempo sin esa negociación, no están administrando nada. Simplemente están tratando de meter 10 libras de mierda en un saco de 5 libras, y es su responsabilidad decirles.
Cuando se presenta a una reunión de planificación de sprint, se espera que se comprometa con un cuerpo de trabajo, y es su responsabilidad prepararse para eso. La preparación significa tener una idea de lo que tendrá que hacer para completar cada elemento de la lista, incluido el tiempo necesario para comprender el código oscuro y el tiempo necesario para escribir pruebas unitarias.
Si alguien lo invita a una reunión de planificación en la que no tendrá tiempo para prepararse, rechace la reunión y sugiera cuándo debe reprogramarse para tener tiempo.
Si tiene un cuerpo de código existente sin pruebas de unidad y es probable que una característica afecte el funcionamiento de ese código, debe escribir pruebas de unidad para la mayor cantidad de código antiguo que pueda verse afectado. Cuando se compromete a escribir la característica, se compromete a hacer ese trabajo. Si eso te deja muy poco tiempo para comprometerte con alguna otra característica, solo dilo. No te comprometas con la otra característica.
Cuando se compromete a corregir un defecto, se compromete a probar su trabajo. Obviamente, eso significa escribir una prueba de unidad para el defecto. Pero si involucra código antiguo sin pruebas de unidad, también significa escribir pruebas de unidad para cosas que aún no están rotas, pero que podrían romperse debido a su cambio. ¿De qué otra manera va a probar la solución?
Si su lista de defectos sigue siendo un tamaño constante, su equipo retrocede tanto como corrige. Explica cortésmente a quien necesite entender que las pruebas unitarias impiden que las regresiones que actualmente mantienen tu lista de defectos se reduzcan.
Si no escribe esas pruebas de unidad porque se compromete con demasiadas funciones, ¿de quién es la responsabilidad?
Refactorización
Cuando refactoriza el código, tiene que probarlo todo, y eso significa escribir pruebas unitarias para todo el código. Si tiene una gran cantidad de código sin pruebas de unidad, tendrá que escribir todas esas pruebas de unidad antes de refactorizar.
Le sugiero que se abstenga de refactorizar hasta que esas pruebas de unidad estén en su lugar. Mientras tanto, si insiste en incluir pruebas de unidad en sus estimaciones para el trabajo que realiza, eventualmente todas esas pruebas de unidad estarán allí. Y luego puedes refactorizar.
La única excepción a eso es la refactorización para la probabilidad. Puede encontrar que parte del código no fue diseñado para la prueba y que tiene que refactorizar las cosas como la inyección de dependencia antes de poder crear sus pruebas unitarias. Cuando se compromete a escribir la función que requiere la prueba de la unidad, se compromete a hacer que el código sea verificable. Incluya eso en su estimación cuando se comprometa con la función.
Compromiso + Responsabilidad = Poder
Dices que eres impotente. Cuando acepta la responsabilidad y se compromete a hacer lo que se necesita hacer, creo que encontrará que tiene todo el poder que necesita.
PD: Si alguien se queja de que alguien haya "perdido el tiempo" escribiendo varias pruebas unitarias al corregir un solo defecto, muéstrele este video de la regla 80:20 y golpee los "grupos de defectos" en sus cerebros.
Algo para intentar: agrupe a su clase en, digamos, el peor 10%, el mejor 10% y el resto. Entregue las listas a su administración diciendo: "Predigo que la mayoría de los errores en el próximo trimestre se encontrarán en el primer grupo". Basado en la longitud, la complejidad ciclomática , la cobertura de prueba, cualquier herramienta que sea útil y cómoda para usted. Luego siéntate y mira, y sé bien. Ahora tiene cierta credibilidad, algo de apalancamiento cuando dice: "Me gustaría invertir algunos recursos para mejorar nuestro código erróneo, para reducir los errores y los costos de mantenimiento, y sé dónde invertir esa energía, ¿ven?"
Aparte de los enfoques mencionados anteriormente que son buenos, también puede probar estos:
Para mantener limpio el código futuro
- Pruebe la programación de pares, al menos para las partes que tienen sentido. Es una forma efectiva de obtener una revisión, refaccionar el código de una práctica.
- Trate de obtener refactorización en la definición de "hecho". Entonces será parte del proceso de estimación y se asignará en consecuencia. Por lo tanto, la definición de hecho podría incluir: codificado, unidad probada, funcionalmente probado, rendimiento probado, código revisado, refactorizado e integrado (o algo así).
Para limpiar el código antiguo:
- Las pruebas unitarias son excelentes para ayudarlo a refactorizar y descubrir cómo funcionan las cosas.
- Estoy de acuerdo con los comentarios de que se debe hacer un caso de negocios para la refactorización a gran escala. Pero, la refactorización a pequeña escala podría incluirse fácilmente en la estimación y proporcionará un rendimiento inmediato. es decir: pasé 2 horas reescribiendo una pieza, pero me hubiera pasado ese tiempo buscando errores de todas formas.
También es posible que desee considerar que el propietario del producto y scrummaster capturen una velocidad separada para el código antiguo en comparación con el código nuevo, y usarlo en consecuencia.
El código antiguo siempre apesta. Probablemente haya algunas raras excepciones escritas por personas con nombres como Kernighan o Thompson pero, por el típico "código escrito en una oficina", con el tiempo va a apestar. Los desarrolladores tienen más experiencia. Las nuevas prácticas, como la integración continua, cambian el juego. Las cosas se olvidan. Los nuevos mantenedores no logran captar los diseños y desean reescribirlos. Así que mejor acepta esto como normal.
Algunas cosas al azar que podrían ayudar ...
- Habla de eso con tu equipo. Comparta sus experiencias y sus preocupaciones, mientras evita "man su viejo código apesta" (por razones obvias) y vea cuál es el consenso. Probablemente no estés solo.
- Olvídate de tus gestores. No los exponga a este nivel de detalle: no necesitan pensar en el código nuevo frente al antiguo, y probablemente no lo entenderán si lo hacen. Este es un problema que su equipo debe abordar y, si es necesario, para que su OP sea consciente
- Esté abierto a la posibilidad de que pueda tirar cosas. Parte de ese código antiguo probablemente se relaciona con características que ya no se utilizan o que los usuarios no adoptaron en primer lugar. Para que esto funcione para usted, realmente necesita ir un nivel más alto y pensar en términos de dónde el código realmente proporciona valor para el usuario o la empresa en lugar de una bola de barro en la que nadie es lo suficientemente valiente para tomar una decisión. Quien se atreve, gana.
- Relaje su visión de la consistencia arquitectónica. Siempre hay una manera de aprovechar un sistema en funcionamiento con un nuevo código en alguna parte, y eso puede permitirle migrar lentamente a un enfoque más nuevo e inteligente, mientras conserva el antiguo el tiempo suficiente para no romper las cosas existentes.
En general, ganar en este tipo de situaciones es menos sobre las habilidades de codificación y mucho más sobre las elecciones inteligentes y el manejo de los aspectos humanos.
Espero que ayude.
Es difícil decir mucho de la información que da. Algunas de las preguntas que tendría es una razón lógica para escribir un nuevo código es reemplazar el código anterior. Si eso es lo que estás haciendo, abandona el código anterior.
¿Es también el código antiguo el que tiene defectos de showstopper? Si es así, ¿de dónde vienen? El código antiguo no tiene defectos de "showstopper", por lo general se aproxima cada vez más a un alto. Después de todo, es un código antiguo: debería tener los mismos defectos antiguos y las mismas limitaciones antiguas, no cosas que deban examinarse de inmediato. Los defectos de Showstopper son nuevos defectos de código. Parece que hay un desarrollo activo en el código antiguo.
Si está escribiendo todo este nuevo código encima del código antiguo que apesta, sin planes para solucionarlo de una vez por todas, lo siento, solo hay mucho que puede hacer cuando está demasiado ocupado enterrándose para desentenderse.
Si este último es el caso. Debes reconocer hacia dónde te diriges y tratar de separarte un poco. Va a colapsarse con el tiempo, si planeas estar cerca, guarda tu fuerza para una batalla que valga la pena.
Mientras tanto, trate de recoger algunos patrones de diseño. Hay varios que pueden al menos ayudar a proteger su nuevo código de las cosas antiguas, pero aún así, en última instancia, es difícil escribir un código bueno contra un código malo.
Y tus sprints suenan tal vez confundidos. ¿No hay una dirección general? Eso debería determinar la cantidad de trabajo atrasado que tiene, aunque las cosas pueden cambiar de mes a mes, ¿no hay un sentido claro de avanzar hacia algún objetivo final?
¿Y nuevo código podrido? La forma de evitarlo es tener un diseño significativo, una dirección significativa y un equipo de calidad comprometido tanto con la calidad de su trabajo como con la visión del diseño. Si tienes eso, disciplina es lo que mantiene la calidad. Si no lo lamenta, básicamente ya estaba escribiendo código sin ningún propósito. Era básicamente podrido en la vid.
No siendo crítico, solo tratando de ser honesto. Tomar una respiración profunda. Ve más despacio. Parece que lo necesitas. Mira lo que has escrito aquí. No dice nada. Hablas de refactor, scrums, showstoppers, defectos, código antiguo, código nuevo. ¿Qué significa eso? Todo está revuelto.
¿Qué pasa con las "nuevas iniciativas frente a los sistemas heredados"? "Es necesario refactorizar el código del ciclo de sprint temprano en términos de la última comprensión, etc." Son showstoppers de hecho "Los primeros componentes de las iniciativas empresariales actuales se han lanzado pero están experimentando problemas y no se ha presupuestado tiempo debido al nuevo desarrollo".
Estos serían conceptos significativos. No nos has dado nada. Entiendo que es intenso. Mis sprints también son una locura, agregamos muchos artículos de atrás; pg porque no pudimos obtener muchos requisitos por adelantado (muchos de mis nuevos requisitos se deben a que también deben competir con organismos reguladores externos, el proceso comercial normal no siempre está disponible ).
Pero, al mismo tiempo, estoy abatido por la magnitud de lo que hay que hacer y el momento de hacerlo. Todo lo que se agrega a mi cartera de pedidos debe estar allí. Es una locura, pero al mismo tiempo tengo una idea muy clara de dónde he estado, adónde tengo que ir y por qué el camino es más difícil.
Retroceda, aclare sus pensamientos, averigüe lo mismo: dónde ha estado y adónde va. Porque si lo sabes, seguro que no es obvio. Si no puede comunicar nada que sus compañeros puedan entender, ¿hasta dónde llegará con un gerente comercial?
Podría crear diagramas y bocetos de cómo funciona el nuevo código y cómo las clases y funciones están relacionadas entre sí. Podrías usar FreeMind o tal vez Dia. Y definitivamente estoy de acuerdo con Documentar y comentar tu código. Una vez tuve un problema con esto también. Escribí una clase de fuentes para J2ME para mi propio idioma. Fue horrible por estas razones que quizás también puedas ver en tu código.
- Sin comentarios ni documentación.
- Menos orientado a objetos
- malos nombres de variables / funciones
- ...
Pero después de unos meses me vi obligado a escribir todo de nuevo. Ahora he aprendido a usar nombres de variables significativos que a veces son MUY largos. Escribir comentarios más que escribir códigos. Y utilizando diagramas para las clases del proyecto y sus relaciones.
No sé si fue una respuesta real pero definitivamente funcionó para mí. y para los códigos antiguos es posible que tenga que volver a leer todo y agregar comentarios cuando recuerde las funcionalidades.
Espero que haya ayudado.
Recomiendo llevar un registro de cuántos errores y cambios de código involucran su "código antiguo" y presentarlo a su gerente o a sus colegas desarrolladores en su próxima reunión de equipo. Con esto en la mano, debería ser lo suficientemente simple como para convencerlos de que se debe hacer más para refactorizar su "código antiguo" y ponerlo a la par con su "código nuevo".
También sería prudente documentar las partes de su "código antiguo" que son más difíciles de entender. Estas también serían las partes de su "código antiguo" que debe refactorizar primero una vez que obtenga la aprobación.
Si hay una nueva característica deseada y puede delinear un trozo de código no abrumador que está en el camino, entonces podría obtener la bendición de la administración para reemplazar el código antiguo con el nuevo código que tiene la nueva característica deseada. Cuando hice esto, tuve que escribir una capa shim algo fea para cumplir con las antiguas interfaces de la parte del software que no iba a tocar. Y un arnés de prueba que podría ejercer el código existente y ejercer el nuevo código para asegurarse de que el nuevo código, tal como se ve a través de la capa de compensación, pueda engañar al resto de la aplicación para que piense que nada ha cambiado. Al volver a trabajar la parte que trabajamos nuevamente, pudimos mostrar enormes beneficios de rendimiento, compatibilidad con el nuevo hardware deseado, reducción de cada una de las necesidades de nuestro sitio de campo en cuanto a experiencia en la administración de espacio para la aplicación, y el nuevo código fue mucho más fácil de mantener. El último punto no importaba para los usuarios, pero las otras ventajas de la revisión eran lo suficientemente atractivas para "vender" a los usuarios en los méritos de una conversión de base de datos algo dolorosa.
Otra historia de éxito más modesta: teníamos un sistema de seguimiento de problemas decente que tenía literalmente años de historia. Hubo un subsistema de nuestra aplicación que era famoso por la velocidad con la que quemaría a los programadores de mantenimiento. Claramente (bueno, claramente en mi mente) necesitaba una reescritura importante, pero la administración no estaba entusiasmada con eso. Pudimos profundizar en la historia en los datos de seguimiento de problemas para mostrar el nivel de personal que se había empleado en el mantenimiento de este módulo y, a pesar de todo ese esfuerzo, los tickets de problemas por mes contra ese módulo continuaron llegando a una tasa constante. Cuando nos enfrentamos a datos reales como ese, incluso los gerentes reacios que durante mucho tiempo habían sido muy estrictos con respecto al trabajo de reelaboración de personal de ese subsistema podrían ver el mérito de asignar personal para volver a trabajar ese módulo.
El enfoque como antes era dejar la entrada y salida de ese módulo solo. La buena noticia fue que el lanzamiento de memoria virtual en el nuevo código con sus nuevas estructuras de datos de lujo dio una notable mejora en el rendimiento del módulo. La mala noticia es que casi terminamos con la reimplementación antes de que entendiéramos realmente lo que estaba mal en la implementación original, de modo que funcionó la mayor parte del tiempo, pero en algunos días pudimos fallar en algunas de las transacciones. El primer corte reproducía fielmente esos errores, pero los errores eran más fáciles de entender en el código modificado, por lo que ahora teníamos la oportunidad de solucionar realmente el problema real. En retrospectiva, quizás hubiéramos sido más inteligentes al haber capturado datos que produjeron los problemas y nos hubiéramos cuidado para asegurarnos de que la versión modificada no reprodujera ese problema. Pero, la verdad es que nadie entendió el problema hasta que estuvimos bastante avanzados en la reescritura. Por lo tanto, la reescritura mejoró el rendimiento de los usuarios y mejoró la comprensión de los programadores actuales, de modo que el verdadero problema podría resolverse por fin.
Un ejemplo de falla: había otro módulo increíblemente feo que persistentemente era un punto delicado. Lamentablemente, no era lo suficientemente inteligente como para poder entender las interfaces de facto de esta miserable colmena de escoria y villanía, al menos no en el marco temporal del programa de lanzamiento nominal. Me gustaría creer que dado más tiempo también podríamos haber ideado un plan adecuado para volver a trabajar esa parte del sistema, y quizás una vez que lo entendamos, incluso podríamos identificar las mejoras deseadas por el usuario que podríamos encajar en el volver a escribir. Pero no puedo prometer que encontrarás un premio en cada caja. Si la caja es completamente oscura para usted, es difícil hacer un trozo de ella y reemplazar esa pieza con un código limpio. El tipo que se encargó de ese módulo es probablemente el mejor posicionado para descubrir un plan de ataque, pero vio los frecuentes choques y llamadas desde el campo para solicitar asistencia como "seguridad laboral". No creo que la gerencia haya reconocido realmente que necesitaba que lo dejaran de lado para alguien con hambre de cambio, pero eso es lo que probablemente era necesario.
Dibujó
Su administración puede estar enfocada en obtener características de trabajo en el producto y mantenerlas funcionando. En este caso, tendrá que presentar un caso de negocios para refactorizar las cosas antiguas, ya que con X la inversión de tiempo y esfuerzo puede reducir el tiempo de mantenimiento necesario en Y durante el período Z. O su administración puede no tener ni idea (esto sucede, pero menos a menudo de lo que la mayoría de los desarrolladores parecen pensar), en cuyo caso nunca obtendrás permiso.
Necesitas ver el punto de vista empresarial. No le importa al usuario final si el código es feo o elegante, solo lo que hace el software. El costo del código incorrecto es la falta de fiabilidad potencial y la dificultad adicional para cambiarlo; la angustia emocional que causa al programador rara vez se considera.
Si no puede obtener permiso para entrar y refactorizar, siempre puede intentarlo por su cuenta, poco a poco. Cada vez que arregles un error, reescribe un poco para aclarar las cosas. Esto puede llegar a ser más rápido que la solución mínima posible, especialmente al verificar que el código ahora funciona. Incluso si no lo es, por lo general es posible dedicar un poco más de tiempo a una corrección de errores sin tener problemas. Simplemente no te dejes llevar.
Si puede dejar el código un poco mejor cada vez que ingrese, se sentirá mucho mejor al respecto.
¡Habla con el propietario de tu producto ! Explique que el tiempo invertido en refactorizar el código anterior lo beneficiará de una mayor velocidad del equipo en las nuevas funciones una vez que se elimine este obstáculo.