Show Me the Architecture and I’ll Show You the Silos
Or: How We Broke Down Our Monolith and Accidentally Built Walls Between Our Teams
Somchai’s cursor hovered over the merge button for three seconds too long. It was 4:20 on a Wednesday, the afternoon light cutting low through the floor-to-ceiling windows on level 27, turning everyone’s monitors into mirrors. He’d just spent the better part of a week implementing client-side round-robin load balancing for his team’s micro frontend — a clean solution, well-tested, exactly the kind of thing you’d want a senior engineer to ship. The problem was that two floors down, another team had shipped an almost identical implementation six weeks earlier. And on the floor above, a third team had built their own version in Scala instead of C#. Nobody had done anything wrong. That was the part that sat in my stomach like cold rice. Three teams, three implementations, zero communication failures — because there was nothing in the architecture that made communication necessary anymore.
The monolith was dead, and we’d thrown a party. We just hadn’t noticed what we’d buried with it.
Charlie Munger, the late investor and Berkshire Hathaway vice chairman, had a line that should be written in permanent marker on every engineering leader’s whiteboard: “Show me the incentives, and I’ll show you the outcome.” He was talking about financial markets, but he might as well have been describing what happens when you decompose a monolithic codebase into microservices and micro frontends. You change the architecture, you change the incentive structure, and the outcomes follow with the reliability of gravity. We just don’t always like what those outcomes turn out to be.
The Monolith Was Your Worst Collaboration Tool (and Your Best)
Here’s the uncomfortable truth that nobody mentions in the “monolith to microservices” migration talks at conferences: the monolith was forcing your teams to collaborate, and it was doing it for free.
Not because anyone designed it that way. Not because there was a collaboration strategy or a cross-team engagement framework or whatever your favourite consultancy is selling this quarter. The monolith forced collaboration because the code lived in the same place. Your changes touched other people’s code. Their deployments included your features. The shared pain of a broken build at 5 PM on a Thursday was everyone’s pain, felt simultaneously, impossible to ignore.
You didn’t choose to learn what the team down the hall was building. You couldn’t avoid it. Their new caching layer showed up in your pull request diff. Their database migration broke your integration tests. The coupling was a constraint, yes, and it slowed you down, absolutely — but it also meant that when someone built a round-robin implementation, everyone saw it. When someone introduced a new pattern, it was visible in the same repository you opened every morning.
Then you did the right thing. You decomposed the monolith. You gave teams autonomy, independent deployments, their own repositories, their own CI pipelines. And every single one of those improvements — each of which was genuinely, defensibly correct — removed a forcing function for collaboration that you didn’t know you were depending on.
The urban planner Jane Jacobs spent her career arguing that the physical design of cities shapes how people interact. Short blocks and mixed-use streets create what she called “the ballet of the sidewalk” — those constant, casual, seemingly random encounters between strangers that form the foundation of a neighbourhood’s trust and social fabric. Long blocks, highways, and separated zones kill that ballet. Not through malice. Through design.
Your monolith was a short city block. Everyone bumped into each other. Your microservices architecture is a suburban highway system — efficient for getting from A to B, but don’t expect your neighbours to know your name.
The Redux Moment
The moment I knew we had a problem wasn’t the round-robin duplication. It was earlier, and subtler.
One team removed Redux from their micro frontend and introduced a different state management approach. Taken in isolation, this was a perfectly good decision. Even Dan Abramov, the creator of Redux, has publicly stated he wouldn’t recommend it for new projects. The team evaluated their options, picked something that better fit their needs, and shipped it. Textbook engineering autonomy.
But they did it for themselves. Just their micro frontend. And suddenly you’ve got a divergence event. One team is on a fundamentally different state management paradigm than the other nine. An engineer who’s been contributing across multiple micro frontends now needs to context-switch between two mental models. Cross-contribution just got harder. Consistency just took a hit.
A friend of mine went to work at Facebook, where the backend runs on Hack — their custom fork of PHP. I asked him the obvious question: “So, is the code shit?” He didn’t even pause. “Yes, it’s shit. But it’s consistently shit, and that makes it okay.” He wasn’t joking. Consistency at scale is worth more than local excellence, because consistency means any engineer can move between systems without relearning the fundamentals. The moment you lose that, every team boundary becomes a context-switch tax that compounds with every rotation, every cross-team contribution, every production incident that requires someone unfamiliar to jump in.
And here’s the thing that makes this genuinely difficult: nobody did anything wrong. The team exercised the autonomy that the architecture gave them. The decision was technically sound. The problem isn’t that they made a bad choice — it’s that the architecture made the choice invisible to everyone else until it was already shipped.
In a monolith, someone would have seen the pull request. Someone would have said, “Interesting — should we do this everywhere, or are we deliberately keeping both?” The shared codebase would have made the divergence a conversation before it became a fact.
Some divergence is innovation. One team finds a better approach and the rest of the organisation benefits. Five teams independently building the same utility is waste. The leader’s job isn’t to prevent divergence — it’s to maintain enough connective tissue that teams can see each other’s work and consciously choose to diverge rather than accidentally duplicate.
Conway Had a Law. We Proved It.
There’s a reason Melvin Conway’s fifty-year-old observation keeps showing up in architecture talks: because organisations keep rediscovering it the hard way. Your system’s structure will mirror your organisation’s communication structure. Or, to flip it around: show me the architecture, and I’ll show you where the silos will form.
When we had a monolith, everyone communicated because the code demanded it. When we moved to microservices with team-aligned boundaries, the teams optimised locally — exactly as Conway’s Law predicts — because that’s what the architecture rewarded. You can’t blame a team for focusing on their own service when the architecture, the CI pipeline, the deployment model, and the code review process all reinforce that boundary.
This is the key insight that separates the conversation from every other “break down silos” article you’ve read: the decomposition that made our architecture better made our collaboration worse, and both of those things were entirely predictable. We traded one kind of coupling — code coupling, which forced uncomfortable but valuable interactions — for another kind of problem: divergence, duplication, and invisible decision-making. And unlike the monolith, microservices don’t have a built-in forcing function for alignment.
So the post isn’t “silos are bad, go fix your culture.” Culture doesn’t exist in a vacuum. Culture is the emergent behaviour of the systems you’ve built. The real message is this: when you remove the architectural forcing function, you have to build an intentional one, or entropy wins.
Building the Intentional Forcing Functions
If the architecture won’t force collaboration for you anymore, you need to create the mechanisms deliberately. Here’s what we’ve tried, what worked, and what fell flat on its face.
Platform Teams as Enabling Teams, Not Gatekeepers
The reflexive response to divergence is centralisation — create a platform team, make everyone go through them, problem solved. Except it’s not, because you’ve just created a bottleneck that slows everyone down and teaches them to work around the system instead of with it.
We had this exact problem with an experimentation library owned by a platform team. Every team needed to run A/B tests, and the library was the official way to do it. But when teams needed changes or extensions, the queue was long and the platform team had their own priorities. So teams didn’t wait. They built on top. Wrappers, helper functions, custom abstractions layered over the library — reasonable decisions made independently by reasonable people. And then the wrappers got copied and pasted between repositories, each copy diverging slightly as different teams modified them for their own needs. Within a year, you had a dozen variations of experimentation logic scattered across the codebase, none of them quite the same, all of them one library update away from breaking. You wanted consistency; you got duplication plus fragility.
The Team Topologies model gets this right: a platform team should be an enabling team, not a gatekeeper. The distinction is critical. A platform team that requires pull requests creates a bottleneck. A platform team that provides golden paths — self-service tooling, shared libraries, well-documented patterns — creates alignment without coupling. The round-robin problem doesn’t get solved by mandating “everyone must use the approved implementation.” It gets solved by making the approved implementation so easy to adopt that building your own would be harder.
Mob Sessions: Force It, Then Let It Breathe
We introduced cross-team mob programming sessions — each participating team sends a representative every two weeks for a half-day session to work together on a common problem. And I’ll be honest with you: we had to mandate participation at first. Not everyone wanted to be there. The early sessions felt forced because they were forced.
That’s okay. As a manager, you can tell your people what to do. You shouldn’t make too much of a habit of it — over-directing is ownership-corrosive — but sometimes the initial push is necessary to start a habit that eventually sustains itself.
The key lessons: attend the first sessions yourself to set expectations for how mob programming works, who drives, how you rotate, what outcomes you’re aiming for. Get regular feedback. Adjust the format ruthlessly based on what you hear. The original mandatory format eventually tanked — people hated being forced into sessions on topics irrelevant to their work. When we evolved to opt-in, interest-based labs where engineers self-selected into problems they cared about, the dynamic completely changed. The forcing function got the ball rolling; the format evolution kept it alive.
Combined Sprint Demos: Make Divergence Visible
This is the one I’d recommend to anyone running multiple teams, and it costs almost nothing. Once a month, all ten teams present their sprint demos to each other in a single session.
The Redux moment I described earlier? In a combined demo, it becomes visible immediately. One team says “we replaced state management” and nine other teams hear it. Half the room is already nodding because they’ve been quietly hating Redux for years. Someone asks “should we all do this?” The conversation happens before the divergence becomes entrenched.
The demo doesn’t prevent divergence. It makes divergence visible and deliberate. That’s the difference between innovation and accidental duplication. Innovation is when you diverge consciously, knowing what everyone else is doing, and choosing a different path for good reasons. Duplication is when you diverge because you had no idea anyone else had already solved the problem.
Cross-Team Technical Work as an Expectation
Your staff engineers and tech leads should be spending meaningful time outside their own team’s sprint. I’ve written about this at length before — your impact needs to extend beyond your team’s backlog. If an IC4+ in a medium-to-large engineering organisation is only working within their team’s sprint boundaries, two things are probably true — they’re not having the wider impact their level demands, and they’re going to get bored and leave.
We back this up with weak code ownership. Following Martin Fowler’s model: every module has an owner, but anyone can contribute via pull request. The owner reviews and maintains quality, but the doors are open. If your team needs something changed in another team’s service, the expectation is that you open the PR yourself rather than filing a request and waiting. This has a compounding effect — it builds cross-team knowledge, it removes bottlenecks, and it moves you closer to genuine feature teams where ownership follows the customer problem rather than the technical component. Tools like Sourcegraph have been a godsend here — its batch changes feature lets you open pull requests across dozens of repositories simultaneously, which means a single engineer can drive a library upgrade or a pattern migration across the entire microservices estate in an afternoon rather than filing tickets with fifteen teams and waiting three sprints.
Give People Time and Make It Visible
None of this works if engineers don’t have protected time for cross-team work. Atlassian gives individuals 20% time. Here at Agoda, we use 30% of engineering time as a guideline for technical improvements — it’s not mandatory, it’s not documented in a policy, it’s guidance that teams balance against their own priorities. But the expectation that a meaningful chunk of engineering time lives outside feature delivery is explicit, and that time includes cross-team initiatives.
But time alone isn’t enough. You need visibility from the top down. Get leadership and business support for the idea that collaboration across team boundaries isn’t a distraction from “real work” — it is the work. Facilitate the infrastructure: regular tech talks, training sessions, brown bag lunches. Have budget, have rooms, have people who help engineers organise these things. Don’t create administrative barriers to collaboration, and if barriers exist, tear them down.
Shared Goals: The Foundation Everything Else Sits On
If I had to pick one forcing function above all others, it’s this: shared goals at the department level. As a tech department, what do we collectively want to achieve this quarter or this year? What do all of our teams value? What do we agree is the most important thing for moving us forward?
Once you’ve agreed on the what, set the measurement, identify the teams that will contribute, and create the structures for those teams to work together toward a shared long-term outcome. When ten teams are all measured against the same result, collaboration stops being a nice-to-have and starts being the rational behaviour. You’ve aligned the incentive structure with the collaboration you actually want.
Munger was right. Show me the incentives, and I’ll show you the outcome. Show me shared goals backed by shared measurement, and I’ll show you teams that collaborate without being told to.
Before You Say “Modular Monolith”
I can already hear it. Someone is reading this and thinking: “This is exactly why modular monoliths are the answer.” It’s the hot take of the moment, and I understand the appeal — you get the deployment simplicity of a monolith with the logical separation of microservices. Best of both worlds.
Except module boundaries are only a single step away from separate git repositories. The moment teams start owning modules, the same silo dynamics appear inside the monolith that we saw outside it. Teams stop looking at each other’s modules. Knowledge clusters around ownership boundaries. Someone builds a utility in their module that three other modules could use, but nobody knows it exists because nobody’s browsing code outside their own area anymore.
We’ve seen it. The modular monolith doesn’t solve the collaboration problem — it just changes the radius. If you don’t build intentional forcing functions, the architecture will teach your teams to optimise locally regardless of whether “locally” means a microservice, a module, or a folder in a monorepo. The shape of the silo changes. The silo doesn’t.
The Architecture Is Not Morally Superior
You’re not going back to the monolith. Nobody wants to. At scale, and done the right way, the microservices architecture is better in a hundred ways that matter: independent deployments, team autonomy, technology flexibility, fault isolation. These are real, significant advantages, and I wouldn’t trade them. At a smaller scale, the monolith is almost certainly the better choice — but that’s a different post.
But you have to be honest about what you lost when you left the monolith behind. The monolith forced collaboration as a side effect. Microservices force isolation as a side effect. Neither architecture is morally superior — they just have different default behaviours, and those defaults shape your teams in ways that are entirely predictable if you’re paying attention.
The question isn’t which architecture to choose. You’ve already chosen. The question is whether you’re willing to build the intentional systems that compensate for whichever architecture you chose.
Show me the architecture, and I’ll show you where the silos will form. Show me the forcing functions, and I’ll show you whether the leader knew they were coming.
Now, if you’ll excuse me, I need to go check how many implementations of client-side retry logic we have across our micro frontends. I’m afraid to count, but I’m more afraid not to.