How to Write Commit Messages That Make Sense: 10 Proven Tips from Practice

Every developer has their own nightmare. A bug that only shows up in production, an endless merge conflict, or a commit message like “fix.” You’re digging through the project history, trying to figure out when the API broke, only to find vague notes like “fixed,” “minor stuff,” “asdf,” or—worst of all—an entirely empty message.

Years behind the keyboard have convinced me that a clear commit history saves hours of detective work and makes team collaboration a breeze. Here are my battle-tested tips to help you commit like a pro—so you won’t be banging your head against the desk a few months down the line.

1. One Commit, One Task

Mixing unrelated changes into a single commit is a recipe for debugging headaches and messy code reviews. Ideally, each commit should be a standalone, logical unit.

💡 Tip: Got multiple changes in your working branch? Split them up before committing with git add -p.

Don’t:

git commit -m "Added new API and fixed footer layout"

Do:

git commit -m "feat(api): added endpoint for order creation" 
git commit -m "fix(ui): corrected footer layout"

2. Describe the Problem, Not Just That You “Fixed” It

Vague messages like “fix” or “fixed” won’t help you when you’re hunting down a bug. Explain what you solved and why it mattered.

💡 Tip: A brief description of the issue and solution saves time for you and your team.

Don’t:

git commit -m "bug fix"

Do:

git commit -m "fix(auth): resolved login freeze on weak connections"

3. Structure Your Commit Messages Clearly

A consistent message format makes history easy to read and changelogs a snap to generate. If your team doesn’t have set rules, try the <type>(<scope>): <description> format.

Examples:

  • feat – new functionality
  • fix – bug fix
  • refactor – code tweaks without behavior changes
  • docs – documentation updates
  • test – adding or tweaking tests
  • chore – small tasks (e.g., package updates)
  • style – code formatting (e.g., indentation)
  • perf – performance boosts
  • ci – CI/CD pipeline changes
  • build – build process updates

💡 Tip: Keep the first line under 72 characters and add details in the description.

Don’t:

git commit -m "update"

Do:

git commit -m "feat(cart): added option to save cart for later"

Description if needed: “Added cart-saving functionality on logout.”

4. Break Big Changes into Bite-Sized Pieces

A massive commit with hundreds of lines and multiple changes is a nightmare for code reviews and rollbacks. Split it into smaller, digestible chunks.

💡 Tip: For a UI refactor, start with buttons, then inputs, and finally remove old code—committing each step separately.

Don’t:

git commit -m "big UI overhaul"

Do:

git commit -m "feat(ui): added new button styles" 
git commit -m "refactor(button): removed old button component" 
git commit -m "feat(ui): updated input styles"

5. Protect Sensitive Data

API keys, passwords, database connections, and other sensitive info don’t belong in your project history. Once they’re in the repo, they’re there forever (unless you rewrite history), and anyone with access can exploit them. Always double-check what you’re pushing.

💡 Tip: Use .gitignore to automatically exclude files, like:

  • .env – environment variables (API keys, passwords)
  • *.log – logs with potential data
  • config/secrets.json – configs with keys
  • *.key, *.pem – private keys
  • node_modules – local dependencies

Avoid hardcoding values in code too (e.g., const apiKey = "xyz123").

Do:

echo ".env" >> .gitignore 
echo "*.log" >> .gitignore 
echo "config/secrets.json" >> .gitignore 
echo "*.key" >> .gitignore

6. Test Before You Commit

Make sure your app works before committing. A broken commit can derail the project and frustrate your team. Run tests or at least manually check key features.

💡 Tip: If you have automated tests, always run them pre-commit.

Don’t:

git commit -m "fix: cart bug fix" # without testing

Do:

npm test git commit -m "fix(cart): fixed price recalculation with discounts"

7. Skip the Clutter

Don’t clog the repo with stuff that doesn’t belong—debug logs, commented-out “just in case” code, or random junk. It only muddies the history.

💡 Tip: Check git diff before committing to ensure you’re only adding relevant changes. Add unnecessary files to .gitignore.

Don’t:

git commit -m "added debug logs"

Do:

echo "debug.log" >> .gitignore git commit -m "feat(cart): added item validation"

8. Work in a Branch, Not Directly in Main

Committing straight to main is a gamble—you risk breaking production or creating unnecessary conflicts for your team.

💡 Tip: Create a working branch and merge via pull request.

Do:

git checkout -b feature/add-payment-method 
git commit -m "feat(payment): added Apple Pay support" 
git push origin feature/add-payment-method

9. Use AI Wisely

Tools like GitHub Copilot can suggest commit messages, but always review and tweak them.

💡 Tip: AI is your assistant, not your boss—tailor its suggestions to match your changes.

10. Sync Up with Your Team

Unified commit rules streamline collaboration and keep your change history consistent.

💡 Tip: Try setting a team template for commit messages, like [type] <short description> (#task_number)—e.g., [feat] Added product filter (#123). It helps you write consistent messages fast and keeps track of which task each change belongs to.

Conclusion

Commits aren’t just a log of changes—they’re a message to the future, for your team and yourself. A clear history simplifies debugging, code reviews, and collaboration.