agentleFS
johnqtcgjohnqtcg/awesome-skills/skills/tdd-workflow/SKILL.md

tdd-workflow

Skill30 starsChanged 4 months agoMITView on GitHub

Enforce practical Test-Driven Development for code changes in Go services. Use for new features, bug fixes, refactors, API changes, and new modules. Requires Red-Green-Refactor evidence, defect-hypothesis-driven tests, killer cases, and coverage gates (line + risk-path).

128 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use brand-discovery, configure-ecc and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • ysyecustSkill

    brand-discovery

    Use when a brand needs to discover or articulate its identity through structured multi-session interviews. Covers purpose, positioning, audience, personality, voice, narrative, and founder-brand tension across 8 modules using laddering, 5 Whys, and projective techniques. Produces a resumable session with disk-persisted state and a master brandbook (90_SYNTHESIS.md).

    Also in 6 projects that use this

  • royaluniondesign-sysSkill

    configure-ecc

    Interactive installer for Everything Claude Code — guides users through selecting and installing skills and rules to user-level or project-level directories, verifies paths, and optionally optimizes installed files.

    Also in 12 projects that use this

  • junimnjwSkill

    ralphinho-rfc-pipeline

    RFC-driven multi-agent DAG execution pattern with quality gates, merge queues, and work unit orchestration.

    Also in 12 projects that use this

  • loulanyueSkill

    nuxt4-patterns

    Nuxt 4 app patterns for hydration safety, performance, route rules, lazy loading, and SSR-safe data fetching with useFetch and useAsyncData.

    Also in 10 projects that use this

  • royaluniondesign-sysSkill

    content-hash-cache-pattern

    Cache expensive file processing results using SHA-256 content hashes — path-independent, auto-invalidating, with service layer separation.

    Also in 16 projects that use this

  • RojiosSkill

    dart-flutter-patterns

    Production-ready Dart and Flutter patterns covering null safety, immutable state, async composition, widget architecture, popular state management frameworks (BLoC, Riverpod, Provider), GoRouter navigation, Dio networking, Freezed code generation, and clean architecture.

    Also in 7 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

19 rules. 0 are common, 10 are distinctive, and 9 are very specific to this project.

  • 0

    Common

    Most of the 117 edited versions we read keep these.

  • 10

    Distinctive

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Start with tests, not implementation.

    line 13
  2. Very specific:

    Keep test files co-located and named <target>_test.go.

    line 15
  3. Very specific:

    If project uses standard library only: use t.Fatalf for fatal, t.Errorf for value checks.

    line 18
  4. Very specific:

    If project uses go-cmp: use cmp.Diff for deep struct comparison.

    line 19
  5. Very specific:

    Detection: Check existing _test.go files for "github.com/stretchr/testify" imports.

    line 20
  6. Very specific:

    Prefer table-driven tests with t.Run.

    line 21
  7. Distinctive:

    Prefer real deps or lightweight fakes.

    line 22
  8. Distinctive:

    Do not add speculative production code not required by failing tests.

    line 23
  9. Distinctive:

    Before writing tests, list concrete defect hypotheses from target code:

    line 29
  10. Distinctive:

    If gate waived, require explicit user approval and documented risk.

    line 51
  11. Distinctive:

    Never claim tests/coverage were run unless actually executed.

    line 55
  12. Distinctive:

    If executed: report exact commands and key result lines.

    line 57
  13. Very specific:

    If not executable in current environment: report Not run in this environment and provide exact run commands.

    line 58
  14. Very specific:

    avoid time.Sleep for synchronization

    line 64
  15. Very specific:

    use channels/barriers/waitgroups/atomics to control ordering

    line 65
  16. Distinctive:

    If exceeding range, justify by distinct logic paths.

    line 78
  17. Very specific:

    Write behavior contract (Given/When/Then bullets).

    line 83
  18. Distinctive:

    PASS: All Critical pass AND ≥4/5 Standard AND ≥3/4 Hygiene

    line 224
  19. Distinctive:

    Run regression before publishing changes:

    line 262

“Common” is measured: kept by at least 6 in 10 of the 117 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Go TDD Workflow

Apply TDD end-to-end: write failing tests first, implement minimal code, refactor safely, and prove quality with coverage plus risk-path checks.

Hard Rules

  • Start with tests, not implementation.
  • Preserve visible Red -> Green -> Refactor evidence in commands/output.
  • Keep test files co-located and named <target>_test.go.
  • Assertion strategy (adapt to project):
    • If project uses testify: require for fatal preconditions, assert for value checks.
    • If project uses standard library only: use t.Fatalf for fatal, t.Errorf for value checks; include got/want: t.Errorf("Name = %q, want %q", got, want).
    • If project uses go-cmp: use cmp.Diff for deep struct comparison.
    • Detection: Check existing _test.go files for "github.com/stretchr/testify" imports. Follow project convention.
  • Prefer table-driven tests with t.Run.
  • Prefer real deps or lightweight fakes; avoid heavy mock chains by default.
  • Do not add speculative production code not required by failing tests.

