Why I Chose a Monolith (and When I'd Break It Apart)
Deliberately building a monolith for a side project — and the criteria I'd use to split it into services later.
Context
For a personal project, I had to choose: start with a monolith or design for microservices from day one. I'd read the usual advice — 'start with a monolith' — but I wanted to be explicit about why, and what would change my mind. The project was a small SaaS tool: one Next.js app, one database, a few background jobs. I had no team, no scale requirements, and no budget for complex infrastructure.
Constraints
- Solo developer — every hour spent on infra was an hour not spent on product
- Uncertain product direction — the app might pivot; the architecture needed to be flexible
- Deployment simplicity — I wanted to ship with minimal operational overhead
Architecture
I chose a monolith: a single Next.js app with API routes, a PostgreSQL database, and background jobs running in the same process (or a simple worker script). Everything lived in one repo, one deploy. I drew clear module boundaries in the code — domains like 'billing,' 'users,' 'projects' — so that if I ever needed to extract a service, the seams were already there. I documented my split criteria: I'd consider extracting when (a) a module had different scaling needs, (b) a module needed a different tech stack, or (c) deployment frequency of one part was blocking the rest. None of those were true yet.
Alternatives considered
- Microservices from day one: Would have added deployment, networking, and debugging complexity with zero benefit at current scale. A monolith deploys once; microservices deploy N times.
- Serverless functions for each domain: Cold starts and connection pooling with PostgreSQL were concerns. A single long-running process was simpler for a small app.
Lessons learned
- Module boundaries in code cost nothing and pay off when you need to extract. Think in domains even inside a monolith.
- Document your split criteria. It forces you to think about when the architecture should change — and prevents premature extraction.
- Deployment simplicity is a feature. One deploy, one log stream, one place to debug.