El registro de cambios es parte del producto, no un git log

Product

Hay dos clases de registro de cambios.

El primero se genera a partir de los mensajes de commit. Tiene líneas como fix: handle null in resolver y chore: bump deps. Técnicamente es un registro completo de lo que cambió, y ningún usuario lo ha leído dos veces.

El segundo se escribe para quien usa tu producto. Cuenta qué se puede hacer hoy que la semana pasada no se podía. Es breve. La gente se suscribe a él.

La diferencia no está en las herramientas. Está en que alguien decidió que el registro de cambios formaba parte del producto.

Dale un tipo a cada entrada

El cambio que más rinde es darle un tipo a cada entrada. Kookee usa siete:

feature · fix · improvement · breaking · security · deprecated · other

No es decoración. Con el tipo, el registro de cambios se puede filtrar, y por eso sirve a más de un público:

TypeScript
// Novedades: para los usuarios
const features = await kookee.changelog.list({
  filter: { changelogType: 'feature' },
  limit: 10,
});

// Lo que va a romperse: para los integradores
const breaking = await kookee.changelog.list({ filter: { changelogType: 'breaking' } });

A un ingeniero que integra tu API le importan breaking y deprecated, y nada más. A un usuario le importa feature. Un equipo de seguridad quiere security en su propia página. Una lista con tipos sirve a los tres; una lista plana no sirve bien a ninguno.

El tipo es un campo estructurado, no una columna de la entrada, y eso importa a la hora de mostrarlo. entry.type es la cadena 'changelog'; el tipo de lanzamiento sale del array de campos:

TypeScript
import { fieldOptionKey, fieldStringValue } from '@kookee/sdk';

const releaseType = fieldOptionKey(entry.fields, 'changelogType'); // 'feature'
const version = fieldStringValue(entry.fields, 'version');         // '1.6.0'

Pon una versión en cada entrada

El tipo responde a «qué clase de cambio es». La versión responde a «¿ya lo tengo?», que es lo que de verdad pregunta un usuario cuando llega desde un hilo de soporte.

La versión es texto libre, así que admite el esquema que uses: 1.6.0, 2024.11, v3-beta. Para que sirva, hacen falta dos hábitos: ponerla en todas las entradas y usar el mismo esquema en todas. Un registro de cambios donde solo la mitad de las entradas tiene versión parece un descuido, y lo es.

Cuando alguien pregunte qué salió en una versión concreta, filtra por la versión exacta:

TypeScript
const release = await kookee.changelog.list({ filter: { version: '1.6.0' } });

Las entradas llegan de la más reciente a la más antigua. Si prefieres un orden estricto por versión (y en una página de versiones seguramente sí, porque las fechas engañan cuando hay parches portados a versiones anteriores o lanzamientos que se retrasan), ordénalas tú mismo, porque al ordenarlas como texto 1.10.0 queda antes que 1.9.0:

TypeScript
const { data } = await kookee.changelog.list({ limit: 100 });

const byVersion = [...data].sort((a, b) =>
  (fieldStringValue(b.fields, 'version') ?? '').localeCompare(
    fieldStringValue(a.fields, 'version') ?? '',
    undefined,
    { numeric: true },
  ),
);

numeric: true es lo que coloca 1.10.0 después de 1.9.0.

Deja que la gente reaccione

Cada entrada del registro de cambios admite reacciones con emoji:

TypeScript
await kookee.changelog.react(entryId, { reactionType: 'fire', action: 'add' });

Parece una pequeña función social. En realidad es el canal de comentarios sobre el producto más barato que vas a montar nunca. Si publicas diez cosas al mes y ves que dos se llevaron todas las reacciones, ya sabes a qué dedicar el mes siguiente. Y al usuario le cuesta un clic, no una encuesta.

Escribe la entrada junto con el lanzamiento

En la práctica, el fallo no es escribir malas entradas, sino escribirlas una semana tarde, de memoria y todas de golpe. Para entonces nadie recuerda por qué importaba el cambio, y acabas por defecto con el registro de cambios hecho de mensajes de commit.

En Kookee, la entrada se redacta mientras el pull request sigue abierto y se publica cuando sale la versión: sin despliegue, sin recompilar, sin esperar a un generador de sitios estáticos. Redáctala en el panel o desde un asistente de programación conectado por MCP, que lee las últimas entradas y escribe la siguiente con el mismo tono, con su tipo y su versión, para que tú la revises.

Después la obtienes con una sola llamada, donde la necesites:

TypeScript
const { data } = await kookee.changelog.list({ limit: 5 });

Tu sitio de marketing, el panel de «Novedades» dentro de la app, el correo de cada versión. Una sola fuente, escrita una vez, por quien hizo el cambio.