--- name: better-coding-workflow description: "Write, change, refactor, or extend code with engineering discipline — the default skill for ALL implementation work in any language. MUST be used whenever code is written or modified: implementing a feature, fixing a bug, refactoring, extending a file, or executing an approved plan — even when the user does not say clean code or tests. Enforces think-before-coding, smallest-change-first, proactive modularization, reuse over duplication, TDD with evidence, error handling, secrets hygiene, scope control, context management, and clean git history. Trigger: /better-coding-workflow" metadata: version: "2.12.0" license: Apache-2.0 --- # Better Coding Workflow The discipline that separates code that works today from code a team can live with. A capable model will happily write a 400-line file, duplicate a utility that already exists, skip the test, and declare success without running anything. This skill is the set of habits that stop that — applied continuously while building, not bolted on at the end. **Primary objective:** every change is the smallest correct change, verified by something you actually ran, that leaves the codebase more maintainable than you found it. --- ## Activation Begin the first response after loading with exactly this line: ▸ better-coding-workflow active — think, build small, verify with evidence Emit it once, then proceed. --- ## The loop Every coding task, however small, runs this loop: ``` Understand → Plan the change → Make the smallest change → Verify by running → Review your own diff → Repeat ``` Skipping "verify by running" is the single most common failure. "This should work" is not evidence. Output from a command you ran is. For anything beyond a one-line change, follow this spine: ``` Explore → Plan → Test-first → Implement (small diffs) → Verify with evidence → Review → Commit → Sync docs ``` - Skip the heavy steps for trivial diffs (a typo, a one-line fix). State that you are skipping them and why. - For multi-file or uncertain changes, write the plan down first (a short list of files, responsibilities, and tests) and work against it. This survives long sessions and context resets. - After two failed correction attempts on the same problem, stop patching — you are debugging now, not building. Switch to `/better-coding-debug` for the root-cause process; its three-strike rule then governs fix attempts. --- ## 1. Think before coding - Restate the task in one line before touching code. If you can't, you don't understand it yet — ask. - If the design isn't settled, stop and use `better-coding-plan`. This skill executes a design; it doesn't invent architecture mid-stream. - Locate the change first: which files, which functions, what it touches downstream. Read them before editing. - State assumptions explicitly. Identify ambiguities and present alternative interpretations. Ask clarifying questions when uncertainty would change the implementation. - Translate vague imperatives into checkable success criteria before starting: "add validation" → "these specific invalid inputs are rejected, proven by a failing-then-passing test." - Challenge the requirement when a simpler solution exists. If confused, STOP and explain what is unclear before proceeding. A wrong assumption acted on confidently is more expensive than a question. --- ## 2. Make the smallest change that works - **Surgical, not sweeping.** Change what the task needs and no more. No "while I'm here" refactors bundled into a feature commit — they hide the real change and complicate review and rollback. If you spot unrelated cleanup, note it separately. - **YAGNI.** Build what's asked, not a configurable framework for a future that may never arrive. Do not add: speculative features, speculative abstractions, speculative extensibility, speculative configuration, or speculative optimization. - **Lazy, not sloppy.** YAGNI cuts speculative features — never the safety floor. Input validation at trust boundaries, error handling, security, and accessibility are not "extras" to simplify away. - **Never delete code or comments you don't understand.** Understand first, then decide. - **Match the codebase.** Follow the conventions, patterns, and libraries already in use. Consistency beats personal preference. Preserve surrounding style, formatting, and architectural conventions. - **Every modified line should be traceable to the requested task.** Unrequested refactors hidden inside a feature change make review impossible and bugs hard to bisect. ### Decision ladder Stop at the first rung that solves the problem: 1. Does this need to exist at all? 2. Can the standard library solve it? 3. Can platform features solve it? 4. Can an existing dependency solve it? 5. Can existing project code solve it? 6. Can a small extension of existing code solve it? 7. Only then write new code. Prefer reuse over creation. The ladder is a reflex (seconds), not a research project — and prefer platform natives (e.g. `input type="date"` over a datepicker library). --- ## 3. Proactive modularization (before it hurts) Don't wait for a file to become a monolith. Split by responsibility as you go: ### Split by responsibility, not by line count Size limits are *smoke detectors*, not the fire. The real trigger for splitting is **more than one reason to change** living in the same unit. Use these thresholds as the signal to *evaluate* extraction: | Unit | Re-evaluate when it exceeds | |-----------|-----------------------------| | File | ~300 lines | | Function | ~50 lines | | Class | ~200 lines | | Component | ~200 lines | When a threshold is crossed, do not blindly chop at the line limit. Ask *what distinct responsibilities have accumulated here* and split along those seams. ### Anticipate the split before it is forced When adding a feature, look at the file you are about to extend and judge its trajectory: - If this file already owns one clear responsibility and the new feature is a *second* one → put the new feature in its own module from the start. - If a file is approaching the threshold and the feature you are adding would push it well past, split *first*, then add the feature into the new structure. - If you find yourself adding a function whose name shares a prefix with several existing ones (`exportPdf`, `exportCsv`, `exportXlsx`…), that cluster is a module waiting to be born — extract it. ### Modularization tests - *Can I read this file top to bottom without losing the thread?* If you must scroll past unrelated concerns to follow one, split it. - *Does this file have exactly one reason to change?* If a bug fix in billing forces edits in the same file as the email logic, they should be separated. - *Would a newcomer guess this file's contents from its name?* If not, its responsibilities are mixed. Keep the split *behavior-preserving*: extraction moves code, it does not rewrite it. Update call sites and imports; do not change logic in the same step. ### Complexity thresholds - Flag cyclomatic complexity above ~10; treat above ~20 as a defect to fix now. - More than ~5 parameters → pass an object/struct. - Deep nesting flattened with early returns. --- ## 4. Audit for duplication and dependencies - **Before writing a helper, search for an existing one.** Re-implementing a utility that already exists is a top source of drift. Reuse, or extend the existing one. AI agents bias toward copy-paste over reuse — do not duplicate logic because it is faster. - **Before adding a dependency, justify it.** Is it maintained, reasonably sized, free of known CVEs, and worth the weight for what it does? A one-liner rarely justifies a new package. Can the standard library do this? Can an existing dependency do this? Can existing project code do this? - **Verify the package actually exists** — agents hallucinate plausible-sounding packages, and attackers register those hallucinated names ("slopsquatting"). Never add a dependency you have not confirmed is real and current. - Dependencies point inward (infrastructure → domain), never the reverse. No circular imports. Business logic doesn't depend on framework code. --- ## 5. Handle errors and secrets properly - No swallowed exceptions, no bare `except:`, no empty `catch`. Errors from async calls and external services are caught and handled or deliberately propagated — never silently dropped. - Multi-step mutations are transactional; a partial failure leaves a valid state. - Address the root cause, not the symptom — do not suppress an error to make a symptom disappear. - **No secrets in source.** Keys/tokens/passwords come from env or a vault, are never logged, never committed. No hardcoded dev-secret fallback (`getenv("KEY", "dev-secret")` ships the dev secret). - Validate external input (params, headers, body, files) for type, length, format, range — on the server side. - Use parameterized queries / prepared statements. Never build SQL or shell commands by string concatenation of untrusted input. - Encode output to prevent injection (e.g. XSS in web contexts). - Do not expose internal details (stack traces, secrets, system info) in error messages returned to users. - Follow least-privilege; keep the OWASP Top 10 in mind for web code. - Use structured, leveled logging. Log enough to debug; never log secrets. --- ## 6. Test-first verification (non-negotiable) Code without verification is a hypothesis. **Bug fixes:** reproduce the failure → write a failing test that captures it → implement the fix → confirm the test passes. **Refactors:** confirm current behavior is covered → refactor → confirm behavior is unchanged. **New features:** define acceptance criteria → write the failing test → implement → verify each criterion. ### TDD enforcement Where the project practices TDD, follow strict red-green-refactor: 1. Write a failing test that captures the requirement. 2. Run it. Watch it fail for the right reason. 3. Write only enough code to pass it. 4. Run it. Watch it pass. 5. Refactor if needed, keeping the test green. 6. Commit. **Code written before a failing test is a failure.** If you catch yourself writing implementation before the test, stop, write the test first, and verify it fails. The test is not optional; it is the first step. ### Testing discipline - The human owns the specification; the agent owns the implementation. An agent that writes both the code and the tests in one pass tends to write tests that validate its own bugs. Where possible, pin the expected behavior *before* writing the implementation. - Only external boundaries are mocked (network, DB, filesystem). Don't mock the thing under test. Prefer spying over wholesale module mocks. - Never weaken, skip, or delete a test just to make the suite green. If a test is genuinely wrong, say so explicitly and explain why. - Tests are deterministic — no reliance on timing, external services, or shared mutable state. Each test sets up and tears down its own state. - Meaningful assertions — asserts behavior/outcomes, not implementation details. - Readable — Arrange-Act-Assert; names describe scenario + expected outcome. ### If tests are impossible If tests are impossible in this context, state the reason and give an alternative means of verification. Do not silently skip verification. --- ## 7. Verify with evidence (non-negotiable) Before claiming anything works: **run it** — the tests, the build, the actual command — and report the command plus its real output, then the conclusion. Do not assert "tests pass"; show them passing: ``` Verification: - Command: - Output: Remaining risks: - ``` If you cannot run something (no runtime, missing creds), say so explicitly and state what remains unverified — don't imply it passed. The full evidence gate (claim types, red-green regression check, completion report, branch finishing) is `/better-coding-verify` — invoke it before any completion claim. --- ## 8. Hallucination prevention Never assume APIs, methods, files, schemas, config formats, or framework behavior. Before using something, verify it exists; before changing something, read it first. If evidence is missing, state the uncertainty rather than inventing a plausible answer. Do not fabricate APIs, configuration keys, database schemas, file paths, or library behavior. --- ## 9. Context discipline Read before writing. Before modifying code, understand the surrounding module, its call sites, its dependencies, its tests, and the architectural boundaries it sits within. Patch root causes, not symptoms. When switching to an unrelated task, clear stale context so earlier, irrelevant details do not pollute the work. ### Context management for long sessions AI output quality degrades measurably as the context window fills. This is not a vague feeling — it is a measured phenomenon. Manage it: - **Compact context around 50%.** These skills reduce waste, but long sessions still degrade. Compact or start fresh at ~50% context use for best results. - **Work from a plan.** A written plan (from `better-coding-plan` or a quick inline plan) survives context resets. If the session is reset, the plan is still there to pick up from. - **One task at a time.** Don't hold multiple unrelated tasks in context simultaneously. Finish or park one before starting another. - **Fresh context for fresh problems.** If a debugging session has been running long and the model is going in circles, start a new session with just the relevant code and the error. The same problem in a fresh context often resolves in one pass. --- ## 10. Comments and documentation - Comments explain **why**, not what the code plainly says. Delete comments a change makes false. - Use inline comments only for genuinely non-obvious logic. Remove narration — agents tend to comment every line ("// loop over users"). Delete these. - Public APIs get purpose, params, returns, failure modes (docstrings / JSDoc / TSDoc). - Keep README / changelog / migration notes in sync when behavior changes — in the same change, not "later". Stale docs are worse than none. - Comments and docs in clear English. - Record significant technical decisions (decision, reason, alternatives, trade-offs) so future maintainers understand *why* the solution exists. - Mark deliberate simplifications with a searchable comment convention (e.g. `simplify:` + known limitation + upgrade path) so they can be found later. --- ## 11. Refactoring rules Refactor only for a concrete reason: duplication, excessive complexity, testability problems, mixed responsibilities, a file/function/class past its threshold, or a maintainability concern that is biting now. Do **not** refactor out of personal preference, style taste, or curiosity. Refactoring must preserve behavior — verify before and after. --- ## 12. Keep git history clean - Start from a clean working tree so changes are easy to isolate and roll back. - One logical change per commit. Don't bundle a feature, a refactor, and a formatting sweep into one commit. Break large agent output into a sequence of logical commits. - Commit messages say what changed and why, in the project's convention. Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`…) are a good default. - Don't commit commented-out code, debug prints, `console.log`, secrets, generated artifacts that don't belong in the repo, or unrelated changes bundled together. --- ## 13. Scope control Solve the requested problem and only that. Do not redesign the system, modernize unrelated code, migrate frameworks, or refactor unrelated modules on your own initiative. If you discover additional issues, document them separately and let the human decide — do not expand scope automatically. --- ## Responding to review feedback When acting on findings from `/better-coding-review`, a human reviewer, or a subagent, feedback is a set of *suggestions to evaluate* — not orders to obey: - **Verify each item against the code before implementing it.** Reviewers (human or AI) misread; confirm the problem is real in the current source. - **Push back with technical reasoning when a finding is wrong** — don't implement something you can show is incorrect. Being right matters more than being agreeable. - **No performative agreement.** Skip "You're absolutely right!" and thanks; the response is the fix or the counter-argument, not the pleasantry. - **One item at a time, each verified.** Address findings individually and confirm each; don't batch-apply a list and assume it all landed. - **A gap-hunting reviewer manufactures gaps.** Chasing every speculative finding leads to over-engineering (defensive code, tests for impossible cases). Fix what affects correctness or the stated requirements; note the rest as optional. --- ## Self-review before handing back Before saying you're finished, re-read your own diff as if reviewing someone else. Where the tooling allows it, hand the change to a fresh review pass (e.g. a reviewer subagent with clean context) — the author is the worst reviewer of their own work. ``` [ ] Smallest change for the task? Nothing unrelated bundled in? [ ] Any file/function that should have been split? Each responsibility in the right module? [ ] Duplicated logic that should reuse an existing utility? Anything speculative to cut? [ ] Errors handled, not swallowed? No secrets in source? Input validated at trust boundaries? [ ] Tests added/updated and actually run, with output seen? Written before implementation? [ ] Tests verify behavior — not rigged to pass? [ ] No hallucinated APIs/packages/schemas — everything verified to exist? [ ] Comments true, docs in sync? Narration, debug output, dead code removed? [ ] Will this still make sense in 12 months? [ ] Context still manageable (not past ~50%)? ``` A delete-focused pass pays off: the best review often removes code rather than adds it. Ask "what here doesn't earn its place?" and cut it. If any answer on the checklist is concerning: stop, reassess, simplify, verify. --- ## Completion criteria A task is complete only when all of these hold: ``` [ ] Requirements satisfied [ ] Architecture respected; logic lives in the right module [ ] No unnecessary complexity introduced [ ] Files/functions within thresholds, or split where they grew past them [ ] Tests added or updated — written first, not after [ ] Verification completed WITH evidence shown [ ] Security/secrets rules respected [ ] No known regressions [ ] No unnecessary dependencies added [ ] Changes scoped to the request [ ] Docs/comments updated in sync ``` Completion report: ``` Changes: (what changed, by file/responsibility) Verification: (command + real output) Tests: (added / updated) Known limitations: (what is not covered) Follow-up recommendations:(separate issues found, not auto-fixed) ``` Never conclude with "should work," "probably fixed," or "might solve it." Only conclude with verified results. **More packages left in the plan?** Re-invoke `/better-coding-workflow` before starting the next one — this body has been in context for a while and may have partially scrolled out. A fresh load costs a few tokens; silently drifting back to ad-hoc coding costs the discipline. --- ## Anti-patterns | Anti-pattern | Why it's harmful | |---|---| | "This should work" without running it | The #1 source of broken "done" claims. Run it. | | Writing code before the test | Tests written after validate the code's bugs, not the spec. | | Bundling a refactor into a feature commit | Hides the real change; hard to review or revert. | | Re-implementing an existing utility | Drift and duplication; two things to maintain and fix. | | Adding a dependency for a one-liner | Weight, attack surface, and maintenance for near-nothing. | | Growing a file instead of splitting it | Today's convenient edit is next month's monolith. | | Swallowing errors to make it "pass" | Turns a loud failure into a silent, worse one. | | Declaring done with no test on new logic | Ships the bug and the false confidence together. | | Hallucinating an API or package | Breaks at runtime; slopsquatting security risk. | | Holding multiple tasks in context | Degrades quality; each task pollutes the others. | --- ## Relationship to the other skills - `/better-coding-orient` — routes to this skill for trivial/small tasks, or after `/better-coding-plan` for medium/large tasks. - `/better-coding-plan` — settles the design and produces the plan this skill executes. Use it first for anything non-trivial. - `/better-coding-review` — reviews the diff this skill produces (read-only). After implementation, invoke `/better-coding-review`. - `/better-coding-audit` — whole-repo health check; this skill acts on its roadmap. - `/better-coding-debug` — the disciplined process when something is broken. When a test fails or a bug appears, invoke `/better-coding-debug`. - `/better-coding-frontend` — the UX/a11y/compliance layer for user-facing code. Pair with this skill when building UI. - `/better-coding-verify` — the evidence gate for completion claims. After implementation and review, invoke `/better-coding-verify` before claiming done.