As teams grow

Published

July 6, 2025

The Alchemist, Pieter Bruegel the Elder, 1558

After many years as a software engineer, one thing has become increasingly clear to me: as engineering teams grow, the nature of their challenges becomes more predictable … yet no easier to solve.

Fred Brooks captured part of the problem in The Mythical Man-Month: communication overhead grows quadratically with team size1. That helps explain why teams slow down, but not how the work itself changes as scale sets in.

What interests me is exactly that: the fundamental transformation that occurs, not just in coordination but in architecture, knowledge, culture, and the very way decisions are made. Engineering transforms itself as it scales, and those shifts warrant attention.

1 Architecture Reflects the Org

In small teams, simplicity isn’t about architecture—it’s about shared understanding. Everyone knows how different features connect, why certain decisions were made, and where the tradeoffs lie. Components may be tightly coupled or loosely defined, but they’re all part of a system that fits in the collective headspace of the team. Coordination happens in conversation, not through interfaces or documentation.

But as teams grow—especially across locations, time zones, or specialisations—that shared understanding begins to suffer. Communication shifts from informal to structured. Decisions that were once made out loud become hidden in outdated documentation or someone’s memory. What was once intuitive now needs to be explained and often re-learned.

Conway’s Law suddenly manifests: your architecture begins to mirror your team structure. When teams are structured around separate concerns, the system tends to fragment along those lines—producing modular but often disconnected parts. The shape of the software starts to reflect the shape of the organisation … even if that was not the plan.

These boundaries aren’t just technical: they shape how teams communicate, share ownership, and coordinate work. When boundaries are unclear or misaligned, collaboration becomes harder, not out of reluctance, but perhaps due to ambiguity. This can lead to duplicated effort, inconsistent tooling, and accountability gaps. Not from a lack of willingness, but from a lack of shared context.

2 Knowledge Doesn’t Scale Automatically

Small teams share knowledge effortlessly. Questions happen during standups. Architecture decisions are made aloud. Everyone is aware, if not of the details, then at least of the design of the system.

At scale, this can fail. Knowledge becomes fragmented. Teams build up local context and local habits, but shared understanding becomes a liability rather than a default.

Documentation helps, but only up to a point. It captures what exists, but not why. In fast-moving areas like Artificial Intelligence, “what works” evolves faster than most internal docs can keep up.

Rather than treating knowledge as a static asset to be documented and filed, we need living mechanisms that evolve and distribute it:

  • Architectural Decision Records (ADRs): record decisions and their rationale, not just outcomes
  • Cross-team technical reviews: focus on patterns of change: how services interact and evolve (rather than surface-level differences)
  • Team rotations: create bridges across teams, exposing assumptions and surfacing tacit knowledge

The goal isn’t comprehensive documentation—it’s shared mental models and understanding that stays aligned as the system grows.

3 From Decisions to Decision Frameworks

In small teams, decision-making often happens through informal consensus. Discussions are live, everyone contributes, and decisions are fast. As teams grow, this model becomes harder to scale—simply because not everyone can be in the room anymore.

One natural response is to introduce more structure: decision groups, technical leads, or designated approvers.
This can bring needed clarity and coordination, but it’s important that structure doesn’t become a bottleneck—or unintentionally distance decision-making from those closest to the work.

A more scalable approach shifts the focus from centralised decision-making to decision-enabling frameworks.
These frameworks clarify expectations and boundaries, while preserving autonomy within them.
The goal is to empower more people to make good decisions confidently—not to control every outcome.

At their best, these frameworks reflect a belief that good ideas can come from anywhere.
Seniority provides guidance and perspective, but it shouldn’t be a gate to contribution.

That means:

  • Clear governance boundaries:
    Define where teams are empowered to decide independently, and where shared alignment is required.
    This removes ambiguity and encourages ownership—without sacrificing consistency.

  • Impact-scaled protocols:
    Not every decision needs the same level of process.
    Lightweight paths for low-impact changes and more structured steps for decisions with broad or lasting effects.

  • Accessible context:
    Invest in documentation, tooling, and visibility so that more engineers can understand enough of the system to contribute meaningfully—even beyond their immediate domain.

The good frameworks don’t slow teams down, they enable high-quality decisions easier, faster, and more repeatable.

