Engineering craft — code the maintainers would approve
The counterpart to minimalism. Minimalism asks "is this the least code that works?";
craft asks "is the code that remains well-structured, honest, and safe to change?" The
target is the intersection: the smallest change that a careful reviewer would approve
without asking you to redo it. This is a lens, not a stage — apply it while you
implement, and confirm it in self-review before submitting.
Craft is never a licence to add code. When craft and minimalism seem to disagree, they
don't: the rule below (real repetition, not speculative) resolves it every time.
The craft bar (hold all of these)
- Reuse only real repetition — never speculative abstraction. DRY applies to
duplication that already exists (the same logic in two places you can see). Do NOT
introduce a base class, generic, config system, or "flexible" helper for a second
caller that doesn't exist yet — that's the over-engineering
minimalism forbids.
Rule of thumb: extract on the third occurrence, inline the first two. When you do
extract, give the shared unit one clear responsibility.
- Boundaries at the edges. Keep transport (HTTP/CLI), business logic, and
persistence (DB/filesystem) separable — don't put a SQL query or a
fetch in the
middle of domain logic. Depend on the seam the repo already uses; don't invent a new
layering the codebase doesn't have.
- Handle errors explicitly — no silent failures. No empty catch blocks, no
swallowed rejections, no ignored return codes. Either handle the error meaningfully or
propagate it with context. A user-facing surface gets a clear message; a server path
logs the detail. Never
catch {} to make a test pass.
- Validate at the boundary. Untrusted input (request bodies, CLI args, external API
responses, file contents) is checked before use — fail fast with a clear message. Use
the repo's existing validation approach (e.g. schema/zod) rather than ad-hoc checks if
one exists.
- Small, focused units. A function does one thing (aim < ~50 lines); a file stays
cohesive. If a function has grown a second responsibility or four levels of nesting,
split it or use early returns — but only when it genuinely helps clarity, not to hit a
number.
- Honest names. Names say what the thing is/does. Booleans read as
is/has/should/can.
No data2, tmp, helper, doStuff. A good name removes the need for a comment.
- Don't mutate what you don't own. Prefer returning new values over mutating shared
inputs/state; treat function arguments as read-only unless mutation is the point.
Respect the language's idiom — this is immutability where idiomatic (a Go pointer
receiver or an in-place sort in hot code is fine); it is not a mandate to copy
everything.
- Composition over inheritance. Reach for a function, a small object, or a passed-in
dependency before a class hierarchy.
- Tests are part of the change. Cover the real logic you added — happy path, the
boundaries, and the error cases — not just the line that's easiest to assert. Test
code is a guarantee, not bloat (
minimalism agrees). Use add-unit-test /
add-integration-test for the mechanics.
- Match the repo, don't reform it. Follow the prevailing conventions, patterns, and
style of the code around you. Craft means your addition is indistinguishable from
well-written existing code — not that you impose a cleaner paradigm the repo doesn't
use. A repo-wide refactor is a separate ticket.
How it composes
- With
minimalism: same goal from two sides — least code and well-structured.
If you're tempted to add structure "for later", minimalism wins (rule 1). If you're
tempted to save lines by swallowing an error or skipping validation, craft wins (rules
3–4): those are guarantees, not bloat.
- With the build skills (
backend-service, add-api-endpoint, frontend-component,
…): those tell you what to build for the domain; craft is how well you build it.
On frontend work, frontend-foundations adds the surface-specific bar (design quality,
accessibility, tokens).
- With
self-review: before submitting, re-read your diff against this bar. A change
that trips rules 1, 3, or 5 is the kind a reviewer sends back.
Marker
When you make a non-obvious structural call — extracted a shared unit, chose a boundary,
declined an abstraction, added validation at a specific edge — record it in one line so
the choice is visible: use request_decision at log_only, or note it in the evidence
you record via record-evidence. This keeps the craft decision auditable (and lets the
factory see the skill was applied). One line, only for real calls — not a running
commentary.
1---2name: engineering-craft3description: Use on every code delivery to hold the structural-quality bar — reusability where repetition is real, clear boundaries, explicit error handling, focused units, honest names, and tests for real logic. Invoke whenever you implement a claimed ticket and want the change to read like production code the repo's maintainers would approve, not a quick hack that merely passes. A cross-cutting lens that composes with `minimalism` (least code) and whatever build skill the ticket needs. For "make this reusable / well-structured", "production quality", "don't leave a hack".4---56# Engineering craft — code the maintainers would approve78The counterpart to `minimalism`. Minimalism asks *"is this the least code that works?"*;9craft asks *"is the code that remains well-structured, honest, and safe to change?"* The10target is the intersection: the **smallest change that a careful reviewer would approve11without asking you to redo it**. This is a lens, not a stage — apply it while you12implement, and confirm it in `self-review` before submitting.1314Craft is never a licence to add code. When craft and minimalism seem to disagree, they15don't: the rule below (real repetition, not speculative) resolves it every time.1617## The craft bar (hold all of these)18191. **Reuse only real repetition — never speculative abstraction.** DRY applies to20 duplication that *already exists* (the same logic in two places you can see). Do NOT21 introduce a base class, generic, config system, or "flexible" helper for a second22 caller that doesn't exist yet — that's the over-engineering `minimalism` forbids.23 Rule of thumb: extract on the *third* occurrence, inline the first two. When you do24 extract, give the shared unit one clear responsibility.252. **Boundaries at the edges.** Keep transport (HTTP/CLI), business logic, and26 persistence (DB/filesystem) separable — don't put a SQL query or a `fetch` in the27 middle of domain logic. Depend on the seam the repo already uses; don't invent a new28 layering the codebase doesn't have.293. **Handle errors explicitly — no silent failures.** No empty catch blocks, no30 swallowed rejections, no ignored return codes. Either handle the error meaningfully or31 propagate it with context. A user-facing surface gets a clear message; a server path32 logs the detail. Never `catch {}` to make a test pass.334. **Validate at the boundary.** Untrusted input (request bodies, CLI args, external API34 responses, file contents) is checked before use — fail fast with a clear message. Use35 the repo's existing validation approach (e.g. schema/zod) rather than ad-hoc checks if36 one exists.375. **Small, focused units.** A function does one thing (aim < ~50 lines); a file stays38 cohesive. If a function has grown a second responsibility or four levels of nesting,39 split it or use early returns — but only when it genuinely helps clarity, not to hit a40 number.416. **Honest names.** Names say what the thing is/does. Booleans read as `is/has/should/can`.42 No `data2`, `tmp`, `helper`, `doStuff`. A good name removes the need for a comment.437. **Don't mutate what you don't own.** Prefer returning new values over mutating shared44 inputs/state; treat function arguments as read-only unless mutation is the point.45 Respect the language's idiom — this is immutability *where idiomatic* (a Go pointer46 receiver or an in-place sort in hot code is fine); it is not a mandate to copy47 everything.488. **Composition over inheritance.** Reach for a function, a small object, or a passed-in49 dependency before a class hierarchy.509. **Tests are part of the change.** Cover the real logic you added — happy path, the51 boundaries, and the error cases — not just the line that's easiest to assert. Test52 code is a guarantee, not bloat (`minimalism` agrees). Use `add-unit-test` /53 `add-integration-test` for the mechanics.5410. **Match the repo, don't reform it.** Follow the prevailing conventions, patterns, and55 style of the code around you. Craft means your addition is indistinguishable from56 well-written existing code — not that you impose a cleaner paradigm the repo doesn't57 use. A repo-wide refactor is a separate ticket.5859## How it composes6061- **With `minimalism`:** same goal from two sides — least code *and* well-structured.62 If you're tempted to add structure "for later", minimalism wins (rule 1). If you're63 tempted to save lines by swallowing an error or skipping validation, craft wins (rules64 3–4): those are guarantees, not bloat.65- **With the build skills** (`backend-service`, `add-api-endpoint`, `frontend-component`,66 …): those tell you *what* to build for the domain; craft is *how well* you build it.67 On frontend work, `frontend-foundations` adds the surface-specific bar (design quality,68 accessibility, tokens).69- **With `self-review`:** before submitting, re-read your diff against this bar. A change70 that trips rules 1, 3, or 5 is the kind a reviewer sends back.7172## Marker7374When you make a non-obvious structural call — extracted a shared unit, chose a boundary,75declined an abstraction, added validation at a specific edge — record it in one line so76the choice is visible: use `request_decision` at `log_only`, or note it in the evidence77you record via `record-evidence`. This keeps the craft decision auditable (and lets the78factory see the skill was applied). One line, only for real calls — not a running79commentary.