Modularity Is as Much About People as Code

A large retail enterprise approached Thoughtworks with two familiar complaints: feature delivery in its mobile app was slowing down, and its multiple in-house market apps had drifted so far apart that code could rarely be shared between them. The company had already tried the obvious fixes — hiring more developers, then splitting into smaller teams. Neither produced lasting relief. The real problem, it turned out, was that architecture, team structure, and developer experience had been treated as separate initiatives when they are in fact deeply interdependent.

By realigning team topologies to a modular architecture and investing in developer experience, the enterprise achieved a 60% reduction in average cycle time, an 18-fold improvement in development costs, and an 80% reduction in team startup costs. The path to those results offers a cautionary tale about cargo-culting industry best practices without understanding why they work.

The Growth Trap

Software degradation is rarely a purely technical problem. Code does not rot in a vacuum; it rots through the interaction of product pressure, organizational communication patterns, and architectural drift. In this enterprise's case, the retail app had grown in features and users, but the team responsible for it had grown commensurately. Adding developers helped for a while, but eventually the coordination overhead consumed more time than the new headcount saved. Technical leaders became bottlenecks, and delivery slowed further.

The natural response was to split the oversized team into smaller squads. Amazon's Two Pizza Team rule is often cited at this juncture, but the enterprise applied its form without its substance. Teams were divided by market rather than by software boundary. Each squad managed an entire customer journey for one market, even though those journeys were largely identical across markets. The result was predictable: communication overhead shifted from tech leadership down to the squad level, and the cognitive load on each developer actually grew because they still needed context on every part of the system. Smaller teams alone do not reduce complexity; they merely relocate the tax of managing it.

Seams Must Exist in Code, Not Just on Paper

Thoughtworks' initial guidance centered on cognitive load limitation, a concept from Skelton & Pais. The idea is to give each team ownership of a single complex or complicated domain, shrinking context switching and sharpening mission focus. This works well — but only when the application's domain boundaries are well defined and consistently enforced inside the codebase itself. The enterprise had invested in Domain-Driven Design and had defined sensible bounded contexts early on. The discipline did not survive contact with urgent product roadmaps.

Code analysis tools revealed an application that had become tightly coupled and low in cohesion. Domains depended on one another directly, and changes made to one routinely broke others in ways that were not immediately visible. Developers therefore had to hold knowledge of multiple domains just to safely modify a single feature, which pushed their cognitive load back up. The blast radius of any change was enormous: work was slow, debugging was harder, and the Four Key Metrics trended downward.

Re-establishing disciplined bounded contexts became a priority. This meant actively rejecting or rectifying cross-domain dependencies as they appeared, and tightening controls on technical debt so that temporary shortcuts could not harden into structural problems. The effect was to keep knowledge and responsibility in the same place: the team that owned the domain was the only team that needed deep expertise in it, and the scope of any breaking change was contained.

Reuse Requires Intentional Design

As the enterprise operated multiple brand market applications with low feature parity, there was an ongoing desire to share code across them. Tight coupling across the system made reuse almost impossible in practice — transplanting a single domain meant dragging along a large portion of the existing app, with high integration and ongoing management costs. This highlighted a missing attribute the organization had not previously considered: optionality of reuse.

Properly enforced bounded contexts were a necessary first step toward modularity, but they were not sufficient on their own. Reuse also demands that modules be written with clear interfaces and minimal external dependencies, so that they can be lifted and dropped into another application without pulling the surrounding architecture along with them. For the enterprise, this was a deliberate architectural shift, not an accidental side effect of good housekeeping.

The move to modularity was architectural, but its success depended on business lines of communication, team topologies, and the day-to-day experience of the developers building the modules.

Modular architecture is often positioned purely as a technical solution, but the enterprise's experience suggests otherwise. As reflected in their before-and-after metrics, the structural discipline of bounded contexts only produced real gains after the organization reshaped its teams around the same seams and improved tooling and workflow for the developers working within them.

When Domain Ownership Stays Tied to a Single App

Splitting an application into domains and assigning a dedicated team to each works well as long as everything stays inside that one app. A feature request flows to the squad owning the relevant domain, and the strict bounded context keeps the change’s blast radius contained. The feature can be built, tested, and deployed without touching other parts of the application, which shortens time to market and lets multiple features proceed in parallel. This approach held up in a single market context, but broke down once the organization tried to solve a second scaling problem: market feature disparity caused by a lack of reusability.

Importing Domains Creates New Dependencies

