Monolith vs microservices and the team problem behind it
Monolith vs microservices debates often mask a team design problem. Learn how to spot the real issue, ask better questions, and act.

Why this argument keeps coming back
Architecture fights usually start a few weeks after delivery gets messy. Releases slip, bugs pile up, and people get tired of waiting on each other. Then someone says the monolith is the problem, and someone else says microservices will fix it.
That sounds like a technical argument, but the trigger is often human. A team misses deadlines and reaches for the thing it can point at: the codebase. Code is visible. Workflow problems are harder to name. It takes more honesty to say approvals are slow, ownership is fuzzy, or too many people need to touch the same feature.
The same complaint can point to very different pain. One engineer means "tests take 40 minutes." A product manager means "every small change needs three teams." Support means "releases are risky, so fixes wait until next week." Those all sound like architecture problems, but they describe different bottlenecks.
Conway's law shows up here. Teams build systems that match how they talk, plan, and hand work off. If the team design is messy, the architecture debate gets louder. People argue about service boundaries when the real issue is that nobody owns a full slice of work from idea to production.
The same few patterns show up again and again. One team owns the database, so every change waits in line. Two teams edit the same part of the code and block each other. Releases need manual checks from several people. Nobody can say who decides when to split or combine parts of the system.
Name the bottleneck first. If builds are slow, fix builds. If ownership is blurry, redraw ownership. If one team carries all the release risk, change the workflow before you change the architecture.
The monolith vs microservices debate keeps coming back because it gives stress a neat label. The pain is real. The cause often isn't.
What the monolith argument often hides
Many monolith vs microservices fights start after work begins to feel heavy. A small change touches three teams, waits for approvals, then sits in a shared release queue. People blame the codebase first because they can see it. The team design problem sits underneath it.
When nobody owns a part of the product from start to finish, every edit looks bigger than it is. A simple checkout tweak may need one engineer for the API, another for the admin screen, a third for deployment, and a manager to sort out priorities. The code did not create all that weight. Unclear ownership did.
Handoffs make this worse. Each one adds delay, drops context, and creates another chance for "I thought you meant something else." Teams often call that an architecture limit, even when the system itself is fine. The slow part is the line of people around the change.
Shared release steps create another false signal. If every small edit rides the same test cycle, approval path, and deployment window, the whole company feels the change. Then the monolith gets blamed for a process problem. A service based setup with the same release ritual would still feel slow.
Blurry product boundaries push people into the same files, tables, and meetings. That usually means the business has not decided where one area ends and another begins. The code reflects that confusion. Conway's law is blunt here: teams and software mirror each other more than most people want to admit.
Trust problems hide in this debate too. If teams do not trust each other's tests, reviews, or judgment, they ask for extra checks and central approvals. That looks like an architecture bottleneck. It is usually a people problem.
A better team topology can remove more friction than a repo split. Small product teams often move faster inside one codebase after they fix ownership and release rules than other teams do after splitting into services. Clear boundaries help, but clear responsibility and trust usually help first.
Questions to ask before you change the system
Before you split code into more services, or pull services back into one codebase, study the work itself. In many monolith vs microservices debates, the code gets blamed first because everyone can see it. The slower part is often less obvious: waiting, handoffs, and shared ownership.
Start with delay. A team may say deployment is hard, but the change might actually sit in review for three days, then wait for QA, then wait for a release manager. That is not a code structure problem. It is a flow problem.
Ask a few direct questions:
- Where does a normal change stop moving?
- How many people must approve it before release?
- Which teams edit the same files every week?
- When something breaks, how many people touch the incident before one person fixes it?
- Did the team, product scope, or customer load change in the last 6 to 12 months?
The answers usually point somewhere concrete. If three teams edit the same billing module every week, the pain may come from fuzzy ownership. If every release needs sign off from four people, a new service boundary will not remove that queue. If incidents bounce from support to backend to infra and back again, the team may not agree on who owns the problem.
Changes in team size matter too. A codebase that worked for six engineers can feel crowded at twenty. The reverse is true as well. A system split across many services can feel manageable with a large platform group, then become heavy when the company shrinks and the same few people carry everything.
Product scope changes can create the same pressure. A product that started with one user type may now have admin tools, billing rules, partner APIs, and custom enterprise work. Teams often read that as "the architecture is wrong." Sometimes it is. Sometimes the team just never changed how it plans, reviews, and owns work.
If you cannot point to where work waits, who approves it, who touches it, and who fixes it, do not change the architecture yet. You would just move the same team design problem into a new shape.
A simple way to find the real problem
Start with one feature that shipped late last month. Pick something ordinary, not a crisis. A delayed billing change or a small onboarding update works better than a dramatic outage because it shows how your team works in a normal week.
Trace that feature from the first request to the release. Write down each step in order: who asked for it, who clarified the scope, who changed the code, who reviewed it, who tested it, who approved it, and who pushed it live. Keep it plain. You are not making a process chart for a slide deck. You are trying to see where work actually moved and where it sat still.
A short checklist helps:
- Mark every team that touched the feature.
- Note each approval or handoff.
- Circle any place where work stopped for a day or more.
- Write down why it stopped, using the simplest reason you can.
- Sort each delay into code, process, or ownership.
That last step matters most. Teams blame architecture first because it feels concrete. But many delays come from fuzzy ownership, slow approvals, or a feature that crosses too many teams. If frontend waits on backend, backend waits on platform, and platform waits on security, the problem is rarely the repo shape by itself.
Say a simple pricing update took 18 days. The code change took two. Review took one. Then the feature sat for four days waiting for product sign off, three more waiting for a database change from another team, and two days because nobody knew who owned the release checklist. That is a team design problem first, not a code structure problem.
This exercise usually makes the monolith vs microservices argument look smaller and more specific. If delays cluster around one overloaded module, the code may need a cleaner boundary. If delays cluster around handoffs and unclear decisions, fix the team path first. Architecture should support ownership, not replace it.
A realistic example from a growing product
Picture a B2B product in its first good year. One team builds everything in one codebase. That is not a problem by itself. Early on, it often helps because everyone can see the whole product, fix bugs fast, and ship without asking three other teams for permission.
Then the customer base changes. Bigger clients want approval rules, special billing terms, access by role, and customer specific behavior. Sales promises faster turnarounds. Support forwards urgent requests. The team grows, so the company splits work across three teams.
On paper, that sounds reasonable. In practice, all three teams still edit the same billing logic and the same user access rules.
A feature team adds a new plan type. The integrations team changes how enterprise accounts map to permissions. The onboarding team tweaks invite flows for a large customer. None of these changes look dramatic alone, but they hit the same parts of the system. Pull requests sit longer. Reviews get tense. One small change breaks another team's release.
After a few cycles, release dates slip. People start saying the monolith is the issue because it gives the pain a simple label.
The harder truth is usually about ownership. The codebase has one shape, while the team works in another. No team fully owns billing. No team fully owns user access. Everyone can change them, so everyone blocks them.
The fix is often less dramatic than a rewrite. One team takes clear ownership of billing rules. Another owns identity and access. Other teams stop editing those areas directly and use agreed interfaces inside the same codebase. The company also sets review rules so the owning team makes the final call.
That does not solve every problem, but it removes a lot of daily friction. Releases get calmer. People spend less time arguing about architecture and more time finishing work. If a boundary change still makes sense later, the team can make it from a much clearer starting point.
When the system really does need a boundary change
Some teams blame the monolith too early. Others keep one codebase long after it starts charging a tax on every release.
A boundary change makes sense when the system keeps mixing business jobs that now move at different speeds. If one build blocks unrelated releases every week, the problem is no longer taste or ideology.
Picture a team fixing invoicing while another updates search and a third adjusts admin screens. If all of that must pass through the same release path, people start batching work, delaying small fixes, and shipping larger changes because production feels expensive.
Retesting is another clear signal. When one small change triggers checks across too much of the product, the code has too many shared assumptions.
That often starts in the data model. One shared schema can tie together billing, customer support, reporting, and account setup so tightly that each change pulls in several workflows that should be separate.
Team ownership can look clear on paper and still fail in code. A subscriptions team, an onboarding team, and an internal tools team may each know their area, but shared modules, shared tables, and shared release steps keep breaking that plan.
That is Conway's law in practice. The system still reflects an older team shape, even when the current team topology says those jobs should move on their own.
Support load usually confirms it. If one fault spreads into login, billing, or customer emails, the system is missing a useful boundary, and users feel the blast radius long before the team agrees on architecture terms.
This is where monolith vs microservices becomes a real architecture question instead of a style debate. Change the boundary when separate business jobs need separate release speed, separate failure isolation, and cleaner ownership.
Start with one seam and make it real. Move the code, data, ownership, and runbook together. Splitting only the code while keeping every other dependency shared rarely fixes the pain.
Mistakes that make the debate worse
Most architecture fights get louder when the team starts drawing service diagrams before it maps real work. Boxes look neat on a whiteboard. They do not show who owns checkout bugs at 2 a.m., who reviews schema changes, or where requests stall between teams.
A common mistake is to split the system around guessed domains instead of daily work. If one team still touches three services for every feature, the new boundary did not reduce coupling. It only moved the coupling into meetings, handoffs, and deployment queues.
Copying structure instead of fixing the team
The monolith vs microservices argument often gets worse when leaders copy patterns from much larger companies. A 12 person product team does not run architecture the way a company with 800 engineers does. Big firms can afford platform teams, service ownership, internal tooling, and full time reliability work. Smaller teams usually cannot, at least not yet.
A small SaaS team with eight engineers can end up with six services, three deployment paths, and no one who wants the pager. That is not scale. It is overhead.
People also split code to solve role confusion. If backend, frontend, and product decisions are messy, breaking code into more repos will not clear that up. People still need to decide who owns a feature, who can change an API, and who gets paged when it fails. Conway's law is blunt about this: systems copy the shape of communication.
Teams make it worse when they redraw boundaries every quarter. Each split resets learning. Logs move, dashboards change, deployment rules change, and everyone spends a month relearning where things live. That churn feels productive because the diagram changes. Users rarely notice any gain.
The hidden bill nobody prices in
Support work is where many bad architecture decisions show up. Every new service adds routine work: watching metrics and logs, handling alerts and on call issues, patching security problems, maintaining CI/CD and deploy scripts, and tracing failures across service boundaries.
If nobody budgets time for that work, the team borrows time from feature delivery. Then people blame the monolith, even though the real problem is that the team took on operating cost it could not carry.
A simple test helps. Before any split, ask who will own each new service for the next six months, not just who will build it this sprint. If the answer is vague, stop there. Fix the team topology first. Change the boundary later, when the work is clear.
A quick check before you choose a direction
Before you redraw boxes on a diagram, look at who can ship work today. Most teams do not have an architecture problem first. They have an ownership problem, a release problem, or a budget problem. The monolith vs microservices choice gets much clearer after a short reality check.
Ask whether one team can own a full customer facing slice, from interface to data. If one product area still depends on two backend teams and an infra team, the boundary is wrong even if the code looks neat.
Check how many approvals a normal release needs. If a team waits for security, QA, platform, and another service owner, a new service will not make delivery faster. It usually adds more waiting.
Compare code boundaries with real product work. Features do not care about package names. If every change for one customer action touches four repos, the split does not match the work people actually do.
Ask what changes in the next quarter, not in some distant plan. Will this move remove handoffs in the next 90 days? If the answer is vague, keep the system simpler for now.
Be honest about skills and budget. More services mean more deployment work, more monitoring, more failure points, and more support duty. A small team can carry that only if it already has the habits and tools.
A simple example makes this obvious. A growing SaaS team with eight engineers wants to split billing into its own service. On paper, that sounds clean. In practice, the same customer flow still needs changes in the web app, auth, admin tools, and support scripts. The same team owns all of it, and they already release the monolith every day. Splitting now adds contracts, retries, and extra alerts, but it removes no handoffs.
This is where Conway's law stops being theory and becomes useful. If the team shape and the code shape pull in different directions, people feel the pain first. Fix the team design problem, or at least name it clearly, before you change the system. If one team can own the work, release it with little friction, and run it with current skills, a boundary change may help. If not, keep the code closer together until the organization is ready.
What to do next
Start with the team, not the diagram. Put team ownership, release flow, and product boundaries on one page and look at them together. Most monolith vs microservices arguments get less dramatic when you can see who owns each area, who waits on whom, and where releases slow down.
A short review usually shows one blocked zone that causes most of the pain. It might be a checkout flow that needs three teams to approve changes, or a shared service that nobody fully owns. Fix that area first. A small ownership change, a cleaner release path, or one clear boundary usually teaches you more than a large rewrite.
Write down the conditions that would justify a split, and keep them plain. A split makes sense when one part of the product needs its own release schedule, when one area has very different load or uptime needs, when one team can own a boundary from start to finish without daily handoffs, or when security, data, or customer rules require stricter separation.
If a proposed split does not meet rules like these, stop treating it as an architecture emergency. It is probably a coordination problem, and the fix belongs in team design, planning, or release habits.
This is also where an outside review can save time. When the same debate keeps looping, people stop hearing each other. A neutral second opinion can show whether the issue comes from Conway's law, unclear product boundaries, or a codebase that grew faster than the team around it.
That review does not need to be heavy. Oleg Sotnikov at oleg.is does this kind of work as a fractional CTO, drawing on experience across startups, enterprise systems, and AI first engineering teams. The useful part is not a big rewrite plan. It is getting a clear read on team design, release flow, and where architecture changes would actually help.
The next move should feel boringly practical. Pick one painful area. Fix ownership. Tighten the release path. Set rules for future splits. Then see if the pressure drops. If it does, you found the real problem.
Frequently Asked Questions
How do I know if the monolith is really the problem?
Look at where work waits. If a normal change spends days in review, QA, or approval, your team flow causes more pain than the codebase. If the code itself forces unrelated work through the same release path or test cycle every week, then the system likely needs a cleaner boundary.
What should we check before splitting into services?
Start with one recent feature that shipped late. Trace it from request to release and mark every handoff, approval, and delay. If most of the lost time came from waiting on people or fuzzy ownership, fix that first.
Can a small team still move fast with a monolith?
Yes. Small teams often move faster in one codebase when one team owns a full product slice and ships without a long approval chain. One repo only turns heavy when too many people touch the same area and nobody owns the final call.
When does a boundary change actually make sense?
Change the boundary when separate business areas need separate release speed, cleaner failure isolation, or different uptime and data rules. Make the move only when one team can own the code, data, deploys, and support for that area from start to finish.
Why do handoffs make architecture feel worse than it is?
Each handoff adds delay and drops context. One person waits, another rechecks the same thing, and a small change starts to feel risky. Teams often blame architecture for that weight, but the queue around the work usually causes it.
What does Conway's law mean in plain English?
Conway's law means your system tends to copy how your teams work. If three teams share one product area, the code usually ends up tangled in the same way. Clean ownership in the org often leads to cleaner boundaries in the code.
Will microservices fix slow releases?
Usually not. If releases already wait for QA, security, platform, and another team, more services often add more waiting. Fix the release path first, then decide whether the code needs a split.
How can we find the real bottleneck without a big audit?
Take one ordinary feature that slipped last month and write down every step it took. Note who asked for it, who changed it, who reviewed it, who tested it, and who pushed it live. The first place where work sat for days usually tells you what to fix next.
What mistakes make a microservices move backfire?
Teams usually get in trouble when they copy a structure from much larger companies, split code before they fix ownership, or ignore the support load that new services create. A split adds deploy work, alerts, logs, and on-call duty whether the team plans for it or not.
Should we ask an outside CTO to review this before we change anything?
An outside review helps when the same argument keeps looping and nobody agrees on the cause. A good CTO review should map ownership, release flow, and code boundaries in plain terms. You want a clear diagnosis and one practical next step, not a grand rewrite plan.