The Same Law Governs Good Code and Fast Queries: Put the Work Where It Belongs

Kai Nguyen

Hatched by Kai Nguyen

Jun 03, 2026

10 min read

88%

0

The Hidden Question Behind Every Slow System

What if the real difference between elegant software and sluggish software is not intelligence, but placement?

That sounds almost too simple. Yet many of the most expensive failures in code happen for one of two reasons: we ask the wrong thing of the wrong layer, or we make a layer do work it was never meant to do. In object oriented design, this shows up as classes that know too much, change too often, or absorb responsibilities they should never have owned. In SQL, it appears when a query forces the database to scan, compute, sort, or negate its way through data that could have been found directly.

The deeper connection is this: good systems are not merely correct, they are arranged so that each part can do its job with minimal interference. That is true whether the system is a cluster of classes or a database engine reading millions of rows.

A well designed system does not make every component smarter. It makes every component more specialized.

That is the real bridge between maintainable object oriented design and fast SQL. Both are disciplines of respecting boundaries. Both reward clarity about where responsibility lives. And both punish the same mistake: making a general purpose layer behave like a magician.


Why Responsibility Is the Real Performance Metric

Most developers first encounter design principles as a cleanliness issue. Keep classes small. Keep methods focused. Avoid tangled dependencies. That is all true, but it is not the deepest reason these principles matter.

A class with one responsibility is easier to change because it has a narrower reason to exist. A query that keeps indexed columns intact is faster because it lets the database use the structure already available. In both cases, the system becomes more efficient when each layer can act on the world in its own native language.

Think of a library. The librarian does not read every book to answer your question. The catalog exists so the librarian can locate the right shelf quickly. An index in a database plays the same role. If you ask for books by a property that matches the catalog, retrieval is fast. If you ask in a way that destroys the catalog value, such as searching by a transformed field or a leading wildcard, the system loses its shortcut and must inspect everything.

This is why SARGable queries matter. They are not a trick for premature optimization. They are a way of phrasing a question so the engine can answer it by using the shape of the data. Likewise, design principles like single responsibility are not about aesthetic minimalism. They are about phrasing your software so change, reuse, and reasoning remain cheap.

The common principle is simple:

A system works best when intent matches structure.

When intent and structure diverge, you get compensating code. That code may still work, but it works by brute force.


The Brute Force Tax

Brute force in software rarely looks dramatic. It usually looks reasonable at first glance.

A class starts with one duty, then gets validation, formatting, persistence, and notification because all of them seem related to the same business process. A query starts with a filter, then adds a function around the indexed column, then an ORDER BY, then a GROUP BY, then a LIMIT, and suddenly the database is doing far more work than the business question requires.

The tax is paid in three currencies: cognitive load, change cost, and machine work.

1. Cognitive load

When a class handles too much, its purpose becomes blurry. A reader cannot tell whether it is a domain object, a service, a repository, or a utility bucket. Similarly, when a query mixes filtering, transformation, sorting, and aggregation in one dense block, the reader cannot easily see what the business question is versus what is merely implementation detail.

2. Change cost

If you alter one behavior in a class with many responsibilities, you risk breaking the rest. If you change a SQL predicate that is wrapped in a function, you may accidentally disable an index or force a table scan. In both cases, a local change has global effects because the system lacks clear seams.

3. Machine work

An object model with tangled responsibilities makes the runtime do more indirect work, more branching, and more coordination. A query that cannot filter early or use indexes forces unnecessary scans, calculations, sorting, and grouping. The result is the same pattern in different clothing: do not ask the system to compute what structure already knows.

This is why optimization should not be seen as a separate discipline from design. It is design under pressure. The best optimizations are not heroic tricks. They are usually the removal of work that never needed to happen.


A Useful Mental Model: The Three Questions

When a system feels slow, unclear, or fragile, ask these three questions:

1. What is the native strength of each layer?

A class is good at encapsulating behavior and hiding representation. A database is good at set based operations, indexing, filtering, and sorting at scale. Problems begin when we force a layer to perform outside its strengths.

If application code filters millions of records one by one that the database could filter using indexes, the application is compensating for a poorly phrased query. If a class is responsible for persistence logic, formatting, and domain rules, it is compensating for a blurry architecture.

2. Where can the decision be made earliest?

Early decision making reduces work downstream. In SQL, this means filtering early with the WHERE clause, minimizing the result set, and avoiding unnecessary calculations before the engine has narrowed the search space. In design, it means pushing decisions into the most relevant class or function instead of leaving them to a central blob.

The earlier a system can eliminate irrelevant possibilities, the less it has to carry forward.

3. What information is being hidden by transformation?

This is the subtlest question. A WHERE clause on an indexed column is powerful because the engine can reason directly about the stored structure. But when you wrap the column in arithmetic, negation, or a function, you often hide the very shape the optimizer needed. The same happens in code when a class exposes only vague methods and hides the real domain rules behind generic utilities.

