Conventional Commits Wiiiiiiii

Joel H. Gomez Paredes
7 min readNov 29, 2020

Desarrollar software es algo complejo porque constantemente estamos lidiando con la siguiente pregunta: ¿Esta es la mejor manera de hacerlo?. Conforme adquirimos experiencia seguimos o creamos prácticas que nos ayudan a hacer mejor (o peor) nuestro trabajo. Con el tiempo estas pueden funcionar para alguien mas, se adoptan, se crean variantes y en algún punto pueden convertirse en un estándar, esto va más allá de la tecnología, es algo que hacemos como humanidad.

Tu vida puede cambiar con cosas muy sencillas como un commit

La comunicación es un eje central de nuestra vida en general y el desarrollo de software no es la excepción. Constantemente nos preocupamos por tener claro qué es lo que el cliente necesita, pero muy pocas veces nos enfocamos en que necesitamos nosotros para comunicarnos mejor, no solo en el contexto de documentación o pruebas sino que es lo que hacemos y cómo impacta en nuestro software.

¿Cómo puedo mejorar mi comunicación un simple commit? La respuesta es Conventional Commits

Pero antes de entrar a ese mundo me gustaría dar a entender que es un buen commit o al menos para mí, un buen commit resuelve 3 preguntas básicas:

  1. ¿Qué? El objetivo de tu cambio y el impacto que tendrá
  2. ¿Porqué? La motivación de hacer este cambio
  3. ¿Cómo? La manera en que estas haciendo el cambio

Eso es todo, esto es importante ya que un producto está en constante cambio y el equipo también, al menos en mi caso no me gustaría encontrarme con una línea de código que deba modificar y termine tirando producción (historia real) ya que esa línea tiene lógica arkana que ya nadie comprende porque los antiguos están en otro reino.

Escribir un buen commit no solo te ayuda a plasmar la historia para las futuras generaciones (quienes no te maldecirán por agregar código que ya nadie entiende) sino que te puede servir en tu standup para decir en que trabajaste el día anterior.

Confía en mi soy un software dogveloper

Y esto no es nuevo, inclusive hay muchos videos en youtube que te indican cómo hacer un buen commit y esto es genial, te dicen el tipo de verbos y tiempos a utilizar, esto con el tiempo se convierte en tendencia y lo adoptamos como una buena práctica.

Conventional Commits es simplemente un formato para redactar nuestros commits

Con este formato es fácil identificar los cambios y su impacto en el software, reduce la ambigüedad al redactar un commit message además de que pueden ayudarnos a automatizar ciertas tareas ya que estamos basándonos en una especificación.

Un ejemplo en el que estoy trabajando actualmente es usar estos commit messages para generar el changelog de un proyecto, actualizar la versión de manera automática y generar el release.

¿Pero cómo es posible hacer eso?

Bueno es una combinación de muchas cosas pero todo se centra en 2 especificaciones: versionamiento semántico y Conventional Commits.

El versionamiento semántico en pocas palabras es un esquema(estructura) que nos permite definir cómo la versión cambia de acuerdo a los cambios que incluye.

La sintaxis es la siguiente

major.minor.patch = 1.0.0

Major es un cambio que rompe de alguna forma la compatibilidad del software, lo que provoca que el usuario deba modificar su código para adaptarse a esta versión

Minor es un cambio que no rompe la compatibilidad y agrega nuevas features al producto

Patch es un cambio que arregla un bug o cumple con cierta labor de mantenimiento, performance, etc. Básicamente no cambia el scope del software.

¿Cómo los commits pueden determinar como cambiar la versión?

Al ser una especificación define los Conventional Commits define un commit message con una estructura y ciertas palabras clave, las cuales pueden asociarse con el semver( versionamiento semántico). Un commit message está formado de 3 partes:

Header: Define el tipo de commit, donde y una breve descripción del cambio ( es la única parte obligatoria en un commit message)

Body: Una descripción más larga, puedes incluir contextos sobre cómo se hizo el cambio y los detalles que consideres necesarios

Footer: Esta parte está destinada a indicar el o los que issues estas cerrando o indicar que este cambio que rompe la compatibilidad del software respecto a versiones anteriores

Ejemplo

<type>[scope]: <descripción>linea vacia[body]linea vacia[footer]

Me emocione al escribir esto último porque te estoy dando pistas de cómo se liga con el semver

Ahora vamos a ver a detalle cada parte del header:

