wandres.dev
CHANGESETS · versionado en monorepo

La anatomía de un changeset: el bump y el resumen

Un changeset tiene dos mitades indivisibles: un frontmatter que declara el bump —major, minor o patch— por cada paquete afectado, y un cuerpo en markdown que se convertirá en la entrada del changelog. Diseccionamos ambas, el significado semántico de cada nivel, los changesets multipaquete y el formato del changelog en el ecosistema de 2026.

⏱ 16 min

Si la lección anterior situó al changeset como artefacto, esta lo abre en canal. Un changeset es un archivo markdown con dos partes que cumplen funciones opuestas y complementarias: un frontmatter que dicta el veredicto —qué paquetes cambian y con qué severidad semántica— y un cuerpo que redacta la narración —el resumen humano que acabará en el changelog que lee tu usuario. Una mitad habla a las máquinas que calculan versiones; la otra habla a las personas que deciden si actualizar. Dominar Changesets empieza por entender que estas dos mitades no son decoración: son las dos caras del contrato que publicas al mundo.

🎯 Al terminar esta lección sabrás
  • Separar las dos mitades de un changeset: el frontmatter que decide el bump y el cuerpo que alimenta el changelog.
  • Fijar el significado exacto de major, minor y patch como promesa semántica hacia quien consume.
  • Escribir changesets multipaquete, donde un solo cambio afecta a varios paquetes con severidades distintas.
  • Elegir el formato de changelog adecuado con la opción changelog de .changeset/config.json.

Dos mitades: el veredicto y la narración

Abre cualquier changeset y verás una estructura invariable: un bloque delimitado por tres guiones arriba y abajo, y debajo, texto libre. El bloque superior es el frontmatter, escrito en YAML, y contiene el veredicto de versionado: una lista de paquetes con el nivel de bump que cada uno recibe. El texto de debajo es el cuerpo, en markdown, y contiene la narración: la frase o el párrafo que describe el cambio para quien lo consume.

---
"@acme/botones": minor
---

Añade la variante fantasma al componente Botón, con estados de foco y disabled accesibles.

Estas dos mitades viajan pegadas por una razón de fondo: la versión y su explicación deben decidirse juntas, por la misma persona, en el mismo momento. Separarlas —dejar que una máquina calcule el número y un humano escriba el texto en otro instante— es precisamente el fallo que Changesets vino a corregir. El frontmatter sin cuerpo produce un changelog mudo, una lista de números sin sentido; el cuerpo sin frontmatter es una nota que no altera nada. Juntas forman una unidad de significado: esto cambia, con esta gravedad, por esta razón.

Conviene fijar a qué público sirve cada mitad, porque se redactan con criterios opuestos:

  • El frontmatter habla a la máquina: es dato estructurado, se valida y se agrega aritméticamente.
  • El cuerpo habla a la persona: es prosa, se lee en el changelog y se juzga por su claridad.
  • El frontmatter decide si actualizar; el cuerpo ayuda a decidir cuándo y cómo hacerlo.

El frontmatter: un bump por paquete

El frontmatter mapea nombres de paquete a niveles de bump. Cada nivel es una promesa concreta dentro del versionado semántico, y confundirlos es romper el contrato con quien depende de ti. La regla es sencilla de enunciar y delicada de aplicar, porque exige juzgar el efecto sobre el consumidor, no el tamaño del cambio.

  • major — rompes compatibilidad. Eliminas o renombras algo exportado, cambias una firma, alteras un comportamiento del que alguien podía depender. Quien actualice tendrá que tocar su código.
  • minor — añades sin romper. Una función nueva, una opción nueva, una variante nueva. El código existente sigue funcionando igual; solo hay más superficie disponible.
  • patch — corriges sin añadir ni romper. Un bug arreglado, una mejora interna, un tipo afinado. Nadie debería notar nada salvo que algo que fallaba ahora funciona.
💥

major

Rompe el contrato. API eliminada, firma cambiada, comportamiento alterado. Quien actualiza debe adaptar su código.

minor

Amplía sin romper. Función, opción o variante nueva. Lo que ya usabas sigue igual; solo hay más disponible.

🩹

patch

Corrige en silencio. Bug resuelto o mejora interna. Sin cambios de superficie: actualizar es seguro y transparente.

Un mismo changeset puede declarar varios paquetes, cada uno con su propio nivel. Esto es la esencia del monorepo: un cambio real rara vez respeta las fronteras de un solo paquete. Si tocas los tokens de diseño y, de paso, adaptas el componente que los usa, un único changeset lo expresa con dos entradas y dos severidades.

---
"@acme/tokens": minor
"@acme/botones": patch
---

Añade la escala de color neutral a los tokens y ajusta el botón para consumirla sin cambiar su API.

Sobre un paquete que ya está en 1.4.2, cada nivel produce un salto distinto y predecible:

# Nivel declarado  ->  version resultante desde 1.4.2
patch  ->  1.4.3     correcciones, sin superficie nueva
minor  ->  1.5.0     features nuevas y compatibles
major  ->  2.0.0     rupturas del contrato publico
⚠️
El escalón peligroso: la 0.x

