SuperAPI AI Directory

incremental-implementation

Coding & development

View repository

Updated 10-16 08:44

About this skill

Incremental Implementation

Overview

Build in thin vertical slices: implement a small piece, test it, verify it, then expand. Avoid implementing the entire feature at once. 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 want to write more than about 100 lines of code before testing

When not to use: Single-file, single-function changes whose scope is already minimized.

The Increment Loop

┌──────────────────────────────────────┐
│                                      │
│   Implement ──→ Test ──→ Verify ──┐  │
│       ▲                           │  │
│       └───── Commit ◄─────────────┘  │
│              │                       │
│              ▼                       │
│          Next slice                  │
│                                      │
└──────────────────────────────────────┘

For each slice:

  1. Implement The smallest complete functional piece
  2. Test: run the test suite (or write tests if there are none)
  3. Verify: confirm the slice works as expected (tests pass, build succeeds, manual check)
  4. Commit: save progress with a descriptive message (for atomic commit guidance see git-workflow-and-versioning)
  5. Move to the next slice: keep moving forward, don't start over

Slicing Strategies

Vertical Slicing (Preferred)

Build one complete path through the entire 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 the backend and frontend need to be developed 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

Handle the highest-risk or most uncertain part 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'll find out before investing in Slice 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 with these checks:

  • Could this be done with less code?
  • Are these abstractions worth the complexity they bring?
  • Would a staff engineer look at this and say "why not just..."?
  • Am I building for an imaginary future need, or for 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 lines of similar code are better than premature abstraction. Implement the naive, obviously correct version first. Optimize only after tests prove correctness.

Rule 0.5: Scope Discipline

Only touch what the task requires.

Don't:

  • "Clean up" code adjacent to your change
  • Refactor imports in files you didn't modify
  • Remove comments you don't fully understand
  • Add features beyond the spec because they "seem useful"
  • Modernize syntax in files you're only reading

If you notice something outside the task scope that's worth improving, 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: Do One Thing at a Time

Each increment changes only one logical thing. Don't mix concerns:

Bad example: One commit that adds a new component, refactors an existing component, and updates the build config at the same time.

Good example: Three separate commits, one for each change.

Rule 2: Keep It Compilable

After each increment, the project must be able to build, and existing tests must pass. Do not leave the codebase in a broken state between slices.

Rule 3: Use Feature Flags for Unfinished Features

If a feature is not yet ready for users but you need to merge increments:

// 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 into the main branch without exposing unfinished work.

Rule 4: Safe Defaults

New code should default to safe, conservative behavior:

// Safe: disabled by default, opt-in
export function createTask(data: TaskInput, options?: { notify?: boolean }) {
  const shouldNotify = options?.notify ?? false;
  // ...
}

Rule 5: Easy to Roll Back

Each increment should be independently revertible:

  • Incremental changes (new files, new functions) are easy to revert
  • Modifications to existing code should be minimal and focused
  • Database migrations should have a corresponding rollback migration
  • Avoid deleting something and then replacing it in one commit; separate them

Collaborating with Agents

When guiding 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 the repository's test and build commands to
verify nothing is broken."

Clearly state what is in scope and what is out of scope for each increment.

Increment Checklist

After each increment, verify using the repo's own commands (see the test-driven-development skill's Discover the Stack First section):

  • The change does one thing and is fully complete
  • All existing tests still pass (the repo's test command:npm test、./gradlew test、pytest , etc.)
  • The build succeeds (the repo's build command)
  • Type checking passes, if the stack has type checking (npx tsc --noEmit、mypy , etc.)
  • Lint passes (the repo's lint command)
  • The new feature works as expected
  • The change is committed with a descriptive message

Note: After every change that would affect verification results, run the corresponding verification command. After one successful run, do not repeat the same command unless the code has changed; repeating a run on unchanged code adds no information.

Common Rationalizations

Rationalization Reality
"I'll test it all together at the end" Bugs compound. A bug in Slice 1 will make Slices 2-5 all wrong. Every slice must be tested.
"It's faster to do it all at once" Itfeelsfaster, until something breaks and you can't figure out which line among 500 lines of changes caused the problem.
"These changes are too small to be worth separate commits" Small commits are free. Large commits hide bugs and make rollback painful.
"I'll add the feature flag later" If a feature is unfinished, it should not be visible to users. Add the flag now.
"This refactor is small enough to bring along" Refactoring mixed in with a feature makes both harder to review and debug. Separate them.
"I'll run the build command again to confirm" After one successful run, repeating the same command is meaningless unless the code has changed. Run it again after subsequent edits; don't use it as a placebo.

Red Flags

  • Wrote more than 100 lines of code without running tests
  • A single increment contains multiple unrelated changes
  • "Let me also add this while I'm at it" scope creep
  • Skipping the test/verify step to go faster
  • Build or tests are broken between increments
  • A large number of uncommitted changes pile up
  • Building abstractions before the third use case actually needs them
  • Touching files outside the task scope because "I'm here anyway"
  • Creating a new utility file for a one-off operation
  • Running the same build/test command twice in a row without any code changes

Verification

After completing all increments of a task:

  • Each increment has been tested and committed separately
  • The full test suite passes
  • The build is clean
  • The feature works end-to-end per the spec
  • There are no uncommitted changes

See also

Verification of each increment is only a local check. Before declaring the task complete, apply the project-level Definition of Done as the final gate. It is the established standard that every increment must pass regardless of the task. See ../../references/definition-of-done.md。