Conventional Commits: el eslabón que falta entre tus commits y tu número de versión

  • VersionDude
  • guides
  • 7 min de lectura

Una forma fija para los mensajes de commit que asocia fix con PATCH y feat con MINOR, de modo que el siguiente número de versión se deduce del historial. Las dos notaciones para los cambios rompedores, y lo que la especificación deliberadamente no hace.

Si ya numeras tus versiones con versionado semántico y mantienes un changelog, entre ambas cosas queda un hueco que la mayoría de los equipos rellena a mano: alguien tiene que leer los commits posteriores a la última publicación y decidir si la siguiente versión es un parche, una minor o una major. Conventional Commits es una especificación que cierra ese hueco poniendo la respuesta en el propio mensaje de commit, en el momento en que se hace el cambio.

La forma del mensaje

Una mano escribiendo en un portátil, con la pantalla mostrando reglas CSS. Cada cambio aquí acaba convertido en un mensaje de commit que tiene que decir qué hizo.
Una mano escribiendo en un portátil, con la pantalla mostrando reglas CSS. Cada cambio aquí acaba convertido en un mensaje de commit que tiene que decir qué hizo.

La especificación define una forma fija para la primera línea, con un cuerpo y unos pies de mensaje opcionales debajo. Escrita en palabras, la estructura es: el tipo, luego un scope opcional entre paréntesis, después dos puntos y un espacio, y por último una descripción breve. Una línea en blanco separa el cuerpo, y otras líneas en blanco separan los pies de mensaje.

Así, la corrección de un error en el parser se convierte en fix(parser): handle empty attribute values, y una capacidad nueva se convierte en feat(api): add pagination to the search endpoint. El scope es opcional, y la descripción es un resumen, no un ensayo. Todo lo que sea más largo va en el cuerpo, debajo.

Como los tipos se relacionan con SemVer

La razón de esa forma fija es lo que desbloquean los tipos. La especificación relaciona dos de ellos directamente con el versionado semántico: un commit fix se corresponde con PATCH, y un commit feat se corresponde con MINOR. Un commit que introduce un cambio rompedor en la API se corresponde con MAJOR.

  • Estructura: tipo, scope opcional entre paréntesis, después dos puntos y un espacio, y luego la descripción. El cuerpo y los pies de mensaje van debajo, separados cada uno por una línea en blanco.
  • fix se corresponde con PATCH en el versionado semántico, y feat se corresponde con MINOR.
  • Un cambio rompedor en la API se corresponde con MAJOR, y se marca con un pie BREAKING CHANGE: o con un signo de exclamación antes de los dos puntos.
  • Si se usa el signo de exclamación, el pie BREAKING CHANGE: puede omitirse y la descripción lo cubre.
  • La especificación es una convención de mensajes, no una herramienta de publicación: se corresponde con los niveles de SemVer pero no calcula nada por sí sola.

Esa única correspondencia es lo que permite que las herramientas calculen el siguiente número de versión sin que nadie tenga que leer el historial. También significa que la decisión pasa a manos de quien está en mejor posición para tomarla, es decir, quien escribió el cambio, en el momento en que lo escribió, en lugar de un responsable de publicación reconstruyendo la intención semanas después.

Dos formas de marcar un cambio incompatible

Los cambios rompedores tienen dos notaciones aceptadas, y conviene conocer las dos porque te encontrarás con ambas en repositorios reales. La primera es un pie de mensaje que empieza por BREAKING CHANGE: seguido de una descripción. La segunda es un signo de exclamación colocado justo antes de los dos puntos, como en feat(api)!: drop support for v1 tokens.

Las dos no son meras alternativas de estilo. La especificación indica que, si se usa el signo de exclamación, el pie BREAKING CHANGE: puede omitirse, y entonces la propia descripción del commit sirve como descripción del cambio rompedor. Dicho de otro modo, la forma breve es completa por sí sola, y por eso es la que más se ve en la práctica.

Lo que la especificacion no hace

Vale la pena ser preciso sobre lo que esta especificación te da y lo que no. Es una convención para escribir mensajes, no una herramienta de publicación. La especificación dice que los tipos se corresponden con los niveles de SemVer; no calcula versiones, no genera changelogs ni publica nada por sí misma. Eso es trabajo de las herramientas que leen la convención, y esas herramientas son una decisión aparte.

El beneficio que sobrevive incluso sin ninguna herramienta es el que los equipos subestiman. Exigir un tipo en cada commit obliga a un pequeño juicio en el momento de escribirlo: ¿esto es una corrección, una funcionalidad, o algo que va a romperle el código a quien lo use? Un equipo que responde a esa pregunta mil cien veces al año tiene una idea mucho más precisa de su propia superficie de publicación que otro que la responde cuatro veces al año mientras prepara una versión.

El beneficio que sobrevive incluso sin ninguna herramienta es el que los equipos subestiman. Exigir un tipo en cada commit obliga a un pequeño juicio en el momento de escribirlo: ¿esto es una corrección, una funcionalidad, o algo que va a romperle el código a quien lo use? Un equipo que responde a esa pregunta mil cien veces al año tiene una idea mucho más precisa de su propia superficie de publicación que otro que la responde cuatro veces al año mientras prepara una versión.

- VersionDude

Adoptarla sin ceremonias

Adoptarla no requiere ceremonia. La convención se aplica a partir del commit en el que decidas aplicarla, y un repositorio con un historial mixto funciona perfectamente, porque las herramientas que leen la convención simplemente ignoran lo que no pueden interpretar. Empezar un lunes, sin migración y sin reescribir el historial, es una forma legítima de adoptarla.

Si lo que quieres es la barrera de protección en lugar de la disciplina, basta con un hook de mensaje de commit que rechace una primera línea que no cumpla el formato, y cuesta un solo fichero de configuración. Pero empieza por la convención en sí. La especificación es corta, las dos correspondencias con SemVer son la parte que rinde, y la notación con signo de exclamación es el detalle que a casi todo el mundo se le escapa en la primera lectura.

Proyecto relacionado