Why Good Systems Need Rules of Order, Not Just Good Parts

Kai Nguyen

Hatched by Kai Nguyen

Jul 23, 2026

9 min read

88%

0

The hidden question behind every reliable system

Why do some systems stay understandable as they grow, while others become tangled the moment they face real complexity? The tempting answer is that the first kind has better components. But that is only half true. A system does not become robust merely by having strong pieces. It becomes robust when those pieces are arranged so that change has a direction.

That is the deeper connection between clean object design and topological ordering. One field asks how to shape classes so responsibilities stay clear, dependencies stay controlled, and code can evolve without collapsing under its own weight. The other asks how to arrange dependent items so they can be processed without violating the logic of what depends on what. In both cases, the real problem is not size. It is dependency.

We usually praise modularity as though it were a matter of carving reality into neat pieces. But modularity is not only about separation. It is about making the order of influence visible. A well designed codebase and a well formed dependency graph both answer the same question: what must come first, what may come later, and what should never know too much about anything below it?

The deepest measure of design is not how many parts you have, but how gracefully those parts can be ordered under pressure.

Dependencies are not a nuisance. They are the structure.

Most people think dependencies are bad because they create coupling. Yet dependency is unavoidable. A class depends on an interface. A task depends on a prior task. A decision depends on information. A build depends on compiled inputs. The real issue is not whether dependencies exist. The real issue is whether they are explicit, directional, and manageable.

That is where the connection becomes powerful. In object oriented design, the goal is often to keep each class focused, open to extension, and closed to reckless modification. In graph terms, this means avoiding cycles of responsibility, reducing hidden incoming knowledge, and ensuring that changes flow in one direction rather than ricocheting everywhere at once. A topological sort succeeds only when the graph admits an order. Good design succeeds only when the system admits change without contradiction.

Think of a restaurant kitchen. Ingredients are not the problem. Dependencies are. The sauce must be prepared before the final plate. The oven must be hot before the bake begins. The garnish comes last. If a prep station needs the final plating outcome before it can chop onions, the kitchen is broken. Likewise, if one class needs to know everything about every other class, the software is not merely messy. It has lost the ability to be sequenced.

This is why the language of sources and sinks is so useful beyond graph theory. A source is something that can begin without waiting on anything else. A sink is something that can complete without forcing others to wait on it. In a healthy system, both roles matter. Sources create momentum. Sinks provide resolution. The middle layers translate between the two. Great design is not about eliminating sources or sinks, but about keeping the path between them legible.

SOLID principles are really rules for keeping the graph acyclic

Viewed through the lens of dependency ordering, the familiar principles of object oriented design stop feeling like separate commandments and start looking like one unified discipline. They are all strategies for preventing the system from becoming a knot.

Single responsibility means a node should not become a miniature network. If a class does too many jobs, it acquires too many reasons to change, which is another way of saying it accumulates too many incoming forces. It stops being a node and starts behaving like a traffic circle.

Open and closed is about preserving order under extension. Good extension should feel like adding a new node or branch, not rewriting the whole graph. You want new capability to attach to the existing structure without rethreading every dependency by hand.

Liskov substitution is what makes nodes interchangeable without breaking the traversal. If a subclass cannot stand in for its parent, then the path through the system depends on hidden assumptions. That means the graph only appears ordered until you try to move through it.

Interface segregation prevents one node from becoming a supernode that every other node must consult. Large interfaces create artificial incoming edges, and those edges make the system more brittle. A lean interface is easier to place in an order because it asks for less and promises more.

Dependency inversion is perhaps the clearest bridge to topological thinking. High level policy should not point directly at low level detail if you want a stable ordering. Instead, both should depend on abstractions. That is equivalent to converting a messy dependency graph into one where the important edges point through controlled intermediaries. You are not deleting dependencies. You are redirecting them into a shape that can be sorted.

Good architecture is not absence of dependency. It is dependency that can be traversed without circular panic.

The same insight explains why some codebases feel easy to change. They are not simpler because they have fewer concepts. They are simpler because the concepts can be arranged in a sequence. When you add a feature, you are not performing surgery on a tangled organism. You are inserting a new node into a graph that already knows how to accept order.

