Mi commit de Git favorito (2019)
(dhwthompson.com)- El commit “Convert template to US-ASCII to fix error” de Dan Carley, surgido durante el trabajo en GOV.UK, muestra que incluso un pequeño cambio de código puede convertirse en documentación de largo plazo para una base de código mediante un mensaje de commit detallado
- Un buen mensaje deja registro de por qué se cambió algo más que de qué se cambió, y este caso documenta en concreto el error
invalid byte sequence in US-ASCIIque ocurría al ejecutarbundle exec rake, junto con las condiciones para reproducirlo - Como quedaron registrados tanto la cadena del error como el proceso de investigación, alguien que más adelante encuentre el mismo problema puede buscar el registro de una solución previa con
git log --grepo con la búsqueda de commits de GitHub - El mensaje también incluye el uso de herramientas Unix como
find -exec,file --mimeeiconv, lo que permite a quienes revisan el cambio y a lectores futuros seguir el flujo de resolución del problema - No todos los commits tienen que ser tan largos, pero los mensajes que conservan contexto, proceso de investigación e incluso emociones ayudan a construir una comprensión compartida de la base de código y confianza dentro del equipo
Un commit que convirtió un cambio pequeño en documentación de largo plazo
- Un mensaje de commit de Git, si está bien escrito, puede ser una herramienta poderosa de documentación que se mantiene durante toda la vida de una base de código
- El commit usado como ejemplo corresponde a un cambio de la época en que se trabajaba en GOV.UK dentro del Government Digital Service
- El autor es Dan Carley y el título es “Convert template to US-ASCII to fix error”
- Gracias al modo de desarrollo abierto de GDS, incluso estos casos internos de una organización pueden compartirse hacia afuera
El contexto importa más que la longitud
- Este commit tiene un mensaje largo en comparación con la cantidad de código cambiado, pero su valor principal no está en la longitud en sí, sino en el contexto útil
- El mismo cambio, en otro lugar, podría haber quedado registrado de forma breve como
change whitespaceofix bug - Dan dejó un registro detallado para que sus colegas cercanos y lectores futuros pudieran entender la causa del problema y el proceso de solución
Más que qué cambió, por qué cambió
- Un buen mensaje de commit explica no solo qué cambió, sino también por qué cambió
- Este commit registra que, después de agregar en una feature branch una prueba que comparaba el contenido de
/etc/nginx/router_routes.conf, los resultados diferían según la forma de ejecución- Si se ejecutaba con
bundle exec rake specobundle exec rspec modules/router/spec, funcionaba correctamente - Si se ejecutaba con
bundle exec rake, fallaba cada bloqueshould - El error era
ArgumentError: invalid byte sequence in US-ASCII
- Si se ejecutaba con
- Sin esa explicación, probablemente habría que adivinar en qué herramienta ocurrió qué error de parsing
- El contexto original del trabajo puede desaparecer fácilmente cuando las personas lo olvidan, cambian de equipo o dejan la organización
Un registro de error que se puede buscar
- Al comienzo del mensaje de commit aparece tal cual el mensaje de error que motivó el cambio
- Quien encuentre el mismo error puede buscar registros previos en la base de código de las siguientes maneras
git log --grep "invalid byte sequence"- Búsqueda de commits en GitHub
- A partir de los resultados de búsqueda, se pudo comprobar que varias personas buscaron este error, quién lo encontró primero y qué medidas tomó
Dejar el proceso de resolución como una historia
- El mensaje está estructurado de forma que permite seguir cómo se veía el problema, en qué orden se investigó y cómo finalmente se corrigió
- Por ejemplo, incluye que el error desaparecía al quitar el matcher
.with_content(//), que no había caracteres extraños en el archivo spec y que podía reproducirse al hacer require de Puppet en el mismo intérprete - Como un mensaje de commit no documenta un archivo, una función o una línea de código en particular, sino el cambio en sí, es un buen lugar para registrar el recorrido que atravesó una base de código
Efectos secundarios que amplían el conocimiento del equipo
- Dan registró los comandos que ejecutó en cada etapa de la investigación, y eso puede ser una forma ligera de difundir conocimiento dentro del equipo
- Quien lee puede aprender usos de herramientas Unix solo con el mensaje de commit
- Se puede pasar el argumento
-execafindpara ejecutar un comando en cada archivo encontrado - Si se agrega
\+al final del comando, en lugar de ejecutarlo una vez por archivo, se pasan varios nombres de archivo a un único comandofile file --mimeindica el tipo MIME de un archivo- Existe una herramienta llamada
iconv
- Se puede pasar el argumento
- La misma información llega no solo a quienes revisan el cambio, sino también a quienes encuentren este commit más adelante
- Con suficiente tiempo y commits acumulados, estos mensajes pueden convertirse en un mecanismo de amplificación del conocimiento del equipo
Un registro que construye empatía y confianza
- Al final del commit aparece la frase: “Now the tests work! One hour of my life I won't get back..”
- Esa frase transmite la frustración de un desarrollador que pasó una hora rastreando un bug sutil y la satisfacción de haberlo resuelto
- Incluso cuando hacks de corto plazo o código prototipo terminan entrando a producción y echando raíces, mensajes como este recuerdan que detrás de cada cambio hubo una persona que tomó la mejor decisión posible con la información disponible en ese momento
No todos los commits tienen que ser largos
- Este caso es un ejemplo extremo, y no hace falta esperar el mismo nivel de detalle para todos los commits de este tamaño
- Aun así, es un buen ejemplo de cómo explicar el contexto detrás de un cambio, ayudar a que otros aprendan y contribuir al modelo mental compartido del equipo sobre la base de código
- Si te interesan los buenos mensajes de commit y las herramientas para estructurar cambios, puedes consultar estos recursos
- Telling stories through your commits de Joel Chippindale
- A branch in time de Tekin Süleyman
1 comentarios
Opiniones de Hacker News
Para bien o para mal, desde mi posición como cofundador de GitHub y autor de libros sobre Git como Pro Git, los mensajes de commit de Git son una vía peculiar para documentar código, pero muy ineficiente.
El problema central es que la mayoría de las herramientas, como Git y GitHub, normalmente solo muestran la primera línea. Incluso el commit de ejemplo solo muestra una línea genérica como “US-ASCII error”, y casi nadie ve en las herramientas modernas el resto del contenido que el artículo elogia.
Git diseñó los mensajes de commit como el cuerpo de un correo electrónico que leería todo el proyecto, pero hoy en día, en general, no cumplen ese rol. A menos que se discutan como una serie de parches en una lista de correo, casi no se lee nada más que los primeros 50 caracteres del título.
Incluso si en
git blamebuscas si es-w -C -C -Co dos-Cpara rastrear el mensaje relacionado, solo ves la primera línea, y al final tienes que encontrar el SHA y hacergit showpara ver el mensaje largo. Una de mis mayores quejas sobre Git es que, aun cuando se usa bien, es demasiado difícil volver a encontrar la información.Si miras el historial del proyecto Git, los mensajes de commit son excelentes casi siempre, pero aun ejecutando
blamesobre un archivo, es muy difícil seguir el contexto de la implementación de una función. La documentación que Jeff King ha dejado durante los últimos 10 años es de nivel genial, y es terrible que casi nadie pueda apreciarla.No sé cuál sea la solución exacta, pero en la mayoría de las comunidades escribir gran documentación en los mensajes de commit, tristemente, es casi una pérdida de tiempo. Porque es demasiado difícil encontrarla.
git blame,git logygit showen la terminal; seguir el historial de un archivo es fácil, y congit log -Gbastan unos segundos para encontrar cuándo se agregó o eliminó algo.Lo difícil es cuando encuentras el commit y el mensaje solo dice “bleh” o “add a thing”. Bastaba con que el desarrollador dedicara 60 segundos a escribir por qué lo hizo.
En cambio, cuando encuentro un mensaje de commit que explica en detalle por qué se hizo un cambio, me da muchísimo gusto. Un buen mensaje de commit ahorra horas o días de trabajo.
GitHub también contribuye al problema de los malos mensajes de commit. Si tienes suerte, la descripción del PR tiene detalles, pero no está justo junto al log de commits; y normalmente el PR lleva a un enlace de Jira, Jira a una conversación de Slack, y Slack a un documento de Google.
La industria es pésima documentando, pero personas como Jeff King están dando la batalla correcta. Al final, creo que no es un problema técnico, sino humano. Escribir documentación no tiene recompensa inmediata, se siente como trabajo extra, y la recompensa recién aparece días, semanas o meses después.
Por favor, escriban buenos mensajes de commit. Si dedicas solo 1 minuto a anotar por qué lo hiciste, no todos los commits se convierten en una maldita resolución de la cerca de Chesterton. Si lo dejas en un mensaje de commit fácil de encontrar, tu yo del futuro y yo lo vamos a agradecer.
Además, tampoco estoy de acuerdo con que sea difícil encontrar los mensajes de commit. Casi no tengo problemas para seguir la historia de una línea de código, una función o un archivo; y aunque el mensaje de commit no se vuelva a leer, tiene valor al momento de escribirlo. Escribir un buen mensaje me obliga a comprobar si realmente escribí el código que pretendía, y también aporta valor al revisor del código.
Lo de que es difícil encontrar las opciones de
git blametambién suena a broma. Un buen IDE debería adjuntar información de blame a cada línea y mostrar el diff con un solo botón; y desde ese diff debería permitir hacer blame recursivamente sobre líneas de contexto o líneas eliminadas. Herramientas como Tig hacen exactamente eso.Sí admito que GitHub hace difícil ver los mensajes de commit.
La documentación adjunta a un commit no se escribe por diversión. Es un mecanismo para reducir el riesgo de aceptar un parche enviado por alguien que quizá no esté después, y para exponer el razonamiento relacionado de modo que otros puedan trabajar sobre esa base. Si se ocultan las ideas, se acumula una propiedad exclusiva sobre el código, y eso normalmente no es bueno. Los mensajes de commit también sirven como prueba de trabajo, y pueden volverse importantes cuando hay demasiados parches. En proyectos comerciales, parte de su importancia puede ser menor.
En realidad no hay otro lugar donde poner este tipo de documentación. No encaja en comentarios de código. Pero ahora existe una generación de desarrolladores que piensa que Git es GitHub, y que el único propósito de Git es subir cambios a GitHub.
Git no es muy bueno, pero es mucho menos malo que las alternativas. Hay que volver a lo básico y entender el propósito real del control de versiones.
Así que quizá no ayude mucho a la comunidad actual, pero sí le sirve a alguien que esté depurando dentro de unos años.
Estoy de acuerdo con la intención general, pero sería mejor poner un BLUF más concreto al principio. Por ejemplo, algo como “Fix test issues caused by non-breaking space character \xa0”
Así se entiende de inmediato cuál es el problema, y si alguien quiere saber más, tiene la libertad de leer lo que sigue
Así que, para buscar en el historial, basta con una buena primera línea del mensaje. No me parece muy relevante el cuentito de ir hasta Narnia y volver para encontrar la causa raíz del bug. Eso sí, no me opongo a que se use como desahogo en la descripción del PR o en el mensaje extendido del commit
He sentido el orgullo de escribir un excelente mensaje de commit, pero estoy menos seguro del valor que aporta a otras personas. Cuando alguien se encuentra con un mensaje de error raro, agrega una funcionalidad nueva o casi en cualquier otra situación, creo que la mayoría no busca en los mensajes de commit
Es un poco triste, pero cada vez sospecho más que los mensajes de commit hermosos tienen algo de vanidad de programador. Quien más los admira suele ser la propia persona que los escribió, mientras los demás pasan de largo sin notarlos
A veces hay lugar para ese tipo de adorno estético, pero no estoy convencido de que tenga mucho valor práctico. Si otra persona commitea “fix whitespace issue”, ya no me molesta demasiado, y creo que eso me convirtió en un mejor compañero
En proyectos como Git o Linux, con equipos distribuidos enormes y muchísimos commits, puede ser distinto. Los proyectos con los que estoy familiarizado tienen entre 1 y 100 colaboradores, y la mayoría pertenece a la misma organización
Cuando Google o Stack Overflow no dan respuesta, a veces busco en GitHub si hay algo parecido en un PR o en un mensaje de commit. Me ha ayudado innumerables veces, incluso en repositorios privados a los que tengo acceso. Un buen mensaje de commit tiene valor claramente más allá de la vanidad. Si muchos desarrolladores no los miran, ellos se lo pierden, y espero que con la experiencia algún día se den cuenta. También se le puede enseñar a un desarrollador junior cómo buscar, o enviarle el artículo original
Desde entonces intento escribir mensajes de commit que al menos le permitan a mi yo del futuro recordar por qué hacía falta el cambio
git blameantes de cambiar una línea de código, lo estás haciendo malVarias veces me pasó que estaba por arreglar un bug evidente y, justo antes de commitear, revisé el commit anterior y vi que ese “bug” había sido introducido a propósito, y que la tarea de JIRA vinculada explicaba muy bien por qué mi cambio obvio volvería a crear un bug de hacía 2 años
git blamedel IDE para entender qué pasó. Cuando encuentro un buen mensaje de commit, lo agradezcoLa primera línea de un mensaje de commit es la más importante porque permite que
git logmaneje la valla de Chesterton. En este caso, creo que el autor erró el golpeLa clave es poner en la primera línea por qué se hizo, no qué se hizo. Quien quiera saber qué cambió puede mirar el código o el diff
Por ejemplo, escribir “nginx .conf files must be in us-ascii”, luego “changed blahblah.erb to remove nonbreaking space character”, y después continuar con el resto, bastante bueno, del mensaje de commit
Hay que pensarlo como una nota periodística. Hay que asumir que el lector puede dejar de leer en cualquier punto, y escribir en orden de importancia decreciente y detalle creciente
Las notas periodísticas también explican el qué en el título, no el porqué
En este caso, la primera línea del original es exactamente adecuada. Al hojear
git log, puedes ver que es muy probable que este commit no haya cambiado el comportamiento funcional de las pruebas y seguir de largo“nginx .conf files must be in us-ascii” puede ser un buen título para un bug o un PR, pero también podría corresponder a varios commits que hacen cosas distintas, y no dice qué ocurrió realmente. No se sabe si se convirtieron archivos a US-ASCII, si se creó una herramienta de conversión, si se actualizó la documentación, si se hicieron pruebas, o una combinación de todo eso. Empezar con el qué, no con el porqué, reduce esa confusión
Da más o menos igual si es “The files must be in us-ascii” o “Changed the files to us-ascii”. Ambas dejan claro que los archivos se cambiaron a US-ASCII
La desventaja de esta forma de documentación es que, en la práctica, una vez escrito, un mensaje de commit no se puede cambiar
Claro, se puede con
rebase, pero si algo se escribió hace un año y ya se fusionó enmain, ¿de verdad lo vas a cambiar y causarles dolor a las ramas de funcionalidades de todos?Comparado con documentación guardada en archivos
.md, una Wiki o Confluence, yo puedo mejorar lo que escribió un colega, y otro colega también puede mejorar lo que escribí yoEn este caso, quizá el bug se corrigió y no vuelva a aparecer. Pero existe la tentación de explicar el diseño de un componente específico al hacerle commit, y ahora la evito. ¿Qué pasa si ese componente cambia más tarde por cambios en los requisitos de negocio, etc.? ¿La documentación del commit terminará explicando solo la diferencia? Entonces, para que un nuevo integrante del equipo entienda el sistema, tendrá que leer varios mensajes de commit y fusionarlos mentalmente
Compararlo con archivos
.md, una Wiki o Confluence es como comparar una bicicleta con un gansoLas consideraciones y concesiones de cuando se creó el componente, o incluso el hecho de que no las hubiera, suelen ser útiles para entender defectos del diseño original, defectos surgidos por su evolución posterior y cambios en los casos de uso
Si un nuevo integrante del equipo necesita entender el sistema, basta con mantener documentación “actual” aparte. Esa documentación no necesita cubrir las concesiones de implementación ni el hecho de que el borrador se escribió bajo presión de tiempo
Si haces squash de los commits, el registro mejora como relato de la incorporación de una funcionalidad; el merge conserva el registro inmutable de los cambios reales de código; y el rebase hace un poco de ambas cosas
No hay razón para no poder tener las dos cosas. Somos gente que programa computadoras, así que podemos construirlo como queramos
Si reescribiera Git desde cero, dividiría los commits en dos partes. Dejaría el historial de cambios inmutable, como en Git, con una estructura tipo Merkle DAG, y guardaría los mensajes de commit en un almacén de datos asociado aparte, para convertirlos en un registro razonable y editable que explique el flujo de trabajo. Bastaría con agrupar commits en torno a etiquetas de funcionalidades, corregir errores tipográficos y cambiar los mensajes como uno quiera, preservando intacto el registro de diferencias subyacente, es decir, “lo que realmente cambió en el código”
git notes. Aunque, si el mensaje es tan largo, parece poco probable que alguien lo noteHay una parte con la que no estoy de acuerdo: “no espero este nivel de detalle en todos los commits, especialmente en commits de este tamaño”
Según mi experiencia, más bien las cosas pequeñas y aparentemente inocuas son las que muchas veces necesitan explicaciones relativamente más largas
Ayer escribí tres párrafos sobre por qué agregué
--limit=999agh pr list. Porque es confuso. Dentro del argumento--jqya hay unlimit(, y si asumimos que hay infinitos PR en total, cuanto más alto sea el valor, el resultado final en realidad es más bajoTambién escribí un comentario en el código, y probablemente pasé más tiempo pensándolo y puliéndolo que escribiendo. Espero que se recuerde este ejemplo la próxima vez que alguien insinúe que esto consiste en producir código a lo loco
Algo como “This was a non-ascii whitespace character that caused
ArgumentError: invalid byte sequence in US-ASCIIwhen runningbundle exec rake” sería suficiente. Tiene palabras clave para buscar un problema parecido más adelante, incluye la causa raíz y no es tan largo como para que la gente probablemente lo pase por altoNo creo que sea un gran commit de Git
Para tanto texto, la primera línea “Convert template to US-ASCII to fix error” podría ser mejor. Bastaría con agregar unas pocas palabras sobre qué carácter de espacio en blanco causaba el error y cuál era el error. Con esa explicación y el diff, el contexto necesario es suficiente
Sinceramente, el resto es casi inútil. No hace daño, pero tampoco aporta mucho valor. El autor documentó el recorrido que hizo para rastrear este bug, pero me pregunto a quién le importa
El artículo también incluye un enlace a resultados de búsqueda que muestran varios commits de personas que aprendieron de esa corrección
Ese mensaje de commit es un tesoro de conocimiento
Para ver excelentes mensajes de commit, mira el historial de Git del kernel de Linux. Ahí esto es el estándar
La primera línea siempre menciona el subsistema afectado por el cambio y luego incluye un resumen imperativo de una línea. Después responde con el mayor detalle posible tres preguntas
Un ejemplo podría escribirse así: “El código actual hace X. Al ejecutar el caso de prueba T, se observó el comportamiento inesperado U. Esto se debe a la razón R. Se corrige haciendo F”
Hace poco, un colaborador me dijo que mi enfoque para los mensajes de Git —es decir, mis requisitos— era “peculiar”. Supongo que se nota mi trasfondo con el kernel de Linux, pero todos mis mensajes de commit se ven como los de aquí.
Si se trata de algo sobre el código existente, el comentario debe estar junto con el código. Los asuntos relacionados con el proceso, por ejemplo código eliminado o código que no funcionaba, deben estar en el commit.
Lo más importante es que, aunque un mensaje de commit puede hacer referencia a un issue por conveniencia, debe reproducir los detalles esenciales. GitHub es temporal; los mensajes de Git no.
Aquí hay un mensaje de commit lleno de contexto[1].
Esto pasa tan seguido que el mantenedor escribió este artículo[2].
[1] https://github.com/git/git/commit/d70f554cdf38b0b05cfaa8e8eb...
[2] https://lore.kernel.org/git/xmqqedevo8ps.fsf@gitster.g/