Why Good Systems Need Boundaries They Can Break On Purpose

tfc

Hatched by tfc

Apr 30, 2026

11 min read

87%

0

The hidden problem behind both architecture and feature flags

What do aggregate boundaries and runtime feature flags have in common? At first glance, almost nothing. One belongs to the world of domain modeling, the other to operational control. One tells you what must stay together, the other lets you change behavior without redeploying. Yet both are really answers to the same deeper question:

How do you let a system change without letting it dissolve into chaos?

That question sits at the center of modern software design. We want speed, but not disorder. We want flexibility, but not accidental complexity. We want teams to ship faster, but we do not want every change to ripple unpredictably across the codebase. The paradox is that the more dynamic a system becomes, the more it needs structure. Not less structure. Better structure.

That is why the most useful way to think about both aggregates and feature flags is not as isolated techniques, but as two kinds of boundaries. One boundary protects the integrity of the domain. The other boundary protects the safety of change. Together, they form a discipline for building systems that can evolve without losing their shape.

The real challenge in software is not whether things can change. It is whether change can be made legible, local, and reversible.


Aggregates: the shape of truth inside the system

An aggregate is more than a pattern. It is a statement about where truth lives.

If every entity in a system can be reached and mutated directly, the domain starts to behave like an open field with no fences. At first, that feels convenient. It is easy to navigate, easy to wire together, easy to query. But convenience is deceptive. Over time, the field becomes trampled by conflicting assumptions, and no one can say which object is responsible for maintaining what rule.

An aggregate solves this by drawing a boundary around a cluster of related objects and naming one root as the sole public point of access. That is not an arbitrary object oriented trick. It is a governance model. The root is the only object allowed to enforce the rules of the cluster, which means outside code cannot casually bypass the logic that keeps the model coherent.

Think of a bank account with transactions, holds, overdraft rules, and balance calculations. You do not want outside code editing the transaction list directly while also updating the balance separately. You want one controlled entry point that decides how the whole unit changes. The aggregate is that controlled entry point. It says, in effect: if you want to change this part of the world, you must speak to it as a whole.

This is why aggregates matter so much in systems that have real business invariants. They prevent the domain from being reduced to a pile of tables and helper functions. More importantly, they turn consistency into an explicit design choice. The boundary does not just organize code. It expresses what must remain true.


Feature flags: the shape of change outside the system

If aggregates answer where truth lives, feature flags answer when truth should change.

A feature flag lets behavior shift at runtime without a redeploy. That sounds tactical, but the strategic value is deeper. It means you can separate shipping code from activating code. In other words, you can create capability ahead of commitment.

That separation is powerful because deployment and release are not the same thing. Deployment moves code into the environment. Release exposes behavior to users. A feature flag inserts a controlled gate between the two. Suddenly, a team can merge unfinished work, test it with a subset of traffic, roll out gradually, or disable it instantly if something goes wrong.

In a fast moving environment, this is not just convenient. It is a form of operational humility. It acknowledges that production is uncertain, that not every change deserves full exposure immediately, and that the system should provide a way to back away from risk without panic.

A good flag system therefore becomes part of the delivery architecture, not merely a conditional statement in code. It enables safer experimentation, targeted rollouts, and time based behavior. A campaign can begin on Monday. A pricing rule can activate next quarter. A new path can be visible only to internal users. These are not just toggles, they are governance controls over behavior.

But feature flags come with their own danger. If used carelessly, they create a second codebase hidden inside the first. Every conditional branch becomes a possible permanent fork. The system stops being one product and becomes a pile of alternate realities. That is exactly the kind of entropy aggregates were invented to avoid.

So the tension becomes clear: feature flags give you flexibility, but flexibility without boundaries becomes fragmentation.


The deeper connection: both are anti entropy mechanisms

The surprising connection between aggregates and feature flags is that they are both designed to manage a single enemy: entropy caused by uncontrolled coupling.

An aggregate limits who can change a set of related objects. A feature flag limits when a behavior becomes active. Both reduce the blast radius of change. Both insist that not every part of the system should be equally reachable at all times. Both reject the fantasy that maximum openness produces maximum productivity.

This reveals a useful mental model: software systems do not fail only because they are too rigid. They fail because they are too permeable. Every boundary that disappears seems like an improvement in the moment. Fewer layers. Fewer restrictions. Faster access. But what disappears with the boundary is not just friction. It is also local accountability.

Consider a shipping service. The order aggregate might control status transitions, payment capture, and inventory reservation. A feature flag might determine whether a new fraud check is active for 10 percent of orders. These are different kinds of controls, but they serve the same organizational purpose. The aggregate says only the order root can authorize changes to order state. The flag says only the rollout policy can authorize exposure of the new fraud logic.

In both cases, you are asking the same question: who gets to decide, and under what conditions?

That question matters because systems are not just collections of functions. They are systems of power. Some code paths are privileged. Some decisions should be centralized. Some changes should be conditional. When these distinctions are unclear, complexity grows silently. The system still works, but it works by accident.

Good design is not the elimination of control. It is the placement of control where it can still be understood.


A practical framework: boundary of truth, boundary of time

Here is a simple way to unify these ideas.

