At the beginning of a project, everything feels simple. The team is small, the codebase is shared, and communication is fast. We move quickly, without too many constraints.
But over time, the team grows. The product grows too. And what worked at the beginning starts to show its limits.
When teams step on each other
I worked on a project where several teams were working on the same codebase. Each team was building its own features, but very quickly, problems started to appear.
A change in one part of the codebase would break something else. Several teams were modifying the same files. Dependencies became harder and harder to manage.
And most importantly, no one really knew where their responsibilities started and ended.
Little by little, this created a lot of coordination, conflicts, and delivery started to slow down: features took more time to be released, changes became more risky, and every evolution required more coordination.
The problem was not technical
The codebase was organized from a purely technical point of view.
A monolith strongly coupled to the framework, structured around its conventions.
At first, it made sense. But over time, it became harder and harder to understand the codebase when we needed to change it.
And more importantly, the language used in the code did not reflect the business.
We were using technical terms or framework-related concepts that did not make sense for product or business teams. Communication became more difficult, simply because we were not speaking the same language.
The real problem was not only technical.
The codebase had grown… but it had never been structured for that.
It was not designed to allow multiple teams to work efficiently on it. And the way teams were organized did not match how the codebase was built.
Teams were working on the same areas of the codebase, strongly coupled together, depending on each other and blocking each other.
Splitting by business domain
To solve this problem, we used DDD.
The idea is not to add more patterns or complexity, but to simplify by structuring the codebase around the business.
The goal is to make the codebase reflect the business, not the other way around.
We identify Bounded Contexts: parts of the codebase that each represent a part of the business.
You can see them as “applications inside the application”. Each context has its own logic, its own language, and its own rules.
They are decoupled from each other, and their interactions are clearly defined.
We know who depends on who, what data is exchanged, and how it should be communicated.
This global view is described in a Context Map, which helps to visualize relationships between contexts and clarify dependencies.
Defining clear responsibilities
Once these contexts were identified, we assigned them to teams.
Each team became responsible for one or more contexts. This reduced code collisions and clarified responsibilities.
Teams knew exactly where they could make changes, and where they should not.
By limiting dependencies and conflict areas, this setup helped teams better understand the codebase and gain autonomy.
Structuring to improve delivery
After this change, frictions between teams decreased and it became easier to evolve the codebase without impacting other teams.
Interactions did not disappear, but they became clearer and less blocking.
The codebase also became easier to evolve.
Conclusion
When a team grows, complexity does not only come from the code.
It also comes from how the codebase and teams are structured.
As long as this structure is not clear, teams step on each other and delivery slows down, which directly impacts the ability to ship new features.
Structuring the codebase around the business brings clarity, defines responsibilities, and reduces friction.
And this is often what allows teams to deliver again in a more smooth and reliable way.



