Skip to content

Programming Fundamentals: Instructions, State and Taking Things Apart

A computer can't guess what you meant. Everything in a first programming course is a way of saying it exactly enough, and of working out why you didn't.

11 min readReadInteractive

Tap "sort by price" and a list of a thousand items rearranges before you've lifted your finger. Somewhere, a person wrote down steps so exact that a machine with no judgment, no memory of yesterday and no idea what a "price" is could do it perfectly, a thousand times over.

That is the whole trade. A programmer isn't someone who knows a language, they're someone who can state a task so precisely that nothing is left to guess. And since people are bad at being precise on the first try, the other half of the skill is finding out what you actually said, as opposed to what you meant.

Mechanical and aerospace engineers use the same habits to simulate a wing before it's built; computer scientists use them to build every other subject on the degree. Python and MATLAB are the common first languages for engineers, and this piece uses Python-style code. The ideas carry to every language.

The course in one box

Who takes it: first-year students of Computer Science (usually their very first course) and Mechanical & Aerospace Engineering (often in MATLAB or Python).

What it's for: telling a machine what to do exactly, and finding out when you didn't.

Comes after: nothing; it has no prerequisites.

Leads to: Data Structures (34/23), Computer Architecture (34/25) and Software Engineering (34/31) on the computing side; Differential Equations (34/03), Dynamics (34/12) and Control Systems (34/20) on the engineering side, all of which are solved on a computer in practice.

Honest line: you won't be able to ship an app after this. You will be able to read ten lines of code, predict what they do, and make a physics model in a loop.

A program is instructions plus state

Strip away the syntax and a program is two things: a list of instructions, and the state, the current values of all its named boxes. Each instruction reads the state and changes it a little. Running the program is just that, one step after another, and the answer is whatever state is left (or printed) at the end.

The graduate habit is to run the program in their head: keep a small table of variable values and update it line by line. Below is a tiny interpreter that does it visibly. Press Step. Then edit the program: change the 6 in the Collatz example, or write your own with =, +=, if/else, while, for … in range(…) and print.

Interactive · Step through a program

    

A real mini-interpreter, written for this page. It stops after 400 steps, in case you write a loop that never ends.

The first program adds 1 + 2 + 3 + 4 + 5 and prints 15. The Collatz one halves even numbers and triples-plus-one odd ones until it reaches 1: starting from 6 it takes 8 steps. Nobody knows if every starting number reaches 1, and yet the program is five lines long. That's what "simple instructions, deep behaviour" feels like.

Variables and types: what a number is made of

A variable is a name for a box in memory. A type says what kind of thing is in the box, and therefore what you may do with it: you can add two numbers, join two pieces of text, but adding "3" and 4 is an error or a surprise. Types exist because the machine stores everything as patterns of bits, and the pattern means nothing without the type.

The box has a size too. A signed 16-bit integer holds −32,768 to 32,767, and nothing more. Decimals are stored as floating point, binary fractions of limited precision, so 0.1 can't be stored exactly and 0.1 + 0.2 gives 0.30000000000000004.

On 4 June 1996 the first Ariane 5 rocket veered off course and broke up about 37 seconds after lift-off, a loss of more than US$370 million. The investigation traced it to the guidance software, reused from Ariane 4: a 64-bit floating-point value (the horizontal bias) was converted to a 16-bit signed integer, and on the faster rocket it didn't fit. The failure shut down both inertial units. A number too big for its box was enough.

Interactive · Does it fit?

Ariane's code reportedly raised an error on overflow; some systems wrap silently instead, which is shown here.

Conditionals and loops: choosing and repeating

With state and instructions, only two more ideas are needed to compute anything: choose (if something is true, do this, else that) and repeat (while something is true, or for each value in a range). Together they let ten lines do work that would take ten million lines written out.

The classic trap is in the edges. In Python's range(1, 10) the loop starts at 1 and stops before 10, so it runs 9 times, not 10. Off-by-one errors are the "fencepost" mistake: a fence of 10 sections needs 11 posts. Try the bounds yourself.

Interactive · Edit the loop
Start
Stop (never reached)
Step

    