4 Rethinking Technical Debt

In large systems, technical debt isn’t just about bad code: it’s about loss of options. The ability to adapt fades, not because the system breaks, but because every change requires negotiation, coordination, and sometimes … new debt.

Small teams refactor opportunistically. Big ones can’t. They need deliberate mechanisms to manage and prioritise change.

This starts by seeing debt not as “bad”, but as part of a broader system of tradeoffs. In my view, debt can be viewed as:

  • Strategic: taken on knowingly to ship faster, with a plan to pay it down
  • Cultural: the result of unclear ownership or conflicting incentives
  • Invisible: decisions so embedded in the system they stop being seen as choices

At scale, managing debt is a portfolio problem: one of risk, timing, and options. Not just code quality.

5 Complexity Is a Cost (Even When It’s Necessary)

Personally, I think any complexity in software is a liability2. Complexity hides intent. It adds friction to change. It makes onboarding harder and diagnosis slower. If it can be avoided, it should be.

But at scale, some complexity becomes inevitable. As systems grow, interactions become harder to reason about. Small changes avalanche into unpredictably.

The only viable strategy isn’t to eliminate complexity3, but to isolate it. And the most effective way to do that is through modularisation. Clearly defined interfaces, narrow contracts, and boundaries that encapsulate complexity rather than expose it.

That said, abstraction (one of our primary tools for modularisation) comes with its own risk. Abstract too early, and we might end up building around imagined concepts. Abstract too late, and duplication becomes a serious problem. Worse, every layer of abstraction becomes another that future engineers must dissect just to understand what’s really happening.

This is why modularity isn’t just about system design: it’s tightly linked to knowledge sharing, governance, and culture. If modules are shaped by disconnected teams, you get silos. If they’re shaped by shared understanding, you get leverage.

Simplicity may not scale, but clarity sure can. We should aim at containing complexity and not justify it.

6 Culture Must Evolve, Too

As teams scale, their culture needs to evolve just as deliberately as their architecture.
What works in early-stage environments—speed, improvisation, informality—can become liabilities at scale.
But swinging too far toward rigidity and heavyweight processes creates its own drag.

The goal is to build a culture of responsible speed: the ability to move fast without breaking what matters.

  • Standards (that empower) :Useful defaults that reduce friction—without being prescriptive mandates.
    • Example: Shared code scanning tools and automated workflow rules (e.g. for security, formatting, or dependency hygiene) that apply across services. These help teams catch common issues early without dictating tech stack or architectural style. The rules are adjustable, but provide a strong baseline to build safely and consistently.
  • Processes (that flex with context): Not all work carries the same risk—and your processes should reflect that.
    • Example: Experimental features can ship quickly with relaxed review and validation, while features destined for production—especially those impacting customers or compliance—follow stricter gates: peer review, staging validation, and usage flags. The process scales with the risk, not the org chart.
  • Tools (that amplify judgment—not replace it): The best tools inform decisions, not make them.
    • Example: CI systems and build notifications that provide rich, contextual feedback (component-level metrics, test flakiness history, and recent changes). Rather than blocking a merge automatically, the system enables engineers to make smart, informed calls based on impact and relevance.

Above all, culture should reflect values that scale (accountability, transparency, and respect for good ideas) no matter where they come from.

The Path Forward

The fundamental challenges of scaling engineering teams aren’t new. Brooks called them out decades ago. What’s changed is how we address them.

Today, we have better tools. Distributed systems. CI/CD. DevOps practices. Cloud-native infrastructure. These give us the building blocks for scale … but not the blueprint.

In my experience, scaling isn’t about holding on to early-stage practices. It’s about letting go of what no longer fits—and being intentional about what comes next.

To me, this means:

  • Investing in infrastructure that supports autonomy without predictability
  • Designing systems where the right thing is the easy thing
  • Creating feedback loops that let teams adapt faster than the complexity around them

The aim is not just to grow bigger but to grow better.

Footnotes

  1. Brooks’s Law: \(\frac{n(n-1)}{2}\), where \(n\) is the number of people involved↩︎

  2. See The Duct Tape Programmer, 2009.↩︎

  3. Although if possible, we should totally do it.↩︎