Delivers changes incrementally. Use when implementing any feature or change that touches more than one file. Use when you're about to write a large amount of code at once, or when a task feels too big to land in one step.
Installs into .claude/skills of the current project.
Are you the author of Incremental Implementation?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ghosteken-incremental-implementation)
---
name: incremental-implementation
description: Delivers changes incrementally. Use when implementing any feature or change that touches more than one file. Use when you're about to write a large amount of code at once, or when a task feels too big to land in one step.
---
# Incremental Implementation
## Overview
Build in thin vertical slices — implement one piece, test it, verify it, then expand. Avoid implementing an entire feature in one pass. Each increment should leave the system in a working, testable state. This is the execution discipline that makes large features manageable.
## When to Use
- Implementing any multi-file change
- Building a new feature from a task breakdown
- Refactoring existing code
- Any time you're tempted to write more than ~100 lines before testing
**When NOT to use:** Single-file, single-function changes where the scope is already minimal.
## Before You Start: Branch Setup
Before any implementation begins, use the `AskUserQuestion` tool to confirm branch setup — never create a branch silently, even when the answer seems obvious.
1. Check whether `dev` exists (locally or on the remote). If it does, fetch and pull it to make sure it's current *before* branching from it — a feature branch cut from a stale local `dev` silently misses whatever's landed since.
2. Ask via `AskUserQuestion`:
- If `dev` exists: propose creating a feature branch from `dev` (pulled to latest) — confirm before creating it, don't assume yes.
- If `dev` doesn't exist: say so explicitly in the same ask ("`dev` branch not found") and ask which branch to use instead, rather than guessing `main` or any other default.
3. Create the feature branch only after that confirmation comes back.
**Never run `git commit` when the work is done** — this applies to the whole increment cycle below, not just the branch-setup step. Draft commit messages and propose them; leave the actual commit to the user unless they've explicitly asked you to commit in that session.
## The Increment Cycle
```
┌────────────────────────────────────────────┐
│ │
│ Implement ──→ Test ──→ Verify ──┐ │
│ ▲ │ │
│ └───── Propose commit ◄─────┘ │
│ │ │
│ ▼ │
│ More slices? ──yes──→ Next slice │
│ │ │
│ no │
│ ▼ │
│ Run quality-assurance (mandatory, │
│ not a checkbox) ──→ THEN task is done │
│ │
└────────────────────────────────────────────┘
```
For each slice:
1. **Implement** the smallest complete piece of functionality
2. **Test** — every slice gets a unit test, not just slices that "need" one; if the slice has real core logic (a calculation, a state transition, a validation rule), the test must assert its actual expected behavior, not just that it runs without error (see `test-driven-development`'s core-component coverage) — that's what catches a regression later, not just now. That coverage must span every scenario type that genuinely applies to the slice's core logic — happy path, edge cases (boundary/empty/null/optional-absent), error handling (invalid/malformed input), and fix/regression confirmation when the slice exists to fix something — not just whichever one was fastest to write. Run the full suite alongside it.
3. **Verify** — confirm the slice works as expected (unit tests pass, build succeeds, manual check)
4. **Propose a commit** — draft a descriptive message and tell the user the slice is ready (see `git-workflow-and-versioning` for atomic commit guidance). **Never run `git commit` yourself** — leave the actual commit to the user unless they've explicitly asked you to commit in this session.
5. **Move to the next slice** — carry forward, don't restart, and don't wait for the commit to happen first
**When the last slice for this task is done — stop. Before saying the task is complete, actually invoke the `quality-assurance` skill against the spec, and it must be genuinely live — a real server, real database, real authenticated user — not mocked tests or a suite that merely happens to be named "e2e."** This is a mandatory action to take, not a box to mentally check off afterward: in practice, agents following this skill have skipped straight to declaring the task done with unit tests passing, and only run `quality-assurance` when the user notices and asks for it — and even then, have substituted mocked/unit-level tests for live verification without saying so. Don't let either be the trigger — run `quality-assurance` live yourself, unprompted, as the actual last step of the cycle. If full live verification is tedious to set up, `quality-assurance`'s own graduated fallback applies (a lighter live check via curl/CLI or an automated test against the real dev server, offered via `AskUserQuestion`) — never downgrade straight to mocks on your own.
## Slicing Strategies
### Vertical Slices (Preferred)
Build one complete path through the stack:
```
Slice 1: Create a task (DB + API + basic UI)
→ Tests pass, user can create a task via the UI
Slice 2: List tasks (query + API + UI)
→ Tests pass, user can see their tasks
Slice 3: Edit a task (update + API + UI)
→ Tests pass, user can modify tasks
Slice 4: Delete a task (delete + API + UI + confirmation)
→ Tests pass, full CRUD complete
```
Each slice delivers working end-to-end functionality.
### Contract-First Slicing
When backend and frontend need to develop in parallel:
```
Slice 0: Define the API contract (types, interfaces, OpenAPI spec)
Slice 1a: Implement backend against the contract + API tests
Slice 1b: Implement frontend against mock data matching the contract
Slice 2: Integrate and test end-to-end
```
### Risk-First Slicing
Tackle the riskiest or most uncertain piece first:
```
Slice 1: Prove the WebSocket connection works (highest risk)
Slice 2: Build real-time task updates on the proven connection
Slice 3: Add offline support and reconnection
```
If Slice 1 fails, you discover it before investing in Slices 2 and 3.
## Implementation Rules
### Rule 0: Simplicity First
Before writing any code, ask: "What is the simplest thing that could work?"
After writing code, review it against these checks:
- Can this be done in fewer lines?
- Are these abstractions earning their complexity?
- Would a staff engineer look at this and say "why didn't you just..."?
- Am I building for hypothetical future requirements, or the current task?
```
SIMPLICITY CHECK:
✗ Generic EventBus with middleware pipeline for one notification
✓ Simple function call
✗ Abstract factory pattern for two similar components
✓ Two straightforward components with shared utilities
✗ Config-driven form builder for three forms
✓ Three form components
```
Three similar lines of code is better than a premature abstraction. Implement the naive, obviously-correct version first. Optimize only after correctness is proven with tests.
### Rule 0.5: Scope Discipline
Touch only what the task requires.
Do NOT:
- "Clean up" code adjacent to your change
- Refactor imports in files you're not modifying
- Remove comments you don't fully understand
- Add features not in the spec because they "seem useful"
- Modernize syntax in files you're only reading
If you notice something worth improving outside your task scope, note it — don't fix it:
```
NOTICED BUT NOT TOUCHING:
- src/utils/format.ts has an unused import (unrelated to this task)
- The auth middleware could use better error messages (separate task)
→ Want me to create tasks for these?
```
### Rule 1: One Thing at a Time
Each increment changes one logical thing. Don't mix concerns:
**Bad:** One proposed commit that adds a new component, refactors an existing one, and updates the build config.
**Good:** Three separately proposed commits — one for each change (the user runs them, or asks you to).
### Rule 2: Keep It Compilable
After each increment, the project must build and existing tests must pass. Don't leave the codebase in a broken state between slices.
### Rule 3: Feature Flags for Incomplete Features
If a feature isn't ready for users but you need to merge increments:
```typescript
// Feature flag for work-in-progress
const ENABLE_TASK_SHARING = process.env.FEATURE_TASK_SHARING === 'true';
if (ENABLE_TASK_SHARING) {
// New sharing UI
}
```
This lets you merge small increments to the main branch without exposing incomplete work.
### Rule 4: Safe Defaults
New code should default to safe, conservative behavior:
```typescript
// Safe: disabled by default, opt-in
export function createTask(data: TaskInput, options?: { notify?: boolean }) {
const shouldNotify = options?.notify ?? false;
// ...
}
```
### Rule 5: Rollback-Friendly
Each increment should be independently revertable:
- Additive changes (new files, new functions) are easy to revert
- Modifications to existing code should be minimal and focused
- Database migrations should have corresponding rollback migrations
- Avoid deleting something in one commit and replacing it in the same commit — separate them
## Working with Agents
When directing an agent to implement incrementally:
```
"Let's implement Task 3 from the plan.
Start with just the database schema change and the API endpoint.
Don't touch the UI yet — we'll do that in the next increment.
After implementing, run `npm test` and `npm run build` to verify
nothing is broken."
```
Be explicit about what's in scope and what's NOT in scope for each increment.
## Increment Checklist
After each increment, verify:
- [ ] The change does one thing and does it completely
- [ ] A unit test was added for this increment's new logic
- [ ] All existing tests still pass (`npm test`)
- [ ] The build succeeds (`npm run build`)
- [ ] Type checking passes (`npx tsc --noEmit`)
- [ ] Linting passes (`npm run lint`)
- [ ] The new functionality works as expected
- [ ] A descriptive commit message has been proposed for the change (do not run `git commit` yourself — leave it to the user unless they've explicitly asked you to commit)
**Note:** Run each verification command after a change that could affect it. After a successful run, don't repeat the same command unless the code has changed since — re-running on unchanged code adds no information.
## Common Rationalizations
| Rationalization | Reality |
|---|---|
| "I'll test it all at the end" | Bugs compound. A bug in Slice 1 makes Slices 2-5 wrong. Test each slice. |
| "This slice is too trivial for a unit test" | Trivial slices are where regressions hide longest — no one re-checks something assumed too simple to break. |
| "It's faster to do it all at once" | It *feels* faster until something breaks and you can't find which of 500 changed lines caused it. |
| "These changes are too small to commit separately" | Small commits are free. Large commits hide bugs and make rollbacks painful — propose them as separate commits even though the user runs the actual `git commit`. |
| "I'll add the feature flag later" | If the feature isn't complete, it shouldn't be user-visible. Add the flag now. |
| "This refactor is small enough to include" | Refactors mixed with features make both harder to review and debug. Separate them. |
| "Let me run the build command again just to be sure" | After a successful run, repeating the same command adds nothing unless the code has changed since. Run it again after subsequent edits, not as reassurance. |
| "All the unit tests pass, so the feature works" | Unit tests confirm the code does what the code does — they don't confirm it does what the spec asked for. Run `quality-assurance` for that. |
| "I'll just branch from dev without asking, it's the obvious choice" | Ask anyway — confirming before creating a branch costs one question and prevents working on the wrong base entirely. |
| "dev doesn't exist, I'll just use main" | Guessing a fallback base branch silently can put the feature on the wrong branch structure for this project — ask which branch to use instead. |
| "I tested that this slice rejects bad input, it's covered" | That's one scenario type. A slice's core logic needs happy path, edge cases, error handling, and fix/regression confirmation covered together, as each genuinely applies — not just whichever was fastest to write. |
## Red Flags
- More than 100 lines of code written without running tests
- Multiple unrelated changes in a single increment
- "Let me just quickly add this too" scope expansion
- Skipping the test/verify step to move faster
- Build or tests broken between increments
- Large uncommitted changes accumulating
- Building abstractions before the third use case demands it
- Touching files outside the task scope "while I'm here"
- Creating new utility files for one-time operations
- Running the same build/test command twice in a row without any intervening code change
- Running `git commit` yourself without the user explicitly asking you to in that session
- The feature declared done once unit tests pass, without running `quality-assurance` against the spec
- `quality-assurance` run against mocks or a suite merely named "e2e", reported as if it were genuinely live verification
- A slice's core-logic test covering only one scenario type (e.g. only error/rejection handling) when happy path, edge cases, or fix confirmation genuinely applied too
- A feature branch created without asking first, or without pulling `dev` to latest before branching from it
- `dev` missing and a fallback branch picked silently instead of asked about
## Verification
Before starting:
- [ ] Branch setup was confirmed via `AskUserQuestion` before any implementation began — not assumed
- [ ] If `dev` existed, it was pulled to latest before the feature branch was created from it
- [ ] If `dev` didn't exist, that was stated explicitly and an alternative base branch was asked for, not guessed
After completing all increments for a task:
- [ ] Each increment was individually tested (a unit test was added, not just an existing-suite check) and left with a proposed commit message
- [ ] Each slice's core-logic test covers every scenario type that genuinely applies (happy path, edge cases, error handling, fix/regression confirmation) — not just the one type that was fastest to check
- [ ] The full test suite passes
- [ ] The build is clean
- [ ] The feature works end-to-end as specified — run the `quality-assurance` skill *live* (real server, real database, real authenticated user) against the spec/acceptance criteria to confirm this with real evidence, not just unit tests passing and not mocks substituted for live verification
- [ ] No unreviewed changes remain — the user has a summary and a proposed commit message for each increment; committing is theirs to do (or explicitly delegated to you)
## See Also
- `quality-assurance` — the live/end-to-end check once all increments are complete; unit tests alone don't confirm the feature works against the spec
- `references/coding-patterns.md` — structural patterns to apply while implementing each slice (clear main path, external systems behind a boundary, unrepresentable invalid states, decisions separated from actions, useful errors)
- `review-findings.md` at the project's external output location (see `references/external-output-paths.md`) — check it before starting a task, if it exists; it's a running log of patterns code review has already flagged in this project, and repeating one is avoidable