14.1 From scripts to programs: modules
- Approved by
- Panaversity ·
In everyday life. A small shop keeps everything in one notebook: prices, suppliers, staff phone numbers. It works until the notebook is too thick to find anything. A larger shop splits it into labeled folders, and each folder says on its cover which other folders it refers to. You can then read one folder without reading them all. But the folder many others refer to, such as the price list, is where one wrong entry reaches every order.
What you will learn. How to find your way around a project an agent split into files, by what each file uses from the others, 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. The import lines tell you which files depend on which. Drawn as boxes and arrows, they form a picture called an import graph. On it, mark where data comes in from outside, the program's edges: each line that reads a file or gets another program's answer, and each function that turns what came in into records. Then look for the foundations. A file that many others import is a foundation: a change there reaches everywhere. A file that nothing imports is either an entry point, the file a command starts, or dead code, which nothing uses.
At work. Before the agent reorganized the code, without changing what it does, Brightline's scripts each read their own CSV files and repeated the same few lines. After it, there are nine modules in src/. Ana does not start with the longest file. She lists every import line, draws the graph on paper, and first marks the edges: the files that read a CSV export, such as due-by-friday.ts, and the functions that turn its rows into records, such as invoiceFromRow. Five files import csv.ts, and three import due-date.ts. Those two are the foundations, so she reads them next and tests them hardest.
In TypeScript. Predict what this prints. It imports two names from two modules.
import { usd } from "../src/money.ts";
import { dueDate } from "../src/due-date.ts";
console.log(usd(1922953), dueDate("2026-10-07", "Net 30"));$19,229.53 2026-11-06The path starts with ./ or ../, so it 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/.
In Python. The same import, from the package.
from brightline_ap.due_date import due_date
from brightline_ap.money import usd
print(usd(1922953), due_date("2026-10-07", "Net 30"))$19,229.53 2026-11-06Where the languages differ.
| TypeScript | Python | |
|---|---|---|
| What is shared | Only names marked export | Every top-level name. A leading underscore means "private by habit". Python allows it, but strict Pyright reports it |
| A local import | A file path, with its .ts extension | A dotted name from the package, such as brightline_ap.money |
| Packages and lock file | npm, package.json, package-lock.json | uv, pyproject.toml, uv.lock |
| Where the code is found | Relative to the importing file | From the folders Python searches, which include the folder you run from. That is why the Python commands run from py/ |
Should I let the agent add a package?
Only after three questions. A package the agent installs is code written by someone else, and it can do anything your account can do on this computer. Check it as you would a new vendor: 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? You might ask: "Before you install that, tell me who publishes it, how widely it's used, and whether a few lines of our own code would do the same job." Agents sometimes suggest a package for a job three lines of code would do, and attackers publish packages whose names differ from popular ones by one letter.
Why commit the lock file?
So a clean copy of the repository next month installs exactly what you tested today. A lock file records the exact version of every package installed, including the packages your dependencies need. In this book, npm ci and uv sync install from the lock files. If a lock file is out of date, npm ci stops, and uv sync updates it unless you add --locked.
The operating rule: read a repository's import graph before its functions. Mark where data comes in, read the foundation modules next, and approve a new dependency only when you have checked its exact name and need.

Figure 14.1. A reading map at the end of the chapter, after the chapter adds the schemas, the loader and the API client, so its import counts are higher than the ones above. Red outlines mark its two main edges: load-invoices reads the files, and ap-client gets the API's answers. After the edges, read the shared files at the bottom. They are imported most, so a change there reaches the files above. Arrows point from a file to what it imports.
Practice it. Practice: modules, in your own tool, fifteen minutes, and a few more the first time, for the setup.
Check yourself
Question 1 / 9
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