- You Probably Don't Need Microservices Yet — And That's Fine
- The Monolith-to-Microservices Playbook Nobody Gave Us
- We Broke Up Our Monolith. Here's What We Wish We'd Known.
There's a moment in every growing engineering org where someone says it: "We need to move to microservices." Maybe deploys are getting painful. Maybe one team's changes keep breaking another team's features. Maybe you just read a blog post from a company ten times your size and thought, yeah, that's the move.
We've been there. And after living through a multi-year migration, here's the honest version of what that journey actually looks like — the parts that went well, the parts that didn't, and the questions we wish we'd asked earlier.
Let's get this out of the way: monoliths aren't bad architecture. A well-structured monolith with clear module boundaries can carry you surprisingly far. For a team of 5–20 engineers working on a single product, a monolith is often the right call. It's simpler to deploy, simpler to debug, and simpler to reason about.
The problems don't come from the monolith itself — they come from what happens when organizational complexity outgrows your deployment unit. When three teams are stepping on each other's toes in the same codebase, when a change to the billing module requires a full regression of the notification system, when your deploy queue is a two-hour bottleneck — that's when the conversation gets real.
But even then, microservices aren't the only answer. Sometimes the fix is better module boundaries within the monolith. Sometimes it's investing in test infrastructure. The key question isn't "should we use microservices?" It's "what specific problem are we solving, and is decomposition the best way to solve it?"
If you do decide to decompose, resist the urge to draw boxes on a whiteboard and call them services. Instead, look at where your monolith already has natural seams — places where the code is loosely coupled in practice, even if it's all deployed together.
In our case, the first service we extracted was authentication. It had a clear API surface, minimal shared state with the rest of the system, and a dedicated team that owned it. It was almost a standalone service already; we just had to make it official.
Contrast that with our first failed attempt: extracting the "order processing" domain. It turned out to be deeply entangled with inventory, pricing, and customer state. We spent weeks untangling dependencies before realizing we'd drawn the boundary in the wrong place. The lesson? Let the code tell you where the boundaries are. Domain-driven design helps here — but only if you're honest about where your actual bounded contexts are, not where you wish they were.
Here's something nobody warns you about: the engineering effort of extracting a service is maybe 30% of the work. The other 70% is everything around it.
You need service discovery. You need distributed tracing, because when a request touches four services and something is slow, grep in a single log file won't cut it anymore. You need to figure out your data strategy — are services sharing a database? (Please don't do this long-term.) Are you doing event-driven communication? Synchronous API calls? Both?
You need deployment pipelines per service. You need to think about backwards compatibility because services will deploy independently. You need contract testing. You need health checks, circuit breakers, and retry logic that doesn't accidentally create a cascading failure.
None of this is insurmountable, but it's a lot of foundational work that doesn't ship features. If your org isn't willing to invest in platform infrastructure alongside service extraction, you're going to have a bad time.
The single hardest part of our migration was data. In a monolith, you can join any table to any other table. Need customer info alongside order history alongside support tickets? One query. Done.
In a microservices world, that data lives in different services with different databases. Now you're looking at API composition, eventual consistency, and the very real possibility that your "simple" dashboard query now requires orchestrating calls to five services.
Our advice: don't try to split the database on day one. Use the "strangler fig" pattern — new services get their own data stores, but the monolith keeps its database. Over time, you migrate data ownership service by service. It's slower, but it's dramatically safer than a big-bang data migration.
And invest in an event backbone early. Whether it's Kafka, NATS, or something else, having a reliable way for services to communicate state changes asynchronously will save you from building a tangled web of synchronous dependencies.
Conway's Law is real, and it cuts both ways. If you reorganize your teams around services before the code is ready, you'll create artificial boundaries that make the migration harder. If you keep the old team structure while deploying new services, nobody will own the cross-cutting concerns.
What worked for us was a phased approach: start with a small "migration squad" that handles the first few extractions and builds the platform tooling. As services stabilize, shift ownership to product teams aligned with the service domains. The squad evolves into a platform team that supports the service infrastructure.
The cultural shift matters too. In a monolith world, engineers are used to having full context. In a services world, they need to be comfortable with not knowing how everything works — and with depending on other teams' APIs. That's a real adjustment, and it's worth being intentional about.
Here's the contrarian take: you probably shouldn't decompose everything. We ended up with about a dozen services extracted from our monolith, with a still-substantial core monolith handling the rest. And that's fine.
Not every module benefits from being an independent service. Some things are tightly coupled by nature, and forcing them apart creates more complexity than it solves. The goal was never "zero monolith" — it was "teams can ship independently without stepping on each other." Once we hit that, we stopped extracting and focused on making what we had robust.
If you're staring at a monolith and wondering if it's time, here's the short version:
Don't migrate because it's fashionable — migrate because you have a specific, painful problem that decomposition solves. Start with the easy seams, not the core domain. Invest heavily in platform infrastructure before you go wide. Treat data as the hardest problem, not an afterthought. And give yourself permission to stop before everything is a microservice.
The best architecture is the one that lets your team ship with confidence. Sometimes that's microservices. Sometimes it's a well-structured monolith. Often, it's something in between.
What's been your experience migrating from a monolith? We'd love to hear what worked — and what didn't — for your team.
- Primary keyword: "monolith to microservices" — included in headline and opening paragraphs
- Secondary keywords: "microservices migration," "strangler fig pattern," "monolith vs microservices," "service decomposition"
- Suggested meta description: "A practical, experience-driven guide to migrating from a monolith to microservices — when to start, where to split, and when to stop." (141 characters)
- Internal linking opportunities: Link to related posts on API design, CI/CD pipelines, or team scaling
- Image alt text suggestions: "Diagram showing monolith to microservices extraction path," "Strangler fig migration pattern visualization"
- Tone applied: Conversational and practical — first-person plural ("we") framing to feel like a peer sharing lessons, not a lecture
- Audience calibration: Assumes familiarity with deployment pipelines, DDD concepts, distributed systems tradeoffs — no hand-holding on basics
- Word count: ~1,450 words (body content)
- Review with your engineering team for accuracy against your own migration experience
- Add specific metrics or timelines from your org to make it more concrete (e.g., "deploy times dropped from 2 hours to 15 minutes")
- Pair with a diagram showing your before/after architecture
- Consider a follow-up post diving deeper into the data migration strategy