agentleFS
robissonrobisson/build-like-amazon-agent-skills/skills/test-driven-development/SKILL.md

test-driven-development

Skill16 starsChanged 37 days agoMITView on GitHub

Red-Green-Refactor cycle with Amazon's test pyramid (80% unit, 15% integration, 5% e2e). Coverage gates, DAMP over DRY in tests, canary tests in production.

190 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use chinese-code-review, chinese-commit-conventions 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.
  • Leodorareluctant259Skill

    chinese-code-review

    中文代码审查规范——在保持专业严谨的同时,用符合国内团队文化的方式给出有效反馈

    Also in 8 projects that use this

  • Leodorareluctant259Skill

    chinese-commit-conventions

    中文 Git 提交规范 — 适配国内团队的 commit message 规范和 changelog 自动化

    Also in 8 projects that use this

  • obraSkill

    diagnosing-superpowers

    Use when a superpowers session went wrong and your human partner wants to know why — repeated work, ignored plans, stumbles, poor results, a skill that didn't fire, "it took too long", "why is it so expensive", "what is it doing" — or wants to build a bug report for the superpowers maintainers, for the current session or a past one identified by id or path, on any harness.

    Also in 19 projects that use this

  • LLl0k0laDSkill

    using-agent-skills

    Use when starting a session or when you need to discover which skill applies to the current task. This is the meta-skill that governs how all other skills are discovered and invoked.

    Also in 39 projects that use this

  • LLl0k0laDSkill

    git-workflow-and-versioning

    Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams.

    Also in 47 projects that use this

  • MrCipherSmithSkill

    constraint-driven-development

    Use when a project needs a persistent, measurable quality bar — starting a project without documented standards, 'set up constraints', or when coverage/perf/a11y thresholds get re-debated per-PR. Produces CONSTRAINTS.md with enforceable numbers, ratchets, and a guard against silent weakening. NOT for throwaway prototypes or a one-off CI setup.

    Also in 15 projects that use this

  • +3 more from Leodorareluctant259
  • +3 more from LLl0k0laD

The file

Show its rules and text

We split this skill into the rules it gives an agent

18 rules. 0 are common, 17 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 178 edited versions we read keep these.

  • 17

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Test-Driven Development (TDD) at Amazon means writing the test before the implementation—always.

    line 14
  2. Distinctive:

    Before writing any production code (the "Red" step comes first)

    line 22
  3. Distinctive:

    When fixing a bug (write the failing test that reproduces it first)

    line 23
  4. Distinctive:

    When refactoring existing code (ensure tests exist before changing behavior)

    line 24
  5. Distinctive:

    When setting up deployment pipelines (configure coverage gates)

    line 26
  6. Distinctive:

    When onboarding a new service (establish canary tests immediately)

    line 27
  7. Distinctive:

    Write one test that describes desired behavior

    line 54
  8. Distinctive:

    Run it. It must fail. If it passes, your test is wrong.

    line 55
  9. Distinctive:

    Write the minimum code to make the test pass

    line 59
  10. Distinctive:

    No premature abstraction. No "while I'm here" changes.

    line 60
  11. Distinctive:

    Run tests after every change.

    line 66
  12. Distinctive:

    Use dedicated canary accounts/data (never real customer data)

    line 192
  13. Distinctive:

    Run in every region independently

    line 196
  14. Distinctive:

    Test suite takes more than 10 minutes to run locally

    line 235
  15. Very specific:

    Test names are test1, test2, testHelper (meaningless)

    line 238
  16. Distinctive:

    No canary tests exist for a production service

    line 241
  17. Distinctive:

    After implementing with TDD, verify:

    line 246
  18. Distinctive:

    Red before Green: Never write production code without a failing test.

    line 260

“Common” is measured: kept by at least 6 in 10 of the 178 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

Test-Driven Development

Overview

Test-Driven Development (TDD) at Amazon means writing the test before the implementation—always. The Red-Green-Refactor cycle is not a suggestion; it is the mechanism that ensures code correctness, enables fearless refactoring, and keeps deployment pipelines green.

Amazon's test philosophy follows the test pyramid: a broad base of fast unit tests (80%), a middle layer of integration tests (15%), and a thin cap of end-to-end tests (5%). This ratio exists because unit tests are fast, deterministic, and cheap to maintain. Integration tests verify contracts between components. End-to-end tests validate customer-visible behavior but are slow and brittle.

Beyond pre-deployment testing, Amazon runs canary tests in production—synthetic requests that continuously verify real system behavior. When canaries fail, alarms fire before customers notice.

When to Use

  • Before writing any production code (the "Red" step comes first)
  • When fixing a bug (write the failing test that reproduces it first)
  • When refactoring existing code (ensure tests exist before changing behavior)
  • During code review (verify test coverage for every behavior change)
  • When setting up deployment pipelines (configure coverage gates)
  • When onboarding a new service (establish canary tests immediately)

