Esta es la Parte 2 de la serie. Si aún no la has leído, empieza por la Parte 1 — Especificación: flujo README primero, contratos MVP y paquetes por capas (@slothy/core, @slothy/angular).
Aquí nos centramos en lo que mantiene ese contrato honesto en el tiempo: versionado, disciplina de changelog y pruebas.
Versionado — SemVer con intención
El versionado semántico (MAJOR.MINOR.PATCH) es una herramienta de comunicación. Un número nuevo cuenta una historia:
| Salto | Significado habitual |
|---|---|
| MAJOR | Cambio incompatible en la API pública soportada (o en el comportamiento del que la gente dependía). |
| MINOR | Novedades compatibles hacia atrás (nueva opción de config, nuevo export). |
| PATCH | Correcciones de bugs que respetan el contrato documentado. |
¿Qué cuenta como “romper”?
Sé más estricto que “el build sigue pasando”. Rompe, entre otras cosas:
- Renombrar o quitar símbolos exportados.
- Endurecer tipos de forma que rechace entradas antes válidas.
- Cambiar el comportamiento por defecto cuando el anterior era observable aunque no estuviera bien documentado—porque la gente igual dependerá de él.
Si debes cambiar un comportamiento del que dependen los usuarios, o documentas el antiguo como no soportado en un MINOR con deprecación, o sacas un MAJOR con guía de migración.
La era 0.x
Mientras la API aún cambia, 0.y.z indica inestabilidad: un MINOR puede incluir rupturas hasta que te comprometas con 1.0.0. Después de 1.0.0, trata los MAJOR como un acontecimiento: guía de migración, codemods si aplica, y relato claro en el changelog.
Alinear varios paquetes (@slothy/*)
Si tienes un monorepo con @slothy/core y @slothy/angular:
- Opción A — versiones independientes: cada paquete tiene su propio semver. Documenta qué versiones del adaptador van con qué rango de core (peer dependency + notas de release).
- Opción B — lockstep: un mismo número para todos los paquetes (más simple para quien consume, más pesado para quien mantiene).
Elige una estrategia y documéntala en el README raíz. Evita el “desfase silencioso” de peers.
Changelog — una sola fuente de verdad
Los logs automáticos de git son ruidosos. Un changelog redactado (véase Keep a Changelog) ayuda a responder: ¿me conviene actualizar hoy?
Secciones recomendadas:
- Added — APIs o funcionalidades nuevas.
- Changed — Cambios de comportamiento que no rompen contrato.
- Deprecated — Qué se eliminará y cuándo.
- Removed — Lo que realmente desaparece en este release.
- Fixed — Corrección de bugs.
- Security — Avisos de seguridad.
Conectar cambios con la API pública
En lugar de “refactor interno,” prefiero:
Fixed:
createSlothyClientya no lanza cuandostorageesindexed_dby el almacén está vacío (issue #42).
Así la nota habla de lo que hace el usuario, no de cómo reorganizaste carpetas.
Guías de migración en MAJOR
Para cada MAJOR, añade MIGRATION.md (o una sección en el changelog) con:
- Fragmentos antes / después.
- Por qué el cambio (un párrafo).
- Pasos mecánicos (búsqueda y reemplazo, exports renombrados).
Releases — ritmo predecible
Tanto si publicas en npm (público o privado) como en un registro interno:
- Etiqueta en git (
v2.0.0) alineada con la versión publicada. - CI en el tag: tests, build, opcionalmente tamaño de bundle.
- Publicación desde un árbol limpio (muchos equipos solo publican desde CI).
En organizaciones, define quién puede publicar y desde qué rama (p. ej. solo main).
Deprecaciones
Usa JSDoc @deprecated en TypeScript, console.warn en runtime (con mesura) y entradas en el changelog con fecha o versión de retirada (“se elimina en v3”).
Pruebas — protege el contrato, no las carpetas
1. La API pública primero
Los tests deben importar @slothy/core igual que quien consume (desde el entry compilado, no desde rutas profundas de src/lib/domain/... salvo que esas rutas estén soportadas oficialmente).
// slothy-core.public-api.spec.ts
import { createSlothyClient } from '@slothy/core';
it('crea un cliente con almacenamiento in_memory', async () => {
const client = await createSlothyClient({ storage: 'in_memory', debug: false });
expect(client).toBeDefined();
});
Si un refactor interno rompe solo tests internos, te has alejado demasiado de la superficie soportada.
2. Tests de contrato para adaptadores
En @slothy/angular, comprueba que forRoot expone un cliente coherente con core:
- Las mismas operaciones sobre tareas tienen el mismo éxito o error con la misma config.
- Los tokens resuelven sin instancias duplicadas de
SlothyClienten escenarios típicos.
3. Evita testear detalles de implementación
No asserts sobre nombres de métodos privados o grafos de módulos internos salvo que formen parte de tu promesa de estabilidad.
4. Matriz solo donde importa
Mínimo razonable:
- Unitarias para lógica de dominio pura.
- Integración para backends de almacenamiento (
in_memoryvsindexed_dben entorno headless cuando sea viable).
Para adaptadores Angular, smoke tests con TestBed que compilen los imports y registren forRoot.
CI como barrera
Una base práctica:
| Comprobación | Para qué |
|---|---|
| Lint + formato | Coherencia de exports y documentación. |
tsc --noEmit / build |
Tipos rotos antes de publicar. |
| Tests | Lo anterior. |
| Empaquetado en seco | Que files / exports del package.json incluyan lo que crees. |
Opcional pero útil: límites de tamaño de bundle en @slothy/core para detectar dependencias pesadas accidentales.
Cómo encaja la Parte 2 con la Parte 1
| Parte 1 | Parte 2 |
|---|---|
| README como contrato | El changelog explica cómo evoluciona ese contrato |
| Límites MVP | SemVer codifica qué es una “subida segura” |
| Paquetes por capas | Alineación de versiones + tests de adaptadores |
¿Siguiente paso?
Sigue con la Parte 3 — Contribuidores y documentación: CONTRIBUTING.md, plantillas de issues, RFCs y documentación de API (TSDoc / referencia generada).
Etiquetas: código abierto, librería, TypeScript, Angular, semver, testing