CONCEPT 10 · EFFECTS & IO

Description vs Execution

"Write a recipe, then cook it — not while writing."

ioeffectsstreaming
Plain English

Separate "what to do" from "when and how to do it." Build a description of a computation as a pure value — a recipe, a query, a plan. Then execute it at the very edge of your system. The description is pure, testable, and inspectable. The execution happens once at the end, in one controlled place.

Analogy

A SQL query. Writing SELECT * FROM users WHERE age > 30 doesn't fetch any data — it builds a description of what you want. The database executes it when you call .execute(). You can inspect the query, transform it, log it, and test it without touching the database. Execution is separate from description.

You already know this
React JSX: <Button onClick={...}>Click me</Button> is a description of UI, not the DOM itself. React executes it.SQL query builders (Knex, ActiveRecord): build a query object, execute with .fetch() or .run()GraphQL: the query string is a description; the resolver runs itWebpack/Vite config: you describe build steps; the build tool executes themDocker Compose / Kubernetes YAML: descriptions of infra; the runtime executes them
Code Example
JavaScript
// EXECUTION-FIRST (bad) — side effects happen immediately
async function loadDashboard(userId) {
  const user    = await fetch('/api/users/' + userId);   // immediate I/O
  const posts   = await fetch('/api/posts?user=' + userId);
  const metrics = await fetch('/api/metrics/' + userId);
  return { user, posts, metrics };
}
// Untestable without mocking fetch. Side effects are everywhere.

// DESCRIPTION-FIRST (good) — build a plan, execute separately
function dashboardPlan(userId) {
  return {
    user:    { url: '/api/users/' + userId,           method: 'GET' },
    posts:   { url: '/api/posts?user=' + userId,       method: 'GET' },
    metrics: { url: '/api/metrics/' + userId,          method: 'GET' },
  };
}

// The executor — lives at the edge of the system
async function executePlan(plan) {
  const entries = Object.entries(plan);
  const results = await Promise.all(
    entries.map(([key, req]) => fetch(req.url).then(r => r.json()))
  );
  return Object.fromEntries(entries.map(([k], i) => [k, results[i]]));
}

// TESTABLE: test the description without any I/O
const plan = dashboardPlan(42);
assert(plan.user.url === '/api/users/42');    // pure, no fetch!
assert(plan.metrics.method === 'GET');

// EXECUTE once at the edge:
const data = await executePlan(plan);
Apply when
Testing IO-heavy logic: build and test the description (pure, no I/O needed); execute separately in production
Building query/request objects: SQL builders, HTTP request builders — describe first, execute when ready
Streaming pipelines: describe the full transformation; execute lazily as data arrives
Configuration-driven systems: represent behavior as data (a description), interpret/execute at runtime
When you want to intercept, transform, or log operations: easier when they're values you can inspect
Check Your Understanding
React components return JSX like <div className="card">...</div>. They do not touch the DOM directly. Which FP concept does this implement?