Mensajes de commit: buenas prácticas con ejemplos

Aprende a escribir mensajes de commit útiles: asunto en imperativo, la regla 50/72, el porqué antes que el qué y Conventional Commits, con ejemplos.

Mensajes de commit: buenas prácticas con ejemplos

Abres git log para averiguar cuándo se rompió el login. Hace tres meses funcionaba. Y lo que encuentras es una columna de veinte líneas que dice “cambios”, “fix”, “más cambios”, “ya va”. Ninguna te dice nada. No sabes qué commit tocó el login ni por qué, así que te toca abrir el diff de cada uno a ciegas. Ese es el precio de no cuidar los mensajes de commit: tu historial deja de ser una fuente de información y se convierte en ruido.

Este post asume que ya sabes hacer git add, git commit y git push, pero nunca te ha quedado claro qué escribir en el mensaje ni por qué importa. Si aún te faltan las bases de Git, empieza por la guía de Git desde cero y vuelve luego. Aquí vamos a lo concreto: cómo escribir un mensaje que tu yo de dentro de tres meses agradezca.

¿Por qué importa lo que escribes en un commit?

El historial de Git es documentación, y el mensaje de commit es la única parte que escribes tú a mano. El diff (los + y - que muestran las líneas que cambiaron) lo genera Git solo. La fecha y el autor, también. Lo único que depende de ti es explicar qué pasó y por qué. Si lo dejas en “fix”, tiras a la basura la mitad de la utilidad de la herramienta.

Piensa en el mensaje como una carta a tu yo futuro. Dentro de seis meses no vas a acordarte de por qué cambiaste ese if. Tu compañero, que ni siquiera estaba, menos. Cuando alguien lea el historial con git log para ver el historial, lo primero que ve es tu asunto. Compara estos dos:

# Un historial que no dice nada
a3f0c21 cambios
7bd9e14 fix
c8e2a05 más cambios
d1f4b90 ya va
e0a7c33 wip
# El mismo trabajo, contado bien
a3f0c21 Corrige el total negativo del carrito al aplicar cupones
7bd9e14 Añade validación de email en el registro
c8e2a05 Elimina los console.log del checkout
d1f4b90 Actualiza la librería de pagos a la 4.2
e0a7c33 Documenta el flujo de recuperación de contraseña

El segundo lo lees de arriba abajo y sabes exactamente qué pasó en la última semana. El primero te obliga a abrir cada commit. Escribir uno u otro cuesta lo mismo, pero el resultado para quien lo lea es opuesto.

La estructura de un buen mensaje

Un buen mensaje tiene dos bloques separados por una línea en blanco: el asunto y el cuerpo. El asunto es la primera línea, la que resume el cambio de un vistazo. La línea en blanco los separa, y Git la usa para distinguirlos, así que no te la saltes. El cuerpo, que es opcional, es donde te explayas cuando el cambio necesita contexto.

Diagrama de la anatomía de un mensaje de commit: un asunto en imperativo de unos 50 caracteres, seguido de una línea en blanco obligatoria y un cuerpo opcional de líneas de hasta 72 caracteres que explica el porqué, distinto del diff que ya muestra el qué.
La estructura de un buen mensaje: asunto corto y en imperativo, línea en blanco, y un cuerpo opcional que aporta el porqué que el diff no puede contar.

Sobre la longitud hay una convención muy repetida, la regla 50/72[1][3]: el asunto en unos 50 caracteres y las líneas del cuerpo en unos 72. No es una ley ni un límite que Git te imponga. Es un número redondo que mantiene el asunto legible en cualquier herramienta y evita que el cuerpo se salga de pantalla. Si te pasas un poco no explota nada; si escribes un asunto de 150 caracteres, se corta en casi todas partes.

Así se ve un mensaje completo:

Corrige el total negativo del carrito al aplicar cupones

Cuando un cupón superaba el importe del carrito, el total
quedaba en negativo y el TPV rechazaba el pago. Ahora el
total nunca baja de cero.

El bug solo aparecía con cupones de importe fijo, por eso
pasó el primer test, que usaba porcentajes.

Para un asunto simple te vale -m en una línea. Cuando necesitas cuerpo, lo cómodo es no poner -m: Git abre tu editor de texto para que escribas asunto y cuerpo con calma[1].

# Asunto en una línea, rápido
git commit -m "Añade validación de email en el registro"

# Sin -m: Git abre el editor para escribir asunto + cuerpo
git commit