The real enemy is the cycle of mutual demand

There is a reason topological sort fails on graphs with cycles. A cycle means one thing depends on another, which depends on another, which depends back on the first. No valid first step exists. That is not just a mathematical inconvenience. It is a design diagnosis.

In software, cycles appear in subtle forms. Class A calls Class B to compute a result, but Class B needs Class A to format the output, and Class A cannot proceed until that formatting is done. Or a domain model reaches down into infrastructure, while infrastructure reaches back up into domain assumptions. Each side thinks it is being pragmatic, but the result is a stalemate disguised as collaboration.

This same pattern appears in systems outside code. A team cannot make a decision because every member waits for consensus before revealing a preference. A business process stalls because finance needs operations to approve, while operations needs finance to estimate, while neither can begin. A person cannot start writing because the outline depends on the draft, and the draft depends on the finished argument.

Cycles feel dynamic, but they are actually a form of paralysis. The system looks alive because information is moving, yet nothing can resolve. Topological ordering gives us a blunt truth: if no source exists, no progress is possible. In design terms, that means some part of the system must be allowed to act without first asking permission from everything else.

That is why good abstractions matter so much. An abstraction is not merely a cleaner name. It is a way to break the cycle of mutual demand. When low level details depend on higher level policy only through an interface, and policy depends on the same interface rather than the details, the system stops asking impossible questions. It gains a direction.

A practical mental model: design for procession, not just composition

If you want one idea to carry away from this synthesis, make it this: design for procession.

Composition asks, what parts belong together? Procession asks, in what order should influence move through those parts? Composition is spatial. Procession is temporal. Most brittle systems are built by people who think in space only. They group related things together, but they never ask whether the result can be advanced, extended, tested, or changed without contradiction.

Imagine building a publishing workflow. You could create separate objects for drafting, editing, reviewing, and publishing. That is composition. But if those objects all know too much about each other, you have only renamed the mess. The real question is whether the workflow has a clear progression. Drafting should not require publishing logic. Reviewing should not rewrite the draft engine. Publishing should consume a vetted artifact, not interrogate the whole production history.

A topological perspective helps here. It forces you to ask:

  1. What must exist before this step can run?
  2. What can be delayed until later?
  3. Which dependencies are genuine, and which are merely convenient shortcuts?
  4. Where are we accidentally creating a cycle by letting two parts explain each other?

Once you ask those questions, design becomes less about making objects and more about arranging responsibility over time. This is why mature systems often feel calm. Their calmness comes from the fact that each layer knows its place in the sequence. It does not need to improvise the entire world before it can act.

The same applies to learning. If you try to understand advanced concepts without identifying prerequisite ideas, you create mental cycles. You demand intuition before structure, but structure before intuition is also impossible if the structure is not connected to examples. A topological approach to learning says: find the sources, find the sinks, and move through the material in a valid order.

Key Takeaways

  • Treat dependencies as first class objects. Before adding a class, task, or workflow step, ask what it depends on and what depends on it.
  • Look for cycles early. If two parts keep requiring each other’s output, you likely have a design problem, not just a coordination problem.
  • Prefer abstractions that create direction. Good interfaces and clean boundaries should make the system easier to order, not merely easier to name.
  • Design for progression. Ask not only whether parts belong together, but whether they can be traversed in a sensible sequence.
  • Use sources and sinks as a diagnostic. If a process has no clear starting point or no clean completion point, the structure probably needs rethinking.

The best systems do not just work, they can be ordered

We often talk about software design as if the goal were elegance, reuse, or cleanliness. Those matter. But underneath them lies a more fundamental requirement: a system must admit an order of execution, responsibility, and change. Without that, it may still function briefly, but it cannot mature.

That is why object oriented principles and topological sorting belong in the same conversation. One teaches us how to build parts that can survive change. The other teaches us how to arrange dependencies so change has a lawful path. Together they reveal a deeper truth: complexity is manageable only when it can be sequenced.

The next time a system feels confusing, do not ask first, “What is the right abstraction?” Ask instead, “What is the order of dependence here?” That question cuts closer to the truth. It turns architecture from a pile of components into a story with a beginning, a middle, and an end. And once you can tell that story, the system can finally move.

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 🐣