type: Nos indica el tipo de cambio a realizar(por defecto las herramientas usan estos pero también ofrecen la posibilidad de agregar nuevos)

  • feat: Agrega una nueva feature para el usuario, está feature puede ser un cambio en el comportamiento esperado de la aplicación
  • fix: Arregla un bug
  • docs: Documentación
  • style: Formato en el código
  • refactor: Reescribe estructura pero no cambia el comportamiento
  • perf: Relacionado con optimización
  • test: Agregar o corregir tests
  • build: Pipeline de construcción
  • ci: Configuración de herramientas de CI
  • chore: Cambios que no afecten el código de la aplicación

Scope

Es un dato opcional que nos ayuda a indicar sobre que se está haciendo un cambio, por ejemplo en un monorepo este se usa para indicar sobre qué paquete se hace el cambio, componente, dominio del cambio, pieza de software que está afectando, etc.

Ejemplos

feat: add 1 click payment

feat(shopping-cart): add 1 click payment

feat(shopping-cart): add 1 click paymentfeat(shopping-cart): add 1 click paymenttake default payment config in button to pay, refers to JIRA-11BREAKING CHANGES: payment config is mandatory field

Cuando nosotros agregamos un cambio de tipo feat, la nueva versión aumenta la sección de Minor pero si este indica que hay un BREAKING CHANGES esto indica un cambio en Major, un cambio de tipo fix, perf o refactor cambia la sección el patch. Así que build, test, style o docs no cambiarán nada en absoluto y así es como funciona. Normalmente usamos chore si cambiamos la versión manualmente pero idealmente no es motivo para cambiar la versión del software.

Suena genial pero ¿como escribo en ese formato? Bueno puedes hacerlo de la manera tradicional usando git commit.

git commit -m “feat(shopping-cart): add 1 click payment” -m “take default payment config in button to pay, refers to JIRA-11” -m “BREAKING CHANGES: payment config is mandatory field”

O podemos usar una herramienta llamada commitizen la cual a través de una interfaz de línea de comandos nos va solicitando datos

Con commitizen tenemos una forma de redactar los commits de manera más amigable pero ahora queda una parte importante, podemos usar commitizen pero esto no he evita que en algún momento alguien pueda usar el comando git commit directamente y esto implica que nos podemos equivocar, para evitar esto hay que usar una herramienta llamada commitlint la cual podemos integrar con husky para que se ejecute en un git hook al momento de hacer nuestro commit. Esto validará que el commit tenga la sintaxis de commitlint evitando problemas en el futuro.

Hasta ahora esto solo nos “garantiza para hacer un buen commit” pero adicionalmente podemos agregar a nuestro pipeline standard-version para generar la versión que necesitamos de manera automática o semantic-release para combinar esto con nuestras herramientas de CI/CD para crear la versión y publicarla en el gestor de paquetes apropiado(Ambas herramientas generan el changelog)

Ya veremos algo de esto en futuros posts, pero por ahora nos vamos a centrar en configurar commitizen y commitlint en nuestro proyecto.

Husky + Commitlint + Commitizen

Instalación

npm install husky — save-dev

o

yarn add husky @commitlint/{cli,config-conventional} commitizen — save-dev

Configuración

Primero crearemos un archivo llamado commitlint.config.js con el siguiente contenido, este nos sirve para configurar las convenciones que serán validadas por commitlint

module.exports = {extends: ['@commitlint/config-conventional'],};

Ahora debemos crear un .huskyrc para configurar los hooks y pasar los commit messages a commitlint

{"hooks": {"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}}

Hasta este punto todos los commit messages pasarán por la validación de commitlint, ahora solo falta configurar commitizen. Aquí hay muchas variantes, podemos conectar commitizen con git para ejecutarlo cada vez que se use el comando git commit o podemos usar un npm script en mi caso optaré por la segunda.

Lo primero que hay que hacer es hacer una configuración de commitizen, puedes hacerlo para npx pero el comando varia si usas yarn o npm

Yarn

commitizen init cz-conventional-changelog --yarn --dev --exact

Npm

npx commitizen init cz-conventional-changelog --save-dev --save-exact

Y para finalizar solo agrega a tus scripts de npm lo siguiente

"commit": "cz"

Listo, ahora cuando quieras hacer un commit solo debes ejecutar npm run commit y verás la magia.

Adicionalmente puedes ver mi repo aquí, esto es todo por ahora y diviértanse :)

--

--

Joel H. Gomez Paredes

Frontend Developer at Platzi, Google Developer Expert in Web Technologies And Google Maps Platform, A Reverse Developer & Fullsnack JS Developer