Some are ten lines long and some are much longer. Together they shape more of the software I ship than any single framework I use.
The Joke That Came True
In early 2023, Andrej Karpathy posted that "the hottest new programming language is English." Most developers I know laughed, and I did too.
Three years later, my repos are full of prose that agents read before they touch a line of code. GitHub shipped an entire toolkit called Spec Kit built around the idea that the spec is what you maintain and the code follows from it.
The prediction landed kind of sideways, with English settling in as the layer above the programming languages, the place where decisions get written down before an agent turns them into TypeScript or Go or Rust or whatever you use.
And once that happened, the quality of my English started showing up in the quality of the software I ship.
Dijkstra Saw This Coming
In 1978, Edsger Dijkstra wrote a short note called On the foolishness of "natural language programming". He thought the whole idea was a mistake, and his reasoning still holds up. I know, from 1978!
Formal languages exist because they make it hard to say nonsense. Natural language makes it easy. In his words:
The "naturalness" with which we use our native tongues boils down to the ease with which we can use them for making statements the nonsense of which is not obvious.
He was so right about that. English lets you write an instruction that sounds perfectly reasonable and means almost nothing.
Where I think he'd be surprised is what happened next. Programming in English moved the rigour out of the syntax and into the actual sentences, and now the people who write clearly are the ones whose agents behave.
Vague In, Vague Out
Every developer using agents has written a line like this one at some point:
Make sure the code is clean and follows best practices.
It feels like an instruction, and it does pretty much nothing. "Clean" and "best practices" mean whatever the model already thinks they mean, so you get its defaults back with a confident summary attached.
Compare it with a line pulled straight from my own files:
No em or en dashes anywhere in the repo, including docs and commit
messages. `scripts/check-dashes.mjs` gates build and test.
That line names exactly what's forbidden and where. Then it does something I've come to love, which is pointing at a script that fails the build if the rule gets broken. The English states the intent, and a few lines of code make sure it holds.
English compiles, and then a test checks the output. Isn’t that how it should be?
What Good Instructions Look Like
After a year of writing these files, the rules that actually change behavior share a few habits though.
| Instruction from my files | What it prevents |
|---|---|
| "Never hand-write anything that claims to be program output." | Made-up terminal output and invented dashboard data in docs and marketing pages |
| "Never store an IP address or a full user agent. No identifier lasts more than two days." | An agent adding tracking that breaks the privacy promise of an analytics tool |
| "Every condition opens once and closes with a recovery; do not add repeat alerts without a design discussion." | Alert spam, and a design decision getting overturned by a well-meaning refactor |
| "It never pushes or publishes." | A release script that ships something before a human has looked at it |
"Behaviour lands in TypeScript first, then conformance/ is regenerated and the other implementations follow." | Language ports drifting apart because each one got fixed separately |
Each one of those is specific enough to be checked. You can look at a diff and say whether it followed the rule, which is the same test you'd apply to a function.
Several of them encode a decision I made once and never want to relitigate.
The privacy rule in particular reads less like a coding guideline and more like a product promise, written where the agent can't miss it.
That was a product decision.
Most of them also say what happens at the edge. "Do not add repeat alerts without a design discussion" tells the agent where its authority ends and mine begins.
After all, a good instruction file draws a line around what the agent can decide alone, and the clearer that line is, the less I have to clean up. And that makes me happy.
Writing Is a Build Step Now
The writing advice I've followed for years turns out to be developer advice now.
- Use specific nouns. "The release script" beats "the deployment stuff."
- Write one rule per line, so a contradiction is visible.
- Say what done means. "Run
npm run checkbefore committing" is a finish line an agent can actually reach. - Name the exceptions out loud, because an agent will otherwise invent its own.
None of that is new. What's new is that bad writing now produces bugs instead of confused readers and devs.
I leaned into this when I released Scratchpad Skills, a set of 90 open source skills for AI assistants. Every one of them is a single Markdown file.
There's no code in the product at all, only instructions, and they run in Claude, Codex, Cursor, and anything else that reads skills.
Shipping 90 files of pure English and watching them work across different agents made the whole idea feel very real to me.
The Newsletter Guy's Unfair Advantage
I've written daily emails for more than a decade. What made those emails work was one idea at a time, said plainly enough that a tired reader gets it on the first pass.
Those are the same skills that make an agent do what you meant.
I watch developers I respect struggle with this. They can architect a distributed system in their sleep, and then they write an instruction file that reads like a stream of consciousness and wonder why the agent keeps wandering off.
The agent read it exactly as written. The writing was the problem.
I've written plenty of bad instruction files myself, so I'm in no position to gloat. The point is that a skill a lot of developers dismissed as soft is now sitting directly in the path between intent and working code.
Prose Rots Too
English instructions go stale exactly the way code does though, and sometimes faster.
A rule written for last year's model gets in the way of this year's. Two rules added months apart end up contradicting each other.
A file starts at ten tidy lines and grows one exception at a time until it's a wall of text that a person can't hold in their head, and an agent's attention isn't infinite either.
I've been guilty of all of it, here and there, across those 41 files.
So I've started treating instruction files the way I treat code:
- They live in version control, and changes go through the diff like everything else.
- When a rule gets added after something went wrong, it gets a reason next to it.
- Every so often I reread the whole file and delete what no longer earns its place.
- Long files get split, so each one covers a single concern.
The fourth one is the hardest for me. Adding a line always feels responsible in the moment. Deleting one feels like losing something, even when the line is doing more harm than good.
Back to Dijkstra
Dijkstra thought natural language would make programming sloppy, and for a lot of people it has.
The agent happily turns vague English into vague software, fast and at scale.
What he didn't anticipate is that the rigour would survive by moving. These days it lives in rules specific enough to check, and in the occasional script that fails the build when the English gets ignored.
Most of the files I care about in a repo these days are still code.
More and more, though, the ones I'm proudest of are written in English.