Blog
About

© 2026 Uzair Tariq

← Back to blog

Why declarative queries age better than imperative code

July 29, 2026System DesignDatabasesSqlQuery LanguagesMapreduceDistributed Systems

I used to think SQL was mostly a convenient syntax for reading data. The bigger idea is that SQL describes what result I want while leaving the database free to decide how to produce it. That separation makes systems easier to change and optimize over time.

Asking for a result versus prescribing steps

An imperative program gives the computer a sequence of instructions. It loops through records, checks conditions, stores intermediate values, and decides what happens next.

A declarative query describes the shape of the result: which records match, how they should be grouped, sorted, or aggregated. SQL is declarative because the query does not specify the exact index, join algorithm, or execution order.

 

IMPERATIVE
  -> describe each step
  -> control the order

DECLARATIVE
  -> describe the result
  -> let the system choose the plan

 

The query optimizer owns the execution plan

When I write a declarative query, the database can choose an index, join method, and execution order. If the database gets a better index later, it can improve performance without forcing me to rewrite the query.

That flexibility also protects applications from storage changes. The database can move records while reclaiming disk space because a SQL query does not promise that records will be read in one physical order. Imperative code may accidentally depend on those implementation details.

 

QUERY OPTIMIZATION
Application
  -> states required result
Database optimizer
  -> picks indexes, joins, and order

 

CSS makes the same idea easier to see

CSS is declarative too. A style rule can say that the title inside the currently selected navigation item should have a background color. The browser keeps that rule true when the selected item changes.

With imperative DOM code, I would need to find elements, set styles, and remember to remove an old style after the selection changes. Browser vendors can also speed up CSS internally without breaking the rule I wrote.

 

CSS RULE
Rule matches
  -> style is applied
Rule stops matching
  -> browser removes the style

 

Declarative queries leave room for parallelism

Modern systems often get faster by using more CPU cores or more machines. Imperative code is harder to split because it describes an ordered sequence of work. A declarative query states the result, so the database has more freedom to run pieces in parallel when it is safe.

That does not make every declarative query fast. It gives the database a chance to choose a better plan without changing the query itself.

Where MapReduce fits

MapReduce processes large batches of data across many machines. It is neither fully declarative like SQL nor fully imperative like manually navigating every record. I write small functions, while the framework handles distribution and grouping.

For a report that counts observations by month, the job has three stages:

 

MAPREDUCE
Map
  -> read one record
  -> emit a key-value pair

Group
  -> gather matching keys

Reduce
  -> combine values for each key

 

For example, each observation can emit its month as the key and the observed count as the value. The framework groups all values for the same month, and the reduce function adds them.

Map and reduce functions should be pure. They use only their input, do not make extra database queries, and do not create side effects. Those limits let the framework run work anywhere, retry it after failure, and process it in a different order without changing the result.

 

PURE FUNCTIONS
  -> safe to distribute
  -> safe to retry
  -> safe to rerun after failure

 

Why higher-level query languages keep returning

MapReduce is powerful, but coordinating map and reduce functions is often more work than writing one declarative query. It also reveals less to an optimizer than SQL does.

That is why document databases added higher-level query tools such as aggregation pipelines. They still filter, group, and aggregate data, but in a declarative form. SQL itself can run across many machines, so MapReduce is not the only way to execute distributed queries.

My takeaway is that a system eventually benefits from asking developers to describe the result instead of every access step. A database that begins without SQL may still grow a query language that looks very similar to it.

What I will carry forward

When I reach for an imperative data-access API, I will ask whether the database or framework could safely own more of the execution detail. Declarative queries are not just shorter. They create room for better indexing, storage changes, retries, parallelism, and future optimization.

These are my personal learning notes from Designing Data-Intensive Applications by Martin Kleppmann.

 

Previous

← Why data models matter: my takeaways on SQL and documents

Next

When relationships become the data: my takeaways on graph models→