La trampa de los microservicios
Hay un patrón que vemos repetidamente en el mundo startup. Un equipo fundador de dos o tres ingenieros comienza un producto nuevo. Antes de escribir su primera línea de lógica de negocio, configuran Kubernetes, arman un service mesh, crean cinco repositorios separados y diseñan una arquitectura event-driven con Apache Kafka.
Seis meses después, tienen infraestructura impresionante y casi ningún producto.
No estamos en contra de los microservicios. Hemos diseñado y operado arquitecturas de microservicios en entornos bancarios donde eran absolutamente la decisión correcta. Pero la frase clave es "donde eran absolutamente la decisión correcta." Para la mayoría de startups, especialmente en etapas tempranas, los microservicios son una optimización prematura que te frena cuando la velocidad es lo único que importa.
El costo real de los microservicios
El marketing alrededor de los microservicios enfatiza los beneficios: despliegue independiente, autonomía de equipos, flexibilidad tecnológica, escalabilidad por servicio. Lo que recibe menos atención es el impuesto operacional que pagas por esos beneficios.
Complejidad de despliegue. En lugar de desplegar una aplicación, estás desplegando cinco, diez o veinte. Cada una necesita su propio pipeline CI/CD, sus propios health checks, su propia estrategia de rollback. El despliegue de una feature simple que toca tres servicios requiere coordinar tres despliegues.
Complejidad de debugging. Cuando un request falla en un monolito, revisas un set de logs. Cuando un request falla en una arquitectura de microservicios, necesitas tracing distribuido, correlation IDs y la capacidad de seguir un request a través de múltiples servicios, colas de mensajes y bases de datos.
Consistencia de datos. En un monolito, tienes una base de datos y puedes usar transacciones. En microservicios, cada servicio es dueño de sus datos, y mantener consistencia entre servicios requiere implementar patrones como sagas o consistencia eventual — patrones que son genuinamente difíciles de hacer bien.
Overhead de red. Cada llamada entre servicios es una llamada de red. Las llamadas de red fallan. Las llamadas de red tienen latencia. Las llamadas de red necesitan autenticación, serialización y manejo de errores. Una llamada de función en un monolito toma nanosegundos. Una llamada HTTP entre servicios toma milisegundos en el mejor caso.
Para un equipo de 2-5 ingenieros trabajando en un producto que aún no tiene product-market fit, este overhead operacional es devastador. Estás gastando ciclos de ingeniería en problemas de infraestructura en vez de problemas de producto.
Cuándo el monolito es la decisión correcta
Un monolito bien estructurado no es una bola de barro. Es una unidad desplegable única con límites internos claros. Puedes tener módulos bien definidos, interfaces limpias entre ellos y reglas estrictas de dependencia — todo dentro de un codebase y un despliegue.
El monolito es la decisión correcta cuando:
Tu equipo es pequeño (menos de 10 ingenieros). El beneficio principal de los microservicios — autonomía de equipos — no importa cuando todo el equipo cabe en una sala.
Aún no tienes product-market fit. Necesitas iterar rápido, cambiar de dirección con agilidad y a veces desechar features enteras. Hacer eso en un monolito significa borrar algunos archivos. Hacerlo en microservicios significa decomisionar servicios, eliminar infraestructura y limpiar puntos de integración.
Tu escala no lo requiere. La mayoría de startups no procesan millones de requests por segundo. Un solo servidor de aplicaciones bien optimizado puede manejar más carga de la que la mayoría de startups verán en sus primeros dos años.
El monolito modular como punto medio
El enfoque que recomendamos para la mayoría de startups es el monolito modular. Esto te da los beneficios arquitectónicos de los microservicios — límites claros, interfaces definidas, dominios independientes — sin la complejidad operacional.
En un monolito modular, estructuras tu código como si fueran microservicios, pero lo despliegas como una unidad. Cada módulo tiene su propio dominio, su propia capa de acceso a datos y se comunica con otros módulos a través de interfaces bien definidas. Los módulos no pueden acceder a las implementaciones internas de otros.
Las reglas clave:
Cada módulo es dueño de sus datos. Aunque uses una base de datos, cada módulo tiene sus propias tablas y no lee ni escribe en las tablas de otro módulo. Si el Módulo A necesita datos del Módulo B, llama a la interfaz pública del Módulo B.
Las interfaces son explícitas. La comunicación entre módulos pasa por contratos definidos — llamadas de función con parámetros tipados, no consultas directas a la base de datos. Este es el mismo principio que los microservicios, pero sin la red.
Las dependencias se verifican. Usa tu sistema de build o herramientas de testing arquitectónico para verificar que los módulos no importen de las implementaciones internas de otros. Esto mantiene los límites reales, no solo aspiracionales.
Esta estructura significa que cuando eventualmente necesites extraer un servicio — porque un módulo necesita escalar independientemente, o porque un equipo separado va a ser su dueño — la extracción es directa. Los límites ya existen. Solo los estás moviendo de in-process a over-the-network.
Señales de que estás listo para separar
Hemos visto equipos extraer microservicios en el momento correcto, y hemos visto equipos hacerlo demasiado temprano. Las señales de que realmente estás listo:
Conflictos de despliegue. Diferentes equipos frecuentemente se bloquean entre sí por sus despliegues. Los releases se retrasan porque la feature del Equipo A no está lista pero la del Equipo B sí.
Desajuste de escalamiento. Una parte de tu sistema necesita diez veces más recursos que todo lo demás. Estás escalando toda la aplicación para satisfacer las necesidades de un módulo.
Límites de ownership de equipos. Has crecido al punto donde equipos distintos son dueños de dominios distintos, y están pisándose el código mutuamente. La estructura organizacional está lista para fronteras de servicios.
Desajuste tecnológico. Un módulo genuinamente se beneficiaría de un stack tecnológico diferente. No "queremos probar Rust" sino "este workload específico sería 10x más eficiente con un enfoque diferente."
Si ninguna de estas aplica, no necesitas microservicios todavía. Y está bien. Algunos de los productos más exitosos del mundo corrieron como monolitos mucho más tiempo del que esperarías.
La mejor arquitectura es la que te permite entregar producto. En las etapas tempranas, eso es casi siempre un monolito.
La lección real
Las decisiones de arquitectura deben ser guiadas por restricciones reales, no por lo que está de moda. Los microservicios resuelven problemas reales — pero solo si realmente tienes esos problemas. Si tu restricción es "necesitamos entregar features rápido con un equipo pequeño," los microservicios están activamente trabajando en tu contra.
Empieza con un monolito modular. Entrega producto. Encuentra product-market fit. Cuando golpees restricciones reales de escala u organización, extrae servicios quirúrgicamente, uno a la vez, con justificación clara para cada extracción.
Las empresas que ganan no son las que tienen la arquitectura más sofisticada. Son las que entregaron el producto correcto en el momento correcto. La arquitectura es una herramienta para eso, no un fin en sí misma.