← Todos los artículos EN

El costo oculto de los componentes compartidos en banca multi-producto

Construir una librería de componentes React compartida para productos financieros suena sencillo. Después de dos años entregándola en cuatro productos en Scotiabank, esto es lo que pasa realmente.

Lanzamos la librería de componentes compartidos con buenas intenciones: un Button, un Input, un Modal — usados de forma consistente en todos los productos financieros. Dos años después manteníamos cuatro forks divergentes, un changelog de semver en el que nadie confiaba, y un proceso de breaking changes que bloqueaba releases entre equipos durante días.

Nadie te dice esto sobre las librerías de componentes compartidos en organizaciones grandes: el problema de ingeniería es fácil. El problema organizacional es brutal.

La promesa versus la realidad

La promesa es obvia. Los componentes compartidos significan UX consistente entre productos, reducción de duplicación, un solo lugar para corregir bugs. La realidad después de escalar:

  • Los equipos de producto tienen cadencias de release diferentes
  • La “mejora” de un equipo es el breaking change de otro
  • Las decisiones de diseño tomadas para un producto no se generalizan
  • Cada consumidor se convierte en stakeholder de facto en cada PR

En Scotiabank CCAU, construimos la librería de componentes para servir a la iniciativa OneBank — una plataforma donde los clientes podían solicitar préstamos, tarjetas de crédito, cuentas de ahorro y seguros a través de una experiencia de onboarding digital unificada. Cada producto tenía su propio equipo, su propio pipeline de deployment y su propio timeline. La librería de componentes debía ser el tejido conectivo.

El problema del version drift

A los meses:

ProductoVersión de la librería
Préstamos3.4.1
Tarjetas de crédito2.8.0
Cuentas4.1.0
Seguros2.7.3

Cuatro productos, cuatro versiones, ninguna actual. El equipo de Seguros estaba dos versiones mayores atrás porque un cambio en v3 rompió su orquestación de formularios personalizada y no habían tenido capacidad para migrar. El equipo de Tarjetas de Crédito había copiado directamente el fuente de un fix de v3.2 — lo que significaba que ya no se beneficiaban de correcciones futuras a ese componente.

Esto es normal. Toda librería grande compartida con la que he trabajado termina aquí si no se combate activamente.

Qué causa realmente el drift

El version drift no es pereza. Es comportamiento racional bajo restricciones.

Cuando un equipo está a tres sprints de una fecha límite regulatoria, actualizar una versión mayor de una librería no es un riesgo aceptable. El cálculo es simple: el costo de una actualización fallida (release bloqueada, regresión descubierta en UAT, rollback de emergencia) supera ampliamente el beneficio de estar en la última versión de un design system.

La causa real es que las actualizaciones de librería se tratan como trabajo de mantenimiento, no como trabajo de producto. No aparecen en roadmaps. No reciben capacidad en el sprint. Se acumulan hasta que la brecha es demasiado grande para cerrarla sin un esfuerzo de migración dedicado — que luego requiere su propia planificación, justificación y recursos.

La solución estructural

Después de dos años de iteración, tres cosas ayudaron realmente.

1. Capas de abstracción separadas

Dividimos la librería en tres paquetes:

@onebank/primitives    — componentes base sin estilos (Button, Input, Modal)
@onebank/components    — implementaciones con estilos y opiniones
@onebank/forms         — orquestación de formularios con validación

@onebank/primitives sigue semver estricto con una política de no breaking changes en la API pública. Cualquier cosa que sería un breaking change pasa por un ciclo de deprecación de al menos dos releases. @onebank/components puede evolucionar más rápido porque es opinionado.

Esto redujo inmediatamente la fricción de actualización. Actualizar primitivos pasó a ser bajo riesgo (API estable, sin cambios de estilo). Los equipos que se preocupaban por la consistencia visual usaban components. Los equipos con requisitos de diseño personalizados usaban primitivos y mantenían su propia capa.

2. Codemods automatizados para breaking changes

Cuando enviábamos un breaking change, enviábamos un codemod junto a él:

npx @onebank/codemods migrate --from 3 --to 4

El codemod manejaba ~80% del trabajo mecánico de migración. No podía manejar todos los casos, pero eliminó el factor miedo de las actualizaciones mayores. Una migración que antes tomaba dos días de find-and-replace cuidadoso se convirtió en una ejecución automatizada de treinta minutos más una revisión enfocada.

3. Greenkeeping como norma del equipo, no como tarea

El cambio más difícil fue cultural. Establecimos una norma: cada equipo ejecuta Renovate con auto-merge habilitado para actualizaciones de minor y patch. Las actualizaciones mayores tienen una ventana de un sprint después del release.

Esto requirió confianza en el equipo de la librería — específicamente, confianza en que patch significaba “sin cambio de comportamiento” y minor significaba “retrocompatible”. Construimos esa confianza siendo conservadores. Si no estábamos seguros de si algo era un breaking change, lo llamábamos breaking change.

Qué haría diferente

Si empezara de nuevo, resistiría el modelo de librería única. En cambio:

  • Un paquete de primitivos con una API estable y mínima. Sin estilos. Piensa en Radix UI o Headless UI.
  • Capas de componentes por producto que poseen su implementación visual.
  • Un paquete de design tokens que sincroniza constantes visuales (colores, espaciado, tipografía) sin acoplar las APIs de los componentes.

La clave es que la consistencia no requiere implementación compartida — requiere contratos compartidos. Un sistema de tokens visuales te da consistencia visual entre productos sin el acoplamiento organizacional que crean las librerías de componentes compartidas.

La métrica que importa

Algo que empecé a seguir tarde pero desearía haber rastreado desde el principio: tiempo en producir para un ingeniero nuevo en el equipo.

No “tiempo para configurar el entorno de desarrollo”. Específicamente: ¿cuánto tarda un desarrollador nuevo en el equipo de producto (pero familiarizado con la librería de componentes) en construir una página de formulario funcional, compliant y on-brand?

Esta métrica captura todo. Si la librería de componentes está bien diseñada, un desarrollador nuevo puede ser productivo en un día. Si no, pasa su primera semana leyendo código fuente para entender qué hace validation="strict" realmente.

En nuestro mejor momento, llegamos a cuatro horas. Esa es la versión de la librería de la que me enorgullezco.