Programming foundations Data, functions and state

Data, functions and state

The three ingredients of every program, and why most bugs live in the third.

  • 14 min read
  • 6 chapters
  • Lesson 1 of 4

What you will learn

  1. 01Every program is data plus transformations
  2. 02Data: model what you know
  3. 03Functions: input to output
  4. 04State: data that changes
  5. 05Putting the three together
  6. 06Where this starts to hurt
01

Every program is data plus transformations

Strip away frameworks and languages and a program is made of three things. Data is what the program knows. A function turns some data into other data. State is data that changes over time. Low-level design is the craft of arranging these three so that a program stays understandable as it grows.

Our running example for this whole part is a parking lot. A car enters, receives a ticket, and later leaves and pays. Even this small story uses all three ingredients.

returnsreads, writesInputREQplate, timeFunctionLLDfee(minutes)OutputCHECKamount dueStateDBcars in the lot

Swipe sideways to pan · drag a box to move it

100%
Data: the facts the program knows.
02

Data: model what you know

Start by writing down the facts the program must hold. In code that is a type: a named shape for a piece of data.

type Ticket = { id: string; plate: string; enteredAt: number };

Good data modeling is half the design. Keep the data honest: store facts, not conclusions that can be recomputed. We store when the car entered, not how long it has been parked, because the second changes every minute and the first never does.

Choose the structure by how you will use it. A list is easy to build but finding one ticket means checking each, which takes time proportional to the list. A map from ticket id to ticket finds one in roughly constant time. Pick the shape that matches the question you ask most.

03

Functions: input to output

A function takes inputs and returns an output. The best functions do one thing, have a name that says what it does, and depend only on what you pass in.

function fee(minutes: number): number {
  const hours = Math.ceil(minutes / 60);
  return hours * 20;
}

Given 90 minutes this always returns 40, today or next year, on any machine. You can test it with a single line and reason about it without reading anything else. Keep that quality as long as you can. It is the most valuable property a function has.

returnsreads, writesInputREQplate, timeFunctionLLDfee(minutes)OutputCHECKamount dueStateDBcars in the lot

Swipe sideways to pan · drag a box to move it

100%
A function turns input into output.
04

State: data that changes

A parking lot must remember which cars are inside right now. That memory changes every time a car enters or leaves, so it is state.

State is where most bugs live, because it gives a program a past. The same call can behave differently depending on what happened earlier, and two parts of the program can disagree about what is true. Three habits keep state manageable.

  • Keep as little of it as you can, and never store what you can recompute.
  • Keep each piece in one place, so there is one answer to "what is true now".
  • Make every change explicit, through a few named operations, not scattered edits.
returnsreads, writesInputREQplate, timeFunctionLLDfee(minutes)OutputCHECKamount dueStateDBcars in the lot

Swipe sideways to pan · drag a box to move it

100%
State is data that changes over time.
05

Putting the three together

Here is a tiny but complete lot using only what we have so far.

const lot = new Map<string, Ticket>();

function enter(plate: string, now: number): Ticket {
  const t = { id: crypto.randomUUID(), plate, enteredAt: now };
  lot.set(t.id, t);
  return t;
}

function leave(id: string, now: number): number {
  const t = lot.get(id);
  if (!t) throw new Error("unknown ticket");
  lot.delete(id);
  return fee((now - t.enteredAt) / 60000);
}

Notice the split. Ticket is data. fee is a pure function with no memory. lot is state, changed only by enter and leave. This is a good start, and it already works.

06

Where this starts to hurt

Nothing stops other code from touching lot directly. A bug elsewhere could delete a ticket, insert a bad one, or read it while it is half changed. As the program grows, every part that can reach the state becomes a suspect when something goes wrong.

returnsreads, writesInputREQplate, timeFunctionLLDfee(minutes)OutputCHECKamount dueStateDBcars in the lot

Swipe sideways to pan · drag a box to move it

100%
State that many parts can touch is where bugs start.

This pressure is why the next lessons exist. We will separate what changes from what does not, put walls around state, and expose only small, safe operations. Before moving on, ask of any piece of code you write: which of these three ingredients is it, and who else can see the state it touches?