Architecture & MVPs
'Monolith' doesn't have to be a dirty word.
Picture the scene.
The architecture diagram is magnificent. Twelve services. An event bus. Arrows going in several directions. There’s a separate authentication service, a billing service and a notification service.
There is also, presumably, a service responsible for explaining why the other services aren’t talking to each other.
The product has no paying customers.
But when they arrive, we are extremely ready to send them a notification.
This is the point where I’d like to suggest something deeply unfashionable: perhaps we should build the product first.
Your MVP needs customers, not a small government
When I’m helping build an MVP, my default is a modular monolith.
One application, deployed as a unit, with sensible boundaries inside it. Something a small team can understand, change and operate without needing an interdepartmental summit to release a feature.
The immediate job is to get a useful product into customers’ hands and find out whether we’ve understood their problem. Will they use it? Will they pay? What have we missed?
Those answers may change the product considerably.
Which makes it an interesting time to invest heavily in separating a business domain we’re still discovering into permanent-looking services.
We don’t yet know whether customers want the feature. We have, however, given it its own infrastructure and a very confident name.
Product-market fit is not a configuration option. No amount of deployment sophistication will enable it.
Microservices come with a bill. Several, actually.
Splitting an application into services doesn’t make its complexity disappear. Some of that complexity moves into the spaces between them.
Instead of calling a function, you’re making a network request. That request can fail, arrive late or succeed while the response disappears, leaving you wondering whether retrying will charge the customer twice.
Now we need to think about retries, timeouts, permissions, monitoring and what happens when one service changes before another.
None of this is impossible. Engineers solve these problems.
But solving them takes time. Time you could be spending learning why everyone abandons your onboarding screen halfway through.
Microservices can provide valuable independence. The question is whether your business needs that independence enough to pay for it.
When three engineers are maintaining twelve services, I’d want a better explanation than “this is how scalable systems are built”.
They’re also how three engineers acquire twelve things to worry about.
A modular monolith is not a junk drawer with a login screen
Let’s clear something up.
I’m not suggesting we put the entire business into one enormous file, call it app_final_v7_really_final and hope for the best.
Simple deployment does not require chaotic code.
A modular monolith should have recognisable business responsibilities. Accounts, billing, orders and reporting might live in the same application, but they shouldn’t be rummaging through each other’s internals like housemates borrowing things without asking.
Billing should have a clear way to request information about a customer. It shouldn’t need intimate knowledge of seventeen unrelated tables and a variable somebody named temp2.
I’d establish clear interfaces, sensible ownership of data changes and rules about which modules can depend on which others. Important behaviour should have tests. Where useful, dependency checks should catch boundaries being crossed.
And I’d keep the boring essentials: appropriate security, repeatable deployments, useful logs and backups we can actually restore.
“Move fast” is a product strategy. “We think the backups work” is a future apology.
The point is to keep the system understandable and changeable without turning every internal interaction into a network conversation.
You can have doors between rooms without putting each room in a different postcode.
Build for the next ten changes, not the imaginary ten million users
Getting the first release out matters. Being able to respond when customers use it matters just as much.
Suppose your early customers reveal that the approval workflow is backwards, your pricing model is wrong and the reporting feature you thought was secondary is the thing they’ll actually pay for.
Excellent. We’ve learned something useful.
I want to be able to change the product without coordinating a diplomatic mission across six independently deployed services.
That’s why the word modular matters. We’re keeping responsibilities clear so changes remain manageable. We’re not predicting every future requirement or building a miniature enterprise platform in anticipation of them.
Some boundaries will turn out to be wrong. That’s easier to address when we haven’t already poured a network boundary around each one.
The architecture should help us learn, not demand that we were right before we started.
Having customers break your original assumptions is a good problem
If the product succeeds and a particular workload starts straining the system, that can be a very good problem to have.
Not because outages are charming. They aren’t.
Because we now have evidence that people want the product, actual usage to examine and a concrete reason to invest.
Perhaps document processing needs different compute. Perhaps reporting is interfering with customer transactions. Perhaps the team has grown enough that independent ownership and releases would genuinely help.
Now we can make a specific change for a specific benefit.
And that change doesn’t automatically mean microservices. It might mean improving a query, adding an index, moving work into a background job or running more application instances.
Sometimes the architectural breakthrough is discovering the database has been doing something extremely silly.
When extracting a service is justified, clear module boundaries give us a better starting point. We know what the component owns, how other parts of the application use it and which behaviour must be preserved.
There will still be work. A folder called billing is not a migration plan.
But we’re evolving a product with evidence, rather than rebuilding everything because the first version had no discernible structure.
“When we scale” needs a follow-up question
Whenever someone says, “We need this for when we scale,” my next question is: what, specifically, will need to scale?
Traffic? Data volume? Processing? The number of engineers? The number of customers with different requirements?
Those are different problems.
Show me the requirement, the likely constraint and why this investment makes sense now. I’m happy to separate a component early when there’s a clear reason.
That doesn’t entitle every noun in the requirements document to its own service.
And if the monolith continues to serve the business well, we keep it. There is no graduation ceremony where a successful application receives its first service mesh.
Build the business. Let the architecture earn its complexity.
I’m not embarrassed to recommend a monolith. I’d be more embarrassed to explain why we spent the MVP budget solving coordination problems we introduced ourselves.
For a small team building an unproven product, a modular monolith is my default. Keep the product focused, the code organised and the cost of changing direction manageable.
Get it into market. Learn what customers need. Invest as the evidence improves.
I’d rather help you refactor a product customers love than maintain a beautifully distributed system nobody wants.
Let’s work together
Building an MVP or wondering whether your architecture has become the project?
I help founders make practical technology decisions and build the software to support them.