Amazon Context

At Amazon, untested code does not ship. Pipelines enforce minimum coverage thresholds (typically 80% line coverage, 90% branch coverage for critical paths). Code review explicitly evaluates test quality—a change without tests is rejected regardless of how correct the implementation appears.

The CI/CD toolchain (build system, deployment pipelines, code analysis) integrates testing at every stage: pre-commit hooks run unit tests locally, CI runs the full suite, and post-deployment canaries verify production health. Teams own their test infrastructure the same way they own their production infrastructure.

Amazon's testing culture learned from expensive failures: services that shipped with "we'll add tests later" invariably accumulated tech debt that made changes risky, deployments scary, and on-call rotations miserable. TDD inverts this—tests are the first thing you build, not the last.

The Process

1. Red-Green-Refactor Cycle

┌─────────────────────────────────────────┐
│  RED: Write a failing test              │
│  ↓                                       │
│  GREEN: Write minimum code to pass      │
│  ↓                                       │
│  REFACTOR: Clean up, tests still pass   │
│  ↓                                       │
│  Repeat                                  │
└─────────────────────────────────────────┘

Red Phase:

  • Write one test that describes desired behavior
  • Run it. It must fail. If it passes, your test is wrong.
  • The test name describes the behavior: test_order_rejected_when_inventory_zero

Green Phase:

  • Write the minimum code to make the test pass
  • No premature abstraction. No "while I'm here" changes.
  • Hardcode values if that's all the test requires—the next test will force generalization.

Refactor Phase:

  • Now clean up both production and test code
  • Extract methods, rename variables, remove duplication
  • Run tests after every change. All must stay green.

2. Test Pyramid

LayerPercentageScopeSpeedCharacteristics
Unit80%Single class/function< 10ms eachNo I/O, no network, no database. Mocked dependencies.
Integration15%Component boundaries< 5s eachReal database (local), real HTTP calls (to test containers).
End-to-End5%Full customer journey< 60s eachReal infrastructure. Fragile. Few but critical.

3. Writing Effective Unit Tests (DAMP over DRY)

DAMP = Descriptive And Meaningful Phrases. In tests, clarity beats brevity.

# BAD: DRY but unreadable
def test_order(self):
    order = self._make_order(status=3, items=2)
    self.assertEqual(order.total, self._expected(2))

# GOOD: DAMP - each test tells a complete story
def test_order_total_sums_item_prices_with_tax(self):
    order = Order(
        items=[
            OrderItem(name="Widget", price=Money(10, "USD")),
            OrderItem(name="Gadget", price=Money(20, "USD")),
        ],
        tax_rate=Decimal("0.08"),
    )

    total = order.calculate_total()

    assert total == Money(32.40, "USD")  # (10 + 20) * 1.08

DAMP principles:

  • Arrange-Act-Assert structure in every test (visible sections)
  • No shared setup that hides context — if a test needs data, show it in the test
  • Test names are documentation — test_<behavior>_when_<condition>_then_<outcome>
  • One assertion per test (one logical assertion; multiple assert calls for one concept is fine)
  • Helper factories over fixtures — make_order(status="pending") beats self.order

4. Coverage Gates

Configure in CI/CD pipeline:

coverage:
  minimum:
    line: 80
    branch: 80
  critical_paths:
    line: 95
    branch: 90
  fail_on_decrease: true
  exclude:
    - "*/generated/*"
    - "*/migrations/*"

Rules:

  • Coverage can never decrease on a PR (ratchet mechanism)
  • Critical paths (payment, auth, data integrity) require 95%+
  • New files must have 90%+ coverage or PR is blocked
  • Coverage is measured on production code only (not test code)

5. Test Categories and Tagging

@pytest.mark.unit
def test_calculate_shipping_cost():
    ...

@pytest.mark.integration
def test_order_persisted_to_dynamodb():
    ...

@pytest.mark.e2e
def test_customer_can_complete_checkout():
    ...

@pytest.mark.canary
def test_homepage_loads_under_2_seconds():
    ...

Execution contexts:

  • Pre-commit: Unit tests only (< 30 seconds total)
  • CI pipeline: Unit + Integration (< 10 minutes total)
  • Pre-deployment: Unit + Integration + E2E (< 30 minutes)
  • Production: Canary tests (continuous, every 1-5 minutes)

6. Canary Tests in Production

Canary tests are synthetic requests that exercise real production infrastructure:

class OrderServiceCanary:
    """Runs every 60 seconds against production."""

    def test_create_order_happy_path(self):
        """Place a test order and verify it appears in the system."""
        order = self.client.create_order(
            customer_id=CANARY_CUSTOMER_ID,
            items=[CANARY_ITEM],
            idempotency_key=f"canary-{uuid4()}",
        )
        assert order.status == "PLACED"
        assert order.id is not None

        # Verify read-after-write
        retrieved = self.client.get_order(order.id)
        assert retrieved.status == "PLACED"

        # Cleanup
        self.client.cancel_order(order.id, reason="CANARY_TEST")

    def test_order_service_latency(self):
        """Verify p99 latency is under SLA."""
        start = time.monotonic()
        self.client.get_order(KNOWN_ORDER_ID)
        latency_ms = (time.monotonic() - start) * 1000
        assert latency_ms < 500, f"Latency {latency_ms}ms exceeds 500ms SLA"

