Your Codebase Ownership Strategy Is Probably a Lie

Your Codebase Ownership Strategy Is Probably a Lie

How engineering teams define areas of ownership in large codebases using services, modules, and DDD, and why most orgs get Conway’s Law wrong.

Assigning ownership in a large codebase sounds straightforward: give Team Payments the /payments folder, give Team Orders the /orders service, and call it a day.

Then reality hits. A new Jira ticket comes in for a “checkout flow” that touches three services, five database tables, and a queue nobody remembers owning. The team responsible for the UI blames the middleware team, who blames the data team, who points out that the event schema was changed by someone who left six months ago.

The problem isn’t malicious teams or bad engineers. The problem is that most ownership strategies define boundaries based on what’s convenient for the org chart, not what’s coherent for the system. And that creates a slow-motion disaster that compounds daily.

How Conway’s Law directly shapes codebase ownership structures and service boundaries

The DDD Litmus Test: One Domain, One Lexicon

The most mature approach to codebase ownership comes from Domain-Driven Design (DDD), and it solves the first-order problem elegantly: each team owns a business domain, and each domain has its own ubiquitous language.

This means within the Orders domain, you have Order, OrderService, OrderController, OrderRepository all living together in the same vertical slice. You never call something /orders in one endpoint and /checkout in another, you pick one term and stick with it for eternity.

The rule is brutal but effective: never use synonyms within the same domain. If your team’s bounded context calls it a “Customer”, every file, every class, every API route uses “Customer.” No “User.” No “Client.” No “Account Holder.” The discipline eliminates ambiguity at the source.

The Vertical Stacking Problem

Here’s where most teams sabotage themselves: they organize code horizontally instead of vertically.

Putting all controllers in one folder, all services in another, and all repositories in a third is the software equivalent of organizing your kitchen by function, all spatulas in one drawer, all pots in another, all ingredients in separate cabinets. It makes perfect sense until you need to cook an omelet and realize you have to visit six rooms.

Vertical organization means that what evolves together lives together:

com.company.domain.orders/
    Order.java
    OrderService.java
    OrderController.java
    OrderRepository.java

This approach makes ownership explicit. One quick git log on that package shows exactly which team has touched it most recently. More importantly, it makes the cognitive load manageable: a developer looking at a feature ticket can open exactly one package tree to understand the full scope of changes.

CODEOWNERS: The Guardrail, Not the Solution

The CODEOWNERS file is the most widely deployed ownership enforcement tool in the industry, but it’s often misunderstood. It doesn’t tell you where a new feature should go or which team owns a concept. It prevents chaos monkeys from modifying files they shouldn’t touch.

The pattern is simple but effective: map every file in the repository to a GitHub team. When a PR touches files outside a contributor’s domain, the file’s owner must approve. This prevents scenarios where a frontend engineer adds fields to the root User object in the identity service without anyone noticing until the next production incident.

One team took this further by building a resolver that reads the active controller from the request and maps it to the CODEOWNERS-defined team, tagging every log entry with the responsible team. This eliminated the “who do I page for this error?” Slack thread that was eating 30 minutes per incident.

The Handoff Escalation: When Ownership Blocks Flow

The inconvenient truth about domain-based ownership is that features rarely respect domain boundaries. A customer-facing “change shipping address” flow might touch the Identity domain (authentication), the Orders domain (order validation), and the Payments domain (refund recalculations).

When this happens, you get the worst outcome of strict ownership: Team A waiting for Team B to build something, with no visibility into the dependency.

The standard workaround is a shared spreadsheet or Jira cross-linking, but these are reactive. The onus has to be on the affected team to provide sufficient evidence to the other team that the problem lives in their domain. No handball hospital-passes allowed, everyone must triage to the edge of their territory, then hand off root-cause evidence.

Some teams address this at the architectural level by embracing a monorepo with shared code. The Polylith Architecture approach, for example, treats all code as shared code, with team-specific boundaries enforced only at the deployment configuration and service setup level. Own the deployment and monitoring, share the code. This sidesteps the approval bottleneck entirely.