With the defaults, range(1, 11) runs 10 times and adds up to 55. Make the step negative and the start bigger than the stop to count down. Set the start above the stop with a positive step and the body never runs at all, which is perfectly legal and a common source of silent bugs.

Functions and recursion: naming a piece, then calling it again

A function packages some instructions under a name, takes inputs, and gives back an output. That is decomposition, the central skill of engineering of any kind: break a big problem into pieces small enough to understand and test alone, give each a name, then build on the names. The tracer above could be one function; so could braking distance.

Recursion is a function that calls itself on a smaller version of its problem, until it reaches a case it can answer directly. The Fibonacci numbers (0, 1, 1, 2, 3, 5, 8, …, each the sum of the previous two) have a one-line recursive definition. Run it naively and the machine re-solves the same sub-problems again and again.

Interactive · Count the calls
Compute fib(n), n =

Naively, fib(5) makes 15 calls, fib(20) makes 21,891, and fib(30), whose answer is just 832,040, makes 2,692,537. Remembering answers brings fib(30) down to 59 calls. Same answer, a different algorithm, and the course after this one (Algorithms, 34/24) is the study of exactly that difference.

Data: lists and dictionaries

Most programs aren't about single numbers but collections. A list is an ordered row of values, found by position (the third item). A dictionary maps a key to a value (a word to its count, a student ID to a name) and finds an entry by key without scanning. Choosing the right container is the first lesson of Data Structures (34/23).

The difference is speed. To find a word in a list of n you may have to look at all n. A dictionary (usually built as a hash table) jumps almost straight to it, in roughly constant time on average, however large it is. Paste some text below and look a word up.

Interactive · Count the words

Debugging is the scientific method

Programs don't do what you meant; they do what you wrote. Finding the gap is science: look at the result, form a hypothesis ("the loop is skipping the last item"), make a prediction ("if I print the count here it will be one short"), run the experiment (print it), and revise. Guessing and changing things at random is the opposite, and it's how you add new bugs.

The strongest move is bisection. If the code is a chain of steps and the result is wrong, check the value halfway along. If it's right, the fault is in the second half; if wrong, the first. Each check halves the suspects. In 1947 operators of Harvard's Mark II computer taped a moth found stuck in a relay into the logbook with the note "First actual case of bug being found" — a joke that only works because engineers already called faults "bugs".

Interactive · Find the bug in as few probes as you can

Start with x = 5. Slide to a step and probe the value there against what you'd get by hand.

Look at x

Twelve suspect lines need at most four probes when you halve each time, because 2⁴ = 16 is at least 12. Checking from the top, one line at a time, can take twelve.

Modelling: a physics simulation in a dozen lines

Engineers model long before they build. Most physical laws give a rate of change (acceleration from forces, as in Physics I), and a computer can follow a rate forward in small steps: update the position using the velocity, update the velocity using the acceleration, repeat. That is the Euler method, and the loop is the whole simulation.

x, y = 0.0, 0.0
vx, vy = 21.2, 21.2        # 30 m/s at 45 degrees
dt = 0.1                   # time step in seconds
while y >= 0:
    x = x + vx * dt
    y = y + vy * dt
    vy = vy - 9.81 * dt    # gravity changes vy only
print(x)               # about 93.9 metres

Smaller time steps are more accurate and cost more. For the cannonball at 30 m/s and 45°, the exact range without air is 91.7 m. With a step of 0.5 s the simulation says 102.1 m, 0.1 s gives 93.9 m, and 0.001 s gives 91.8. Now add air drag, for which no tidy formula exists: the same loop still works, which is why engineers simulate.

Interactive · A cannonball in code
Launch speed
Launch angle
Time step

Drag is modelled as a quadratic term with a baseball-like coefficient (0.007 per metre); a model, not a measurement.

Readable code is a letter to a person

A program is run by a machine once and read by people many times: the next engineer, the reviewer, you in six months. The well-known line from the preface of Structure and Interpretation of Computer Programs is: "Programs must be written for people to read, and only incidentally for machines to execute." The machine will accept either version below. Only one of them can be checked by a human.

def f(a, b):
    return (a / 3.6) ** 2 / (2 * b * 9.81)