The next move was to transplant parts of the tidy monolith into existing market applications to capture development savings. A common framework allowed domains like Loyalty Points and Checkout to be reused outside their origin. In one example, a US-based team imported both domains into their new application alongside their own US-specific ones. On paper this represented an order-of-magnitude saving over rewriting domain functionality from scratch.

The reality was messier. The technical modularity did not account for the organization’s existing communication structures, which ultimately dictated work priority. After using the imported domains, the US team wanted to add a feature to one of them. Because they did not own that domain, they submitted a feature request to the UK application team that had originally built it. The UK team acknowledged the idea as solid but were busy with requests from UK stakeholders, leaving the timeline unclear.

This prioritization conflict directly limited how much reuse a consuming team could expect, and market teams grew frustrated at the lack of progress on imported domains. Two alternatives were considered:

  • Forking the domain: The consuming team could maintain its own version with a dedicated team around it. But learning an entire domain to add a small feature is inefficient, and divergence creates problems for future shared upgrades or feature parity between markets.
  • Pull request contributions: This put a heavy cognitive load on the contributing team, forcing them to work in a second codebase while still depending on the primary domain team for architectural guidance and PR reviews. It was unclear whether that team would have capacity between their own market-focused work.

Aligning Teams to the Architecture

The root issue was organizational. Conway’s law holds that a business designs its systems to mirror its communication structure. In both previous scenarios, functionality was modular from a technical standpoint but monolithic in ownership: Loyalty Points was built for the UK app, so it belonged to that team. The Inverse Conway Maneuver suggests altering the structure of development teams so that the intended technical architecture can emerge.

Applying that here meant abstracting domains from any specific mobile application and making each domain an autonomous development team. This changed the relationships between app teams. They no longer depended on one another for functionality across markets. Instead, new relationships formed along consumer and provider lines. Domain teams supplied functionality to market teams, who consumed it and fed back feature requests to improve the domain product.

The main advantage of this restructuring was clarified focus. In the earlier setup, a conflict of interest arose whenever one market requested a change to a domain originating from another. Abstracting the domain from its market shifted the mission from building functionality for a single market to building functionality that serves all consumers. Success was measured by consumer uptake and end-user reception, and new features were assessed solely by the value they brought to the domain and its overall consumer base.

Developer Experience Becomes a First-Class Concern

Assigning domains to autonomous teams gives you a topology that looks clean on paper. Consumer and provider relationships plot neatly, and you can assume domains are independently built and deployed while apps simply pull in whatever they need. In practice, those idealised properties are hard to deliver. Brand consistency across teams, mobile-specific constraints, discoverability, testability and cross-market compatibility all become open questions. These are solvable problems, but they add cognitive load that previously had no clear owner in the structure. The answer was to create one: a domain team responsible for “the platform”.

The platform domain, in our usage, refers to the tooling and guidance that lets other teams deliver independently within the agreed architecture. It keeps the same provider/consumer relationship we have seen elsewhere, with the added mission of improving the developer experience (DX) for teams building domains and apps on top of it. We expected better DX to drive adoption of the new architecture. Since DX is an imprecise term, we broke it into concrete capabilities, starting with efficient bootstrapping.

Frameworks always have a learning curve; DX aims to soften it. Sensible defaults and starter kits reduce onboarding friction without imposing rigid mandates. For our platform domain, we defined it as:

We Promise that:

  • You will be able to quickly generate a new domain with all associated mobile dependencies, common UI/UX, Telemetry and CI/CD infrastructure in one command
  • You will be able to build, test and run your domain independently
  • Your domain will run the same way when bundled into an app as it does independently”

These promises describe a self-service developer productivity platform. The goal is to let teams focused on end-user functionality concentrate on their mission instead of wading through unproductive tasks.

The second platform capability was technical architecture as a service. Architectural responsibilities followed Conway’s law within our organisation, concentrating in a silo disconnected from the teams that needed guidance. Our autonomous teams could make their own decisions, but still wanted technical shepherding for principles, patterns and governance. Framing that as an on-demand service produced:

We Promise that:

  • The best practice we provide will be accompanied with examples that you can use or actual steps you can take
  • we'll maintain an overall picture of domain usage per app and when needed, orchestrate collaboration across verticals
  • The path to production will be visible and correct
  • We will work with you”

These are servant-leadership promises, acknowledging that everyone is responsible for the architecture, rather than command-and-control governance.

A successful platform team stays close to its customers. The Toyota principle of “Genchi Genbutsu” — go and see for yourself — applies directly. The team must empathise with developers using its product to understand their needs. We underinvested here initially, and autonomous teams went their own way. That produced duplicated effort, incompatibilities and a loss of faith in the architecture that took time to repair.