New Mandatory Gates

1) Defect Hypothesis Gate

Before writing tests, list concrete defect hypotheses from target code:

  • boundary/index (n-1, i+1, last-item behavior)
  • error propagation/wrapping
  • mapping loss/data mismatch
  • concurrency/order/timing
  • idempotency/retry behavior

Each hypothesis must map to at least one test case name. For detailed defect-hypothesis patterns and BAD/GOOD examples, cross-reference the unit-test skill: references/bug-finding-techniques.md, references/killer-case-patterns.md, and Fixed Boundary Checklist.

2) Killer Case Gate

Per changed method/use-case, add at least one killer case that:

  • targets a high-risk defect hypothesis
  • includes assertion(s) that must fail on known bad mutation/path
  • is explicitly marked in report

3) Coverage Gate (Line + Risk Path)

  • Line coverage gate: changed package(s) >=80% by default.
  • Risk-path gate: all high-risk branches/hypotheses must be covered even if line coverage already passes.
  • If gate waived, require explicit user approval and documented risk.

4) Execution Integrity Gate

Never claim tests/coverage were run unless actually executed.

  • If executed: report exact commands and key result lines.
  • If not executable in current environment: report Not run in this environment and provide exact run commands.

5) Concurrency Determinism Gate

For concurrency-sensitive code:

  • avoid time.Sleep for synchronization
  • use channels/barriers/waitgroups/atomics to control ordering
  • run with -race

6) Change-Size Test Budget Gate

Choose test depth by change size to avoid test bloat. Use concrete criteria first, then case budget:

SizeCriteriaTest Budget
S≤2 files touched, ≤50 LOC, single critical path3-6 cases/method, essential regression
M3-5 files, 50-150 LOC, or 2 critical paths6-12 cases/method, selected cross-package regression
L>5 files, >150 LOC, or 3+ critical paths10-20 cases/method + broader regression matrix

If exceeding range, justify by distinct logic paths. For security-sensitive code (auth, input validation, SSRF guards, crypto), the budget may be doubled — document the security rationale in the output contract.

Workflow

  1. Classify change size (S/M/L) and target scope.
  2. Write behavior contract (Given/When/Then bullets).
  3. Build defect hypothesis list and map each to tests.
  4. Red: write failing tests first (include killer case).
    • Characterization testing (for pre-existing code): when adding tests AFTER implementation already exists, satisfy the Red evidence gate via one of:
      • Mutation: temporarily break the production code, verify new tests fail, then revert.
      • Hypothesis: document the specific defect hypothesis each test targets — the hypothesis itself serves as Red evidence that the test guards a real risk.
  5. Green: implement minimal code to pass failing tests.
  6. Refactor: improve structure without behavior change.
  7. Validate quality gates:
    • focused tests
    • coverage (line + risk-path)
    • -race when concurrency is relevant
  8. Report with evidence and residual risks.

Command Playbook (Go)

# Red phase (expect at least one fail)
go test ./path/to/pkg -run TestXxx -v

# Green phase
go test ./path/to/pkg -v

# Coverage (line)
go test ./path/to/pkg -coverprofile=coverage.out
go tool cover -func=coverage.out

# Concurrency safety (when applicable)
go test ./path/to/pkg -race -v

# Broader regression (as needed)
go test ./...

Anti-Examples (Core TDD Mistakes)

For the full set of 7 anti-examples, read references/anti-examples.md. The most critical TDD mistakes are inlined here so the contract is self-contained:

Mistake 1: Writing all tests before any implementation (Big-Bang Red)

// BAD: 15 test cases written at once before any production code
// You lose the tight feedback loop — hard to tell which test drives which behavior
func TestUserService(t *testing.T) {
	t.Run("create", func(t *testing.T) { /* ... 5 subtests */ })
	t.Run("update", func(t *testing.T) { /* ... 5 subtests */ })
	t.Run("delete", func(t *testing.T) { /* ... 5 subtests */ })
}
// Then implement everything at once — this is test-first, NOT TDD

// GOOD: one failing test → minimal implementation → next failing test
// Iteration 1: TestUserService/create/success → implement Create()
// Iteration 2: TestUserService/create/duplicate_email → add uniqueness check
// Each cycle is Red → Green → Refactor before moving on

Mistake 2: Testing implementation details instead of behavior

