Loading
Learn conventional commits, atomic changes, and how to write commit messages your future self will thank you for.
A commit message is a note to your future self and your teammates. Six months from now, when something breaks, you will search the git log trying to understand when and why a change was made. The quality of your commit messages determines whether that search takes 30 seconds or 30 minutes.
Conventional Commits is a specification that adds structure to commit messages. The format is:
Types you will use most:
| Type | When to use |
| ---------- | ------------------------------------------------------- |
| feat | A new feature or capability |
| fix | A bug fix |
| docs | Documentation only changes |
| style | Formatting, whitespace, semicolons (not CSS) |
| refactor | Code change that neither fixes a bug nor adds a feature |
| perf | Performance improvement |
| test | Adding or fixing tests |
| build | Build system or dependency changes |
| ci | CI/CD configuration changes |
| chore | Maintenance tasks (dependency updates, tooling) |
Scope is optional but helpful — it tells the reader which part of the codebase changed:
The description should:
An atomic commit contains exactly one logical change. If you need the word "and" in your commit message, you probably need two commits.
Why atomic commits matter:
git revert undoes just that change without touching unrelated work.git log tells a story. Each commit is a chapter.git bisect finds the exact commit that introduced a bug — but only if commits are small and focused.Practical workflow for staying atomic:
The git add -p (patch mode) command is essential. It lets you commit the authentication fix from a file without including the unrelated formatting change in the same file.
The first line (type and description) answers "what changed." The body, when needed, answers "why."
When to include a body:
When a one-liner is fine:
Things to never put in a commit message:
git diff is forGood commit messages pay off when you need to search history.
A clean git log looks like a changelog:
Anyone can read that log and understand the project's recent history without looking at a single line of code.
A messy git log looks like this:
Nobody — including the person who wrote those messages — can learn anything from that log.
Your commit history is documentation. It is searchable, blameable, bisectable, and permanent. Every message is an investment. Write them like someone will read them during an outage at 2 AM, because eventually, someone will.
type(scope): description
[optional body]
[optional footer]a1b2c3d feat(lesson): add code sandbox to lesson viewer
d4e5f6a fix(progress): prevent duplicate completion events
g7h8i9j refactor(auth): extract session management to hook
k0l1m2n perf(home): lazy-load hero image below the fold
o3p4q5r docs(api): add authentication flow diagram
s6t7u8v chore(deps): upgrade React to 19.0a1b2c3d fix bug
d4e5f6a updates
g7h8i9j WIP
k0l1m2n more changes
o3p4q5r final fix
s6t7u8v ok actually this is the final fixfeat(auth): add password reset flow
fix(lesson): prevent progress from exceeding 100%
refactor(api): extract validation into middleware
docs(readme): add deployment instructions
chore(deps): upgrade Next.js to 15.1
perf(home): lazy-load below-fold images# Bad: multiple unrelated changes in one commit
git commit -m "fix(auth): fix login bug and update header styles and add new API route"
# Good: three separate commits
git commit -m "fix(auth): prevent login form submission with empty password"
git commit -m "style(header): align navigation items on mobile"
git commit -m "feat(api): add GET /api/lessons endpoint"# Stage only specific files
git add src/components/LoginForm.tsx src/lib/auth.ts
# Stage specific lines within a file
git add -p src/components/Header.tsx
# This opens an interactive prompt where you choose which hunks to stage
# Check what you are about to commit
git diff --stagedgit commit -m "fix(progress): clamp completion percentage to 0-100 range
Users who rapidly clicked the 'complete' button could trigger multiple
concurrent updates, resulting in values above 100%. The progress bar
rendered incorrectly and the completion badge appeared prematurely.
Closes #142"git commit -m "fix(typo): correct 'recieve' to 'receive' in error message"
git commit -m "chore(deps): update eslint to 9.5.0"
git commit -m "style(button): increase padding for better tap target"# See recent commits
git log --oneline -20
# Search commits by message
git log --grep="login" --oneline
# See commits that changed a specific file
git log --oneline -- src/components/LoginForm.tsx
# See what changed in a specific commit
git show abc1234
# See who last changed each line of a file
git blame src/lib/auth.ts
# Find the commit that introduced a bug
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Git will binary search through commits
# You test each one and mark it good or bad