Chapter 1 of 18

The machine

What a program is and what happens when one runs, from the first line of code to the terminal.

An agent is an AI that can act on a computer as well as talk: it reads files, runs programs, installs things, and reads back what they said. Most of that happens in a terminal, a plain window for controlling a computer by typing. The friendly sentences it writes to you are its own summary of all that. The commands are what really happened.

Say it tells you all the tests pass, a test being a small program that checks other code. The line it ran can hide a failure and still report success, and the only way to know is to read that line. If you can't, you are approving things blind. So this chapter starts at the very beginning, assuming you have never opened a terminal: what a program is, what happens when one runs, and how a machine says no.

By the end, a line like cd apps/web && bun test 2>&1 | tail -20 should read like a sentence, and you should be able to say what might be wrong with it. Nothing here needs memorising. Between the words are working panels, which this site calls benches. They really run, inside this page, and none of them can touch your computer, so press everything and break whatever you like.

Start with the thing an agent actually produces.

Source code

The text of a program, written by a person or an agent and saved in ordinary files. It is a precise set of instructions. The computer does what the text says, which is not always what its author meant.

Source code is what an agent hands you and what you answer for. It can read perfectly sensibly and still be wrong, so you judge it by what it does when it runs, not by how confident it sounds.

Say it to your agent

“For each file you changed, show me the lines that changed and tell me what they do when they run. Don't summarise what you meant them to do.”

Program

A set of instructions a computer can carry out. Your browser is a program, and so is the agent you talk to. A program sits in a file doing nothing until something starts it.

What an agent can really do is start programs: ones that run tests, ones that install things, ones that delete. Each does exactly what it does, whatever the agent intended, so a command deserves more of your attention than a sentence.

Say it to your agent

“Before you run that program, tell me in one sentence what it does and what it changes on my machine. If it deletes or overwrites anything, stop and ask me.”

Source code and programs both live in the same place.

Files and folders

Also called Directory

A file is a named chunk of data kept on a computer. A folder, also called a directory, is a named container for files and other folders, so everything on the machine forms one big tree.

A code project is only a folder of files, and nearly everything an agent does is read, change, create or delete them. It can touch dozens in a minute, so you need to know the tree well enough to notice when it strays outside the job.

Say it to your agent

“List the folders you plan to change before you start. Stay inside src/checkout/, and tell me first if you need to touch anything outside it.”

Code editor

Also called Text editor, Editor, IDE, VS Code

A program for opening and changing plain text files, made for code. It colours the text so the structure stands out, shows the project's folders down one side, and marks some mistakes as you type. VS Code is a common one.

You need somewhere to read what an agent wrote that isn't the agent's own account of it. Open the file it says it changed and you are looking at the thing itself, in the same folders it is working in.

Say it to your agent

“List every file you changed as a path I can open in my editor. Don't reformat anything you weren't asked to change, so that I can see the real edit.”

Those files sit still until something reads them. Two parts of the machine do the reading and the working.

CPU and memory

Also called RAM, Processor

The CPU is the part that carries out instructions, one tiny step at a time, very fast. Memory, or RAM, is the quick short-term space where a running program keeps its instructions and the values it is working on.

Agents test on small examples, so they often write code that loads a whole file into memory because that worked on the sample. At real size it runs out and gets killed. You will notice it as a loud fan, a crash, or a process that vanished.

Say it to your agent

“This reads the whole log file into memory, and say our logs reach 4 GB. Rewrite it to read one line at a time, then run it under /usr/bin/time -l and show me the peak memory.”

Disk

Also called Storage, SSD

Long-term storage, where files stay when the power goes off. It is much slower than memory. A program that only keeps data in memory loses it when it stops, crashes or the machine restarts.

An agent reaches for whatever makes the demo work, and that can be a list in memory. It passes every test. Then your first restart wipes all your users. So always ask where each piece of data lives.

Say it to your agent

“You stored sessions in a module-level Map, so they vanish on every restart. Store them in the database, then prove it: log in, restart the server, and show me that the session still works.”

A program on disk does nothing. Starting it is what makes it matter.

Process

Also called PID

One running copy of a program, with its own private memory and an ID number called a PID. Start a program twice and you have two processes that know nothing about each other. Each runs until it finishes or something stops it.

Agents start servers and leave them running, then start them again. The leftovers keep hold of resources and go on serving old code, so you can lose an hour debugging a change that isn't even the thing running.

Say it to your agent

“Before you start the dev server, run lsof -i :3366 to see if one is already running. If it is, tell me its PID and don't start another. Stop anything you started when you finish.”

