DiseñoVersionar un design system: qué cambios rompen la compatibilidad
El SemVer solo protege a quien usa el sistema si la API pública está escrita. Restringir el tipo de una prop es una major, y a menudo quitar una variable CSS.

Versionar un design system es numerar cada release de una librería de componentes compartida para que quien la usa sepa, solo con el número, si actualizar va a cambiar su producto sin pedirle permiso. La regla detrás del número es el Semantic Versioning: una major para todo lo que rompe a quien usa la librería, una minor para lo que se añade, un patch para las correcciones. Lo difícil es lo primero. En un design system, un cambio incompatible va mucho más allá de una prop renombrada.
La pregunta se vuelve concreta el día en que un segundo producto depende del sistema. Aquí tienes qué incluye la API pública de un design system, qué cambios la rompen y cuáles no, dónde encajan los cambios visuales y cómo se publica un cambio incompatible sin dejar tirados a los equipos que siguen en la versión anterior.
La versión en 30 segundos
Un cambio es incompatible cuando alguien actualiza sin tocar su propio código y obtiene otro resultado: un error de build, un error de tipos, un layout que se ha movido, una auditoría de accesibilidad que falla, un selector de test que ya no encuentra nada. Quitar un componente, quitar una prop o restringir su tipo siempre es una major. Mover las props propagadas a otro elemento, cambiar el display del contenedor de los hijos, dejar de leer una custom property CSS que los consumidores fijan o añadir un landmark main suele ser una major. Añadir un componente, añadir una prop opcional, ampliar un tipo y marcar algo como obsoleto son minor. Un cambio incompatible sale después de al menos una minor que marca como obsoleto el camino antiguo, con una guía de migración y, cuando el trabajo es mecánico, un codemod.
¿Por qué pesa tanto el número de versión?
Porque los gestores de paquetes actúan sobre él sin que nadie lo revise. Con npm install, npm guarda la dependencia con el prefijo caret, así que la versión 1.2.3 se escribe ^1.2.3 (configuración de npm, save-prefix). Un rango caret acepta cualquier minor y patch hasta la siguiente major, sin incluirla: ^1.2.3 cubre todo desde 1.2.3 hasta antes de 2.0.0 (node-semver). Un cambio incompatible publicado como 1.8.0 llega a todos los consumidores en su siguiente instalación limpia, y nadie ha decidido aceptarlo.
La especificación Semantic Versioning 2.0.0 pide una cosa antes que nada: quien la adopta tiene que declarar una API pública, en el código o en la documentación, de forma precisa y completa. Muchos design systems se saltan ese paso. Versionan el paquete y dejan el contrato implícito, así que cualquier comportamiento observable acaba formando parte del contrato igualmente. Hyrum Wright, ingeniero de Google, lo formuló como ley: con suficientes usuarios, alguien dependerá de cada comportamiento observable del sistema (la ley de Hyrum).
¿Qué es la API pública de un design system?
Bastante más que la tabla de props. Quien usa el sistema puede depender de cualquiera de estas siete partes, y cada una necesita una respuesta escrita a la pregunta «¿podemos cambiar esto en una minor?».
- Nombres de componentes y props. La ruta de import, el nombre exportado, cada prop y los valores que acepta.
- Tipos de las props. En TypeScript el tipo es el contrato. Una unión de tres strings promete que nunca hará falta una cuarta.
- Estructura renderizada. Cuál es el elemento raíz, cuál recibe las props propagadas, cómo se colocan los hijos.
- Puntos de estilo. Clases, custom properties CSS y atributos data sobre los que un consumidor puede actuar.
- Tokens. Primero los nombres, después los valores. Un token renombrado rompe cada hoja de estilos que lo cita.
- Semántica de accesibilidad. Roles, landmarks, etiquetas y gestión del foco, que las auditorías de página leen.
- Valores visuales. Color, tipografía y espaciado, sobre los que otros equipos montan sus layouts.
Escribir esta lista es el paso que pide el SemVer. Nuestras reglas de API de componentes cubren las tres primeras partes. Las otras cuatro no suelen estar escritas en ningún sitio hasta que algo se rompe.
¿Qué cambios rompen a quien usa el sistema?
El equipo de Primer, el design system de GitHub, publica la tabla más detallada que conocemos: cada tipo de cambio con su salto de versión (guía de versionado de Primer React). Las filas de abajo la siguen, con lo que el consumidor ve de verdad después de actualizar.
| Cambio | Salto | Qué ve el consumidor |
|---|---|---|
| Se elimina un componente o una prop | Major | Error de build o de tipos al actualizar |
| Se restringe el tipo de una prop (de cualquier string a tres strings permitidos) | Major | Errores de tipos en valores que antes funcionaban |
| Una dependencia pasa a una nueva major | Major | Conflictos de peer o dos copias del mismo paquete |
| Las props propagadas pasan a otro elemento | Potencialmente major | Handlers y props de layout en el nodo equivocado |
| El contenedor de los hijos cambia de display, de block a flex | Potencialmente major | Los espacios desaparecen, los hijos se recolocan |
| Un componente deja de leer una custom property que los consumidores fijan | Potencialmente major | Las personalizaciones dejan de funcionar, sin errores |
| Un componente añade o quita un landmark main o contentinfo | Major | Landmark duplicado o ausente en las auditorías de axe |
| Se renombra o se quita un atributo data-component | Major | Los tests y los selectores de analítica dejan de encontrar el elemento |
Dos filas merecen una mirada más atenta. El cambio de display es el propio ejemplo de Primer: un botón con la etiqueta «Save» seguida de un span «changes» pasa de block a flex, el espacio entre las dos palabras desaparece y el botón muestra «Savechanges». Ningún control de tipos lo detecta. La fila de las custom properties depende de la intención. Si un consumidor fija --example-component-gap y el componente empieza a leer --gap, después de actualizar la personalización ya no hace nada. Primer considera privadas las custom properties de componente salvo que estén documentadas como públicas, que es justo la declaración que pide el SemVer.
¿Qué cambios no rompen nada?
Una minor añade sin quitar. Añadir un componente, añadir una prop opcional y ampliar un tipo (una prop numérica que ahora también acepta un string) son minor en la tabla de Primer, porque todo valor que funcionaba antes sigue funcionando. Marcar como obsoleto un componente o una prop también es una minor: el SemVer exige una minor cuando una parte de la API pública se marca como obsoleta, y la eliminación espera a la siguiente major.
Un patch corrige un comportamiento que contradice la documentación. La trampa es el bug sobre el que los consumidores ya han construido. Si un desplegable siempre se ha cerrado al hacer scroll y tres productos cuentan con ello, la corrección es un cambio de comportamiento para esos tres. Esos los publicamos como minor, con una línea en el changelog que describe el comportamiento antiguo, para que nadie se entere por una incidencia de soporte.
¿Los cambios visuales son cambios incompatibles?
A veces, y es justo donde casi todas las políticas de versionado se callan. En 2018 Nathan Curtis, de EightShapes, defendió que el color, la tipografía y el espaciado necesitan criterios propios para decidir qué rompe, porque quien adopta el sistema monta sus layouts encima. Pocos equipos, señalaba, documentan esos criterios (Visual Breaking Change in Design Systems). Su ejemplo de cambio seguro: oscurecer el fondo del botón principal para subir su contraste.
La prueba que aplicamos es dimensional. Un cambio que deja cada caja del mismo tamaño es una minor: un tono nuevo dentro de la misma franja de contraste, una sombra nueva, un radio retocado. Un cambio que altera las medidas sale como major o detrás de un flag que se activa a mano: un interlineado que suma 2 px a cada fila de una tabla, un token de espaciado que lleva una rejilla de tarjetas de cuatro columnas a tres, una fuente más ancha que manda la etiqueta de un botón a dos líneas. Los consumidores los ven en capturas, nunca en el build, así que los tests de regresión visual en su lado son la única alarma fiable.
¿Cómo se publica un cambio incompatible sin dejar tirado a nadie?
En cinco pasos, cada uno tomado de un sistema que publica su proceso.
- Primero, márcalo como obsoleto en una minor. Las FAQ del SemVer piden al menos una minor con el aviso de obsolescencia antes de la major que elimina. Márcalo en los tipos, muestra un aviso en los builds de desarrollo y pon la alternativa en la documentación.
- Anuncia el plazo. El design system de Atlassian se compromete a no eliminar ninguna función en disponibilidad general sin un periodo de obsolescencia anunciado con claridad, y a hacer cambios incompatibles solo en las major, que deben ser poco frecuentes (fases de release de Atlassian).
- Prepara un codemod para la parte mecánica. La actualización de MUI a la v6 reescribe las props antiguas de Grid en la nueva forma
sizeyoffsetcon un solo comando,npx @mui/codemod@latest v6.0.0/grid-v2-props(guía de migración de MUI v6). Shopify acompaña cada major de Polaris con un migrator por el mismo motivo (guía de Polaris de la v11 a la v12). - Escribe la guía de migración para el resto. Un codemod resuelve los renombres. Un layout que se recoloca no lo puede resolver, así que la guía lista cada cambio visual con un antes y un después.
- Convierte el salto de versión en una decisión en cada cambio. Primer exige un archivo changeset en cada pull request que toca la API pública o el comportamiento en runtime, declarando patch, minor o major (Changesets). El salto se discute en la revisión, junto al diff, en lugar de adivinarlo el día del release.
¿Una versión para toda la librería o una por componente?
Curtis describe los dos modelos (Versioning Design Systems). Con un solo paquete, un cambio incompatible en cualquier componente lleva toda la librería a la siguiente major, también para quien nunca importa ese componente. Con un paquete por componente, como hace Atlassian, un botón puede estar en una major y un modal en otra, y cada uno actualiza solo lo que ha cambiado. El coste pasa a las dependencias: un componente que envuelve a otro tiene que volver a publicarse cada vez que cambia su dependencia, y un consumidor puede acabar con dos versiones de la misma primitiva en un solo bundle.
Para un sistema con pocos consumidores, un paquete único es más fácil de seguir, y una major de toda la librería es más un problema de calendario que técnico. Las versiones por componente compensan cuando los consumidores actualizan a ritmos muy distintos. Las reglas de salto de versión valen en los dos modelos.
Conceptos cercanos
El versionado es una parte de la gestión de un sistema compartido. Gobernar un design system en diez proyectos explica quién decide qué sale. El component drift cuenta qué pasa cuando los consumidores dejan de actualizar y copian los componentes. Una documentación que se lee es el sitio donde se escribe la API pública. Y la comparación entre design tokens y variables CSS explica por qué un token renombrado llega más lejos que una prop renombrada.
Preguntas frecuentes
Depende de lo que mueva el valor. Renombrar un token siempre rompe, porque cada hoja de estilos que cita el nombre antiguo deja de resolverlo. Un valor nuevo que mantiene las medidas, como un tono de marca más oscuro dentro de la misma franja de contraste, suele ser una minor. Un valor nuevo que cambia tamaños o flujo, como un token de espaciado o de interlineado, puede recolocar cada layout montado encima y merece una major o un flag que se active a mano. Primer considera incompatible pasar a un token que solo existe en la siguiente major de su paquete de tokens, salvo que el valor antiguo se mantenga como fallback.
No desde el momento en que un segundo producto depende de él. El SemVer dice que en la major cero cualquier cosa puede cambiar en cualquier momento y que la API pública no debe considerarse estable, así que el número no le dice nada del riesgo a quien actualiza. El caret de npm también se comporta distinto por debajo de la 1.0: ^0.2.3 solo acepta patches anteriores a la 0.3.0, de modo que cada minor se convierte en una actualización manual para cada consumidor. Publicar la 1.0.0 es el momento en que se declara la API pública, que es la parte del versionado que protege de verdad a quien usa el sistema.
El SemVer fija un mínimo, no una duración: al menos una minor con el aviso de obsolescencia antes de la major que elimina. En la práctica el plazo tiene que cubrir el ciclo de actualización del consumidor más lento. Un producto que actualiza una vez por trimestre necesita un aviso visible durante un trimestre; si no, su equipo se encuentra la eliminación sin haber visto nunca el aviso. Atlassian se compromete públicamente a un periodo de obsolescencia anunciado con claridad para cada función en disponibilidad general, y dejar el plazo por escrito importa más que su duración exacta.
Necesita las mismas reglas, no necesariamente el mismo número. Nathan Curtis ya señalaba en 2017 que los equipos de design system aplicaban el SemVer también a sus librerías de diseño, no solo al código. Un componente o una propiedad de variante renombrados en una librería de Figma rompen cada archivo que los usa, igual que una prop renombrada rompe el código. Mantener alineados los números de diseño y de código ayuda a que diseñadores e ingenieros hablen de la misma release; mantenerlos separados funciona siempre que el changelog relacione uno con otro.
Servicios relacionados
Artículos relacionados
Diseño
NegocioDesarrollo de apps de consumo en la nube: 10 puntos antes de firmar
Diez comprobaciones antes de contratar un estudio para una app de consumo en la nube: modelo de sync, cobro en tiendas, coste por usuario activo.
Web