Think about every nontrivial system as having two essential boundary types:

  1. Boundary of truth: What must stay consistent together?
  2. Boundary of time: When should a behavior become visible?

Aggregates belong to the boundary of truth. They decide which objects are allowed to change together and which invariants must be preserved atomically. Feature flags belong to the boundary of time. They decide when a capability becomes live, for whom, and under what conditions.

This framework is useful because many design mistakes come from confusing the two.

For example, a team may use feature flags to manage a domain inconsistency. That is a mistake. If an order can be half created or a payment can be both captured and uncaptured depending on a flag, the flag is compensating for a weak domain model. Flags should not be used to paper over broken invariants.

The reverse mistake is also common. A team may harden the domain model but refuse to use flags for rollout safety, forcing every change into a big bang release. That is an operational weakness. A strong aggregate does not protect you from a risky deployment.

The best systems use both boundaries deliberately. The aggregate makes the business rule precise. The feature flag makes the rollout controlled. One keeps the meaning stable. The other keeps the exposure gradual.

An analogy helps. Imagine a theater production. The script, actors, and stage blocking are your aggregate. Everyone has to stay in sync so the scene makes sense. The lighting cue that brings the spotlight up at the right moment is your feature flag. The scene does not become coherent because the spotlight exists. And the spotlight does not save a poorly written scene. But together, they let the performance be both disciplined and adaptable.


Why mature systems need the ability to change in layers

The most important insight here is that change should not be one thing. Mature systems treat change as layered.

At the deepest layer is the domain model. This is where you decide what counts as an order, a customer, a subscription, a transaction, a reservation. This layer wants stability because business meaning must not oscillate with every deployment.

Above that is the release mechanism. This is where you decide when a behavior is activated, how widely it is exposed, and whether it can be rolled back. This layer wants flexibility because delivery risk changes from day to day.

When those layers are confused, teams either move too fast with no guardrails or move too slowly because every domain change is also a deployment crisis. But when they are separated, you can evolve the product while preserving trust in the model.

This is especially important in serverless and event driven systems, where services can be small, numerous, and highly decoupled. Decoupling is useful, but it also makes it easy to lose sight of the boundaries that preserve coherence. In such systems, an aggregate boundary prevents business rules from becoming scattered across functions. A feature flag prevents rollout decisions from becoming hard coded in permanent branches.

In practice, this means a single change can move through multiple controlled states:

  • It is first modeled inside the aggregate, where invariants are enforced.
  • It is then deployed behind a feature flag, where behavior is inert or limited.
  • It is gradually exposed, tested, and observed.
  • Once stable, the flag is removed, and the system returns to a simpler state.

That last step matters. The ideal is not to accumulate flags forever. The ideal is to use them as temporary scaffolding. Likewise, the ideal is not to create ever larger aggregates. The ideal is to define the smallest boundary that protects the truth of the domain.


The discipline of reversible power

There is a final conceptual link between these ideas that is easy to miss: both are about making power reversible.

An aggregate prevents arbitrary mutation from the outside. A feature flag allows a capability to be switched off if reality turns against your assumptions. One prevents unauthorized change. The other enables rapid retreat from dangerous change.

That combination is a hallmark of robust systems. Robustness is not the absence of failure. It is the presence of safe reversal.

In a fragile system, every change is a leap. In a robust system, every change is a probe. You can test, observe, and correct. You can protect the domain from corruption and protect the rollout from overconfidence. You can keep core rules stable while making operational exposure fluid.

This is why teams that take boundaries seriously often ship faster over time, not slower. Boundaries do not merely constrain. They reduce the cost of uncertainty. They make it easier to know where a problem lives when something breaks. They make it easier to disable one behavior without dismantling the whole service. They make it easier to ask whether a failure is a domain bug, a rollout bug, or a misunderstanding of the boundary itself.

The hardest part of modern software is not adding more capability. It is preserving intelligibility as capability grows. Aggregate boundaries and feature flags are two complementary answers to that problem. One defines the unit of meaning. The other defines the unit of exposure.

Key Takeaways

  • Use aggregates to protect business truth. If a rule must always hold, the objects involved should change through one controlled root.
  • Use feature flags to control exposure, not logic drift. Flags should govern rollout and timing, not hide broken domain design.
  • Separate deployment from release. Code can exist in production before users see it, which lowers risk and increases learning speed.
  • Treat every boundary as a form of governance. Ask who can change what, and under what conditions.
  • Remove temporary complexity. Delete stale flags and avoid oversized aggregates, or your safeguards become liabilities.

Conclusion: boundaries are not obstacles, they are the design of freedom

Software teams often talk about freedom as if it means fewer rules, fewer layers, fewer constraints. But in complex systems, the opposite is usually true. Freedom comes from well placed boundaries. A domain model with clear aggregates is free from accidental corruption. A delivery pipeline with feature flags is free from risky all at once releases. The system can move because it knows what must stay still.

That is the deeper lesson connecting these ideas: good architecture does not eliminate control, it concentrates control where it can be intentional. When you design boundaries carefully, you are not making the system less dynamic. You are making its dynamism trustworthy.

And that may be the most valuable kind of speed there is: not the speed of acting without friction, but the speed of changing without losing your shape.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