Why minimalism can simplify ownership boundaries compared to microservice sprawl

Conway’s Law: The Organization Designs the System (Or Vice Versa)

Melvin Conway’s 1968 paper “How Do Committees Invent?” gave us the rule that organizations design systems that mirror their own communication structures. What’s less appreciated is that this applies to everything, including your testing infrastructure.

If your test team is sprinkled across organizational silos, you’ll end up with three separate test platforms: one for firmware, one for the host library, and one for the host application. In a small org, that’s manageable. In a large one, it’s a nightmare of duplicated effort and integration failures.

The more powerful application is reverse-engineering Conway’s Law: design your organization to match your product’s intended structure. If you want modular, independently-deployable services, you need teams that can operate independently. That means each team needs all the skills required to take a feature from conception to production, including testing, deployment, and monitoring.

The Antipattern: Ownership Slices That Don’t Match Workflow

The most expensive mistake in codebase ownership is creating boundaries that don’t align with how work actually flows.

Consider a company that defines ownership by layer (frontend, backend, database) but routes work by feature. Every feature ticket crosses three ownership boundaries, requiring approvals from three teams, even though one person could have implemented the whole thing. The “revolutionary” ownership strategy mostly just added bureaucracy.

The fix is to define ownership boundaries that match the unit of deployment and the unit of value. If a feature can be independently deployed, that feature’s code should be owned by one team. If it can’t, you’ve found your architectural bottleneck.

Interfaces Matter More Than Names

During a recent interview, Joe Justice (of WikiSpeed and Tesla fame) made a critical point about hardware interfaces that translates directly to software: a name or version number must not be used as a shortcut for testing.

Many software teams practice “semantic versioning theater”, declaring a major version bump and assuming downstream consumers will check compatibility. The reality is that most consumers pin to ^1.2.3 and never run integration tests until something breaks in production.

The correct approach is what Tesla does with vehicle components: emergent architecture. The system constantly asks: “What is connected to me? What interface does it offer? What are its safety mechanisms?” This allows the system to handle components that didn’t exist when it was originally designed.

In software terms, this means contracts should be discovered and verified at runtime, not assumed at design time. Schema registries, consumer-driven contract tests, and canary deployments all serve this purpose. They let ownership boundaries be strict while allowing the system to evolve.

Living documentation approaches for tracking evolving ownership and architectural boundaries

The 30-Day Experiment: Fix One Flow, End to End

If your ownership strategy is broken, you don’t need a grand reorganization. You need a 30-day experiment.

Pick one feature flow that crosses multiple ownership boundaries. Assemble a team with all the skills needed to take that feature from end to end. Give them authority to make any decision required. Measure the current cycle time, then let them fix the bottlenecks they discover.

The experiment reveals everything: which interfaces are stable, which dependencies block every change, which approvals are theater, and which handoffs are actually necessary. After one iteration, you’ll know more about your codebase ownership patterns than any architecture review board could tell you.

The hidden cost of AI-generated code on team ownership understanding and maintenance

Codebase ownership at scale isn’t about drawing boxes on a diagram. It’s about creating boundaries that reduce coordination costs without creating artificial obstacles.

The best teams I’ve seen use a combination of:

  1. Domain-based vertical slicing (not horizontal layers)
  2. CODEOWNERS enforcement with team-level resolvers
  3. Shared code, owned deployments to avoid approval bottlenecks
  4. Runtime contract verification to make ownership boundaries flexible
  5. Small, measurable experiments to identify real bottlenecks

And they all accept one uncomfortable truth: perfect ownership alignment is impossible. Features will always cross boundaries. Dependencies will always leak. The goal isn’t elimination, it’s making the remaining friction transparent, measurable, and continuously improved.

The criterion remains simple: how quickly can a legal, secure, tested, and usable result be produced? Everything else is just queue management with fancier terminology.

Share:

Related Articles