brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
test-driven-developmentUse when implementing a feature, fixing a bug, or changing behavior — write a failing test first, watch it fail, write minimal code to pass, then refactor. Adds Kent Beck's Tidy First (separate structural vs behavioral changes) and a Canon test list. Language-agnostic core; per-language notes under references/languages/. Not for throwaway prototypes, genera…
Independent GitHub snapshot · Automated classification · Original documentation governs
Write the test first. Watch it fail. Write minimal code to pass. Then tidy.
Core principle: If you didn't watch the test fail, you don't know if it tests the right thing.
Announce at start: "I'm using the test-driven-development skill to drive this change test-first."
This skill is language-agnostic. The discipline below holds in every language; concrete framework, runner, and idiom examples live in references/languages/ — load the one matching your project.
references/languages/<lang>.md)Always:
Exceptions (ask your human partner first):
Thinking "skip TDD just this once"? Stop. That's rationalization.
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
Wrote code before the test? Delete it. Start over.
No exceptions: don't keep it as "reference", don't "adapt" it while writing the test, don't even look at it. Delete means delete. Implement fresh from the test.
Before writing any test, list the behaviors you need to cover — the basic case plus every variant you can think of ("what if the input is empty? what if the service times out? what if the key is missing?").
pick one item → RED → verify red → GREEN → verify green → REFACTOR → back to the list
Run tests in the quietest mode that still surfaces failures. Verbose pass logs burn context you don't need — quiet runners stay silent on green and print full detail on red, so you keep the failures without the noise. Switch to verbose (
-v/-s/--nocapture) only when isolating one test's output. Per-language quiet flags are in eachreferences/languages/<lang>.md.
One behavior, a name that describes that behavior (e.g. shouldRetryThreeTimesThenSucceed, not test1), real code over mocks. Work backwards from the assertion. See your language file for concrete framework syntax.
Run the single test. Confirm:
Test passes already? You're testing existing behavior. Fix the test.
Write the simplest code that makes this test (and all previous tests) pass. No speculative APIs, no extra options, no "while I'm here" cleanup. YAGNI.
Run the test. Confirm it passes, all previous tests still pass, and output is pristine (no warnings/errors). Test fails? Fix the code, not the test.
Remove duplication, improve names, extract helpers. Keep every test green. Don't add behavior here — that's the next RED.
Then mark the item done and return to the list. Repeat until the list is empty.
Kent Beck's distinction. Every change is one of two kinds — never mix them in one commit:
| Kind | What it is | Reversible? |
|---|---|---|
| Structural | Rearranging without changing behavior: rename, extract method, move code, organize imports | Usually yes |
| Behavioral | Adding or changing what the system actually does | No |
Commit only when:
Prefer small, frequent commits over large, infrequent ones. (For this repo's commit format, the git-commit skill applies — structural vs behavioral maps cleanly onto its refactor: vs feat:/fix: types.)
Tests written after code pass immediately — and passing immediately proves nothing: they may test the wrong thing, test the implementation instead of behavior, or miss the edge case you forgot. You never saw them catch anything.
Test-after answers "what does this do?" Test-first answers "what should this do?" — and forces edge-case discovery before you implement. 30 minutes of tests-after gives you coverage but loses the proof the test works.
| Excuse | Reality |
|---|---|
| "Too simple to test" | Simple code breaks. The test takes 30 seconds. |
| "I'll test after" | Tests passing immediately prove nothing. |
| "Tests after achieve the same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" |
| "Already manually tested" | Ad-hoc ≠ systematic. No record, can't re-run. |
| "Deleting X hours is wasteful" | Sunk cost. Keeping unverified code is the real debt. |
| "Keep it as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. |
| "Need to explore first" | Fine. Throw away the exploration, restart with TDD. |
| "Hard to test = need to push through" | Hard to test = hard to use. Listen to the test; simplify the design. |
| "TDD will slow me down" | TDD is faster than debugging in production. |
| "This case is different because…" | It isn't. Start over with TDD. |
Test-first only works if you don't game it. These failures are especially tempting for AI agents under pressure — treat each as a STOP:
For bug fixes, add at least one test that reproduces the bug and fails first — never fix a bug without a failing test that proves the fix.
The discipline above is the same everywhere. For framework choice, runner commands, idioms, and language-specific anti-patterns, load only the file matching your project — and only when you actually need that detail:
| Project language | Read |
|---|---|
| Java | references/languages/java.md |
| TypeScript / JavaScript | references/languages/typescript.md |
| Python | references/languages/python.md |
| Go | references/languages/go.md |
| Rust | references/languages/rust.md |
No file for your language? The core skill is sufficient — apply the same red-green-refactor and Tidy First discipline with your stack's idiomatic runner.
When adding mocks or test utilities, read references/testing-anti-patterns.md — cross-language pitfalls like testing mock behavior instead of real behavior, test-only methods on production classes, and incomplete mocks.
| Problem | Solution |
|---|---|
| Don't know how to test it | Write the wished-for API in the test first. Ask your human partner. |
| Test is too complicated | The design is too complicated. Simplify the interface. |
| Must mock everything | Code is too coupled. Use dependency injection. |
| Test setup is huge | Extract helpers; if still complex, the design needs simplifying. |
Before marking work complete:
Can't check every box? You skipped TDD. Start over.
testing-anti-patterns.md are adapted from superpowers (MIT).