Code used to have one reader worth worrying about: the next human. Everything we know about writing it well, naming, structure, comments, small functions, came from asking what that person would need six months from now.
Now there is a second reader. It reads the file in one pass, has no memory of yesterday’s standup, and will act on what it sees in the next second. I work with agents that write code every day, on my own projects and at work, and they have changed how I think about readability more than any style guide ever did.
The surprising part is that they changed it in the same direction.
The second reader has no context
A colleague reading your function knows the product. They remember why the weird flag exists. They can walk over and ask. A model reading your function has the file, maybe the folder, and whatever your names and types are willing to tell it.
So the model punishes exactly the things good engineers already frowned on. Abbreviations it has to guess. Comments that explain what the code should have said. Types loose enough that anything is possible and nothing is certain. A human tolerates these and pays slowly, in confusion. A model pays immediately, in a wrong edit.
// get usr + chk active
async function getU(id: any) {
const r = await db.q(id); // TODO: types
if (!r || r.st !== 1) return null;
return r as any;
} async function findActiveCustomer(
customerId: CustomerId,
): Promise<Customer | null> {
const customerRow = await database.selectCustomer(customerId);
if (!customerRow) return null;
const customer = parseCustomer(customerRow);
return customer.status === "active" ? customer : null;
} the author, this afternoona colleague next year, a model right now
Names carry the meaning
I write identifiers long, fully spelled out and named after their domain meaning. Not usr, not u, not data. customer, customerRow, findActiveCustomer. It looks verbose until you notice it removed every comment in the file.
For a human, a good name saves a lookup. For a model, a good name is the lookup. It cannot open the ticket that explains what st !== 1 meant. It can read status === "active" and get it right the first time.
Comments are what the name failed to say
I write zero comments, and the second reader made the reason sharper. A comment is a claim about the code that nothing verifies. The name is checked every time the code is called. The type is checked every time it compiles. The comment is checked never, drifts quietly, and a model will believe it over the code with total confidence.
If a comment feels necessary, the code needs a better name or a better shape. That was always true. Now there is a reader that will take the comment literally.
Types are the documentation the compiler reads
No any. No casts. No non-null assertions. Parse external input at the boundary, turn it into a named domain type, and pass that inward. Inside the boundary, trust the type completely.
For a human, this means fewer defensive checks and fewer surprises. For a model, it means the space of possible edits shrinks to the ones that compile, which is most of the way to the ones that are correct. A precise type is a specification the second reader cannot ignore, because the build will not let it.
Shape is context
Small files, named after one thing, in folders that carry the division. A model that can see the whole file can reason about the whole file. A two-thousand-line module is a context window problem for the model and a comprehension problem for the human. Same problem, same fix.
The same direction
I expected working with agents to force compromises in how I write code. Instead it made the old advice non-negotiable. Everything that helps a colleague who joins next year helps a model that reads the file right now, and the model is the one that gives you feedback in seconds.
Write for the reader who has no context and no coffee. The reader who has both will thank you too.