¿Por qué el asunto va en imperativo?

El asunto se escribe en imperativo, como una orden: “Añade”, “Corrige”, “Elimina”, “Actualiza”, nunca “Añadido” ni “Añadí”. Es la convención que sigue el propio Git en los mensajes que genera de forma automática (cuando haces un merge, escribe “Merge branch…”, no “Merged”). Ir por el mismo carril que la herramienta hace que todo el historial se lea igual.

Hay un truco para saber si tu asunto está bien: ponle delante “Si se aplica, este commit va a…” y comprueba que la frase encaja[3].

  • “Si se aplica, este commit va a corregir el total negativo.” Encaja.
  • “Si se aplica, este commit va a corrigiendo el total.” No encaja.

En español el imperativo suena raro al principio (“Añade”, “Corrige”), pero es la forma más corta y consistente. Elige una y mantenla en todo el proyecto. Lo peor es mezclar “Añade”, “Añadido” y “Añadiendo” en la misma pantalla.

El diff ya dice el qué. Escribe el porqué

El error más común de un junior no es escribir mal, es escribir lo que ya se ve. Si tu commit cambia precio > 0 por precio >= 0, no escribas “Cambia > por >=”. Eso ya lo cuenta el diff. Lo que el diff no puede contarte es por qué: “Permite productos gratuitos en el catálogo”.

Diagrama de decisión: tras un cambio de código, si el porqué se entiende con solo mirar el diff basta un asunto en imperativo; si no, el commit necesita también un cuerpo que explique el porqué.
Cuándo un commit necesita cuerpo: solo cuando el diff, por sí solo, no explica el porqué del cambio.

El asunto dice qué cambió en una línea. El cuerpo dice por qué, y solo lo necesitas cuando el porqué no es obvio. Un renombrado de variable no necesita cuerpo. Un cambio de lógica que arregla un bug raro, sí: qué fallaba, en qué caso, y por qué esta es la solución. Ese contexto es oro cuando alguien (tú incluido) vuelve al código dentro de medio año y se pregunta “¿y esto por qué está así?”.

Conventional Commits: una convención opcional

Conventional Commits es una convención que da formato al asunto: un tipo, un ámbito opcional entre paréntesis, dos puntos y la descripción[2]. La forma es tipo(ámbito): descripción.

feat(login): admitir acceso con Google
fix(carrito): evitar total negativo con cupones
docs(readme): explicar cómo arrancar el entorno local

La especificación solo define dos tipos con significado propio: feat para una funcionalidad nueva y fix para arreglar un bug. El resto (docs para documentación, refactor para reorganizar sin cambiar comportamiento, test, chore y demás) son tipos habituales que la convención permite pero no impone[2]. El ámbito entre paréntesis es opcional y sirve para señalar qué zona del código tocaste: login, carrito, api, pagos.

¿Para qué sirve tanto prefijo? Para que el historial sea legible por máquinas, no solo por humanos. Hay herramientas que leen los tipos y generan el changelog o suben el número de versión automáticamente. Aunque no uses esas herramientas, un asunto que empieza por fix(carrito): te dice de un vistazo qué tipo de cambio es y dónde. No es obligatorio, pero está tan extendido que lo verás en casi cualquier proyecto serio.

Ejemplos: de malo a bueno

La forma más rápida de coger el ojo es ver mensajes reales antes y después. Esta es la tabla que uso mentalmente en cualquier code review, línea a línea:

Mensaje maloPor qué fallaVersión mejorada
fixNo dice qué se arregló ni dónde. Dentro de un mes es indistinguible de los otros diez “fix”Corrige el redondeo del IVA en facturas
cambios en el loginVago y en presente descriptivo, no imperativo. “Cambios” puede ser cualquier cosaAñade bloqueo tras cinco intentos fallidos de login
Añadido testPasado en vez de imperativo, y no dice qué pruebaAñade test para el cálculo de envío gratis
UpdateEn inglés suelto, genérico, sin contextoActualiza el copy del botón de pago
arreglé un montón de cosas del checkout y de paso toqué el headerUn solo mensaje para dos cambios sin relaciónDos commits separados, uno por cada cambio

Fíjate en la última fila. Ahí el mensaje no tiene arreglo posible, porque el problema está en el commit: mete demasiadas cosas. Y eso nos lleva al error más difícil de resolver solo con palabras.

Errores comunes de un junior

