Why Good Systems Think in Responsibilities and Windows

Kai Nguyen

Hatched by Kai Nguyen

May 09, 2026

9 min read

78%

0

The hidden problem is not complexity, it is confusion about scope

Most people think systems become hard to manage because they get too big. But size is rarely the real enemy. The deeper problem is that a system keeps mixing concerns that should live at different levels of abstraction. A class tries to do too much. A query tries to answer too many questions at once. A developer changes one thing and accidentally breaks three others because the boundaries were never clear in the first place.

That is why two ideas that seem to belong in different worlds, object oriented design and SQL querying, actually point toward the same discipline. Both are about making scope explicit. In software design, that means organizing responsibilities so each unit has one clear reason to change. In SQL, that means deciding whether you want a row, a group, or a window over a group. The real skill is not writing more code or more SQL. It is knowing what level of truth you are operating on.

The best systems are not the ones that do everything. They are the ones that know exactly what kind of thing they are doing at each moment.

This is the deeper connection: good design is a way of protecting meaning. When meaning stays clear, code stays flexible, queries stay readable, and changes stay local.


A class and a query fail for the same reason

Imagine a class that handles user data, sends email, calculates analytics, and formats reports. It may work at first, but every new feature creates a negotiation with the past. Is this class responsible for persistence, notification, presentation, or business rules? The answer becomes: all of them. Once that happens, every change carries hidden side effects.

Now imagine a SQL query that joins several tables, aggregates data, filters by date, ranks results, and then tries to reuse those same calculations in multiple places. It may return the right answer today, but no one can easily tell which part of the logic defines the dataset, which part defines the grouping, and which part defines the final presentation. The query becomes a pile of intentions instead of a clear pipeline.

These failures look different on the surface, but the underlying flaw is identical: scope collapse. When one construct contains too many kinds of responsibility, it stops being a tool and becomes a fog machine. The system still runs, but understanding it becomes expensive.

A well designed class and a well designed query both avoid this by creating layers. First you define the raw material. Then you define how to slice or group it. Then you define how to interpret the result. This layered structure is not just aesthetic. It is what makes future change possible.


SOLID and SQL are both languages of boundaries

The most useful way to connect these ideas is to see them as two versions of the same mental model: designing boundaries around change.

In object oriented design, the familiar principles behind SOLID all reduce to one practical goal: make each component depend on as little unstable knowledge as possible. A class should not know more than it needs to know. It should expose a narrow interface. It should depend on abstractions when possible. It should be open to extension without requiring surgery everywhere else.

SQL has a parallel logic, even if it uses different vocabulary. A common table expression, or CTE, creates a named boundary. Instead of embedding every transformation in one giant statement, you give each step a shape and a name. That makes the logic easier to inspect and reuse. Window functions go even further by separating what row you are on from what set you are reasoning about. A row can be the current observation, while the window defines the context around it. That distinction is surprisingly powerful.

This means SQL is not just a language for retrieving data. It is a language for stating levels of context.

Consider the difference between these three modes of thought:

  1. Row thinking: What is true for this record?
  2. Group thinking: What is true for this category?
  3. Window thinking: What is true for this record in relation to its surrounding group?

This is almost the same as the way good software design asks you to separate an object, a service, and a policy. Each should answer a different question. When you confuse them, you create brittle systems. When you separate them cleanly, you create systems that can evolve.

Strong boundaries do not reduce power. They increase it by making context selectable instead of implied.

That is the real bridge between the two disciplines. Both teach you to stop assuming that all logic belongs in one place.


The underrated superpower is naming the layer you are in

Many people think advanced programming is about knowing more syntax. The deeper advantage is something subtler: knowing which layer you are in and naming it clearly.

In Python design, this is the difference between a class that says, “I save users,” and a set of classes that say, “I validate input,” “I persist records,” “I send notifications,” and “I format output.” The latter sounds less magical, but it is vastly more durable. Each piece has a specific job, so each can be replaced or tested independently.

In SQL, the same thing happens when you build a query in stages. A CTE can define the population. Another CTE can compute metrics. A final select can format the result. Window functions then let you say, for example, “for each customer, rank purchases within the last 90 days.” That phrase contains three layers of meaning: customer, purchases, and time window. If you write that in one opaque block, the logic becomes hard to audit. If you isolate the layers, the intent becomes legible.

