Table of Contents
- Introduction Why clean commit history matters—even for side projects
- The Problem with Messy Commits– “Fix bug” and “test” everywhere– Hard to track what actually changed– Painful future debugging
- Adopting a Clean Commit Strategy– Write commits like you’ll read them in 6 months– Commit only what matters– Group related changes together
- Using Git Effectively During Development–
git add -p
for selective staging–git commit --amend
to fix your last message–git rebase -i
to clean up before pushing - Avoiding Commit Spam– Why “commit often” doesn’t mean “commit everything”– Avoid noise like formatting, log removals, etc.
- Best Practices for Side Projects– Use feature branches—even when working solo– Keep your main branch deploy-ready– Write meaningful commit messages.
- Sample Commit Message Templates– Practical examples you can copy– How to use prefix conventions (e.g.
feat:
,fix:
,chore:
) - Conclusion Keeping history clean so your future self doesn’t suffer
Introduction
Why clean commit history matters—even for side projects
It’s easy to treat side projects like a sandbox. No rules. No PR reviews. No pressure. Just code, commit, ship.
And that’s fine—until you come back a month later and wonder:
“What does
fix-stuff-final3
even mean?”
We’ve all done it. Messy commit histories feel harmless in the moment. But they quickly become a problem when:
- You need to debug a regression
- You want to roll back a change
- You’re trying to extract reusable code
- Or you’re prepping the repo for open source or job applications
Clean commits aren’t just for large teams or open-source contributors. They help you, the solo developer, keep control over your own project.
A Messy History Slows You Down
Here’s a real commit log from an old side project:
commit 1a3f… fixed nav bug
commit 23cd… fixed it again
commit 91f0… test
commit 3fa1… oops
commit b7d2… backup before trying something
At the time, it worked. But now? It’s unreadable. Unusable. Untrustworthy.
Try running git bisect
to track down a bug, and you’ll lose hours stepping through vague changes.
A Clean History = A Clear Story
Now compare that to a cleaned-up commit log:
commit a34f… feat: add responsive mobile nav
commit b28d… fix: handle logout button positioning bug
commit 9d1c… refactor: extract NavBar component logic
commit f4a1… chore: update README and screenshots
Each commit:
- Describes what changed
- Has a clear purpose
- Is scoped to a single idea
This isn’t about perfection. It’s about thinking clearly and working with intention—even when no one else is watching.
Side Projects Grow Too
That weekend prototype? It might turn into a paid product. Or a code sample you send in a job application. Or the base of your next freelance contract.
When that happens, a clear commit history becomes your superpower:
- You can isolate features
- Roll back mistakes
- Understand old logic
- And hand it off to others, if needed
Example: Before vs After Cleaning Up Commits
Let’s say you build a feature in a rush and commit like this:
git commit -m "trying dark mode"
git commit -m "ok fixed theme bug"
git commit -m "final version"
Instead, you can squash and rewrite them into something like:
git commit -m "feat: add dark mode support with toggle button"
It’s now one meaningful unit. You know what it does. You know when it was added. And it’s easier to revert or improve in the future.
The Problem with Messy Commits
Why “fix bug” and “test” are slowing your team down
When you’re in the middle of solving a bug or building a feature, it's tempting to hit:
git commit -m "test"
Or worse:
git commit -m "fix"
You’re moving fast. You just want to save your progress. But over time, this creates a real mess.
Let’s break down what actually goes wrong.
1. “Fix bug” and “test” Everywhere
Scroll through a commit log on a real project and you’ll often see:
commit 8d12a3c - fix
commit 4cfa33e - test
commit a7b84fe - more fixes
commit f1dc990 - test again
None of these tell you what changed, or why.
And if something breaks? You’re left guessing:
- What did this commit fix?
- What was the original bug?
- What part of the system was involved?
It's like trying to read a story when every chapter is titled “Chapter”.
2. Hard to Track What Actually Changed
Imagine you're debugging a production issue two months later.
You run git blame
and land on this:
// dashboard.js
renderData(); // added in commit 8d12a3c - "fix"
Now you have to:
- Manually check the diff
- Hope the PR has context
- Maybe guess what the "fix" was about
Compare that to this:
git commit -m "Fix: prevent empty data crash in dashboard view"
Now you immediately know:→ What the change was→ Why it happened→ Where to look next if something else goes wrong
Good commit messages are breadcrumbs for your future self.
3. Painful Future Debugging
Bad commits don’t just waste time. They cause bugs to go unnoticed longer.
Here’s an example:
git log
commit 5a8b13a - "test new filter"
commit 64fef21 - "update"
commit a4d9e7c - "trying something"
Now the QA team finds a regression.
Which commit caused it? What exactly changed in “update”? Is “trying something” safe to revert?
You won’t know—until you manually inspect every one.
With clean commits, this becomes much easier:
commit 84f2ad3 - "Refactor: simplify filter logic for multi-select dropdown"
commit c73f8da - "Fix: prevent crash on empty filter array"
You can now:
- Use
git bisect
with confidence - Revert specific logic without breaking others
- Understand intent just from the commit log
Quick Tip: What a Clean Commit Looks Like
Bad:
git commit -m "fix"
Better:
git commit -m "Fix: prevent null value from breaking pagination"
Even better if paired with structured commits (like Conventional Commits):
fix(api): handle undefined query param in /users endpoint
This tells the story clearly and consistently. And when paired with automation (e.g., changelogs or releases), it’s powerful.
The Takeaway
Messy commits feel fast in the moment. But they cost you clarity, time, and trust later.
Avoid:→ One-word commits→ Meaningless messages like “temp” or “test”→ Piling unrelated changes into a single commit
Do:→ Write why something changed—not just what→ Keep commits small and focused→ Use consistent naming (e.g., Fix:
, Refactor:
, Add:
)
Good commits don’t just save time. They make your repo understandable—for you, your teammates, and even strangers months from now.
Adopting a Clean Commit Strategy
Because “fixed stuff” isn’t helpful six months from now
Your commit history tells the story of your codebase. And like any story, it’s either:→ Clear and helpful→ Or messy and hard to follow
We’ve all seen (or written) commits like this:
git commit -m "fix"
git commit -m "wip"
git commit -m "final final fix"
They make sense in the moment. But later? When you're debugging a regression? They’re worthless.
Here’s how we cleaned up our commit habits—and made our history readable, searchable, and actually useful.
1. Write Commits Like You’ll Read Them in 6 Months
A good commit message is like a headline. It tells you what changed and why, without opening the diff.
Bad:
git commit -m "Update"
Better:
git commit -m "Fix spacing issue in mobile nav"
Even better:
git commit -m "Fix mobile nav layout: add responsive gap between links"
That second version tells you:
- What changed (layout fix)
- Where (mobile nav)
- Why (gap between links)
**Rule of thumb:**If your commit shows up in git blame
, will someone understand what happened?
2. Commit Only What Matters
Sometimes, we commit everything out of habit. Even console logs, temporary experiments, or unrelated test files.
That bloats the history and hides the real changes.
Before committing, ask:
- Does this change belong here?
- Is this related to the current task?
- Is this debug code I forgot to remove?
Use git add -p
(patch mode) to stage changes interactively:
git add -p
This lets you commit only the relevant parts of a file.
Example:
You fixed a button style and added a new logging function in the same file. Use patch mode to commit only the style fix:
git commit -m "Fix button hover state on Safari"
Then stage the log function separately:
git commit -m "Add debug log for checkout process"
Now your commits are clean, atomic, and easier to roll back or review.
3. Group Related Changes Together
Each commit should represent a single unit of change.
Avoid this:
git commit -m "Refactor card component and add analytics and fix dark mode"
This does three different things:
- A UI refactor
- An analytics feature
- A bug fix
If something breaks, which part was responsible?
Instead, break it into three commits:
git commit -m "Refactor <Card> layout for cleaner spacing"
git commit -m "Add click tracking to <Card> CTA button"
git commit -m "Fix dark mode background for <Card>"
Now if dark mode breaks again, you know exactly where to look.
And if analytics needs to be rolled back? You can revert that commit without touching the others.
Bonus: Use Conventional Commit Patterns (if you work in a team)
If you want structure, consider using Conventional Commits:
feat: add new pricing card layout
fix: resolve mobile spacing issue in <Header>
refactor: clean up form logic for <Checkout>
These patterns make changelogs easier to automated make the purpose of each change clearer at a glance.
The Takeaway
Good commit hygiene pays off over time.
You:→ Save your future self (and teammates) hours of debugging→ Make PRs easier to review→ Reduce fear when refactoring
A clean commit history isn’t about perfection. It’s about respecting the story of your project—so you can understand it later.
So write commits like they matter. Because when you’re trying to undo a bug in six months, they really do.
Using Git Effectively During Development
How to stage, fix, and clean up your commits like a pro
Most developers use Git just enough to get by:git add .
, git commit -m "something"
, git push
.
It works. But it also leads to messy histories, mixed changes, and hard-to-review PRs.
Here are 3 simple Git techniques that can drastically improve your development workflow—without adding complexity.
1. git add -p
– Stage Only What You Mean To
You made changes to multiple things:– Fixed a bug– Added a log statement– Tweaked some spacing– Wrote a new feature
You don't want to commit all of that in one blob. That's where git add -p
helps.
git add -p
It breaks your changes into small “hunks” and lets you interactively pick what to stage:
Stage this hunk [y,n,q,a,d,s,e,?]?
y
: yes, stage this partn
: no, skip this parts
: split into smaller partse
: manually edit the change before staging
Example Workflow:
git add -p
git commit -m "Fix header spacing issue"
git add -p
git commit -m "Refactor auth logic"
Now your commits are focused, readable, and easy to review.
2. git commit --amend
– Fix Your Last Commit
Just made a commit but forgot something? Or noticed a typo in the message?
No need to make a second “fix” commit. Just amend the last one:
git commit --amend
By default, this:
- Opens your editor to modify the commit message
- Includes any newly staged changes in the same commit
Example 1: Fix the commit message
git commit --amend -m "Fix navbar link alignment"
Example 2: Add a forgotten file
git add nav.css
git commit --amend
Now your history stays clean—no noisy "oops" commits.
3. git rebase -i
– Clean Up Before You Push
After a day of work, your commit history might look like this:
[fix bug]
[console.log]
[add real fix]
[update style]
[rename file]
Before pushing to the shared repo, use git rebase -i
to clean it up:
git rebase -i HEAD~5
You’ll see:
pick 3f1a8f2 fix bug
pick 7ab3e9c console.log
pick 5ca9c11 add real fix
pick b7e1dd4 update style
pick 0db1ae2 rename file
Now you can:
- Change
pick
tosquash
to merge commits - Reorder commits
- Edit commit messages
Example: squash the "console.log" into the "fix bug" commit:
pick 3f1a8f2 fix bug
squash 7ab3e9c console.log
pick 5ca9c11 add real fix
...
After saving, you’ll be prompted to edit the combined commit message.
This keeps your branch history clean, logical, and meaningful—especially before opening a PR.
The Takeaway
Git isn’t just for saving code. It’s for communicating intent.
Using these tools helps you:→ Stage only what matters→ Avoid noisy fix commits→ Keep your commit history clean and reviewable
It’s not about perfection—it’s about clarity.
Start with one small habit, like git add -p
, and build from there. Your future self—and your teammates—will thank you.
Avoiding Commit Spam
Why “commit often” doesn’t mean “commit everything”
You’ve probably heard the advice:
“Commit early, commit often.”
That’s true—but only if your commits are meaningful.
Because when you push 17 commits like this:
fix spacing
remove console.log
update var name
final fix I swear
another tweak
formatting
…you’re not helping anyone.
You're creating commit spam—noisy history that’s hard to read, review, or roll back.
Clean commit history matters. It’s the story of your project.
Let’s look at how to avoid spam and keep commits useful.
1. Why “Commit Often” Is Misunderstood
The goal of committing often is to:
- Save work at meaningful points
- Break up features into reviewable chunks
- Make rollbacks safer
It doesn’t mean you should commit every time you press save.
A commit should represent a logical unit of work—not just a moment in time.
✅ Good commits:
feat: add responsive navbar with mobile drawer
fix: prevent crash on empty user input
chore: remove unused dependency from package.json
🚫 Bad commits:
fix again
remove debug log
tiny change
oops
These don’t tell the next dev (or future you) why the change happened or what it solved.
2. Avoid Committing Noise
Formatting, console logs, and linter fixes aren’t “features. ”They clutter your history and make it harder to spot real changes.
Bad example:
git add .
git commit -m "fix bug in homepage"
git commit -am "remove console.log"
git commit -am "run Prettier"
These could be a single commit:
git commit -am "fix: remove console logs and fix homepage rendering bug"
What helps:
- Run formatters before you commit
- Remove console/debug logs as part of your real change
- Use
.gitignore
to skip auto-generated files and build output
3. Use Staging Wisely
You don’t have to commit everything at once. Use git add -p
or a GUI to stage changes selectively.
git add -p
This lets you:
- Group related changes together
- Exclude debugging leftovers
- Keep commits focused and clean
If you're working in VS Code, use the Source Control sidebar to stage only what matters.
4. Clean It Up Before You Push
It’s fine to make messy commits while working.
But before you open a pull request or merge into main, clean up the history:
Option 1: Squash commits in your PR (GitHub/Bitbucket UI)
This turns:
feat: start sidebar
fix sidebar width
remove temp logs
final fix before merge
into:
feat: add responsive sidebar layout
Option 2: Use interactive rebase
git rebase -i HEAD~4
Pick the commit to keep. Squash the rest. Write a clear message.
Now your branch looks clean—and easier for reviewers to understand.
Summary: Best Practices for Commit Hygiene
✅ Do This |
🚫 Avoid This |
---|---|
Commit logically grouped changes |
Commit every single file edit |
Use clear commit messages |
Messages like “fix again” or “update” |
Run formatters/lint before staging |
Committing auto-formatting as separate noise |
Stage changes intentionally |
Blindly |
Squash or rebase before pushing |
Letting 15 noisy commits hit main |
Remember: Commits are more than snapshots. They’re how your team learns from the past, tracks progress, and reviews code efficiently. A clean commit history saves time, builds trust, and makes collaboration smoother.
So yes—commit often. But commit with care.
Best Practices for Side Projects
Because solo ≠ sloppy
Side projects are where we experiment, learn, and build without pressure. But that doesn’t mean we should throw away good habits.
In fact, the way you treat your side projects often mirrors how you'll work in production. Clean habits here lead to better outcomes later—especially when your project grows or becomes public.
Let’s talk about a few simple practices that keep your side projects fast, clean, and ready to ship.
1. Use Feature Branches — Even When Working Solo
It’s tempting to push everything to main
.
But using feature branches helps:
- Keep your changes scoped
- Make it easier to track what you’re working on
- Avoid breaking something that’s already working
Example workflow:
git checkout -b feat/login-form
Make your changes, then commit and push:
git add .
git commit -m "Add basic login form with validation"
git push origin feat/login-form
Once it works, merge into main
:
git checkout main
git merge feat/login-form
git push origin main
Even if you're working alone, this gives you:→ A cleaner commit history→ A safer rollback path→ Better organization as the codebase grows
And if you open source the project later? You’ve already built the habit of clean collaboration.
2. Keep Your Main Branch Deploy-Ready
Treat main
like production—even for side projects.
That means:
- No broken builds
- No experimental half-finished features
- No console logs everywhere
This makes it easy to:
- Deploy to Vercel / Netlify without thinking
- Demo your project to friends, clients, or future employers
- Roll back safely if something goes wrong
Tip: Set up GitHub Actions or another CI tool to test your build on every push to main
.
# .github/workflows/ci.yml
name: Build Check
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install deps
run: npm install
- name: Run build
run: npm run build
Now if your build fails, you’ll catch it early—before pushing something broken live.
3. Write Meaningful Commit Messages
Your commits are your timeline. Don’t make it read like:
fix
update
stuff
final changes
Instead, write messages that explain what you did and why it matters.
Good examples:
feat: add dark mode toggle with system preference support
fix: handle empty email input in signup form
refactor: simplify navbar layout for better responsiveness
This pays off when:
- You revisit the project after months
- You're debugging something odd
- Someone else wants to contribute
You can even follow a consistent convention like Conventional Commits:
git commit -m "feat: add local storage hook for user preferences"
It makes changelogs easier and helps automation tools later if your project grows.
The Takeaway
Even if it’s a solo project, treat it like it matters.
→ Use branches so you don’t break your base→ Keep main
clean so deploys are easy→ Write clear commits so future-you isn’t lost
These small habits don’t slow you down. They actually help you move faster, with fewer mistakes—and more confidence.
Because every great product started as a side project someone took seriously.
Sample Commit Message Templates
How to write clear, consistent, and useful commit messages
Good commit messages are more than a nice-to-have. They improve collaboration, simplify debugging, and make tools like git log
, changelogs, and CI pipelines far more useful.
But many teams still treat commits like an afterthought.
So here’s how to fix that—without overthinking it.
Why Use Commit Message Conventions?
A structured commit history helps you:
✅ Understand what changed at a glance
✅ Auto-generate changelogs
✅ Trigger semantic releases
✅ Keep pull requests clean and readable
✅ Reduce noise when debugging
We follow a common convention called Conventional Commits, where each commit starts with a prefix:
<type>(optional scope): description
Common Prefixes and What They Mean
Prefix |
Use it when... |
---|---|
|
You’re adding a new feature |
|
You’re fixing a bug |
|
You’re doing non-feature work (build scripts, config) |
|
You’re improving code without changing behavior |
|
You’re updating documentation only |
|
You’re updating formatting, spacing, or linting only |
|
You’re adding or updating tests |
|
You’re improving performance |
✅ Practical Examples You Can Copy
Here are some commit templates you can start using right now.
🚀 Features
feat(auth): add JWT-based login flow
feat(ui): support dark mode toggle in header
feat(api): expose /reports endpoint for analytics
🐛 Bug Fixes
fix(form): prevent double submission on enter key
fix(auth): handle expired token redirect properly
fix(api): correct timezone offset in report results
🧹 Chores and Maintenance
chore: update dependencies
chore(lint): apply prettier formatting
chore(ci): add GitHub Actions workflow for tests
🛠 Refactoring
refactor(modal): simplify open/close logic
refactor(routes): use centralized route config
📚 Docs
docs(readme): clarify setup steps
docs: add usage examples for CLI commands
🧪 Tests
test(api): add tests for auth middleware
test(button): ensure loading state is rendered
⚡️ Performance
perf(list): memoize filtered results
perf: avoid unnecessary re-renders on scroll
💡 Bonus: Use Scopes for Clarity
Adding a scope in parentheses can make large projects easier to navigate.
Example:
feat(user-settings): allow profile image upload
fix(auth): reset state on logout
chore(deps): bump react from 18.1.0 to 18.2.0
Think of the scope as the “area of code” you’re touching.
How to Use These in Practice
When writing a commit:
- Start with the right prefix
- Use the imperative mood (e.g. “add feature”, not “added feature”)
- Keep it short and specific
- Optionally add a description body:
feat(nav): add mobile menu
Allows navigation drawer toggle on smaller screens.
Accessible via hamburger icon.
This extra context is helpful when reviewing git log
or PRs.
The Takeaway
Clear commit messages pay off later—when you're debugging, writing changelogs, or explaining a decision six months from now.
Start with just this:
feat: new feature
fix: bug fix
chore: internal changes
Then expand as your team grows.
Good commit hygiene is like good code style: invisible when done right, painful when ignored.
Conclusion
Keeping history clean so your future self doesn’t suffer
When you’re in the zone, shipping features fast, it’s easy to overlook one thing: your project history.
But fast-forward a few months—or worse, a year—and someone (probably you) will ask:
“Why was this changed? ”“What was the bug here again? ”“Who wrote this and what were they thinking?”
Clean commit history and clear code changes aren’t just good habits. They’re future-proofing for your team—and your future self.
Think of Your Git Log as a Story
The worst git history looks like this:
commit 7ae8d2d - "fix"
commit 114bc9a - "debug"
commit f12d10e - "oops"
commit 3a5e22f - "final version"
It’s unsearchable, untraceable, and useless.
A clean history tells a story:
commit e89c4b2 - "Add input validation to user form"
commit 5a93db7 - "Fix timezone parsing bug in booking calendar"
commit 4e32d17 - "Refactor dashboard layout into reusable grid system"
Each commit should answer:
- What changed
- Why it changed
- Optionally, what it fixes or relates to
Use Atomic Commits
Don’t lump 10 changes into one commit.
Bad:
commit 2410adb - "update styles, fix button, add tests, remove logs"
Good:
commit b67f9c1 - "Remove unused console logs in Dashboard"
commit d21a1b9 - "Fix button alignment on mobile in Header"
commit a42ebd4 - "Add unit tests for NavBar collapse behavior"
Atomic commits:→ Make it easier to revert specific changes→ Keep pull requests readable→ Help reviewers (and future devs) spot regressions fast
Code Comments That Age Well
You don’t need to explain what the code does (that’s what the code is for).But you should explain why it’s done that way—especially if it’s a workaround.
// Avoid using `getDate()` here due to DST shift on March 9
const tomorrow = new Date(date);
tomorrow.setDate(date.getDate() + 1);
That kind of note saves hours of future debugging.
Squash Before Merging (When It Makes Sense)
Feature branches can get messy:
commit b0d… - "start dropdown"
commit b1e… - "make it blue"
commit b2f… - "revert blue, try gray"
commit b3g… - "fix shadow on hover"
Before merging to main
, squash those into one clean commit:
"Add responsive dropdown with gray styling and hover effects"
It keeps your main branch professional and searchable.
Your Future Self Will Thank You
Clean history isn't for perfection. It's for debugging, onboarding, and understanding the past.
Next time you're:→ Blaming a random change→ Rewriting a component→ Untangling a merge conflict→ Asking "why is this here?"
A clean history gives you the answers.
Final Tip: Treat Code Like a Journal
Write it like someone else will read it tomorrow. Because someone will.
And 90% of the time—that person is you.
So leave them a map.