It was the kind of conversation that seems ordinary — until, suddenly, it isn’t.
My colleague hadn’t been on the team long — long enough to see the problem clearly, without the layers that time inevitably adds. He leaned back slightly, crossed his arms, and with the calm confidence of someone who had just spotted the obvious answer, said: “Why push modularization this far? We’re not an ecosystem like Uber’s.”
There was a brief silence. Not the uncomfortable kind — the thinking kind.
Because he was right. And I knew it.
It wasn’t the first time I’d heard that argument. Years earlier, at the start of my career, I made it myself.
I dismissed modularization efforts on a project convinced we were being pragmatic, not over-engineering. What followed wasn’t dramatic — it was worse than that. High coupling between parts of the system that were never meant to touch. Conflicts that took days to untangle. Launches that dragged. Time to market that stretched long enough to become a constant source of stress for the team. It wasn’t a spectacular failure. It was something slower, more draining, and entirely avoidable.
That’s why my colleague’s comment stopped me. Because it sounded exactly like me, years earlier.
If you take that statement in isolation — zoomed in on that specific fragment — the logic holds. Large, highly modularized systems exist because they emerged from real scaling needs. Replicating those patterns without that necessity can look like over-engineering: more complexity, more indirection, more surface area for things to go wrong. There’s even a principle that supports this directly: YAGNI — You Aren’t Gonna Need It. One of the most respected in software engineering, and for good reason. Complexity that doesn’t solve a real problem isn’t architecture — it’s debt.
I hesitated too.
But there was something that perspective, seen up close, couldn’t quite reach.
Modularization isn’t only a decision about scale. It’s a decision about designing with clear boundaries. When you divide a system into well-defined modules — each with a delimited responsibility and an explicit interface — you’re not imitating Uber. You’re making a decision about how your system is going to change — and who’s going to be able to change it without breaking everything else.
Robert C. Martin articulates this in Clean Architecture with an idea that stuck with me: the goal of good design isn’t to prepare for size — it’s to prepare for change. And that was exactly what we were living through. New requirements arrived every week. Entire flows were being redesigned. Priorities shifted, turning what was a minor detail today into the center of the product tomorrow. The system didn’t need modularization because it was large. It needed it because the kind of changes it faced constantly crossed its internal boundaries — and without structure, every modification became a high-risk operation: hard to isolate, hard to test, hard to deliver with confidence.
But — and this matters — poorly executed modularization can create exactly the problem it’s meant to solve. If you draw boundaries in the wrong place — or for the wrong reasons — the cost of change doesn’t disappear. It just moves. And refactoring boundaries that cross multiple modules is often more expensive than not having drawn them at all. The decision to modularize matters. But the decision of where to draw the boundaries matters just as much.
My colleague’s initial take wasn’t wrong. It was incomplete. YAGNI is a valid principle — but it assumes the cost of adding structure later is low. When the business moves fast and changes are unpredictable, that assumption breaks. Modularization wasn’t a technical aspiration. It was an investment in the product’s future velocity.
This left me with a discomfort I’m not sure ever fully goes away:
How many times have we made a decision completely convinced it was right — simply because we hadn’t zoomed out far enough?
It’s not about experience. It’s not about knowing more patterns or having read more books. It’s about something harder: knowing which level you’re standing on when you’re making the decision.
My colleague wasn’t wrong. He was looking from a perfectly valid level. What changed when you stepped back wasn’t the logic — it was the problem that logic was trying to solve.
And that’s what makes architecture so hard to teach — and so easy to misunderstand.
It’s not a rulebook. It’s a system of lenses. And the question we should ask before every decision isn’t “is this correct?” — it’s “from where am I evaluating it?”
Because a decision can be technically brilliant and completely wrong at the product level. It can be perfect today and obsolete in three months. It can be justified in theory and indefensible in practice.
The architecture that works isn’t the one that follows the pattern. It’s the one that survives contact with reality.
In the next post, I’ll talk about something I find even harder than choosing the right architecture: how to read context before you choose. The business, the product, the timelines, the constraints. Everything the books don’t tell you — and that ultimately decides whether your architecture lives or dies in production.
The opinions expressed in this article are my own and do not represent any organization.