Microservices did not reduce the complexity of building large software systems. They distributed it. And distributed complexity does not disappear — it migrates. It moves out of the codebase and into the spaces between teams: into API contracts, service ownership, deployment coordination, and the conversations that nobody has calendared. The monolith hid this complexity in one repository. The distributed architecture made it everyone's daily problem. That is a leadership challenge, not a technical one.
I have spent the better part of two decades building systems — first inside the Java enterprise ecosystem, then through the microservices transition, and now in organisations where Event-Driven Architecture is the default and the monolith is spoken of the way veterans speak of a difficult posting. The technical evolution was real and necessary. But the leadership evolution it demanded was harder, less discussed, and far less complete.
This is what I observed — and what I believe every architect and technical lead needs to understand about the world we now operate in.
The Monolith Was Not a Mistake. It Was the Right Answer to a Different Question.
Enterprise Java in the late 1990s and early 2000s was not a legacy mistake waiting to be corrected. It was the most rational response to the technical and organisational realities of its time. Application servers like WebLogic, JBoss, and later WebSphere provided a complete runtime: transaction management, connection pooling, messaging, security, and deployment — all in one container. Enterprise JavaBeans handled distributed component coordination. Frameworks like Struts and Hibernate added the web and persistence layers. The ecosystem was complete, opinionated, and, critically, deployable by teams that did not need to solve the same infrastructure problems from scratch on every project.
The monolith made sense because organisations were monolithic. A single large codebase matched a single large team. Deployment happened infrequently, after long testing cycles, because the cost of breaking something in production was high and rollback was manual and painful. The architecture was a mirror of the organisational reality: centralised, carefully coordinated, risk-averse.
This is important to name honestly, because the temptation when looking back is to treat Enterprise Java as an obviously bad idea that the industry eventually wised up to. That is not what happened. It was the right tool for the problem it was built to solve. The problem changed.
Scale, Speed, and the Cloud Made the Monolith Load-Bearing in All the Wrong Places.
Three forces arrived roughly simultaneously and made the Enterprise Java model untenable at the frontier of software development.
Scale. Consumer internet companies — Amazon, Google, Netflix — were building systems that needed to serve millions of concurrent users across geographically distributed infrastructure. A single Java application server, no matter how well tuned, had a ceiling. Horizontal scaling of a monolith meant scaling everything, even the parts of the system that were not under load. The economics were brutal.
Deployment velocity. The market began rewarding teams that could ship faster. Weekly or monthly release cycles, which were standard in the Enterprise Java world, were too slow. The cost of a failed deployment had to come down. The only way to reduce that cost was to reduce the scope of each deployment — which meant smaller, independently deployable units of software.
The public cloud. AWS, launched in 2006, and the services that followed it changed the infrastructure calculus entirely. Compute, storage, messaging, databases — all available as managed services, billed by consumption. The application server's job of providing a managed runtime became redundant when the cloud provided a better, more elastic one. You no longer needed to run your own JMS broker when SQS existed. You no longer needed container-managed transactions when DynamoDB or RDS handled consistency guarantees at the infrastructure level.
These three forces did not make the monolith bad. They made it the wrong shape for the new problem.
Independent Deployability Changed the Velocity of Everything. At a Cost.
Microservices architecture, at its core, is a single promise: independent deployability. Each service owns its domain, its data, and its deployment pipeline. A change to the payment service does not require coordinating a release with the notification service or the user profile service. Teams can move at their own cadence. A failure in one service can be contained rather than cascading through a shared runtime.
Event-Driven Architecture extends this further. Instead of services calling each other synchronously — which creates tight temporal coupling even if the services themselves are loosely coupled — EDA uses asynchronous message streams. A service publishes an event when something significant happens. Other services that care about that event subscribe and react. The publishing service does not know who is listening, does not wait for a response, and does not fail when a downstream consumer is slow or unavailable. The system becomes resilient at the architectural level rather than through defensive coding.
The polyglot consequence follows naturally. When each service is independently deployable and communicates through well-defined events or APIs, the internal implementation becomes irrelevant to the rest of the system. A payment service can be Java. An ML inference service can be Python. A real-time notification service can be Go. The architecture enables the right tool for the right problem — and, critically, it enables the team best suited to solve a problem to solve it in the language and framework they know best.
This is the genuine transformation. Java did not become irrelevant because microservices arrived. Java became one choice among many, rather than the mandatory foundation of everything. The ecosystem stopped being the point. The domain became the point.
"The architecture reflects the organisation. Change the architecture without changing the organisation and you get the worst of both worlds."
Conway’s Law Is Not a Metaphor. It Is a Warning.
In 1967, Melvin Conway observed that any organisation designing a system will produce a design whose structure mirrors the organisation's communication structure. This became known as Conway's Law. For decades it was cited as an interesting academic observation. In the microservices era, it became a practitioner's daily reality.
The monolith worked, in part, because the organisation was structured to maintain it. A large, centralised team, with strong coordination mechanisms and a shared codebase, could manage a monolith. The architecture and the organisation were aligned.
When organisations adopted microservices without reorganising their teams, they created what has come to be called the distributed monolith — a system decomposed into many services that are nonetheless so tightly coupled, so dependent on shared databases or synchronous call chains, that they cannot be deployed independently. The benefits of microservices — independent deployability, team autonomy, fault isolation — disappear. The costs — network latency, distributed tracing complexity, service discovery overhead — remain. It is the worst possible outcome, and it is surprisingly common.
The inverse strategy — sometimes called the "Inverse Conway Manoeuvre" — is to design your organisation first, then let the architecture follow. If you want a payment service that can be deployed independently, you need a team that owns payment end to end: the API, the data model, the deployment pipeline, the on-call rotation. That team cannot share a database with the user service, because shared databases are shared ownership — and shared ownership means deployment coordination, which defeats the purpose.
This is where architecture stops being a technical problem and becomes an organisational leadership problem. Drawing service boundaries is drawing team boundaries. Defining API contracts is defining team interfaces. Deciding which team owns which service is deciding how your organisation will communicate, coordinate, and move.
The Hardest Problems Are No Longer in the Code.
In the Enterprise Java world, the architect was often the person who knew the most code. They understood the framework, the transaction boundaries, the deployment topology. Technical depth was the primary qualification. The role was largely about managing a shared codebase on behalf of a large team.
In the distributed services world, the architect's primary value is different. It is the ability to draw good boundaries — service boundaries, team boundaries, data ownership boundaries — and to reason about the second and third-order consequences of where those lines are drawn. A poorly drawn service boundary creates years of operational pain. A well-drawn one creates years of team autonomy. The decision looks the same at the whiteboard. The consequences play out over quarters and years in production.
The tech lead's role changed in parallel. In the monolith world, a tech lead could reasonably have an opinion about every part of the codebase. In the microservices world, that is neither possible nor desirable. The tech lead's job is to cultivate a team that owns its service genuinely — that understands its domain deeply, makes good decisions locally, and communicates effectively with the teams that depend on it. This requires a fundamentally different kind of leadership: less technical arbitration, more enabling autonomy.
The failure mode I have seen most often in distributed architecture is the senior engineer who builds microservices but maintains a monolith mindset — who wants to review every change in every service, who treats service API contracts as internal implementation details, who designs shared libraries that create the coupling the architecture was supposed to eliminate. The distributed system requires distributed trust. That is a leadership challenge.
The Architecture Changed. The Coordination Problem Didn’t — It Just Became Yours to Own.
The monolith coordinated everything in one codebase, with one team, under one deployment pipeline. The complexity was visible, contained, and manageable by people with deep knowledge of a single system. The price was coupling: you could not change one part without understanding its effect on every other part.
The distributed architecture deferred that coordination cost. Each service is simpler. Each team is smaller and more focused. Each deployment is safer and faster. But the system as a whole is more complex — it has more moving parts, more failure modes, more places where things can go wrong in ways that are invisible to any single team.
Someone has to hold the map of the whole system. Someone has to own the decisions about where service boundaries sit, who resolves ownership conflicts when two teams both need a piece of data, how the organisation's communication structure is kept in sync with the system's architecture. In the monolith world, the codebase was the map. In the distributed world, the leader is the map.
This is the real transformation the architectural shift demanded: not better tooling, not a different programming language, not a more expressive framework — but leaders who understand that architecture and organisation are two descriptions of the same thing, and who take responsibility for both.
What This Demands of Technical Leaders Today
Learn to draw boundaries before you learn to split services. The most dangerous moment in a microservices adoption is when a team starts breaking apart a monolith without a clear principle for where the cuts should go. Domain-Driven Design's concept of the bounded context — a cohesive model with a clear linguistic boundary — is the most useful tool available for this. It forces you to ask: what is this service responsible for, and where does that responsibility end?
Treat API contracts as organisational commitments, not technical details. When your service publishes an event or exposes an endpoint, you are making a promise to every team that depends on it. Breaking that contract silently is the distributed equivalent of merging code that breaks the build without telling anyone. The discipline of versioning, deprecating, and communicating API changes is a leadership discipline, not a developer one.
Measure coordination overhead as seriously as you measure system performance. One of the early signals that your service boundaries are wrong is that teams are spending significant time in cross-team meetings, debugging failures that span multiple services, or waiting for another team to make a change before they can ship. These are architectural symptoms showing up as organisational friction. When engineers complain about process, look at the service map first.
Resist the urge to add a shared library for everything in common. Shared libraries are monolith thinking in distributed clothing. Every shared dependency is a coupling point. When the library changes, every service that depends on it must be re-tested and re-deployed. Ask instead: does each service need to solve this problem independently, or does this shared concern belong in a platform service that other services call? The difference matters.