Measuring the Change

Proving the value of modularisation is easier when you have a controlled comparison. In our organisation, the transition happened one app at a time, leaving us with two functionally similar retail apps: a legacy monolith with high coupling and a mature, productive team, and a domain-bounded context app with newer individual domain teams. Cycle time is a fair metric here, as it represents the time to make a code change and excludes the variable-length store release process.

Mobile App Type Cycle Time
Legacy Monolith17 days
Domain Bounded Context (Avg)10.3 days

Averaged across all domain teams, the new app still showed a clear uplift over the legacy app, even with a less experienced team.

The other significant metric was optionality of re-use. When the legacy app needed existing domain functionality, the team had no choice but to write it themselves. The modular app could plug and play an existing domain. Ignoring the common production path steps, we measured from pre-production product sign-off to dev-complete for one development pair working with a full-time designer.

Integration Type Avg Development Time
Non-modular90 days
Modular5 days

The difference highlights the power of modular architecture when there is a genuine business need for it.

External factors excluded above still deserve measurement. Optimising development performance can expose other bottlenecks. If a release takes six months and governance takes one month to approve, governance looks minor. If development drops to five days while approval still takes a month, compliance becomes the next constraint to tackle.

Our results also miss the effect on integration. Autonomous domain teams often seconded themselves to market application teams to speed things up. We attribute this to domain squads defining their success by adoption of their domain product. That produced two concentric feedback loops. The outer loop is the developer-facing integration experience how easily the app container incorporated the domain. The inner is end-user experience with the complete journey. Domain teams that collaborated closely with consumers and had direct access to end users achieved the fastest feedback and were the most successful. Poor integration feedback risks insulating a domain team from the actual users of its capability.

Finally, our platform domain produced results of its own. Starting new domain work is costly. By targeting startup pain points, the platform team cut per-team startup costs by over 80%. A pair completed in one afternoon what had been estimated at a week of early team development.

Limitations Worth Heeding

The positives are substantial, but no architecture shift comes free. A change of this kind requires ongoing time and effort and should only be undertaken to solve real speed-to-market problems. Giving teams autonomy also introduces a decentralisation risk. The platform squad can propose sensible defaults, but the teams choose. Common UI/UX and internal dependencies tend to converge when domains want to get accepted into a market app, but managing bloat from overlapping dependencies and eclectic design patterns is difficult. Ignoring it invites uncontrolled app growth and poor customer-facing performance. We found that sustained investment in technical leadership, alongside robust guardrails, communication and architecture oversight, kept that tendency in check.

Putting It All Together: What the Shift Achieved

The two delivery problems we set out to solve — lengthening lead times for new features and growing feature disparity between similar in-house applications — were never going to yield to a single fix. The resolution came from evolving technical architecture, team structure, and the approach to technical debt as one linked system. By restructuring teams around modular, domain-centric boundaries, we reduced cognitive and contextual load while giving teams the autonomy to ship without waiting on others.

Elevating those teams and domains to be agnostic of their originating application or market was the natural next step. That move weakened the constraints of Conway’s law that an application monolith enforces, and allowed a consumer/provider dynamic to emerge between domains. The final piece was identifying and investing in a dedicated “platform” domain to absorb the cross-cutting concerns that decoupling inevitably surfaces.

The results, measured across all modular domains in a market application, were concrete:

  • 60% reduction in average cycle time.
  • 18x improvement in development cost when integrating modular domains into a market app versus building from scratch.
  • 80% reduction in startup costs for new domains, sustained by ongoing platform team support.

For the client, these gains translated into the ability to pursue market opportunities previously dismissed as too low-ROI — openings that competitors had owned uncontested for years.

The Conditions for Success

A modular architecture intrinsically linked to teams delivers real benefits, but only under the right circumstances. The numbers above came from one specific organisation and its particular context. Before acting, take time to understand your own landscape and look for the signs and antipatterns that indicate readiness. Do not underestimate the upfront and ongoing effort required to bring such an ecosystem together; an ill-considered attempt will likely create more problems than it solves.

Your situation will be unique in scope, so resist the pull of the cargo cult. If you focus simultaneously on empathy, autonomy, and lines of communication that enable the architecture, then there is every reason you could replicate the successes we have seen.

Acknowledgments

Special thanks to Carl Nygard, Tim Cochran, and Greg Davis for suggestions and critical feedback. Thanks also to Rob Horn for persuading the author to put thoughts in order, and to Martin Fowler for guidance.