Now the place where you start and stop all of this, and where your agent spends its life.

Terminal

Also called Command line, Console, CLI

A window where you control a computer by typing instead of clicking, and it answers in text. It looks old-fashioned, but nearly every tool a programmer uses can be driven from it.

Text is what an agent reads and writes, so the terminal is its natural home: commands go in, results come out. Anything you can do here, it can do. Reading its session is how you find out what it really did.

Say it to your agent

“Run these in the terminal one at a time: pwd, ls, git status. Paste exactly what each one printed, with nothing summarised.”

Shell

Also called bash, zsh

The program inside the terminal that reads your line, works out what you mean, starts the right programs and shows you the result. Common ones are bash and zsh. It can also join programs together, and that is most of its power.

An agent's command goes to a shell first, with all its rules, and a stray space or a * means something to it. Those rules are why one wrong character can delete the wrong thing, and why a short line can do far more than it appears to.

Say it to your agent

“This has to work on my Mac and in CI. Start the script with #!/usr/bin/env bash, write it for bash, and run it once with bash explicitly before you tell me it works.”

Command

Also called Flag, Argument, Option

One line you give the shell: the name of a program, then flags that change how it behaves, then arguments saying what to work on. In rm -r build, rm is the program, -r is a flag and build is the argument. Spaces separate the pieces.

When an agent asks permission to run something, the command is what you are approving. Read it left to right: which program, which flags, which targets. Slow down for -f or --force, and for a * among the targets.

Say it to your agent

“You want to run rm -rf dist build node_modules. Before I approve it, tell me what each of those folders holds, whether it can be rebuilt, and whether anything outside this project is affected.”

Most arguments are places. A command needs to know where.

Path

The written address of a file or folder. An absolute path starts from the top, as in /home/you/notes.txt, and means the same everywhere. A relative path, as in src/cart.js, starts from where you are. .. means the folder above and ~ means your home folder.

A relative path is right from one place only, and agents get lost in big projects. No such file or directory often means a correct path from the wrong folder. And an absolute path that works on your machine, an agent's favourite shortcut, works nowhere else.

Say it to your agent

“That path only works on my machine. Replace /Users/me/shop/data with a path relative to the project root, then run it from a different folder to prove it still works.”

Working directory

Also called Current directory, cwd, pwd, cd

The folder a shell is standing in right now. Relative paths start from here. pwd prints it and cd changes it. Every process has its own, so a cd inside a script doesn't move the shell you started it from.

A lot of agent trouble is the right command in the wrong folder: things installed at the wrong level of a project, or the wrong folder deleted. Agent tools also differ on whether a cd sticks between commands, so have it print where it is.

Say it to your agent

“Run this from apps/web, not the project root. Write it as cd apps/web && pwd && bun test, and paste all of the output.”

A process also inherits settings it never asked for.

Environment variable

Also called Env var, export

A named setting a process carries with it, such as TZ=Europe/London for its time zone. A program can read it without it being in the code, and passes a copy to any program it starts. A child can't change its parent's copy.

This is how the same code uses a practice database on your laptop and the real one in production, the live copy that real users rely on. It is also where secrets such as passwords belong. And it is a trap for agents: "I set it" may mean it was set in a shell that has already ended.

Say it to your agent

“Read the key from process.env.PAYMENT_API_KEY and fail at startup with a clear message if it is missing. Add the name to .env.example with no value, and never print it in a log.”

The PATH variable

Also called $PATH

An environment variable holding a list of folders. When you type a program's name, the shell looks through those folders in order and runs the first match. If none has it, you get command not found, even when the program is on the machine.

Command not found is what an agent often meets straight after installing something, and the usual cause is a folder missing from this list. It also explains a command running the wrong version: another copy sits earlier in the list.

Say it to your agent

“You got command not found for tsc. Don't install it globally. Run which tsc and echo $PATH, then use bunx tsc or a script in package.json, so that it runs this project's version.”

Programs say a lot in words. They also hand back one number, and that number is what machines trust.

Exit code

Also called Exit status, Return code, $?

The number a program hands back when it ends. 0 means it went fine, and anything else means it didn't. You rarely see it, but the shell keeps the latest one in $?. Automatic checks in software rest on this.

An agent, or any automatic check, decides whether to carry on from this number, not from the words on screen. A program can print success and exit 1, or print nothing and exit 0. When you want proof that something passed, ask for the code.

Say it to your agent

