Write Effective Commit Messages
Learn to write clear, effective Git commit messages that improve collaboration and project history. Step-by-step guide with examples and best practices.
Focus: write effective commit messages
Ever stared at a git log output wondering what on earth happened in commit a1b2c3d? Maybe the message was "bugfix" or "stuff changed", and now you're spelunking through code to reconstruct intent. Poor commit messages turn your project history into a minefield, wasting hours of developer time across every team member. This lesson gives you the exact technique to write effective commit messages that transform git log from a chore into a roadmap of your project's evolution — a skill that separates junior from senior developers.
The problem this lesson solves
Commit messages are the memory of your codebase. They answer questions like: Why was this line added? What was the intent behind this refactor? Should I trust this change? Without clear messages, you lose that context the moment you type git commit.
Imagine debugging a critical production issue. You run git log --oneline and see:
3f7a9c2 fix stuff
e8b4d1a update
9c2a8e7 more changes
Which commit introduced the bug? You have no idea. Now imagine the same history with messages like:
3f7a9c2 fix: prevent null pointer in payment processing
e8b4d1a feat: add discount coupon validation
9c2a8e7 refactor: extract address formatter utility
Suddenly, the history is scannable. Each message is a headline that tells you what happened and why. This lesson solves the messy-history problem by teaching you a consistent, actionable format for write effective commit messages.
Core concept / mental model
Think of a commit message like the subject line of an email — it's the first (and often only) thing anyone reads. But unlike email, your audience includes your future self, your teammates, and potentially thousands of open-source contributors. So treat each commit as a miniature release note.
The golden rule: The message should explain why you made the change, not just what you changed. The diff already shows the what. Your message adds the human reasoning.
The two-part structure
An effective commit message has two parts:
- Subject line (the summary) — 50 characters or fewer, imperative mood, capitalized, no trailing period.
- Body (optional but encouraged) — explains the why and what in more detail, wrapped at 72 characters.
Here's a mental model: imagine your commit message is a tweet. Keep it short, punchy, and informative. If it needs more explanation, use the body like a thread.
How it works step by step
Follow this simple workflow every time you commit:
- Stage your changes with
git addas you normally would. - Review what you're committing with
git diff --cachedto confirm you've included exactly what you intended. - Write your subject line using the imperative mood (e.g., "Add", "Fix", "Update") — this matches Git's own generated messages (like "Merge branch...").
- Use a short prefix to signal the type of change (see the comparison table in the next section).
- Add a brief body if the change is non-trivial. Explain the why: the problem you solved, any trade-offs, or references to issue numbers.
- Commit with
git commit(using-mfor a one-liner or-mmultiple times for subject + body). - Review your history with
git log --onelineto see your clean, informative trail.
Pro tip: Use
git commit -m "Subject" -m "Body paragraph"to create a commit with a body without opening an editor. Each-mcreates a separate paragraph.
Hands-on walkthrough
Let's put this into practice. We'll create a simple Python project, make changes, and commit them with effective messages.
First, set up a scratch repository:
mkdir commit-demo && cd commit-demo
git init
Now create a file and make your first commit. Notice the imperative style:
echo "print('Hello, world!')" > app.py
git add app.py
git commit -m "Add initial greeting script"
Check the log:
git log --oneline
Output:
1234abc Add initial greeting script
Now let's make a more meaningful change — add a function that calculates the length of a name. This commit should include a body explaining the why:
cat >> app.py << 'EOF'
def get_name_length(name):
return len(name)
print(f"Length of 'Alice': {get_name_length('Alice')}")
EOF
git add app.py
git commit -m "Add name length function" -m "Why: Needed a helper for the upcoming profile page. Refactored from inline logic to enable unit testing."
View the detailed log:
git log
Output (abbreviated for clarity):
commit 4567def
Author: You <you@example.com>
Date: Mon Oct 21 10:00:00 2024 +0000
Add name length function
Why: Needed a helper for the upcoming profile page. Refactored from inline logic to enable unit testing.
Now, try writing a bad commit message on purpose, then use git log to see how it feels. The contrast will make the good practice stick.
echo "print('Goodbye!')" >> app.py
git add app.py
git commit -m "done stuff"
git log --oneline
Output:
789abc1 done stuff
4567def Add name length function
1234abc Add initial greeting script
See the difference? The "done stuff" commit is a dead end — you have no idea what changed unless you open the diff. Now let's amend that message (since it's the latest commit):
git commit --amend -m "Add goodbye message for exit"
git log --oneline
Now your history is clean again. This hands-on exercise demonstrates the write effective commit messages core skill: making each commit self-explanatory.
Compare options / when to choose what
There are several naming conventions for commit messages. Each has trade-offs. Here's a comparison to help you choose:
| Convention | Example | When to use | Pros | Cons |
|---|---|---|---|---|
| Plain imperative | "Add login page" | Small projects, solo work | Simple, low overhead | Lacks type context |
Prefix style (e.g., feat:, fix:) |
"feat: add login page" | Team projects, semantic versioning | Combines type and summary, auto-changelogs | Requires discipline |
| Detailed body | "Add login page\n\nWhy: User request #42.\nImplements OAuth2." | Complex changes, open source | Full context for reviewers | More effort per commit |
Recommendation: For most teams, the prefix style hits the sweet spot. It's minimal overhead but gives you instant scannability, and tools like semantic-release can automate versioning based on these prefixes.
If you're contributing to a project, always follow the existing convention — consistency beats style preference. Check the CONTRIBUTING guide or recent history.
Troubleshooting & edge cases
Here are common pitfalls and how to fix them:
- Message too long: Git truncates the subject line at 72 characters in some views. Keep it under 50 for the subject. If you need more, use the body.
- Imperative mood confusion: Write "Fix bug" not "Fixes bug" or "Fixed bug". Practice by mentally adding "This commit will..." before the subject — if it reads naturally, you're doing it right.
- Inconsistent style across commits: Agree on a team template (like the prefix style) and enforce with a
commitlinthook in CI. - Amending a message after pushing:
git commit --amendonly affects local history. Once you've pushed, you must force-push (risky) or create a new commit with a note. Never amend public commits. - Accidentally committing the wrong message: If you haven't pushed,
git commit --amend -m "New message"saves you. If you've pushed,git revertor a corrective commit is safer.
What you learned & what's next
You now understand the core idea behind write effective commit messages: a well-crafted message explains the why and what of a change, making your history self-documenting. You've practiced writing subject lines in the imperative mood, adding explanatory bodies, and using a prefix convention. You can apply this to any project, ensuring your git log is a useful tool rather than a mystery.
This skill directly impacts future lessons. When we cover branching strategies, you'll use commit messages to communicate across feature branches. When we explore bisect workflows, clear messages help you pinpoint exactly which change introduced a bug. And in reflog escapes, the commit message is your breadcrumb trail for recovery. Master this, and every Git interaction becomes more deliberate.
Next in the track, you'll build on this foundation — likely learning how to organize your work with branches, where your commit discipline will shine.
Now go forth and commit with clarity!
Practice recap
Create a throwaway repo and practice writing three commits: one with a simple subject, one with a body explaining why, and one with a prefix like fix:. Then run git log to see how scannable your history is. Try amending the last commit's message and observe how the log changes — this solidifies the workflow.
Common mistakes
- Using vague messages like 'fix' or 'update' — these force teammates to open diffs and guess intent, wasting everyone's time.
- Forgetting the imperative mood, writing 'Fixed bug' instead of 'Fix bug' — keep the style consistent with Git's own generated messages.
- Writing only a subject line for complex changes — if the change took thought, the body should explain the why so reviewers and your future self understand the reasoning.
- Amending a pushed commit —
git commit --amendrewrites history and causes conflicts for anyone who has fetched; use a new commit instead.
Variations
- Use a commit message template with placeholders for type, scope, and description — great for teams using CI bots.
- Adopt semantic commit messages (feat:, fix:, chore:...) to enable automated semantic versioning and changelog generation.
- Write commit messages in a non-English language if your team is international, but ensure consistency and consider a shared glossary.
Real-world use cases
- Debugging a production incident by scanning
git log --onelineto quickly identify the commit that introduced a regression. - Onboarding a new developer who reads the commit history to understand the project's evolution and rationale behind key decisions.
- Automating release notes and version bumps using semantic commit prefixes like
feat:andfix:for a library's changelog.
Key takeaways
- Effective commit messages explain the why behind a change, not just the what.
- Use the imperative mood in the subject line (e.g., 'Add test') — it matches Git's own convention.
- Keep the subject under 50 characters and wrap the body at 72 characters.
- Adopt a consistent prefix convention (like
feat:orfix:) for scannability and tooling integration. - Never amend a commit that has already been pushed to a shared remote.
- Review your
git log --onelineregularly to ensure your messages remain useful.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.