def braking_distance(speed_kmh, friction):
    speed = speed_kmh / 3.6          # convert to metres per second
    return speed ** 2 / (2 * friction * 9.81)

Both give 14.05 m for 50 km/h and a friction of 0.7, the braking distance of Physics I. Only the second tells you what it means, and so lets a reader spot a wrong constant. Good names, small functions and comments that say why are the cheapest debugging tools there are. People disagree on how much structure is enough: some teams favour heavy up-front design, others short cycles of build, test and revise; Software Engineering (34/31) covers both camps.

What a graduate just sees

  • A program is instructions plus state; to understand it, track the variables line by line.
  • Every value has a type and a size, and a number too large for its box is a real way to lose a rocket.
  • Loops run until their stop value, not through it: watch the edges.
  • Break a problem into named functions you can test one at a time.
  • A function that calls itself can be elegant and wildly wasteful; counting calls shows which.
  • The right container (list or dictionary) can be the difference between instant and never.
  • Debugging means hypotheses and experiments; halve the suspects each time.
  • Simulation is a rate of change plus a loop, and smaller steps cost more for less error.
  • Code is read far more often than it is written.

Where it shows up next

  • Data Structures (34/23) and Algorithms (34/24): the list-versus-dictionary and naive-versus-memo contrasts above, measured properly.
  • Computer Architecture (34/25): the fixed-size boxes and overflow are the hardware's registers; instructions plus state is literally how a processor works.
  • Software Engineering (34/31): decomposition, testing and readable code, scaled to teams and years.
  • Differential Equations (34/03), Dynamics (34/12), Control Systems (34/20): the Euler loop is the starting point of numerical solvers, vehicle simulators and the code that runs a controller.
  • Machine Learning (34/32): training is a loop that nudges numbers until the error falls.

If you want to go deeper

  • 📘 Standard textbook. John Guttag, Introduction to Computation and Programming Using Python (3rd ed., MIT Press, 2021), the text behind MIT's introductory course, written for engineers and scientists, with modelling and data.
  • 📘 Gentler, free. Allen Downey, Think Python (3rd ed., Green Tea Press / O'Reilly), free to read online at greenteapress.com. For the deeper "why", Structure and Interpretation of Computer Programs by Abelson and Sussman is free online from MIT Press.
  • 🌐 Free course. MIT OpenCourseWare 6.0001 Introduction to Computer Science and Programming in Python (Fall 2016), or Harvard's CS50x (starts in C, then Python and SQL) and CS50P (Python only), free on OpenCourseWare.
The takeaway

A program is instructions acting on state. Variables, types, choices, loops and functions are how you say it exactly; lists and dictionaries are how you hold data; debugging is hypothesis and experiment; and a simulation is a rate of change inside a loop. The graduate's real skill is reading code, predicting it, and taking a big problem apart.

Try this: take any small thing you do by hand, like splitting a bill or converting recipe units, and write it as exact steps for a friend to follow without any judgment. Then watch where they get stuck: those are your bugs.

Sources & Further Reading

Sources. Ariane 5 flight 501 (4 June 1996; 64-bit floating-point to 16-bit signed integer conversion overflow; about 37 seconds; more than US$370 million): Wikipedia, "Ariane flight V88", which summarises the Inquiry Board's report. The Mark II moth (9 September 1947, logbook note "First actual case of bug being found", now at the Smithsonian) from the Computer History Museum and JSTOR Daily, "The bug in the computer bug story", which notes the term was already in use. The SICP quotation from the preface of Abelson & Sussman, Structure and Interpretation of Computer Programs (MIT Press, 1985). CS50x language order from cs50.harvard.edu/x; Think Python 3rd ed. from greenteapress.com. Computed and checked in node, including by running the page's own interpreter on its example programs: 15; 8 Collatz steps; −2; 40,000 wraps to −25,536 in 16 bits; fib call counts 15, 177, 21,891 and 2,692,537 (and 2n−1 with a memo); projectile ranges 91.74 m exact and 102.1, 93.9, 91.8 m for steps of 0.5, 0.1 and 0.001 s; 14.05 m braking distance.