Over-engineering
SkillDev toolsApply when writing or reviewing any change, when deciding whether a refactor or a simplification is worth making, when a helper, wrapper, flag, map, abstraction or mechanism is about to be added, and when a review's quality lane asks whether a thing earns its existence. Esposter catalogue of over-engineering, the goal every abstraction is measured against (it lowers the reader's cognitive burden, never line count), the rule that syntax is never extracted into a helper, and the one index of the shapes a change takes when it builds more than the problem needs, each at the skill that holds its rule.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Over-engineering skill
What this skill tells your AI
The instructions your AI receives, as published by esposter/esposter in .agents/skills/over-engineering/SKILL.md and read by ahel’s review.
The one list of the shapes a change takes when it builds more than the problem needs. Each rule lives in the skill named beside it, stated there in full; this page is the index a review's quality lane walks, so nothing is stated here that another skill owns. The rules with no other owner are marked owned here.
The goal — owned here
An abstraction earns its place when it lowers the reader's cognitive burden, and for no other reason. Duplicated business logic, a value reused across sites, a shape several callers have to agree on: naming one of those means a reader learns it once and recognises it everywhere, and a change to it lands in one place. That is the whole of what a refactor or a "simplification" is for, and it is the question every entry below is a failed answer to. Fewer lines is not the goal; fewer things a reader has to hold in their head is — and a name they have to chase is one more, not one fewer. The sharper form, for a helper, is file-organization's: an extraction earns its existence only when a call site stops being able to get something wrong. "Might need it" is never that. A branch collapsed into a default is the same trade in a step's clothing: two explicit steps — a filtered install and a bare pnpm i — say what each does, and one step whose default is a glob that happens to select everything asks the reader to know the equivalence. Fewer steps bought implicit behaviour; the two stay.
Syntax is never extracted — owned here
A basic construct carries no burden to remove: it was learned once with the language or the library, and it is written at every site — a for loop, a destructure, a .match((value) => value, (error) => { throw error; }) terminator, a getResultAsync(() => …) call, an await expect(…).rejects assertion. A helper that only spells one of them shorter — unwrap(result) over the terminator, each(items, fn) over the loop, expectFoo(promise) over the assertion — is a forwarding wrapper (below) wearing a shorter name, and it hides the one line a reader arrived to see: the terminator says how a rejection is handled, the loop says what it iterates, and both now say "look elsewhere". Fourteen sites spelling the same terminator are not duplication, because none of them can drift from the others — the syntax is the same everywhere by definition, which is exactly what makes it syntax. The tell is that the helper's body is one expression with no logic, no invariant and no default, applied to whatever was passed in. A value that recurs is the other side of the same rule: a literal repeated across sites is a thing the reader has to re-derive at each one, so it earns a name (file-organization, "no duplicate constants").
The counter-test is drift, and it is the half that gets misread. A condition can drift where syntax cannot. If a site could write one clause of it and still compile, still pass its tests and still read right at a glance, then those sites are not spelling a construct — they are restating a rule of this domain, and the rule earns a name even though its body is one expression and nothing but && separates its halves. user.login === login && body.includes(text) at eight sites is that shape: drop the login half and a comment anyone could have forged is trusted. So the tell above is read as a conjunction — one expression and nothing a site can get wrong by omission — never on the shape of the body alone. A predicate reused under two terminators is also never one function: extract the predicate, and let each site keep its .some, its .findLast, its .filter().length, because which one it wants is the question it came to ask.
The catalogue
| Shape | The rule and its owner |
|---|---|
| A wrapper that forwards its arguments and adds nothing | file-organization — an extraction earns its existence only when a call site stops being able to get something wrong; pass-through-helper/no-forwarding-wrapper lints it |
A Result around a body that cannot fail | error-handling — wrap only what can actually fail; the err branch that needs a spy to reach is the tell |
| A flag, field or primitive nothing behaves differently without | file-organization — a distinction with no behavioural consequence is not debt |
| An export, schema, type or constant with no call site | typescript — never declare what nothing uses; removing a last consumer cascades |
| A return type written where inference already answers | typescript, vue-composable-patterns — annotate only where inference fails or a contract must be enforced |
A use* that re-exposes a store, a store function that forwards to another | vue-composable-patterns, pinia — delete the layer, use the underlying one directly |
| A primitive hand-rolled beside the one that already owns the job | vue-composable-patterns — the table of primitives; a count of in-flight anything is the tell |
| A map or factory generalising over callers that share a name and little else | apps/web/content/docs/resource/resource-snapshots.md — a createSnapshot(id, channel) driving six axes from a map is a branch with indirection between it and its reader |
| A capability, shared package or mechanism promoted for a single consumer | apps/web/content/docs/architecture/resource.md (the admission rule); file-organization (references/cross-package-placement.md) — name the second consumer or leave it |
| A test that pins what typecheck, a Zod constraint or another test already enforces | testing — "What to Test"; the enforcer already decides it, so the test can only pin today's shape |
| A test helper file with one suite importing it | testing (references/test-helper-files.md) — it lives in that suite; the file earns its place at the second consumer |
| A benchmark of a wrapper rather than the unit inside it | bench — the wrapper's overhead drowns the signal and adds none of its own |
| A durable mechanism — an outbox, a queue, a sweep — for a failure the next write repairs on its own | apps/web/content/docs/resource/storage-quotas.md — a counter that self-corrects does not earn a second write path |
| A script bent to fit a runner, or a shim built so a runner fits the script | package-scripts — the runner is picked to fit the code: node where it can, tsx where it cannot |
Reading a finding against this list
- A review finding that names a shape here cites the owning rule, never re-argues it (
code-review, the written record as tiebreaker). - A shape that recurs without an owner is a missing row and a missing rule: add the rule to the most specific skill, then the row here — never the rule here alone, because this page is an index and a rule stated only in an index is one nobody loads for the file at hand.
- The argument that made the shape look necessary — a check nothing reads, a cost nobody measured — is one step earlier and has its own index:
fallacies. - The opposite failure has its own owners: a helper that should exist and was hand-rolled twice is
file-organization's duplication rule, and a primitive that exists and was not used isvue-composable-patterns' table.
Signals
- GitHub stars
- 23
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
over-engineering- Source
- github.com/esposter/esposter