Think of it like architecture. A building is easier to navigate when you know whether you are in the lobby, the office floor, or the mechanical room. The problem is not that a building has many functions. The problem is when all functions are crammed into one indistinguishable space.

Software design works the same way. Good systems are not flat. They are stratified.

There is a practical consequence here: whenever you cannot explain a piece of code or SQL in one sentence, you may have mixed layers. The sentence test is a surprisingly effective diagnostic. If a class needs five different verbs to describe it, or a query needs a paragraph to explain its intent, the boundaries may be wrong.


A useful mental model: separate the world into actor, context, and result

If you want a simple framework that unifies these ideas, use this one:

Actor, context, result.

  • The actor is the thing doing the work, such as a class, function, or query step.
  • The context is the scope within which the work makes sense, such as a customer, a dataset, a group, or a time window.
  • The result is the specific output you want, such as a ranking, an aggregate, or a transformed object.

This model is useful because it forces you to ask three different questions instead of one vague one. What is acting? What is it acting within? What is the outcome?

For example, suppose you want to find the top three orders per customer in the last quarter.

At the query level, the context is each customer and the relevant time period. The actor is the ranking logic. The result is a list of the top three orders per customer. A window function helps because it lets you preserve row level detail while calculating within the customer context. A CTE helps because it can isolate the quarter filter or the order normalization before ranking begins.

The same structure appears in object oriented design. A reporting class should not also own the database connection and the presentation template if those belong to different layers. The actor should be only as broad as necessary. The context should be explicit. The result should be easy to consume.

This model matters because it changes how you debug. When something breaks, you can ask: is the actor wrong, is the context wrong, or is the result wrong? That is much more useful than asking a generic question like, “Why is this broken?”

Most design mistakes are not logic errors. They are boundary errors.

That is why the best systems feel calm. Their parts know where they end.


Actionable insight: write code and SQL as if future readers must reconstruct your thinking

A useful design principle is to assume that the next person reading your work will not have your memory, only your structure. That person may be future you.

If you are writing Python, ask whether each class has a single, obvious job. If you are tempted to add one more method because it is convenient, pause and check whether you are violating the class’s identity. If the class now needs to validate, persist, notify, and render, split it before it gets socially too expensive to do so.

If you are writing SQL, ask whether each transformation can be named. CTEs are especially useful when a query has stages that a human would naturally describe in sequence. First we define the relevant records. Then we compute the measure. Then we rank or aggregate. The database can execute a complex plan, but the human still needs a narrative.

Window functions are especially revealing because they force you to think about context without destroying detail. That is a rare and valuable ability. Aggregation collapses data into fewer rows. Windows let you calculate across a set while keeping the original rows visible. In many real problems, that is exactly what you want: insight without erasure.

The broader lesson is that clarity is not a luxury feature. It is how systems survive growth. If boundaries are clear, change remains local. If boundaries are fuzzy, change spreads like a stain.


Key Takeaways

  1. Separate responsibility before you optimize anything else. A small, focused class or query stage is easier to test, change, and reuse than a clever but overloaded one.

  2. Name the layer you are in. Distinguish between raw data, grouped data, windowed context, and final output. The same applies to domain logic, persistence, and presentation.

  3. Use CTEs and window functions as boundary tools, not just convenience tools. They make intent visible by separating stages of reasoning.

  4. Apply the sentence test. If you cannot describe a class or query stage in one clear sentence, it may be doing too much.

  5. Think in actor, context, result. This simple framework helps you diagnose design problems before they become maintenance problems.


The real lesson: good systems do not just compute, they preserve meaning

The deepest connection between clean object oriented design and thoughtful SQL is not technical, it is epistemic. Both are ways of preserving what something means as it moves through transformations. A class with one job preserves the meaning of its responsibility. A CTE preserves the meaning of a step. A window function preserves row level identity while adding context.

That is why these tools matter beyond their syntax. They teach a discipline of thought: do not confuse detail with structure, do not confuse aggregation with explanation, and do not confuse convenience with clarity.

When you learn to design with boundaries, you stop asking only, “How do I make this work?” You start asking a better question: What level of truth should this piece of the system be responsible for? That question changes everything. It is the difference between code that merely runs and systems that remain intelligible as they grow.

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 🐣