Abstract cobalt, violet, and cyan light for A Lazy Engineer

Composable skills beat one giant prompt

The first version of my coding harness was a single system prompt. It grew, the way these things do, one exception at a time. A rule for how to write commit messages. A rule for how to handle merge conflicts. A rule for what counts as a finished feature versus a half-finished one. Eighteen months later the file was enormous, most of it irrelevant to any given task, and the agent was visibly worse at following it than it had been at a tenth the length.

The failure mode is not really about token budget, though that matters too. It’s that a flat instruction file has no notion of relevance. Every rule competes for attention on every task, whether or not it applies. Debugging guidance sits next to PR-review guidance sits next to guidance for writing release notes, and the model has to hold all of it in mind even when it’s writing a one-line config change. Attention is a scarce resource, and a monolithic prompt spends it indiscriminately.

The fix that actually worked was splitting the file into small, named, independently loadable units — skills, in the vocabulary I’ve settled on. Each skill is a short document describing one recurring kind of task: how to debug a flaky test, how to review a pull request, how to run a database migration safely, how to write a blog post in a consistent voice. A skill is loaded only when the task at hand matches its description. The system prompt shrinks to a thin router: a table of what skills exist and when to reach for them.

This is not a novel idea in software engineering generally — it’s just modularity, applied to prompts instead of code. But it’s worth stating plainly because the default gravity of prompt engineering pulls the other way. Every time an agent gets something wrong, the easy fix is “add a sentence to the system prompt explaining the right way.” That sentence is cheap to write and expensive to keep. It never gets removed, even once the situation it was written for stops recurring, because nobody remembers why it’s there or whether it’s still load-bearing. A skill has a name and a boundary, so it can be revisited, merged, or deleted without archaeology.

The other benefit shows up in maintenance. When a rule lives in an undifferentiated prompt, correcting it means re-reading the whole document to find where it lives and checking whether the fix touches something else nearby. When a rule lives in its own skill file, correcting it means opening one document, changing one thing, and knowing exactly what its blast radius is. Skills compose the same way well-factored functions do: a skill for reviewing code can call out to a skill for running tests, without either one needing to know the other’s internals. You get reuse instead of copy-paste, and you get a place to put a new capability that doesn’t require re-touching everything else.

There’s a temptation, once you have this pattern, to make a skill for everything — one for writing a commit message, one for choosing a variable name, one for every small decision the agent makes. That’s a mistake in the other direction. A skill only earns its keep if it captures something non-obvious enough that the model would get wrong without it, and general enough to recur across tasks. If a rule applies exactly once, it belongs in the task instructions for that one task, not in a permanent skill. If it applies to literally everything, it probably belongs in the thin system prompt after all — the router itself, not a skill the router points to. The discipline is closer to API design than to documentation: a good skill has a narrow, well-named purpose and a description precise enough that the router picks it correctly without false positives.

I’ve also learned to write skills the way you’d write a runbook for a competent but unfamiliar colleague, not the way you’d write internal notes to yourself. Skills that assume context the model doesn’t have — “the usual way,” “like last time,” implicit knowledge of a particular codebase’s quirks — fail silently, because the model will confidently proceed with its own guess about what “the usual way” means. A skill needs to state the decision criteria explicitly: when to use this over an alternative, what the concrete steps are, and what “done” looks like. That’s more work upfront than a one-line rule bolted onto a system prompt, but it’s work that compounds instead of decaying.

None of this eliminates the need for judgment. The router still has to correctly match a task to the right skill, and it still sometimes doesn’t — an ambiguous request can trigger the wrong playbook, or none at all. But the failure is now legible: you can look at which skill fired, read its description, and see exactly why the match happened or didn’t, instead of trying to guess which paragraph of a five-thousand-word prompt the model weighted too heavily. Legible failure is most of what you need to iterate quickly, and a monolithic prompt never gives you that.

If you’re building an agentic harness and find yourself continuously appending “and also remember to X” to a system prompt, that’s the signal. Stop appending. Start naming the recurring task, write it up as its own short document, and teach the router to reach for it. The prompt stays small. The capability keeps growing. That trade is almost always worth making.

← Back to all posts