The Hidden Similarity Between Great SQL and Great Software Design
Hatched by Kai Nguyen
Apr 30, 2026
10 min read
6 views
66%
What if readability is not a luxury, but the real definition of power?
Most people learn SQL and object-oriented design as if they belong to different mental worlds. SQL is for asking questions of data. SOLID is for organizing code. One feels declarative and sharp, the other feels architectural and abstract. But the deeper connection is this: both are disciplines for controlling complexity by naming structure before doing work.
That sounds subtle, but it changes how you write both queries and code. In both cases, the hardest problem is not making the computer do something. It is making the system reveal its shape in a way your future self can understand, extend, and trust. The best SQL query is not the shortest one. The best class design is not the cleverest one. In both domains, the real win is making relationships explicit.
The quality of a system is often measured less by what it can do today than by how clearly it exposes its logic tomorrow.
That is why SQL concepts like CTEs, partitioning, and window functions feel so powerful once they click. And it is why SOLID principles matter far beyond object-oriented style. They are both ways of answering the same question: how do we turn tangled operations into legible structure?
The first clue: good systems separate structure from action
A common beginner mistake in SQL is to cram everything into one dense statement. It works, but it is hard to reason about. A CTE changes that. By using a WITH clause, you create a named intermediate result, something you can think about on its own before using it in the larger query.
That naming step is more profound than it looks. A CTE is not just a convenience. It is a declaration that the problem has layers. First, define the meaningful subset. Then, operate on it. This makes the logic readable, testable, and easier to modify.
The same thing happens in good software design. SOLID principles encourage you to split responsibilities so each class has a clear purpose. A class should not do everything. A function should not calculate, validate, format, and persist in one motion. When you separate responsibilities, you create units that can be understood independently.
This is the first bridge between SQL and SOLID:
CTEs are to queries what well-designed classes are to code.
Both create named structures that let you reason in stages instead of wrestling with everything at once. The moment you give a complex process a shape, it becomes editable.
Consider a messy business question: “Find the top customer in each region by revenue, but only after filtering to active accounts and excluding refunds.” A beginner might write that all in one nested pile. A clearer approach uses a CTE to isolate active transactions, another to calculate revenue, then a final selection step to rank customers within each region.
That layered approach is not just prettier. It reduces cognitive load. You are no longer debugging a monolith. You are checking one idea at a time.
The same is true in software. If your order-processing class also knows how to email, log, validate, and calculate discounts, every change becomes risky. A class with one reason to change is not a dogma. It is a way to keep the system from becoming one giant knot.
The deeper pattern: partitioning is the grammar of complexity
One of the most useful SQL ideas is partition by. It groups data into logical buckets so a function can operate within each group, not across the whole table. A ranking function like RANK() then works inside each partition, producing a local ordering rather than a global one.
That sounds technical, but it points to a deep design principle: many problems are not single problems. They are many smaller problems living side by side.
If you rank all employees across the company, you get one answer. If you rank them within each department, you get a more useful answer. The same data, different boundary, different meaning. That boundary is everything.
This is where SQL and SOLID become unexpectedly aligned. In software, one of the main causes of rigidity is treating distinct concerns as if they were one concern. A payment service should not be mixed with user interface rendering. A notification system should not know the business rules of invoice generation. Boundary confusion creates coupling.
Partitioning is SQL’s way of reminding us that context matters. SOLID, especially the Single Responsibility Principle, says the same thing in code: isolate the reasons a thing exists, or you will trap unrelated changes inside it.
Complexity becomes manageable when you decide what belongs together and what only appears to belong together.
This is a powerful mental model. A table of data is often not one thing. It is a collection of entities, states, and timelines. A class is often not one thing. It is a bundle of behaviors that may or may not deserve to live together. The skill is learning to see the hidden partitions.
Imagine a product analytics query. You might calculate each user’s lifetime value, then rank users within each cohort month. Without partition by, you would be comparing users across cohorts that should not be compared. The analysis would be technically correct and practically useless.
That same mistake happens in code when a method tries to serve multiple contexts. A generic “save” method that handles drafts, published records, archival rules, and audit trails may be elegant on paper, but in reality it erases meaningful distinctions. The system becomes less flexible because it has become less precise.
Why window functions feel like good architecture
Window functions are a special kind of SQL magic. They let you compute aggregates, ranks, and positional information without collapsing rows. In other words, you can add intelligence to data while preserving its granularity.
That is an underappreciated design lesson.
A normal aggregate often destroys detail. If you group sales by region, you no longer see each individual sale. A window function says: keep the individual rows, but enrich them with context. Show me the total, the rank, the running average, the position within the group.
This is what good architecture does. It does not flatten everything into one blurry abstraction. It preserves useful detail while adding structure.
A strong class design often works the same way. You do not want a giant all-purpose object that hides everything behind a single interface. You want objects that carry enough local behavior to remain meaningful, while still participating in a broader system. The goal is not to erase local identity. It is to coordinate it.
That is why some systems feel expressive and alive, while others feel like a spreadsheet taped onto a factory. The expressive ones maintain context. They let individual parts know where they stand in relation to the whole.
Think about RANK() in a leaderboard. Each player’s row remains visible, but the ranking gives it meaning. Or consider a sales dashboard where each rep’s monthly number is shown alongside the team total and their percentile. You get both the part and the pattern.
This is exactly what robust software tries to do. A class should not be so abstract that it becomes empty. It should provide local meaning and global fit. SOLID is often misread as “make everything tiny.” That is not the point. The point is to build components that are cohesive enough to be understood and flexible enough to evolve.
The real art is maintaining detail without drowning in it.
SOLID and SQL are both about future change, not present elegance
At first glance, SQL patterns like CTEs and window functions might seem purely about query convenience. SOLID might seem like a code organization philosophy. But both are really about change management.
A query that is easy to read today is easier to alter tomorrow. A class that has one responsibility is easier to extend without breaking unrelated behavior. In both cases, clarity now becomes optionality later.
This is why the most valuable systems often look slightly more verbose than the quickest hack. They are paying an upfront cost to avoid future chaos. That is not bureaucracy. It is leverage.
There is also a shared aesthetic here. Good SQL often reads like a story: define the set, transform the set, rank the set, return the answer. Good design often has the same flow: identify the responsibility, isolate the dependency, expose a narrow interface, compose the parts. The computer does the work, but the human should be able to follow the reasoning.
A useful way to think about both disciplines is this:
Structure is a form of kindness to your future self.
If a query takes 20 seconds to decode every time you revisit it, it is not simple. If a class only makes sense to the person who wrote it, it is not clean. The hidden cost of poor structure is not CPU time or line count. It is repeated mental reconstruction.
That is why the best engineers often obsess over naming. Naming a CTE, naming a class, naming a responsibility, naming a partition: these are not cosmetic moves. They are acts of conceptual compression. They say, “This chunk of logic is a thing.” Once something becomes a thing, it can be discussed, tested, replaced, and composed.
A practical framework: ask three questions before you write
The overlap between SQL clarity and SOLID design becomes actionable when you ask three questions before you build anything complicated.
1. What are the natural layers here?
If a problem has multiple conceptual steps, do not force them into one expression or one method. In SQL, this often means introducing a CTE. In code, it means splitting logic into focused units.
Example: calculate eligible customers, then compute revenue, then rank within a region. That is three layers, not one.
2. What should be grouped, and what should stay independent?
Use partition by thinking even outside SQL. Ask what context changes the meaning of the data or behavior. In code, this helps prevent classes from becoming accidental junk drawers.
Example: a subscription system may need separate logic for free users, trial users, and paid users. Treating them as one bucket hides important differences.
3. What should be visible after the transformation?
Window functions preserve row-level detail while adding context. Good architecture does the same. Before you collapse or abstract, ask what information you will wish you had later.
Example: a dashboard that shows only totals may be too blunt. A service that only exposes a final result may be too opaque. Keep the detail that makes change and debugging possible.
Key Takeaways
- Name intermediate structure early. In SQL, use CTEs to break large queries into meaningful steps. In code, use focused classes and functions to make responsibilities visible.
- Think in partitions, not just in tables or modules. Context changes meaning. Separate logic by the boundary that matters, whether it is region, cohort, user type, or business rule.
- Prefer preserving detail over collapsing it too soon. Window functions are powerful because they enrich rows without destroying them. Good design does the same.
- Optimize for future change, not just immediate correctness. Readability and separation are not luxuries, they are insurance against tomorrow’s complexity.
- Use naming as a design tool. A clear name often reveals whether you have found a real concept or merely assembled a temporary hack.
The real lesson: complexity is not defeated, it is negotiated
We often talk about simplification as if the best systems eliminate complexity. They do not. They negotiate it. They decide what to isolate, what to compose, what to preserve, and what to compute locally. That is what SQL teaches through CTEs, partitions, and window functions. That is what SOLID teaches through responsibility, dependency direction, and interface design.
The deeper insight is that great engineering is not about making the world small. It is about making it legible.
A query becomes powerful when it can answer a hard question without becoming unreadable. A class design becomes powerful when it can evolve without becoming brittle. In both cases, the art is the same: introduce just enough structure for the system to explain itself.
So the next time you write a query or design a module, do not ask only, “Will this work?” Ask a better question: Can I see the shape of the problem in the shape of the solution?
If the answer is yes, you are not just writing SQL or software. You are building understanding.
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 🐣