// BAD: locks test to implementation details and blocks safe refactor
repo.AssertCalled(t, "Save", mock.Anything, user)
// GOOD: assert observable behavior such as returned state, persisted fields, and domain errors
require.Equal(t, "active", got.Status)

Mistake 3: Refactor phase changes observable behavior

// BAD: "refactor" silently changes API behavior
func NormalizeEmail(s string) string { return strings.TrimSpace(strings.ToLower(s)) }
// GOOD: keep behavior unchanged during refactor; add a new Red cycle for any behavior change
func normalizeEmail(s string) string { return strings.TrimSpace(s) }

Mistake 4: Skipping Red evidence — "it compiles so it works"

// BAD: write test and implementation simultaneously, never see a failure
func TestAdd(t *testing.T) {
	// Written AFTER Add() was already implemented
	assert.Equal(t, 3, Add(1, 2))
}

// GOOD: write test FIRST, run it, see it FAIL, then implement
// Step 1: write test → go test → FAIL (Add undefined)
// Step 2: stub: func Add(a, b int) int { return 0 }
//         go test → FAIL (got 0, want 3)
// Step 3: implement: return a + b → go test → PASS
// Red evidence proves the test actually validates something

Mistake 5: Change-size mismatch

// BAD: S change but 40 test cases and 6 helper files added without new logic paths
func TestTinyBugfix(t *testing.T) { /* massive matrix */ }
// GOOD: size the regression to the change budget, then justify any extra cases with distinct risk paths
func TestTinyBugfix_LastElementBoundary(t *testing.T) { /* focused killer case */ }

Mistake 6: Speculative helper extraction before tests demand it

// BAD: extract helpers and abstractions before any failing test proves the need
type orchestrator struct{ repo Repo; audit Audit; cache Cache }
// GOOD: add the smallest code needed for the current Red case, then refactor after Green
func createUser(repo Repo, in CreateUserInput) (User, error) { /* minimal path */ }

Quality Scorecard

Mark each as PASS / FAIL / N/A (reason).

Critical (all must pass for overall PASS)

#CheckCriteria
C1Red evidence existsFailing test demonstrated before implementation
C2Killer case presentEach changed method/use-case has at least one killer case
C3Risk-path coverageAll high-risk hypotheses have test coverage

Standard (≥4/5 must pass)

#CheckCriteria
S1Defect hypothesis listExists and maps to test case names
S2Success + error + boundary pathsAll three path categories covered
S3High-signal assertionsBusiness fields asserted, not just nil/not-nil
S4Coverage ≥80%Changed package(s) meet line coverage gate
S5Execution integrityRun evidence provided, or explicit not-run note

Hygiene (≥3/4 must pass)

#CheckCriteria
H1Test file naming/location<target>_test.go, co-located
H2Subtest hierarchyt.Run with clear naming
H3Table-driven styleMulti-scenario behavior uses table-driven pattern
H4Regression scopeFinal regression proportionate to change size (S/M/L)

Scoring:

  • PASS: All Critical pass AND ≥4/5 Standard AND ≥3/4 Hygiene
  • FAIL: Any Critical fails → overall FAIL regardless of other scores

Output Contract

  • Changed files
  • Change size: S/M/L with reason
  • Defect hypotheses -> test mapping
  • Killer cases
  • Red -> Green evidence (or Not run in this environment)
  • Coverage (line + risk-path)
  • Scorecard (3-tier: Critical / Standard / Hygiene)
  • Residual risks / follow-ups — must include:
    • untested edge cases beyond budget
    • design limitations of the target function
    • security implications for sensitive code (e.g., bypass vectors, upstream stdlib assumptions)
    • dependencies on upstream behavior (stdlib, libraries, environment)

References (Load Selectively)

Always read (every TDD task):

  • references/boundary-checklist.md — defect hypothesis patterns and killer case design

Read for API/service layer TDD (skip for pure-function or utility TDD):

  • references/api-3layer-template.md — Handler/Service/Repo TDD template
  • references/fake-stub-template.md — fake design and error injection

Read for first-time TDD or complex refactors:

  • references/tdd-workflow.md — end-to-end walkthrough, Outside-In vs Inside-Out, characterization testing

Read when reviewing or generating TDD code:

  • references/anti-examples.md — 5 additional TDD mistakes with BAD/GOOD examples

Read for characterization testing (adding tests to pre-existing code):

  • references/golden-characterization-example.md — full output contract with mutation-based Red evidence for a security-sensitive function

Skill Maintenance

Run regression before publishing changes:

scripts/run_regression.sh

Validates frontmatter, gates, anti-examples, scorecard, references, and golden fixtures.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.