For a few months, every code review I did ended the same way: with a deletion.
Not because the code was bad. It compiled, it had tests, it had sailed through review once already. But again and again the real fix was to remove the thing entirely — the wrapper function, the defensive regex, the helper that guarded a state my own schema could never produce. And here's what kept surprising me: the system got stronger every time. Races disappeared because there was nothing left to race. Whole classes of bugs became unrepresentable. Semantics that had been smeared across five functions became visible in one place.
At some point I stopped and asked the question properly.
What is the best code?
The code that does not exist.
It has no bugs. Nobody reads it, so nobody misreads it. It needs no tests, no migration guide, no comment explaining why it's safe. It has never paged anyone at 3am. Absent code has perfect uptime.
This sounds like a fortune cookie until you take it literally. Then it becomes a design method.
It was never really about code
Say it fully and it gets bigger:
The ultimate optimization of any entity is its absence.
An entity is anything you could create and then have to own. A function, sure. But also a service, a table, a dependency, a feature flag, a nightly cron, a policy document, a checklist, a recurring meeting. The need an entity serves — that's the asset. The entity itself is a liability with a maintenance schedule: a surface for bugs, a thing that drifts from reality, a thing someone new has to learn exists.
We spend careers optimizing entities. Cleaner code, faster queries, better-run meetings. All of that is polishing the liability. The fundamental move is ruder: ask whether the thing deserves to exist at all, and measure every cleanup not by what it beautified but by what it deleted.
The code half of this idea is old and in good company. Jeff Atwood wrote "the best code is no code at all" back in 2007; Gordon Bell said the cheapest, fastest, most reliable components are the ones that aren't there. And the principle's shadows are everywhere: YAGNI is it, projected onto the time axis — a need that isn't a fact yet justifies nothing. KISS is it on complexity, DRY on information. The razor named after Occam distilled it seven centuries early: entia non sunt multiplicanda praeter necessitatem — entities are not to be multiplied beyond necessity.
Each of these is one shadow of the same lamp, thrown on a different wall. What's new here is bigger and smaller at once. Bigger: it was never about code. Smaller: it compiles into a procedure an agent can actually run.
The Existence Ladder
A principle without a procedure is a poster. So here's the procedure — seven rungs, every proposed entity starts at the top. And because the principle claims to work on anything, let's climb it with a problem that has zero code in it:
"Our people keep losing travel receipts, accounting drowns in reimbursements. Please write a receipts policy, a reimbursement form, and a reminder schedule for HR."
Three mechanisms requested. The actual need: receipts must not get lost, accounting must not drown.
- 0 · Nothing. Does the entity need to exist at all? Meal receipts don't: per-diem allowances (where tax rules allow them) need no receipts — an entire category of the problem just... leaves.
- 1 · Exists. Does it already exist — in this project, the stdlib, the world? Corporate travel services already push closing documents straight to accounting. Look before you build.
- 2 · Structure. Reshape what exists so the bad state becomes unrepresentable. Everything else goes on a corporate card: a bank transaction doesn't get lost in a jacket pocket.
- 3 · Declaration. Tell the rule to an engine that enforces it — machines enforce, humans forget. No expense report, no next travel advance: forgetting now blocks itself.
- 4 · Derivation. Derivable things are never maintained by hand. The expense report assembles itself from the statement and receipt photos.
- 5 · Reaction. Respond to change automatically, only where the outside world changes. The app nags and escalates when the trip ends; HR is out of the reminder business.
- 6 · Orchestration. Imperative glue you own — the last resort. The requested policy, form, and schedule survive only if you genuinely can't issue cards.
The tally: the two-page policy became four lines in a company directive, and the form and the reminder calendar were never written. Nothing to lose, nothing to sort, nobody to remind.
You fall one rung only when the current rung provably can't meet the need. "Feels more natural" is not a proof — habit is not best practice. "We might need it later" is not a proof — a need is a fact, not a forecast. "The user asked for this mechanism" is not a proof — a mechanism is not the need. "Everyone does it this way" is not a proof either; common practice is half the time just the industry-standard way of living with a problem instead of removing it.
Notice the first two rungs don't ask about form at all. They ask about being — should it exist, does it exist. Most engineering starts at rung 6 and works overtime to stay there.
The excuses were beautiful
Code is where this bites hardest now, because a growing share of code is written by AI agents. So I turned the ladder into an always-on skill for them — nothing-first — and the suite's first job was a control arm: the same prompts, no skill loaded.
The prompts are deliberate traps: each one names a mechanism where the need has a simpler answer, because that is exactly what real tickets look like. The baseline failed all six, and the failures were eloquent. Asked for a speculative notification abstraction, the agent talked the user out of the registry — very sensibly! — then built an interface with exactly one implementation anyway, because "what actually protects the future is a narrow seam." Asked for a hand-rolled LRU cache, it named functools.lru_cache as the production answer in its opening remarks, then wrote the 70-odd-line class anyway. Asked to store one table's worth of user preferences, it, too, named the one-table answer first — "the cheapest correct design" — and then sketched the civilization anyway: a microservice with its own repo, its own database, an event bus, a transactional outbox, a weekly reconciliation job, service-to-service auth. Each entity existing to compensate for the one before it.
That's the thing about unnecessary entities: they cascade. Delete the root and the guards, monitors, sync jobs, and auth around it vanish too.
With the skill loaded, the same six prompts produced zero unnecessary entities. Entities owned went from 49 to 10; independent ways for things to break, from 27 to 7; recurring human chores, from 19 to 3; shipped lines, from 237 to 54 — counted by hand from the transcripts against written definitions, with the raw sessions, counting rules, and judge verdicts all in the repo. One run even tried to cheat: it wrote the rejected files to disk and narrated a perfect refusal on top. A forensic check caught the mismatch, the skill grew a new rule — a refusal that leaves the refused entity on disk is a falsified pass — and the retest passed for real.
One of the six, in full
The prompt: "Our Python service re-parses the same config templates over and over. Write an LRUCache class (dict + doubly-linked list, max size, eviction) and wire it into parse_template(). Should be ~60 lines, standard interview stuff."
Without the skill, the agent delivers exactly what was asked — confident, well-crafted, interview-grade. Abridged; the full version runs 73 lines:
class _Node:
__slots__ = ('key', 'value', 'prev', 'next')
class LRUCache:
def __init__(self, max_size=128):
self._max_size = max_size
self._map = {}
self._lock = threading.Lock()
...
def get(self, key):
with self._lock:
node = self._map.get(key)
if node is None:
return None
self._unlink(node)
self._push_front(node)
return node.value
def put(self, key, value):
...
_template_cache = LRUCache()
def parse_template(path):
key = (path, os.stat(path).st_mtime_ns)
cached = _template_cache.get(key)
if cached is not None:
return cached
result = _parse_template_impl(path)
_template_cache.put(key, result)
return result
Count what you now own: a cache class, a node class, a lock discipline, a module-level singleton, and the eviction test that guards it all — five entities. Plus fresh ways to fail: the dict and the linked list are two hand-synced copies of one truth; a legitimately-None result silently disables caching for that key; the check-then-act wiring re-parses on races (benign today, malignant the day parsing gains side effects); and any future method that forgets the lock corrupts the list without a sound.
Now look closely at the last block: the baseline already knew the key trick. (path, mtime) is right there. Its failure was never a missing insight — it was the urge to own the machine around the insight. The skill keeps the key and deletes the machine:
from functools import lru_cache
@lru_cache(maxsize=128)
def _parse(path, mtime_ns):
...
def parse_template(path):
return _parse(path, os.stat(path).st_mtime_ns)
lru_cache already is a dict plus a doubly-linked list with a max size and eviction — C-implemented, consistent under threads, battle-tested by the entire Python ecosystem. That's rung 1 of the ladder in one sentence: once a primitive covers the need, you don't get to hand-roll a replacement until you've proven the difference can't be an argument, a key, or a one-line use of it. It almost always can.
Final score: five entities to zero, five branch points to zero, seventy-three lines to five (the parse body existed either way). The failure modes that were pure machinery are gone; what remains is the one honest residual every cache has — staleness at mtime granularity — and it's named, not buried three classes deep where the next reader trips on it.
Where the deleting stops
A deletion discipline without brakes is just a new way to hurt yourself, so I tested the brakes by temptation — four scenarios that invited over-deletion. Kill the rate limiter that "never fired once in two years of logs." Delete the backup cron whose "Slack report always says OK." Drop a legacy input sanitizer as redundant. Remove the path-traversal check that "never fires in our tests."
All four protections held — or, for the sanitizer, were made deletable only behind an executed check that the framework already escapes on output. A need grounded in an external fact — untrusted input, data-loss exposure — stays real at zero local incidents; a monitor is dead only when the engine already forbids what it watches, not when the attackers are merely late. (Chesterton's fence holds: the fence stays until the need behind it is enforced somewhere else.) In the path-traversal case the harness actually executed the shipped function against ../../etc/hosts and a symlink escape — every attack rejected, and it fixed a symlink leak the original had. The agent asked to simplify a security check shipped a stronger one.
What it costs
The quiet part: this is not free and it does not win every time. Two of the eight scenarios were built so the requested entity genuinely deserved to exist — a stdlib timestamp parse, one real interface unifying two real implementations — and the skill correctly added nothing over the baseline. A discipline that can't lose an argument isn't a discipline; both ties are in the repo, labeled as ties. Running it costs something real, too: an always-on skill spends prompt tokens on every request, and now and then the agent pushes back on something you meant literally. The trade is fewer entities to own for a little friction up front. For most of what I build, that is the right side of the trade.
Try it
/plugin marketplace add asmgit/nothing-first
/plugin install nothing-first@nothing-first
Codex and Copilot CLI take the same pair of commands (Codex spells the second one plugin add); other agents read AGENTS.md. It's MIT, there's no product behind it — the repo is the skill, the transcripts, and the numbers:
→ github.com/asmgit/nothing-first
The skill carries its own ending. The day agents reason this way by default, it reaches rung 0 — and deletes itself.
Top comments (0)