---
name: incremental-commits
description: Break multi-file changes into atomic commits ordered by dependency. Use for "split this into commits", "commit strategy", "break this up", refactors.
metadata:
author: epicenter
version: '1.0'
---
# Incremental Commits
When a feature touches multiple files, implement in **waves**. Each wave is one logical concern, one standalone commit, ordered by dependency. This creates a clean git history that reviewers can audit one commit at a time.
> **Related Skills**: See `standalone-commits` for making each wave reviewable and auditable. See `git` for commit message conventions and PR guidelines.
## Relationship To Standalone Commits
`incremental-commits` decides commit order. `standalone-commits` decides whether each commit boundary is good.
Use this skill to plan the sequence:
```
foundation -> implementation -> consumers -> cleanup
```
Then use `standalone-commits` to test each wave:
```
Can this commit be reviewed, verified, and reverted as one coherent unit?
```
If a planned wave fails that test, change the wave boundary before committing.
## The Pattern
```
Wave 1: Foundation (types, interfaces)
↓
Wave 2: Factories/Builders (functions that create instances)
↓
Wave 3: Contracts/APIs (public interfaces that use types)
↓
Wave 4: Infrastructure (utilities, converters, dependencies)
↓
Wave 5: Consumers (apps, UI, integrations)
```
Not every change needs all waves. A simple bugfix might be one standalone commit. A cross-cutting refactor might need five.
## Wave Characteristics
Each wave must be:
| Property | Description |
| ------------- | ---------------------------------------------- |
| **Atomic** | One logical concern per wave |
| **Buildable** | Code compiles after this wave (run type-check) |
| **Focused** | Changes relate to ONE layer/concern |
| **Complete** | No half-done work within a wave |
| **Auditable** | Reviewers can inspect this wave without waiting for a later wave |
## Real Example: Schema Refactor
This feature moved metadata from workspace to tables. Five waves:
### Wave 1: Types
```
feat(schema): add IconDefinition, CoverDefinition, and FieldMetadata types
- Add IconDefinition discriminated union (emoji | external)
- Add CoverDefinition discriminated union (external)
- Add FieldMetadata with optional name/description to all field types
- Update TableDefinition to use icon/cover instead of emoji/order
```
Files: `types.ts` only. Foundation for everything else.
### Wave 2: Factories
```
feat(schema): add optional name/description to field factory functions
All factory functions (id, text, richtext, integer, real, boolean, date,
select, tags, json) now accept optional name and description parameters.
```
Files: `factories.ts` only. Uses types from Wave 1.
### Wave 3: Contracts
```
feat(schema): remove emoji and description from WorkspaceSchema
Workspace is now just a container with guid, id, name, tables, and kv.
Visual metadata (icon, cover, description) now lives on TableDefinition.
```
Files: `contract.ts` only. API change using new types.
### Wave 4: Infrastructure
```
feat(schema): use slugify for human-readable SQL column names
- Add @sindresorhus/slugify dependency
- Add toSqlIdentifier() helper using slugify with '_' separator
- SQLite columns now use field.name (or derived from key) instead of key
```
Files: `to-drizzle.ts`, `package.json`. Utility that uses field metadata.
### Wave 5: Consumers
```
feat(schema): update epicenter app to use TablesWithMetadata
- WorkspaceSchema now accepts TablesSchema | TablesWithMetadata
- Export new types from package index
- Update app to create proper TableDefinition with metadata
```
Files: App files that consume the new types.
## The Workflow
1. **Plan waves before coding**
- List files that need changes
- Group by layer/concern
- Order by dependency (foundations first)
- Define the standalone claim each wave will prove
2. **Implement one wave**
- Make changes for that wave only
- Resist temptation to "fix one more thing"
3. **Verify the wave**
- Run type-check: `bun run tsc --noEmit`
- Re-read `git diff --staged` against the wave claim
- Ensure no errors introduced
4. **Commit the wave**
- Use conventional commit format
- Message describes what this wave accomplishes
- Body can list specific changes
5. **Repeat for next wave**
## When to Use Waves
| Scenario | Waves? | Why |
| ------------------------ | ------ | -------------------------- |
| Single file bugfix | No | One change, one commit |
| Add new type + factory | Maybe | Could be 1-2 waves |
| Refactor across 5+ files | Yes | Need logical grouping |
| Breaking API change | Yes | Types → API → Consumers |
| Add dependency + use it | Yes | Infra wave then usage wave |
## Anti-Patterns
### Giant Commit
```
refactor: update schema system
- Add new types
- Update factories
- Change contracts
- Add slugify
- Update app
```
Problem: One monolithic commit. Can't bisect, can't revert partially, no story.
### Micro Commits
```
feat: add IconDefinition type
feat: add CoverDefinition type
feat: add FieldMetadata type
feat: update IdFieldSchema
feat: update TextFieldSchema
...
```
Problem: Too granular. 20 commits for one logical change. Noise.
### Wrong Order
```
Wave 1: Update app to use new types ❌
Wave 2: Add the types ❌
```
Problem: Wave 1 won't compile. Bottom-up, not top-down.
## Dependency Order Heuristic
When deciding wave order, ask: "What does this file import?"
```
types.ts → imports nothing (foundation)
factories.ts → imports types.ts
contract.ts → imports types.ts
converters.ts → imports types.ts, may add deps
app/ → imports everything above
```
Files that import nothing come first. Files that import everything come last.
## Branch Strategy
For multi-wave work:
```bash
# Create feature branch
git checkout -b feat/my-feature
# Wave 1
# ... make changes ...
git add <files> && git commit -m "feat(scope): wave 1 description"
# Wave 2
# ... make changes ...
git add <files> && git commit -m "feat(scope): wave 2 description"
# ... continue waves ...
# When done, all waves are individual commits on the branch
# PR shows clean history of how the feature evolved
```
## Quick Reference
Before starting:
- [ ] List all files that need changes
- [ ] Group by layer (types, factories, contracts, infra, consumers)
- [ ] Order by dependency
For each wave:
- [ ] Change only files in this wave
- [ ] Run type-check
- [ ] Commit with descriptive message
- [ ] Move to next wave
After all waves:
- [ ] Final type-check
- [ ] Run tests if applicable
- [ ] Create PR with clean commit history