Design for the Work You Want, Not the Work You Hope the System Will Ignore
Hatched by Kai Nguyen
Jul 03, 2026
9 min read
2 views
86%
The hidden similarity between good code and fast queries
What do a beautifully designed class and a lightning fast SQL query have in common? At first glance, almost nothing. One belongs to the world of software architecture, the other to database performance. But both are secretly answering the same question: Where should the work happen?
That question sounds simple until you realize how many systems fail because they answer it badly. A class takes on too many responsibilities and becomes brittle. A query asks the database to do unnecessary computation and becomes slow. In both cases, the mistake is not just inefficiency. It is a failure to respect the natural boundaries of the system.
The deeper lesson is this: good design is not about writing less code or using fewer features. It is about placing each kind of work in the place where it can be done most cleanly, most predictably, and with the least collateral damage.
Why systems break when they become responsible for everything
The strongest design principles in object oriented programming all push toward one idea: each part should do one job well, depend on stable interfaces, and remain open to change without forcing change everywhere else. That is not just a code style preference. It is a way of preventing entanglement.
The same pattern appears in query design. A database performs best when the query gives it something it can reason about efficiently: a direct predicate, a usable index, a small result set, and minimal post processing. When you wrap an indexed column in a function, or lead a search with a wildcard, or force the engine to sort and group more than necessary, you are asking the database to solve a harder problem than needed. The result is similar to a bloated class: the system still works, but every future change becomes more expensive.
Think of it like hiring.
A highly specialized employee is easy to manage because their role is clear. You know what to ask of them, what not to ask of them, and where to escalate when a problem falls outside their scope. A brilliant but overburdened generalist, by contrast, becomes the bottleneck. They can do everything, but now every task depends on them. That is what happens when a class absorbs too many responsibilities or a query absorbs too much computation.
The best systems are not the ones that can do everything. They are the ones that make the right things easy and the wrong things awkward.
That is the real connection between object oriented design and query optimization. Both are about preserving the shape of the problem so the system can solve it cheaply.
The real enemy is not complexity, it is transformation
Most people think bad performance comes from large workloads. In practice, it often comes from unnecessary transformation.
A database can scan rows quickly when the filter is simple and index friendly. But if you force it to transform every row before filtering, it loses its shortcut. Similarly, a class can remain flexible when it exposes a small, coherent interface. But if every caller must understand its internal logic, you are no longer working with an abstraction. You are working with a leak.
This is the common structure behind both disciplines:
- Preserve the original shape of the data or responsibility.
- Move work to the place that already owns the relevant knowledge.
- Avoid wrapping core operations in unnecessary layers of interpretation.
Let us make that concrete.
Suppose you have a table of orders with an index on created_at, and you want all orders from the last 30 days. A sargable query expresses that directly, so the database can use the index. But if you write a condition like DATE(created_at) = ... or apply arithmetic to the column, you have hidden the indexable structure inside a function call. The database now has to do more work to rediscover what you already knew.
The software design equivalent is a class whose public methods all do their own validation, formatting, fetching, and business logic. The caller no longer knows which part of the behavior is essential and which part is incidental. The object has transformed a simple request into a mini workflow engine.
The pattern is the same: the moment you force a system to infer what you could have stated directly, you pay in speed, clarity, or both.
A mental model: keep operations sargable in design, not just in SQL
“Sargable” is a useful word because it names more than a database trick. It describes a broader design ideal: make important operations searchable, legible, and cheaply executable by the system that owns them.
In SQL, that means writing predicates the optimizer can use efficiently. In design, it means creating objects and modules that can be understood and changed without reading half the codebase. In both cases, the system should not need heroic effort to do ordinary work.
Here is a useful way to think about it:
1. State intent directly
If you want a subset, express the subset. If you want an order, express the order. If you want one responsibility, make it one responsibility. Indirection is not the same as abstraction. Good abstraction removes noise; bad indirection hides structure.
2. Keep the critical path visible
The fastest query is often the one that lets the engine see the obvious route. The cleanest class is often the one whose primary responsibility is obvious from the outside. If the core path is buried under helper methods, computed columns, nested conditionals, or hidden side effects, the system must do interpretive work before it can do useful work.
3. Let the right component own the work
Databases are built for filtering, joining, sorting, and indexing. Classes are built for encapsulating behavior, enforcing invariants, and limiting ripple effects. When you ask one layer to impersonate another, you often get a technically correct but strategically poor result.
This is why premature optimization and poor abstraction are relatives. Both emerge when developers forget that the system itself has strengths and constraints. They try to make every layer competent at every task, and the result is usually a mess.
The architecture of laziness, in the best sense
There is a kind of laziness that produces excellence. It is not carelessness. It is refusal to do redundant work.
A good SQL query is lazy in a precise sense: it asks for only what it needs, when it needs it, in a form that the database can process efficiently. A good design principle is similarly lazy: it refuses to duplicate logic, refuses to couple unrelated concerns, and refuses to force future changes through unnecessary surfaces.
This is where the deeper thesis emerges: great engineering is the art of creating systems that can do less and accomplish more.
That sounds paradoxical until you see that most wasted effort is not productive effort done badly. It is effort spent compensating for a poor interface. The query that sorts millions of rows before filtering is compensating for a poor predicate. The class that exposes internals and requires callers to orchestrate behavior is compensating for a poor abstraction.
The best systems remove the need for compensation.
Consider two teams building the same feature: a customer search page. Team A writes a query that applies functions to the search column, uses leading wildcards everywhere, and sorts all results before trimming them. The page works in development and becomes sluggish in production. Team B models search behavior around indexes, filters early, limits result size, and keeps display formatting separate from data retrieval. Their system is faster not because they were more clever, but because they made fewer layers do the wrong job.
Now map that back to object oriented design. Team A places search parsing, business rules, persistence, and presentation inside one class. Team B separates responsibilities so each part can change independently. Their system is more maintainable for the same reason the query is faster: less needless transformation at the boundaries.
A practical framework: the three questions every design should answer
Before writing a query or introducing a class, ask three questions.
1. What is the natural unit of work here?
For SQL, that might be rows, keys, partitions, or indexed ranges. For object oriented design, that might be a domain concept, a behavior, or an invariant. Do not split the unit of work without reason, and do not bundle several unrelated units together.
2. What information is already available to the system?
The optimizer can only use what it can see. A class can only maintain coherence if its interface exposes the right level of meaning. Hiding a useful pattern inside computation is often a self inflicted handicap.
3. Where will future change be cheapest?
This is the heart of both SOLID thinking and query tuning. A query that is easy to extend with new filters is more valuable than one that is clever today but fragile tomorrow. A class that isolates one reason to change is more valuable than one that saves a few lines right now but spreads edits across the application later.
When you answer those three questions honestly, many design decisions become obvious.
If the database can use an index, let it. If a class can own a responsibility cleanly, let it. If a caller needs to know too much, the abstraction is leaking. If a query needs to compute too much before filtering, the predicate is hiding structure.
Key Takeaways
- Design for directness. Whether you are writing a class or a query, state the intent in a form the system can use immediately.
- Avoid unnecessary transformation. Functions on indexed columns, leading wildcards, and overloaded classes all force extra work that could often be avoided.
- Keep responsibilities narrow. A single responsibility in code and a single filtering strategy in SQL both reduce ripple effects and make change cheaper.
- Filter early, conceptually and literally. In SQL, apply
WHEREas early as possible. In design, narrow scope before layering on behavior, orchestration, or presentation. - Ask where the work belongs. The database should do database work. A class should do class work. Crossing those boundaries is possible, but it should be deliberate, not accidental.
The deeper lesson: elegance is the absence of avoidable work
We often praise elegant code as if elegance were decoration. It is not. Elegance is a performance property. It is what remains when a system no longer wastes effort explaining itself to itself.
That is why clean class design and fast SQL feel similar when they are done well. Both produce a sense of inevitability. The pieces fit. The system does not fight you. You can see where the responsibility lives, what the optimizer can exploit, and what future changes will cost.
So the next time you are tempted to add one more layer, one more function call around a column, or one more responsibility to an existing class, pause and ask a sharper question: Am I making the system more powerful, or am I making it work harder to find what is already obvious?
That question is a better design principle than any checklist. Because in the end, the most maintainable code and the fastest queries are both doing the same thing: respecting the shape of reality instead of forcing reality to adapt to the code.
Sources
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 🐣