I commit constantly. After every passing test, every completed function, every logical checkpoint. Here's why.
The Anti-Pattern
End-of-day commits:
commit abc123
Author: Developer
Date: Friday 6pm
WIP: lots of changes
- Added user auth
- Fixed bug in payments
- Refactored database layer
- Updated tests
- Changed config format
500 lines changed. Five unrelated things. Good luck reviewing that.
The Better Way
Incremental commits:
commit def456: feat: add password validation to signup
commit ghi789: fix: handle null user in payment flow
commit jkl012: refactor: extract database connection pool
commit mno345: test: add coverage for auth edge cases
commit pqr678: config: switch to YAML format
Each commit is one logical change. Each can be reviewed, reverted, or cherry-picked independently.
Why It Matters
Easier Code Review
Reviewers can understand one change at a time. "This commit adds validation" is reviewable. "This commit does five things" isn't.
Safer Reverts
Something broke? git bisect finds the exact commit. Revert it. Done.
With giant commits, reverting means losing good changes along with the bad.
Better History
Six months later, "why does this code exist?" The answer is in the commit message—if each commit is focused enough to have a clear message.
Psychological Checkpoints
Each commit is progress. Stuck on a hard problem? At least your last three small wins are saved.
When to Commit
After:
- A test passes
- A function works
- A bug is fixed
- A refactor is complete
- Any logical unit of work
Before:
- Switching to a different task
- Taking a break
- Ending the day
The question isn't "is this big enough to commit?" It's "is this a coherent change?"
What Makes a Good Commit
One thing. Not "add feature and fix bug and refactor." One thing.
Working state. Tests pass. Code compiles. Don't commit broken code.
Clear message. Future you should understand what changed and why.
feat: add rate limiting to API endpoints
Adds a sliding window rate limiter to prevent abuse.
Limit: 100 requests per minute per IP.
Closes #234
The Fear
"But my history will be messy!"
Squash before merging if you want clean history. The messy incremental commits are your development log. The squashed merge is the official record.
"But I'll commit too often!"
There's no such thing. You can always squash later. You can't un-squash a giant commit.
"But it's slower!"
git commit -m "message" takes two seconds. The time you save debugging and reviewing dwarfs the commit overhead.
My Actual Flow
- Make a small change
- Run tests
- If tests pass:
git add -p && git commit -m "description" - Repeat
The -p flag lets me stage hunks interactively. Even within one file, I can split changes into separate commits.
Habits That Help
Commit messages first. Write the message, then make the change. If you can't write a clear message, the change might not be focused enough.
Atomic changes. Don't mix refactoring with features. Refactor first, commit. Add feature, commit.
Work in progress is okay. WIP: trying new approach is fine—just squash before merging.
Push frequently. Commits on your machine aren't safe. Push to remote regularly.
The Compound Effect
Good commit habits compound. Clean history makes code review faster. Faster review means faster merging. Faster merging means less divergence. Less divergence means fewer conflicts.
Small commits → fast reviews → quick merges → smooth collaboration.
Start small. Commit often. Your future self will thank you.