Canary rules:

  • Use dedicated canary accounts/data (never real customer data)
  • Clean up after themselves (no test data accumulation)
  • Alert on failure within 5 minutes (high-severity alarm)
  • Cover critical customer journeys (top 3-5 paths)
  • Run in every region independently

7. Test Quality Checklist

For every PR, verify tests meet the bar:

  • Tests fail for the right reason (verified during Red phase)
  • Tests are independent (can run in any order)
  • Tests are deterministic (no flaky timing dependencies)
  • Tests are fast (unit < 10ms, integration < 5s)
  • Test names describe behavior, not implementation
  • No logic in tests (no if, no loops, no try/catch)
  • Mocks verify interactions, not implementation details
  • Boundary conditions tested (empty, null, max, overflow)
  • Error paths tested (exceptions, timeouts, invalid input)

Mechanisms Over Good Intentions

MechanismWhat It EnforcesHow
Coverage gates in CIMinimum test coveragePR blocked if coverage below threshold or decreases
Pre-commit test hooksTests run before pushGit hook executes unit tests; push rejected on failure
Canary alarm escalationProduction healthPagerDuty alert if canary fails 3 consecutive times
Test-to-code ratio metricSufficient test investmentDashboard tracks ratio; reviewed in sprint retrospective
Mutation testing (periodic)Test effectivenessMonthly mutation run; low mutation kill rate triggers action

Common Rationalizations

RationalizationWhy It's WrongWhat To Do Instead
"I'll add tests after the implementation is stable"Code without tests is never stable. It's unknown. You'll ship bugs, then write tests around the bugs, cementing them as "expected behavior."Write the test first. Always. The test defines what "stable" means.
"This code is too simple to test"Simple code becomes complex code. When it does, you'll be afraid to change it. Getters today become business logic tomorrow.Test the behavior, not the complexity. Simple tests for simple code are fast to write and valuable as documentation.
"Mocking makes tests brittle"Over-mocking makes tests brittle. Strategic mocking at boundaries enables fast, deterministic tests.Mock at architectural boundaries (database, network, clock). Don't mock internal collaborators—test them together.
"100% coverage is our goal"Coverage measures lines executed, not correctness. 100% coverage with no assertions is useless. Quality over quantity.Target meaningful coverage: 80% overall, 95% for critical paths. Focus on branch coverage and mutation testing for quality.
"End-to-end tests give more confidence"E2E tests are slow, flaky, and expensive. A failing E2E test tells you something broke but not what. They create false confidence.Invest in the pyramid. Strong unit tests + focused integration tests give better fault isolation than E2E suites.

Red Flags

  • PR has production code changes but no new or modified tests
  • Test suite takes more than 10 minutes to run locally
  • Tests break when implementation details change (over-mocking)
  • Flaky tests are "retried until green" instead of fixed
  • Test names are test1, test2, testHelper (meaningless)
  • Tests have complex setup shared across unrelated tests
  • Coverage is high but bugs still escape to production (assertion-free tests)
  • No canary tests exist for a production service
  • Team says "we'll write tests during the hardening sprint"

Verification

After implementing with TDD, verify:

  • Every public behavior has at least one test (preferably written first)
  • Test pyramid ratios are approximately correct (80/15/5)
  • Coverage gates are configured and enforced in CI
  • All tests are deterministic (run suite 3x, same results)
  • Canary tests are running in production and alarming on failure
  • Test suite runs in under 10 minutes in CI
  • Mutation testing kill rate is above 70%
  • New team members can understand behavior from test names alone
  • No test relies on test execution order

Tenets

  1. Red before Green. Never write production code without a failing test. The test defines the requirement. No test = no requirement = no code.
  2. Tests are production code. They deserve the same quality, review, and maintenance attention. Sloppy tests create sloppy confidence.
  3. Fast feedback above all. A test that takes 10 seconds to tell you something is broken is 1000x more valuable than a test that takes 10 minutes.
  4. DAMP over DRY in tests. Readability and independence matter more than avoiding duplication. Each test is a specification—it should be self-contained.
  5. Test behavior, not implementation. Tests should survive refactoring. If renaming a private method breaks tests, you're testing the wrong thing.
  6. Canaries never sleep. Production testing is not optional. If you can't verify your service works right now, you don't know if it works right now.
  7. Fix flaky tests immediately. A flaky test is worse than no test—it trains the team to ignore failures. Delete it or fix it. Today.

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.