“Run bun test, then echo $? on its own line, with nothing piped. Show me the number. Don't tell me the tests passed unless it is 0.”

The number says whether it worked. The words travel separately, and they travel on more than one channel.

stdin, stdout and stderr

Also called Standard streams, Standard output, Standard error

Three channels every process starts with. Stdin is what comes in. Stdout is its results. Stderr is for errors and remarks. On screen, stdout and stderr look alike, but they are separate and can each be sent somewhere different.

An agent reads these the way you read a screen. The split lets a program pass clean results onward while its complaints still reach someone. Agent hooks, scripts that run at set moments in an agent's work (chapter 6), speak this too: they get their data on stdin and answer on stdout or stderr.

Say it to your agent

“Send only the JSON result to stdout, and every progress and error message to stderr, so that mytool | jq . keeps working when something goes wrong.”

Redirection

Also called 2>&1, /dev/null

Sending a stream somewhere other than the screen. > writes stdout to a file, replacing what was there, and >> adds to the end. 2> does the same for stderr. 2>&1 sends stderr wherever stdout is going. /dev/null throws away whatever reaches it.

Agent commands are full of this. 2>&1 is there so errors get captured too. > /dev/null 2>&1 means nobody will see anything, including failures. Read the end of a long command first, because that is where output gets hidden.

Say it to your agent

“Don't send that to /dev/null. Run it with > build.log 2>&1, then show me the exit code and the last 30 lines with tail -30 build.log.”

Pipe

Also called Pipeline, pipefail

The | character. It feeds one program's stdout into the next program's stdin, so small tools combine: grep ERROR app.log | sort | uniq -c finds lines, sorts them and counts repeats. By default, the whole line reports only the last program's exit code.

It is how an agent reads without drowning: tail keeps only the last lines, so a long test run costs it far less to read. But bun test | tail -20 reports how tail went, so failing tests look fine unless a setting called pipefail is on. An agent can fool itself this way, and so can you.

Say it to your agent

“Run this as set -o pipefail; bun test 2>&1 | tail -30. I want the last 30 lines and the real exit code of the tests, not of tail.”

One more thing can stop a command before it even starts.

File permissions

Also called rwx, chmod, sudo, root

Rules saying who may read, write and run each file. ls -l shows them as -rwxr-xr--: three letters for the owner, three for a group, three for everyone else. chmod changes them. root, the administrator account, ignores most of them.

A script an agent writes can't run until it is marked executable, so Permission denied usually calls for chmod +x, not sudo. Be wary when an agent reaches for sudo or chmod 777. They don't fix the problem. They remove the protection that reported it.

Say it to your agent

“Permission denied on scripts/deploy.sh means it isn't executable. Run chmod +x scripts/deploy.sh and commit that. Don't use sudo or chmod 777. If you think you need to, stop and ask me.”

So far we have run programs. Now, how text becomes one.

Programming language

A strict, made-up notation for writing instructions. Python, JavaScript, TypeScript, Go and Rust are all programming languages. Each has exact rules, so a missing bracket is not a typo to read past. It is a program that won't run.

An agent can write any of them, so you choose by how cheaply its mistakes get caught. A language with strict checks gives an agent an automatic no. If you don't say which language, the agent will pick, and you will own that choice.

Say it to your agent

“Write this in TypeScript with strict mode on, not JavaScript. Match the language and versions already used in this repo, and tell me before adding a new language to it.”

Compiler

Also called Compile, tsc

A program that reads all your source code and translates it into a form a machine can run. If anything is broken, it refuses and produces nothing. Go, Rust and C are compiled. TypeScript's compiler, tsc, checks each value is the right kind and writes JavaScript.

A compiler is a free no. It arrives in seconds, before anything has run, in a form an agent can read and fix, so a compile step is one of the cheapest checks you can put in an agent's way.

Say it to your agent

“Run bun run typecheck. Fix every error by changing the code, not by adding any or @ts-ignore. Paste the last lines of output and the exit code.”

Interpreter and runtime

Also called Runtime, Node.js

An interpreter runs source code directly, working through it as it goes, so many problems only appear when reached. A runtime is what a program needs around it while running. Python, Node.js and Bun are examples. The line with compilers is blurry.

With no compile step, code that never ran is code nothing has checked. An agent can show you a working demo while a misspelt name waits on a path through the code it never took. So running the script once proves that one path. Tests have to go down the others.

Say it to your agent

“This is Python, so a bad name in that error branch only fails when the branch runs. Add a test that forces the payment to fail and goes through that branch, and show me it passing.”