Mensajes vagos. “cambios”, “fix”, “wip”, “ya va”. Son cómodos de escribir y no cuestan nada en el momento. La factura llega después, cuando buscas algo en el historial y no encuentras nada.

El commit cajón de sastre. Metes en un solo commit el arreglo del checkout, un cambio de estilos y un typo del README. Da igual lo bien que escribas el asunto: ningún mensaje puede resumir tres cosas sin relación entre sí. El mensaje malo aquí es solo el síntoma. La cura es hacer commits más pequeños, uno por cambio con sentido propio. Eso es un commit atómico, y tiene su propia guía en cómo hacer commits atómicos.

Escribir en pasado. “Añadido X”, “Arreglé Y”. No es grave, pero rompe la consistencia con el resto del historial y con lo que genera Git. Cambia a imperativo y listo.

Poner el qué cuando el porqué no es obvio. Ya lo vimos: “Cambia el valor a 0” no aporta; “Permite carritos vacíos tras vaciar el último producto” sí.

Un asunto kilométrico sin cuerpo. Meter toda la explicación en la primera línea hasta llegar a 200 caracteres. Si necesitas explicar tanto, ese texto va en el cuerpo. El asunto resume; el cuerpo detalla.

Checklist para tu próximo commit

  • El asunto está en imperativo (“Añade”, “Corrige”) y ronda los 50 caracteres.
  • El asunto pasa la prueba de “Si se aplica, este commit va a…”.
  • Si el porqué no es obvio, hay una línea en blanco y un cuerpo que lo explica.
  • El cuerpo cuenta el porqué y el contexto, no repite lo que ya muestra el diff.
  • El commit hace una sola cosa con sentido propio (si no, divídelo).
  • Si tu equipo usa Conventional Commits, el asunto empieza por su tipo (feat, fix, docs…).

Practica con commits de verdad

Leer sobre buenos commits está bien, pero el hábito se coge escribiendo. Si quieres pasar de “wip” a un historial que tu equipo agradezca, el curso de Git de cero a profesional te lleva por el flujo completo con ejercicios reales, no solo teoría.

Y si te sirve este tipo de guía sin relleno, déjame tu email y te aviso cuando publique las siguientes guías de esta serie sobre Git:

Un concepto nuevo cada semana

Fuentes

  1. Documentación oficial de git commit (git-scm.com): confirma que sin -m se abre el editor, que -m pasa el mensaje en línea y que --amend reescribe el último commit.
  2. Conventional Commits v1.0.0 (conventionalcommits.org): formato tipo(ámbito): descripción, con feat y fix con significado propio y el resto como opcionales.
  3. How to Write a Git Commit Message, de Chris Beams: origen popular de la regla 50/72 y del truco del imperativo.

Preguntas Frecuentes

¿En qué idioma escribo los mensajes de commit?

En el idioma que use tu equipo, y con consistencia. Si el proyecto es interno y todos habláis español, escribe en español. En proyectos open source y en equipos internacionales lo habitual es el inglés. Lo que de verdad importa es la consistencia: no mezclar los dos idiomas en el mismo historial.

¿Cuánto debe medir el asunto?

Unos 50 caracteres es la referencia habitual (la regla 50/72). No es un límite estricto: si te pasas por poco no ocurre nada. Pasa de ese número solo cuando de verdad no puedas resumirlo, y nunca escribas un asunto de 200 caracteres para meter ahí toda la explicación.

¿Tengo que usar Conventional Commits sí o sí?

No. Es una convención opcional, muy extendida pero no obligatoria. Úsala si tu equipo o tu proyecto la sigue, porque entonces mantener el formato ayuda a las herramientas de changelog y versionado. Si nadie la usa a tu alrededor, unos buenos mensajes de commit en imperativo ya te llevan la mayor parte del camino.

¿Puedo corregir el mensaje del último commit?

Sí, con git commit --amend. Reescribe el último commit y te deja editar el mensaje. Cuidado con una cosa: si ya hiciste git push, --amend cambia la historia que otros pueden tener, así que evítalo en commits que ya estén compartidos salvo que sepas lo que haces.

¿git blame me enseña el mensaje del commit?

No. git blame muestra, por cada línea, el hash abreviado del commit, el autor, la fecha y el número de línea, pero no el mensaje. Para leer el mensaje, copia ese hash y pásalo a git show <hash>, o búscalo en el historial con git log. Ahí es donde tus buenos mensajes acaban pagando.