Transformation is useful when it clarifies meaning. It is harmful when it obscures the path to efficient action.

Optimization is often the art of preserving meaning while removing camouflage.


Why Clean Design and Fast Queries Are the Same Discipline

It is tempting to treat architecture as a human concern and query tuning as a machine concern. But the separation is artificial. Good architecture is often simply the recognition that machines also prefer clean boundaries.

Consider a service that loads orders and then computes totals in application code because the developer wants “more control.” That control is often illusory. The database can group and aggregate more efficiently because it already stores the rows in a structure designed for that work. If the service takes over aggregation, it has not become more flexible. It has just duplicated a responsibility at a worse layer.

Now consider a model object that validates its own invariants and exposes a narrow, expressive interface. It becomes easier to reason about because its boundaries are clear. This is similar to an index: both create a predictable path to the answer. One does it for humans, the other for the query planner, but the underlying principle is the same: make the right path obvious.

A helpful analogy is traffic design. A city does not reduce congestion by making every street wider everywhere. It reduces congestion by creating roads with clear purpose: main routes, local streets, exits, signals. Good class design and good query design do the same thing for software. They reduce accidental traffic by making each route serve one job.

This is why the most maintainable systems often feel boring in the best way. They do not constantly reinvent their own pathways. They let each layer do what it already knows how to do well.


When Flexibility Becomes Fragility

There is a seductive idea in software that flexibility is always good. Put logic in the application, because you may need to change the database later. Make classes generic, because future use cases are unpredictable. Add abstraction, because it seems safer than specificity.

But too much flexibility often becomes fragility.

A generalized class that tries to serve every role often ends up serving none of them well. It has many branches, many flags, and many special cases. A generalized query that wraps every column in functions and negations may look portable or expressive, but it strips the engine of information. Flexibility, in this sense, is achieved by discarding structure. That is not adaptability. It is ignorance.

Real flexibility comes from small, strong boundaries. A class that does one thing well can be composed with others. An index that is designed around common lookup paths can support many queries efficiently. Specificity at the right layer creates freedom at higher layers.

This is the paradox: the more faithfully a component stays within its purpose, the more reusable it becomes.

That is why broad abstractions often age poorly. They were built to cover imagined futures, but they pay for those futures immediately through complexity. By contrast, a well scoped class or a SARGable query earns optionality by being legible to both humans and engines.


A Practical Framework: Ask Where the Cost Is Paid

Before changing code, ask a deceptively simple question: where is the cost being paid?

If the cost is paid in readability, the issue may be a design smell. If it is paid in repeated computation, the issue may be a query shape or a missing index. If it is paid in both, you likely have a boundary problem.

Here is a practical way to think about it:

  1. If the logic belongs to business meaning, keep it near the domain. A class or function should own the rules that define what the thing is.

  2. If the logic belongs to set operations or data retrieval, let the database do it. Filtering, sorting, grouping, and indexing are not afterthoughts. They are part of the data layer’s expertise.

  3. If a transformation hides useful structure, move it later. Do not wrap an indexed column in a function if you want the index to help. Do not bury a class responsibility behind generic utilities if you want the design to stay readable.

  4. If a component is changing for too many reasons, split it. That is just as true for classes as it is for queries. A query that tries to do everything is as brittle as a class that tries to be everything.

This framework changes the way you read code. Instead of asking only, “Does this work?” you start asking, “Does this work in the place where it is cheapest and clearest to work?”

That question is often more important than micro performance tuning. It identifies the kind of work that should have been avoided altogether.


Key Takeaways

  • Respect the native strengths of each layer. Let classes model behavior and let databases perform indexed retrieval, filtering, sorting, and aggregation.
  • Filter early and specialize early. The sooner a system can eliminate irrelevant work, the less it has to carry downstream.
  • Avoid hiding structure behind transformation. Functions on indexed columns, broad abstractions, and generic utility layers can all obscure the path to efficient action.
  • Treat single responsibility as a performance principle, not just a design principle. A component with one job is easier to change and cheaper to execute.
  • Ask where the cost is paid. If the answer is “everywhere,” the design is probably forcing brute force where structure should have done the work.

The Real Lesson: Make the System Honest

The best software is not clever in the way people often mean clever. It does not constantly outsmart itself. It tells the truth about where things belong, what depends on what, and how work should flow.

That is why clean design and query optimization are not separate arts. They are both methods for making the system honest about its responsibilities. When that honesty is present, classes become easier to understand, queries become easier to optimize, and performance emerges as a consequence rather than a rescue operation.

So the next time code feels slow or tangled, do not ask only how to make the machine run faster. Ask a deeper question: what work is happening in the wrong place?

Answer that well, and you usually do not need a cleverer system. You need a truer one.

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 🐣