Por debajo de la versión 1.0.0, el versionado semántico afloja sus garantías: por convención, en la serie 0.y.z un cambio que rompe suele expresarse como minor y una función nueva como patch, porque la API todavía se considera inestable. Changesets aplica la aritmética que le declaras, así que la responsabilidad de elegir el nivel correcto en 0.x es tuya. Decide una política de equipo y escríbela: sin acuerdo explícito, cada autor interpretará la 0.x a su manera y el contrato se volverá ruido.

El cuerpo: el resumen que será changelog

El cuerpo del changeset no es un comentario interno: es texto de cara al público. Cuando llegue la release, Changesets lo trasladará, casi literalmente, a la entrada del CHANGELOG.md del paquete correspondiente. Por eso se escribe pensando en el lector externo que abre el changelog para decidir si actualizar, no en tu yo del futuro revisando el código.

flowchart LR
cs[Un changeset md] --> fm[Frontmatter con el bump por paquete]
cs --> body[Cuerpo con el resumen del cambio]
fm --> ver[Calculo de la proxima version]
body --> log[Entrada del changelog]
style fm fill:#a6e3a1,color:#11111b
style body fill:#89b4fa,color:#11111b

Un buen resumen empieza por el efecto, no por la mecánica. Añade soporte para temas oscuros en el selector de fecha le sirve al usuario; refactoriza el hook interno de estado no le dice nada, porque describe tu cocina y no su plato. Para los major, el cuerpo debería incluir además la nota de migración: qué se rompió y cómo adaptar el código. Ese párrafo, redactado en caliente por quien hizo el cambio, es infinitamente más fiable que la guía de migración que alguien intenta reconstruir meses después.

Un puñado de reglas separa un resumen útil de uno inútil:

  • Empieza por el efecto para el usuario, no por el nombre del archivo que tocaste.
  • Escribe en presente y en voz activa: añade, corrige, elimina, no se ha refactorizado.
  • En un major, incluye siempre la nota de migración: qué se rompió y cómo adaptarse.
  • Evita la jerga interna del repositorio; el lector no conoce tus hooks ni tus módulos privados.
  • Una frase suele bastar; si necesitas un párrafo, que sea por el usuario, no por ti.

El formato exacto del changelog lo gobierna la opción changelog de .changeset/config.json. En 2026, la elección por defecto para proyectos serios en GitHub es @changesets/changelog-github, que enriquece cada entrada con enlaces al PR y al autor, transformando el changelog en un documento navegable en lugar de una lista plana.

{
  "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
  "changelog": ["@changesets/changelog-github", { "repo": "acme/design-system" }],
  "commit": false,
  "access": "public",
  "baseBranch": "main"
}

Existen tres familias de generador. La cadena false desactiva el changelog por completo, útil en paquetes privados donde nadie lo lee. El generador por defecto @changesets/cli/changelog produce un changelog escueto, solo con tus resúmenes. Y @changesets/changelog-github añade la capa social —enlaces a PR, menciones al autor— a cambio de un token de GitHub. La regla práctica: si publicas open source en GitHub, usa el de GitHub; si publicas interno, el escueto basta; si el paquete es privado y efímero, desactívalo.

// Las tres formas de configurar la opcion changelog
{ "changelog": false }
{ "changelog": "@changesets/cli/changelog" }
{ "changelog": ["@changesets/changelog-github", { "repo": "acme/design-system" }] }
El nivel de bump es un acto de empatía institucionalizada hacia quien no está en la sala

Detrás de la trivialidad aparente de elegir entre major, minor y patch se esconde la disciplina más difícil del desarrollo de librerías: pensar en quien no está en la sala. Tu changeset no lo lees tú; lo lee, dentro de seis meses, una desarrolladora que nunca conocerás, que tiene tu paquete enterrado tres niveles bajo sus dependencias, y para quien un major mal etiquetado como patch significa un despliegue roto en producción un domingo. El frontmatter es, en el fondo, un mecanismo para forzar esa empatía y convertirla en un dato verificable: te obliga a declarar, por escrito y en el momento del cambio, qué tan lejos llega la onda expansiva de lo que acabas de tocar. Y el cuerpo institucionaliza la otra mitad de la cortesía: explicar por qué, en el idioma del que recibe y no del que emite. Aquí está la lección que trasciende a Changesets: las mejores herramientas de un ecosistema no automatizan el juicio humano, lo estructuran —le dan un lugar, un formato y un momento— para que ese juicio ocurra siempre, se revise siempre y quede registrado siempre. Un ingeniero junior ve dos campos en un archivo de texto; un ingeniero senior ve el contrato entre su presente y el futuro de miles de personas que dependerán de que ese campo diga la verdad. Aprende a redactar el changeset como quien redacta ese contrato: con la severidad medida y la razón explicada, porque del otro lado siempre hay alguien confiando en tu palabra.

⚔️ Disecciona y redacta con rigor
  1. Escribe tres changesets para el mismo paquete: uno patch, uno minor y uno major, y justifica en el cuerpo por qué cada uno merece su nivel.
  2. Redacta un changeset multipaquete que declare minor en un paquete y patch en otro que depende de él, con un único resumen coherente.
  3. Toma un major y escribe en su cuerpo una nota de migración clara: qué se rompió y cómo adaptar el código.
  4. Configura @changesets/changelog-github en .changeset/config.json y describe qué añade frente al generador por defecto.
  5. Reescribe un resumen malo centrado en la mecánica interna para que hable del efecto que percibe el usuario final.