Small, atomic commits
One logical change per commit. A change may touch multiple files if they are part of the same concept. Commits should happen as work progresses — do not batch up unrelated changes.
The build-verify-commit loop
For every logical change:
- Make the change — implement one coherent unit of work
- Build — run the platform build command (
xcodebuild,./gradlew build,npm run build,dotnet build,cargo build) - Verify — the build MUST pass and existing tests MUST still pass before committing
- Commit — commit the passing change with a descriptive message
- Repeat — move to the next logical change
Multiple uncommitted changes MUST NOT be stacked. If a change breaks the build, fix it before moving on — do not add more changes on top of a broken state. This prevents compound debugging sessions where multiple interacting changes all break at once.
What counts as one logical change
A single logical change is the smallest unit of work that makes sense on its own:
- Adding one function and its tests
- Renaming a symbol and updating all references
- Fixing one bug
- Adding one configuration option
A change may touch multiple files if they are part of the same concept — an interface and its implementation, a component and its test file.
Why this matters
Batched, uncommitted changes create compound failures that are difficult to debug. When three changes interact in a broken build, isolating which change caused the failure requires significantly more effort than catching each failure as it occurs. Small, committed changes are also individually revertible, bisectable, and reviewable.