El mensaje de Slack llegó a las 22:47 de un viernes. Un tester había pagado en el sandbox de Stripe, el webhook se disparó, la fila de suscripción apareció en la base de datos, y el web app seguía diciendo "Plan gratuito."
Sabía dónde mirar. Había escrito la feature tres semanas antes y todavía recordaba su forma. Lo que no recordaba, porque nadie lo recuerda hasta que duele, es que la palabra en pantalla y el valor en la API no se mantienen alineados solos.
El bug nunca estuvo en un solo repositorio
La API guardaba el estado del plan como un enum entero. El web app mapeaba ese entero a una etiqueta con un archivo de constantes compartido. El admin panel tenía su propia columna con una badge legible.
Los tres eran correctos el lunes. El viernes alguien había añadido un estado de prueba nuevo en la migración de la API, actualizado el copy del web app, y olvidado el color de la badge en el admin. Tres ediciones pequeñas en tres sitios que debían representar el mismo hecho.
En un setup multi-repo eso son tres pull requests, tres revisiones si tienes suerte, tres pipelines de deploy, y una ventana en la que producción muestra tres verdades distintas según qué URL abras. Un viernes por la noche no tienes paciencia para esa ventana. Tienes a un tester refrescando la página de facturación y preguntándose si tu producto funciona.
En CastorStack las cuatro aplicaciones viven en un monorepo: marketing site, web app de cliente, admin panel y API en .NET. Los contratos compartidos están en shared/. El nombre del producto y la lista de locales están en config/. Cuando cambia el enum, TypeScript y C# ven la misma fuente de verdad en un solo commit.
Ese arreglo del viernes fue una rama, un diff, un push. La API devolvió el estado nuevo, el web app leyó el mapeo compartido, el admin panel recogió el mismo nombre de enum. Sin puzzle de orden de deploy. Sin "¿terminó Vercel antes que Azure?"
Lo que la gente teme de los monorepos
La objeción que más escucho es ruido. Cambias el color de un botón en el hero del marketing y el CI corre tests del admin panel. Preocupación válida, y también resuelta si el pipeline está acotado.
El CI de CastorStack no trata cada archivo igual. Un cambio bajo apps/web-app/ no tiene que bloquear un spec del marketing site que no toca contratos compartidos. El objetivo no es un job gigante que corre para siempre. El objetivo es un solo sitio donde puedes buscar en todo el producto cuando un bug cruza fronteras.
La segunda objeción es control de acceso. "Los contratistas de marketing no deberían ver código de billing." Cierto en equipos grandes. Para un fundador solo o una dupla, todos ya ven todo porque todos son todos. Separar repositorios antes de tener un problema de permisos compra pureza organizativa al precio de coherencia un viernes por la noche.
La tercera es miedo a herramientas: "Los monorepos necesitan Nx o Turborepo o scripts custom." A veces sí a escala. A escala de lanzamiento necesitas un package.json raíz que sepa correr cada app, disciplina de un solo lockfile, y el hábito de poner tipos compartidos en una carpeta en vez de copiar interfaces. CastorStack trae ese layout para que no lo inventes en la semana dos.
El momento en que el monorepo se paga solo
Nunca es en el primer commit. El primer commit se siente pesado porque el árbol de carpetas es grande.
Se paga la primera vez que una feature toca más de una superficie. Billing toca el handler del webhook en la API, la página de suscripción del cliente, la lista de suscripciones del admin, y a menudo una línea de copy en la tabla de precios del marketing. La autenticación toca cada app más configuración CORS más plantillas de email.
Cuenta las superficies. Ese es el número de repositorios que te ofreces a mantener alineados si separas pronto.
Un monorepo no elimina coordinación. Mueve la coordinación al historial de Git en vez de a tu calendario. Ves, en un diff, que el handler del webhook y el string de la UI cambiaron juntos. La revisión de código, incluso la auto-revisión a la mañana siguiente, atrapa el drift antes que los usuarios.
Lo que un monorepo no te da
No reemplaza decisiones de producto. Sigues eligiendo si los usuarios en prueba ven features premium. Sigues eligiendo si el admin puede cancelar una suscripción en nombre del cliente. La forma del repositorio no responde esas preguntas. Solo hace visibles en un solo sitio las consecuencias de tus respuestas.
Tampoco reemplaza independencia de deploy. CastorStack despliega el marketing site en Vercel, las dos apps Vite en sus propios proyectos Vercel, y la API en Azure Container Apps. Mismo repo, cuatro destinos de deploy. El monorepo es alineación de código fuente, no un solo binario.
Y no es complejidad gratis para siempre. Si creces hacia equipos separados con trenes de release distintos, sacar un bounded context puede tener sentido. La mayoría de productos nunca llegan a ese día. La mayoría sí llegan al día en que un usuario de pago encuentra una discrepancia entre lo que dice la factura y lo que muestra el dashboard.
Por qué importa ahora que construir se volvió barato
Generar una página React o un controller en C# lleva minutos ahora. Lo caro es cablear la página, el controller, la migración, la vista del admin y el copy del email para que sigan significando lo mismo después de tu tercera iteración.
Un modelo puede escribir cada archivo aislado. No puede, por sí solo, garantizar que ese aislamiento sobreviva al contacto con producción. Un monorepo con contratos compartidos es cómo le das al modelo, y a ti, un solo mapa.
CastorStack existe para que empieces desde ese mapa: cuatro apps, configuración compartida, tests y CI que ya conocen los límites. El arreglo del viernes que describí no es un argumento de venta porque sea glamuroso. Lo es porque es trabajo ordinario que se vuelve caro cuando el layout del repositorio pelea contigo.
Si estás eligiendo dónde gastar las semanas del lanzamiento, gástalas en la feature que solo les importa a tus clientes. Deja que el repositorio defienda la coherencia por su cuenta.