Types of Programming

Declarative vs Imperative

Describing what you want versus specifying how to achieve it step by step.

Overview

Imperative programming describes how to achieve a result: explicit step-by-step instructions that mutate state. Declarative programming describes what the result should be, leaving the how to the runtime or framework. SQL, HTML, CSS, and React's JSX are declarative; C, assembly, and most loop-based code are imperative. Most real programs mix both styles.

Origin

The distinction is as old as programming itself: FORTRAN (1957) was imperative; LISP (1958) introduced declarative list operations. SQL (1974, IBM) popularised declarative data querying. The web brought HTML (declarative layout) alongside JavaScript (imperative logic). React (2013) re-popularised declarative UI after years of jQuery-driven DOM mutation.

Examples

Imperative vs declarative array transformation

const orders = [
  { id: 1, status: 'shipped', total: 120 },
  { id: 2, status: 'pending', total: 80 },
  { id: 3, status: 'shipped', total: 200 },
];

// Imperative: tells the machine exactly what to do
const imperativeResult: number[] = [];
for (let i = 0; i < orders.length; i++) {
  if (orders[i].status === 'shipped') {
    imperativeResult.push(orders[i].total);
  }
}

// Declarative: expresses the desired shape of the result
const declarativeResult = orders
  .filter(o => o.status === 'shipped')
  .map(o => o.total);

console.log(declarativeResult); // [120, 200]

The declarative version is shorter, but the more important benefit is that filter and map communicate intent: we want shipped totals. The imperative version requires reading the loop body to infer that.

Declarative DOM with React vs imperative jQuery-style

// Imperative: manually manage DOM mutations
function imperativeToggle(buttonId: string, panelId: string) {
  const btn = document.getElementById(buttonId)!;
  const panel = document.getElementById(panelId)!;
  btn.addEventListener('click', () => {
    const isOpen = panel.style.display !== 'none';
    panel.style.display = isOpen ? 'none' : 'block';
    btn.textContent = isOpen ? 'Show' : 'Hide';
  });
}

// Declarative: describe what the UI should look like given state
import { useState } from 'react';

function Toggle() {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button onClick={() => setOpen(prev => !prev)}>
        {open ? 'Hide' : 'Show'}
      </button>
      {open && <div className="panel">Content</div>}
    </div>
  );
}

React reconciles the virtual DOM against the actual DOM. The developer declares the target state; React determines what mutations are needed. This inversion eliminates a class of bugs where state and DOM drift apart.

Use Cases

  • 01Database querying via SQL where the query planner chooses indexes and join order better than hand-written loops
  • 02Infrastructure as code (Terraform, Pulumi) where the desired cloud state is declared and the tool computes the diff and applies it
  • 03CSS where the browser layout engine handles box model calculations the developer does not want to perform manually
  • 04React and SwiftUI UIs where components describe target state and the framework minimises DOM/view tree mutations

When Not to Use

  • //Performance-critical algorithms where the runtime's chosen strategy is suboptimal; sometimes manual loop control is necessary
  • //Debugging complex declarative pipelines when the abstraction hides where an error originates; imperative code gives a clearer call stack
  • //Highly stateful workflows (game loops, hardware drivers) where step-by-step mutation is the natural model and declarative abstractions add friction

Technical Notes

  • SQL is declarative in the query language but imperative in stored procedures; the boundary between them is explicit and meaningful
  • React's reconciler (Fiber, rewritten in 2017) uses a diffing algorithm to translate declarative component trees into minimal imperative DOM operations, achieving O(n) diffing by constraining the key heuristic
  • Datalog (Clojure's Datomic query language) is a declarative query language derived from Prolog; Datomic uses it to query an immutable, append-only database
  • Pure declarative systems lose expressive power for certain problems; most mature declarative tools (GraphQL directives, Terraform provisioners) include escape hatches for imperative logic