Nobody writes everything. Most of a modern program is other people's code, and one tool fetches it.

Package manager

Also called npm, Registry

A program that downloads code other people have published, from a public store called a registry, and keeps track of what you use. npm, pnpm and Bun do this for JavaScript, and pip and uv for Python. You run it in the terminal like anything else.

An install puts strangers' code on your machine, and some package managers let it run scripts as it installs. An agent types install as easily as ls, and it can name a package that doesn't exist, which an attacker could then register.

Say it to your agent

“Before you add a package, show me its registry page, its last publish date and why we need it. Say whether we could write it ourselves in under 20 lines. Install with bun add, with no --force and no sudo.”

Dependency

Also called Transitive dependency, node_modules, Package

Code your project relies on but didn't write. The ones you name, in a file called package.json for JavaScript, are direct. They have dependencies of their own, and those have theirs, so these transitive ones are usually most of what you install, into a folder called node_modules.

Each one is code you now run without having read it, from people you don't know. Agents add them readily, because that solves today's problem. The cost arrives later, as updates, size and exposure. So ask for a reason each time.

Say it to your agent

“Don't add a dependency for this yet. Tell me what it would do for us and how many packages it brings, using bun pm ls --all before and after. If the job is under 20 lines, write it here with a test.”

package.json says what you would accept. Something else has to say what you actually got.

Lockfile

Also called package-lock.json, bun.lock

A file the package manager writes, recording the exact version of every dependency it installed and a fingerprint of its contents. It means everyone, and every automated run, installs the same code. Don't write it by hand, but do keep it in the project.

Without one, an unchanged project can install different code next week and break for no visible reason. Agents change it in passing, so look at what changed: a one-package request shouldn't rewrite most of the file. And make automated runs refuse a mismatch.

Say it to your agent

“Only bun.lock should change for this, and only for that one package. Show me the lockfile diff, commit it, and make CI run bun install --frozen-lockfile so a mismatch fails the build.”

Once you can read one command, you can read a file full of them.

Script

Also called Shell script, set -e, bun run

A text file of commands that run in order, so what you would type by hand happens the same way every time. Projects also give short ones names in package.json, which is what bun run check means. A shell script has to be told to stop at a failure.

Scripts are how a fix outlives a working session: an agent runs the same check you did, or the same deploy (putting new code live for users), without improvising. But a plain shell script carries on after a failure and reports only its last line, so without set -e it can't really say no.

Say it to your agent

“Put those steps in scripts/release.sh. Start it with #!/usr/bin/env bash and set -euo pipefail, and make it executable. Then make one step fail on purpose, so we can both see it stop.”

Underneath every command here sits one more program, and all the others have to ask it for things.

Operating system

Also called OS, macOS, Linux, Kernel

The program that runs the machine: it shares out the CPU and memory, keeps files on the disk, and starts and stops processes. macOS, Linux and Windows are operating systems. Programs ask it for everything, so a program built for one often won't run on another.

Your agent may run on a Mac while the code ships to Linux, and the two differ in small ways that pass on your laptop and then fail on a server. Agents write commands for one system out of habit, so tell yours what the target is.

Say it to your agent

“CI runs on Ubuntu and I am on a Mac. Don't use sed -i or anything else that differs between them: do this edit in a small Node script, and check that every import matches its file name's exact capitals.”

At the very bottom, there is only one kind of thing.

Binary and bytes

Also called Bit, Byte, Executable

Computers store everything as bits, each one 0 or 1. Eight bits make a byte, which can hold 256 different values. Text, pictures and programs are all runs of bytes, and only the reader decides what they mean.

Agents and people both read text far more easily than anything else, and neither of you can readily check a compiled program. So you review and keep the source, and build the rest from it.

Say it to your agent

“Don't commit the built bundle or any other binary. Add dist/ to .gitignore, and commit the source and the command that builds it, so the result can be reproduced and reviewed.”

Text encoding

Also called UTF-8, Unicode, Mojibake

The rule for turning characters into bytes and back. UTF-8 is the usual one today: English letters take one byte, é takes two and 日 takes three. Read with the wrong rule, the same bytes come out as garbage, such as café for café.

Agents tend to test with plain English, so these bugs wait for your first customer called José. Bytes and characters also differ, so a limit of 100 bytes can cut a character in half. Ask for a test with accents in it.

Say it to your agent

“Add a test that stores and reads back the name José Ñandú 日本 through the whole path: form, API, database and page. Set the database and the HTTP charset to UTF-8, and show me that it comes back identical.”

That is enough to read what an agent does on your machine. So put one there and watch it work.

Your first agent

Also called Installing an agent, Getting started

A coding agent is a program you install and then start in a terminal, from inside a project's folder. It reads and changes what is there and runs commands, usually asking you first. A project is only a folder, so a new empty one will do.

From here on, the course assumes you have one, because its drills, the exercises at the end of each chapter, happen on your machine and not on this page. Start it in a folder holding nothing you would miss, and read each command before you approve it.

Say it to your agent

“This is an empty practice folder. Build a one-page to-do list I can open in my browser, with no packages. Before each command, tell me in one sentence what it does. When you finish, list every file you made and what it is for.”

Do it for real

Reading gets you the words. This gets you the experience behind them, and something to show.

Open a terminal and make it say no

You only need to do this once for the terminal to stop being a strange place. The hour also gives you the habit that matters most: looking at the exit code.

  1. Open a terminal. Run pwd, then ls -a. Find something you recognise.
  2. Make a place to practise with mkdir ~/harness-drill && cd ~/harness-drill. Check with pwd.
  3. Run echo hello > a.txt, then cat a.txt, then echo $?. You should see hello, then 0.
  4. Run cat nope.txt, then echo $?. Now run cat nope.txt 2> err.txt and see that the screen stays quiet. Read err.txt.
  5. Install a coding agent, as described under Your first agent, and start it in this folder. Ask it to explain one line you did not understand, then check its answer by running the line.

Done when: A saved copy of your terminal session showing a 0, a non-zero code, and an error you sent to a file.

Catch a pipe hiding a failure

This is the most useful trick in the chapter, and the result is a rule for your own project that came from something you saw with your own eyes.

  1. In a project folder (your practice folder will do), ask your agent to write fail.sh, which prints a line and then exits 1, and to make it executable.
  2. Run ./fail.sh | tail -1; echo $?. You should see 0.
  3. Run set -o pipefail; ./fail.sh | tail -1; echo $?. You should see 1.
  4. Find every script in the project that pipes a check through another program, and fix any that could hide a failure. A new project may have none. If so, come back to this step when you have a real one.
  5. Add one line saying how checks must be run, and why, to a file called AGENTS.md. Agents read that file when they start, and chapter 6 covers it properly.

Done when: The two outputs saved in a file, and that line in AGENTS.md. Once you have reached chapter 3, make this your first commit.

Supervise one install

Installing is the riskiest routine thing an agent does. Doing it once with your eyes open shows you how much arrives with one small request.

  1. Pick a small need in a project, such as formatting dates. Ask your agent to propose a package for it and to justify it.
  2. Before installing, save the output of bun pm ls --all or npm ls --all to a file, and do it again afterwards.
  3. Compare the two files and look at what changed in the lockfile. Count how many packages arrived, and check the new one really is the package you meant, on its registry page.
  4. Decide whether to keep it or write the code yourselves. If you already run automatic checks on a server (chapter 9), make them install from the lockfile with bun install --frozen-lockfile or npm ci.

Done when: A note saying how many packages it added, how you checked the new one, and why that was worth it. Once you have reached chapter 3, make it a commit message.

The build, stage 1 of 18

First words with an agent

The agent installed, a folder called kitty, and one small program that splits a bill in whole pence. You can say what its exit code means.

About 30 to 60 minutes, on the same app as every other stage. We ran it first and show what went wrong.

Open stage 1 of the build

Gate 1: The machine

0 of 6 right

  1. 1Your agent runs bun test | tail -20, sees exit code 0 and reports that all tests pass. Scrolling up, you spot a line saying FAIL. What do you conclude?

  2. 2You have a folder called my old files. Your agent asks to run rm -r my old files. What will that do?

  3. 3In Claude Code, your agent runs export DATABASE_URL=postgres://localhost/test. Its next command, bun run migrate, fails with DATABASE_URL is not set. What happened?

  4. 4Your agent wrote deploy.sh. Running ./deploy.sh says Permission denied with exit code 126, and the agent proposes sudo chmod -R 777 . to fix it. What do you say?

  5. 5A project passed its checks yesterday. Nobody has changed a line, and today a fresh install fails its tests. There is no lockfile in the project. What is most likely?

  6. 6Your agent ran bun add -d typescript, and then tsc --version says command not found with exit code 127. What is the best next step?

Get every answer right to install it, and to put 4 files in your repo. Wrong answers cost nothing.

Next chapterThe network

The workshop

Name a word, or describe what you mean in everyday language.