Why I stopped reaching for microservices
For the last decade, we've been told that microservices are the eventual destination for every successful application. We decouple, we containerize, and we orchestrate. But for most teams, this overhead is a tax on velocity that never pays dividends.
The cognitive load of managing fifty repositories, disparate CI/CD pipelines, and tracing requests across a network boundary is immense. Often, the "scaling" problem we think we're solving is actually a "modularization" problem within a single codebase.
services:
monolith:
image: internal/core-app:latest
replicas: 3
deploy:
mode: replicated
resources:
limits:
cpus: '0.50'
memory: 512M
The Modular Monolith
Instead of physical decoupling, I've returned to the Modular Monolith. By enforcing strict internal boundaries using language-specific features (like Go packages or Rust modules) and a shared database, we keep the deployment simplicity of a single unit while maintaining the organizational benefits of microservices.
The goal isn't just to write code faster; it's to maintain it longer with fewer sleepless nights debugging network timeouts between services that could have just been a local function call.