14.1 From scripts to programs: modules
- Approved by
- Panaversity ·
In everyday life. A small shop keeps everything in one notebook, until it is too thick to use. A larger shop splits it into labeled folders, and each folder says on its cover which others it refers to. One wrong entry in the folder many others refer to, such as the price list, reaches every order.
What you will learn. How to find your way around a project an agent split into files, and when to let it add code someone else wrote.
Why it matters. The files that every other file uses are where one change reaches everything.
Where do I start in a repository I did not write?
At the top of each file, with the lines that say what it uses from other files. These words name the parts.
| Idea | What it means | In brightline-ap |
|---|---|---|
| Module | One file, one job | src/money.ts turns cents into dollars |
| Export | A name other files may use | export function usd(...) |
| Import | A name this file uses from another | import { usd } from "./money.ts" |
| Package | A named group of modules | py/brightline_ap/, or zod from npm |
| Dependency | A package your code needs | zod in package.json, pydantic in pyproject.toml |
| Lock file | The exact version of every dependency, and of theirs | package-lock.json, py/uv.lock |
Read a repository by its imports. A file's import lines are the note on a folder's cover that says which other folders it refers to. Drawn as boxes and arrows, they form an import graph. Look for three things on it.
- Edges, where data comes in from outside. Mark these first.
- Foundations, the files that many others import, like the shop's price list. A change there reaches everywhere.
- A file that nothing imports. It is either an entry point, the file a command starts, or dead code, which nothing uses.
At work. The agent split Brightline's scripts into nine modules in src/. Ana does not start with the longest file. She draws the graph on paper and marks the edges first: files that read a CSV export, such as due-by-friday.ts, and functions that turn its rows into records, such as invoiceFromRow. Five files import csv.ts, and three import due-date.ts. Those are the foundations, so she reads them next and tests them hardest.
Here is the note on one folder's cover: two import lines, then a line that uses both names. Predict what this prints.
import { usd } from "../src/money.ts"; // from the money file, take usd
import { dueDate } from "../src/due-date.ts"; // from the due-date file, take dueDate
console.log(usd(1922953), dueDate("2026-10-07", "Net 30"));Both print:
$19,229.53 2026-11-06| Language | How to read where an import comes from |
|---|---|
| TypeScript | A path that starts with ./ or ../ is a file in this repository, and it ends in .ts, which Node requires. A name with no path, such as "zod", is a package from node_modules/. |
| Python | A local import is a dotted name from the package, such as brightline_ap.money. |
Should I let the agent add a package?
Only after three questions. A package is someone else's code, and it can do anything your account can do on this computer. Is the name spelled exactly right, is it the real, well-known package and not a made-up or look-alike name, and is it needed at all? To find out, ask the agent to show you each package's page on npmjs.com or pypi.org, and to say what this project uses it for. A real, well-known package has years of history and many downloads. Attackers publish packages one letter away from popular names, and agents sometimes suggest a package for a job three lines of code would do.
Why commit the lock file?
So a clean copy of the repository next month installs exactly what you tested today. In this book, npm ci and uv sync install from the lock files.
How do I set up to try this?
Once, for the whole chapter.
- Download brightline-practice-ch14.zip, and unzip it.
- Open the
brightline-practice-ch14folder in your agent, in the mode that asks before it acts, as in Chapter 13. - Its
AGENTS.mdsays TypeScript. If you chose Python, send: "Please set my language in AGENTS.md to Python." - Open
package.jsonandpyproject.tomlin a text editor. Answer the three questions for each package they name. Then send: "Please install this folder's packages from its lock files, withnpm cianduv sync. Before each command, tell me which packages it will install." Compare the names with your list before you approve.
Try it
Run. Send: "Please run the file in 14-1 and show me what it prints. Don't explain it yet."
Modify. Send: "Please also import TERMS_DAYS from the due-date module in the file, and print it on a new last line. Don't run it yet." TypeScript can import only a name marked export. Python can import any name that is not inside a function or a class. Open the due-date file in 14-1 in a text editor, find TERMS_DAYS, and write down what will happen. Then ask the agent to run it.
Investigate. Ask about anything you got wrong.
Make. Send: "Please make a module in 14-1 called fees, with one function that adds a $25.00 fee to an amount in cents, and a new file called mine that imports it and prints one answer. Don't run it yet." Find the import line, say what it will print, and then ask the agent to run it.
What you should see. TypeScript stops with a SyntaxError, Node's error for importing a name that is not exported. It prints nothing else, because Node loads every import before it runs any line. Python prints the usual line, then the terms and their days.
The operating rule: read the import graph before the functions. Mark where data comes in, read the foundations next, and approve a package only after you check its exact name and need.

Figure 14.1. A reading map at the end of the chapter, with more files than above. Red outlines mark its two main edges. After the edges, read the shared files at the bottom. Arrows point from a file to what it imports.
Next. 14.2 writes down which values those files may pass to each other.
Check yourself
Question 1 / 6
0 answered
An agent split a report into two files, and Node stops on the import with Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../cents'.// cents.ts
export function cents(dollars: number): number {
return Math.round(dollars * 100)
}
// report.ts
import { cents } from "./cents"
console.log(cents(912.5))What is the cause?
Sources
- Owner
- Panaversity