¿Cómo es un commit "perfecto" en git?
Durante los últimos meses, y con el contexto de estar cerca de finalizar de cursar Ingeniería de Software 2, he tratado de centrar mi trabajo en la creación de lo que considero el commit perfecto en git. Este es un único commit que, a mi parecer, debería contener todo lo siguiente:
- La aplicación: un cambio único y centrado.
- Pruebas unitarias que demuestran que la aplicación funciona.
- Documentación actualizada que refleja los cambios.
- Un enlace al issue de Github conteniendo más contexto con el problema.
Nuestro trabajo como ingenieros de software generalmente no consiste en escribir nuevo software desde cero: pasamos la mayor parte de nuestro tiempo añadiendo características y corrigiendo errores en el software existente. El commit es nuestra principal unidad de trabajo. Merece ser tratada con cuidado y atención.
Implementación
Cada confirmación debe cambiar una sola cosa. La definición de "cosa" aquí es deliberadamente vaga. El objetivo es tener algo que pueda ser fácilmente revisado, y que pueda ser entendido en el futuro cuando se revise usando herramientas como git blame o git bisect. Me gusta mantener mi historial de commits lineal, ya que me parece que hace que sea mucho más fácil de comprender más tarde. Esto refuerza aún más el valor de que cada commit sea un cambio único y enfocado. Los commits atómicos también son mucho más fáciles de revertir de forma limpia si algo va mal, o de hacer un cherry-pick en otras ramas.
Para cosas como las aplicaciones web que pueden ser desplegadas en producción, un commit debe ser una unidad que pueda ser desplegada. El objetivo de mantener la rama principal en un estado desplegable es una buena regla general para decidir si una confirmación es un cambio atómico razonable o no.
Pruebas unitarias
El objetivo final de las pruebas es aumentar su productividad. Si tus prácticas de prueba te están ralentizando, deberías considerar formas de mejorarlas. A largo plazo, esta mejora de la productividad se debe a la libertad de hacer cambios y a la seguridad de que el cambio no ha roto nada más. Pero las pruebas también pueden ayudar a aumentar la productividad a corto plazo.
¿Cómo se sabe cuándo el cambio que se ha realizado está terminado y listo para confirmarlo? Está listo cuando las nuevas pruebas pasan. Creo que esto reduce el tiempo que dedico a cuestionarme si he hecho lo suficiente y si he pensado en todos los casos extremos. Sin pruebas, hay una gran posibilidad de que tu cambio haya roto alguna otra característica no relacionada. Tu confirmación podría estar retenida por horas de tediosas pruebas manuales. O podría marcarme un YOLO y descubrir que ha roto algo importante más tarde.
Escribir pruebas consume mucho menos tiempo si ya tienes buenas prácticas de pruebas. Añadir una nueva prueba a un proyecto con un montón de pruebas existentes es fácil: a menudo se puede encontrar una prueba existente que tiene el 90% del patrón que necesita ya trabajado para usted. Si tu proyecto no tiene ninguna prueba, añadir una prueba para tu cambio será mucho más trabajo.
Por eso empiezo cada uno de mis proyectos con una prueba de paso. No importa cuál sea esta prueba: ¡afirmar 1 + 1 == 2 está bien! La clave es conseguir un marco de pruebas en su lugar, de tal manera que se puede ejecutar un comando (para mí que suele ser jest) para ejecutar el conjunto de pruebas, y tener un lugar obvio para añadir nuevas pruebas en el futuro.
Yo suelo utilizar estas plantillas cookiecutter para casi todos mis nuevos proyectos. Configuran un marco de pruebas con una única prueba de paso y flujos de trabajo de Acciones GitHub para ejercitarlo todo desde el principio. No soy un gran defensor del desarrollo "test-first", donde las pruebas se escriben antes que el propio código. Lo que me interesa es el desarrollo con pruebas incluidas, donde el commit final agrupa las pruebas y la implementación.
Documentación
Si tu proyecto define APIs que están destinadas a ser utilizadas fuera de tu proyecto, es necesario documentarlas. En la carrera, estos proyectos suelen ser uno de los siguientes.
- APIs de Python (módulos, funciones y clases) que proporcionan código diseñado para ser importado en otros proyectos.
- Las APIs de la web, por lo general JSON, sobre HTTP en estos días- que proporcionan funcionalidad para ser consumida por otras aplicaciones.
- Herramientas de interfaz de línea de comandos, como las implementadas con Click o Typer o argparse.
Es fundamental que esta documentación viva en el mismo repositorio que el propio código. Esto es importante por varias razones. La documentación sólo es valiosa si la gente confía en ella. La gente sólo confiará en ella si sabe que se mantiene actualizada. Si tu documentación vive en una wiki separada en algún lugar, es fácil que se desactualice, pero lo más importante es que es difícil para cualquiera confirmar rápidamente si la documentación se está actualizando en sincronía con el código o no.
La documentación debe ser versionada. La gente necesita ser capaz de encontrar la documentación para la versión específica de su software que están utilizando. Mantenerla en el mismo repositorio que el código le proporciona un versionado sincronizado de forma gratuita. Los cambios en la documentación deben ser revisados de la misma manera que el código. Si viven en el mismo repositorio, puedes detectar los cambios que deben reflejarse en la documentación como parte de tu proceso de revisión del código. E idealmente, la documentación debería ser probada. Escribí sobre mi enfoque para hacer esto usando pruebas de unidad de documentación. Ejecutar el código de ejemplo en la documentación utilizando un marco de pruebas es una gran idea también.
Al igual que con las pruebas, escribir la documentación desde cero es mucho más trabajo que la modificación incremental de la documentación existente. Muchos de mis commits incluyen documentación que es sólo una frase o dos. No me lleva mucho tiempo escribirla, pero con el tiempo se convierte en algo muy completo.
¿Qué hay de la documentación para el usuario final? Yo mismo todavía estoy averiguando eso. Creé mi herramienta de captura para ayudar a automatizar el proceso de mantener las capturas de pantalla actualizadas, pero todavía no he encontrado hábitos y estilos personales para la documentación del usuario final en los que confíe.
Enlaces a issues de Github
Cada confirmación perfecta debería incluir un enlace a un hilo de problemas que acompañe a ese cambio. A veces incluso abro un tema segundos antes de escribir el mensaje de confirmación, sólo para tener algo que pueda enlazar desde la propia confirmación. La razón por la que me gustan los hilos de incidencias es que proporcionan un espacio ilimitado para los comentarios y los antecedentes del cambio que se está realizando. La mayoría de mis hilos de incidencias son yo mismo hablando, a veces con docenas de comentarios de incidencias, todos escritos por mí.
Pasé por una fase de varios años de escribir ensayos en mis mensajes de confirmación, tratando de capturar la mayor cantidad de contexto y pensamiento de fondo como sea posible. Mis mensajes de confirmación se hicieron mucho más cortos cuando empecé a incluir la documentación actualizada en la confirmación, ya que a menudo gran parte del material que había incluido previamente en el mensaje de confirmación estaba ahora en esa documentación.
A medida que ampliaba mi práctica de escribir hilos de emisión, descubrí que eran un lugar mejor para la mayor parte de este contexto que los propios mensajes de confirmación. Admitían medios incrustados, eran más fáciles de encontrar y podía seguir ampliándolos incluso después de que la confirmación hubiera llegado. Hoy en día, muchos de mis mensajes de confirmación son un resumen de una sola línea y un enlace a una cuestión.
El mayor beneficio de los mensajes de confirmación largos es que están garantizados para sobrevivir tanto tiempo como el propio repositorio. Si vas a utilizar los hilos de emisión de la manera que describo aquí, es fundamental que consideres su valor de archivo a largo plazo. Espero que esto sea controvertido. Estoy defendiendo el abandono de una de las ideas centrales de Git: que cada repositorio debería incorporar un registro completo y descentralizado de su historia que se copie en su totalidad cuando alguien clone un repositorio.
Entiendo esa filosofía. Todo lo que diré aquí es que mi propia experiencia ha sido que dejar de lado ese requisito ha resultado en un aumento neto de mi productividad general. Otras personas pueden llegar a una conclusión diferente. Si esto te ofende demasiado, puedes construir un commit aún más perfecto que incorpore información de fondo y contexto adicional en un mensaje de commit extendido también.
Una de las razones por las que me gusta GitHub Issues es que incluye una completa API, que puede utilizarse para extraer todos esos datos. Utilizo mi herramienta github-to-sqlite para mantener un archivo continuo de mis problemas y comentarios de problemas como un archivo de base de datos SQLite.
No todos los commits deben ser "perfectos"
Creo que la gran mayoría de mi trabajo se ajusta a este patrón, pero hay excepciones. ¿Se ha corregido un error tipográfico en la documentación o en un comentario? Simplemente envíalo, está bien. ¿Corrección de un error que no merece documentación? Sigue incluyendo la implementación y la prueba, además de un enlace a un problema, pero no es necesario actualizar la documentación, especialmente si ya describe el comportamiento esperado sin errores. Sin embargo, en general, creo que el objetivo de la implementación, las pruebas, la documentación y el enlace a un problema cubre casi todo mi trabajo. Es un buen modelo por defecto.
- Zant.
No hay comentarios: