PullNotifier Logo
Published on

8 Git Commit Message Best Practices for 2025

Authors

In a fast-paced development cycle, it's easy to treat Git commits as a chore, firing off vague messages like 'fix bug' or 'update code'. But a well-crafted commit history is one of the most powerful, yet often overlooked, tools a development team possesses. It functions as a project's technical diary, a roadmap for debugging, and a crucial component for efficient code reviews.

Poor commit messages create a subtle form of technical debt, making it exponentially harder for future developers-including your future self-to understand the context behind a specific change. They slow down reviews, complicate rollbacks, and obscure the logical evolution of the codebase. A chaotic log is a liability; a clean one is a strategic asset.

By adopting a standardized approach, teams can transform their commit log from a confusing archive into a clean, searchable, and incredibly valuable resource. This guide outlines 8 essential git commit message best practices designed to immediately improve your team's communication, streamline your workflow, and elevate the quality of your entire software development process. From mastering the imperative mood to leveraging conventional commits, you'll gain actionable techniques to write messages that provide clarity and lasting value.

1. Use the Imperative Mood

One of the most foundational git commit message best practices is to write the subject line in the imperative mood. This means phrasing your message as a command or an instruction, telling the codebase what to do. Instead of describing what you did (e.g., "Added a new feature"), you state what the commit does (e.g., "Add new feature"). This small change creates a clear, actionable, and consistent history.

This convention isn't arbitrary; it aligns with how Git itself generates messages for automated actions. When you perform a git merge or git revert, Git creates messages like "Merge branch 'feature-x'" or "Revert "Add new login component"". Adopting this style for your own commits makes the entire project log read like a consistent series of steps, making it much easier to understand the repository's evolution.

Use the Imperative Mood

Why It's a Best Practice

Using the imperative mood results in messages that are more direct and understandable. It focuses on the effect of the commit on the codebase, not the past actions of the author. This approach is highly valued in collaborative environments, especially in large-scale projects like the Linux Kernel, where clarity and conciseness are paramount.

Key Insight: A simple mental trick is to complete the sentence: "If applied, this commit will..." Your commit subject line should naturally complete this phrase. For example, "If applied, this commit will Refactor user authentication service."

Practical Implementation

Here’s how to put this into practice and a comparison of good versus bad examples:

  • Bad (Past Tense): Fixed bug in the payment processing module

  • Good (Imperative): Fix bug in payment processing module

  • Bad (Descriptive): I updated the documentation for the API

  • Good (Imperative): Update API documentation

  • Bad (Gerund): Removing deprecated user endpoints

  • Good (Imperative): Remove deprecated user endpoints

By following this simple rule, you contribute to a cleaner, more professional, and highly readable commit history that benefits the entire development team.

2. Keep the Subject Line Under 50 Characters

Another critical git commit message best practice is to limit the subject line to 50 characters or less. This isn't just a matter of style; it's a practical constraint that ensures your commit history remains readable and scannable across various Git tools and interfaces. Many command-line tools, GUIs, and platforms like GitHub are optimized to display subject lines of this length without awkward wrapping or truncation.

Enforcing this limit forces you to be concise and distill the commit's purpose down to its most essential core. A short, well-crafted subject line is often a sign of a well-scoped commit that does one thing well. This practice, widely promoted by sources like the Pro Git book and GitHub's own guidelines, leads to a much cleaner and more professional log.

Keep the Subject Line Under 50 Characters

Why It's a Best Practice

The primary benefit of a 50-character limit is readability. When you run commands like git log --oneline, the subject line is all you see. Keeping it brief allows you to quickly scan dozens of commits to find what you're looking for. It also prevents important information from being cut off in email notifications, integrated development environment (IDE) plugins, and GitHub pull request summaries.

Key Insight: Think of the subject line as an email subject. It should be a short, powerful summary that entices the reader to open it (i.e., look at the commit body or diff) if they need more context. Everything else belongs in the body.

Practical Implementation

Sticking to this limit is easier with the right tools and mindset. Configure your editor to show a character count or a ruler at the 50-character mark. If a summary feels too long, it might be a signal that your commit is trying to do too much.

  • Bad (Too Long): Fix a bug where the user's session would not expire correctly after logout (80 chars)

  • Good (Concise): Fix session expiration bug on user logout (41 chars)

  • Bad (Too Long): Implemented the new user profile page with avatar upload and bio editing (73 chars)

  • Good (Concise): Add user profile page with avatar and bio forms (47 chars)

  • Bad (Too Long): This commit updates all of the dependencies in the package.json file (69 chars)

  • Good (Concise): Update package.json dependencies (30 chars)

For more complex changes, move the detailed explanation into the commit body, separated from the subject by a blank line. This keeps the summary scannable while preserving crucial context for future developers.

3. Separate Subject from Body with a Blank Line

A fundamental yet often overlooked convention is to separate the subject line from the body with a single blank line. This isn't just a stylistic preference; it's a structural requirement that many Git tools and commands rely on to correctly parse and display commit information. The first line is treated as a summary, while everything after the blank line is considered the detailed explanation.

This separation allows tools like git log --oneline, GitHub, and various IDE integrations to display a concise summary of changes without being cluttered by longer descriptions. When a more detailed context is needed, these tools can then expand the commit to show the full body. Adhering to this convention ensures your commit history is both scannable and deeply informative.

Separate Subject from Body with a Blank Line

Why It's a Best Practice

Failing to add a blank line causes Git to treat the entire message as a single, long subject line. This breaks the formatting in many contexts, making logs difficult to read and automated tools less effective. Proper separation is crucial for clarity and tooling compatibility, a standard heavily influenced by conventions from the Linux kernel development and popularized by Tim Pope's seminal blog post on commit messages.

Key Insight: Treat the blank line as a hard separator. It signals to Git and other developers: "This is the summary; here comes the detailed context." This simple act makes your commit history exponentially more useful.

Practical Implementation

Here’s how to correctly structure a multi-line commit message and how it contrasts with a poorly formatted one:

  • Bad (No Blank Line):

    Fix user authentication bug
    The login validation was failing for users with special characters
    in their email addresses. Updated the regex pattern to handle all
    valid email formats.
    
  • Good (With Blank Line):

    Fix user authentication bug
    
    The login validation was failing for users with special characters
    in their email addresses. Updated the regex pattern to handle all
    valid email formats.
    
  • Good (Another Example):

    Add caching layer to API responses
    
    Implemented Redis-based caching for frequently accessed endpoints
    to improve response times by up to 60%. Cache expiration is set
    to 5 minutes with automatic invalidation on data updates.
    

How to Write Multi-Line Commit Messages

Many developers only know git commit -m "message", which makes multi-line messages awkward. Here are three ways to write a proper subject + body commit:

  • Open your editor: Run git commit without the -m flag. Git will open your configured text editor (set via git config --global core.editor "code --wait" for VS Code, or vim, nano, etc.), where you can write the subject, leave a blank line, and write the body naturally.

  • Use multiple -m flags: Each -m adds a new paragraph separated by a blank line:

    git commit -m "Fix user authentication bug" -m "The login validation was failing for users with special characters in their email addresses."
    
  • Set up a commit template: Create a template file and configure Git to use it:

    git config --global commit.template ~/.gitmessage
    

    Your ~/.gitmessage file can pre-fill the structure you want every developer to follow.

By consistently applying these rules, you ensure your commit messages are correctly interpreted by the entire Git ecosystem, improving readability for everyone on the team.

4. Wrap the Body at 72 Characters

While the subject line should be concise, the commit message body is where you provide context. An important formatting guideline is to wrap the body text at 72 characters per line. This formatting rule isn't about aesthetics; it’s a practical standard that ensures your messages are readable across a wide array of tools and environments, from terminals and code editors to email clients and web interfaces like GitHub.

This convention originates from classic email formatting standards and was popularized by Tim Pope's influential 2008 blog post and the Pro Git book. It prevents awkward line wrapping in tools like git log, which indents the commit body by 4 spaces, leaving less horizontal space. With 4 spaces of indentation, a 72-character line fits perfectly within an 80-column terminal. Adhering to this limit ensures your detailed explanations remain clean and easy to scan, regardless of where they are viewed.

Wrap the Body at 72 Characters

Why It's a Best Practice

Properly wrapping the body text significantly enhances readability and maintains a consistent, professional-looking log. It allows developers to quickly read detailed commit information directly in their terminal without horizontal scrolling or encountering broken lines. This practice is especially valuable when generating patch files or reviewing project history on the command line, where screen real estate is limited.

Key Insight: The 72-character limit for the body (and 50 for the subject) is a strong guideline, not an unbreakable law. The goal is readability. Sticking to this convention ensures your commit message is universally accessible and easy to digest.

Practical Implementation

Most modern text editors can be configured to help you follow this rule. Here’s a look at how to apply this practice effectively:

  • Bad (Unwrapped):

    Implement user preference synchronization across devices. This feature allows users to maintain consistent settings and preferences when switching between different devices. The implementation uses a cloud-based storage system with conflict resolution to handle simultaneous updates from multiple devices.
    
  • Good (Wrapped at 72 Characters):

    Implement user preference synchronization across devices
    
    This feature allows users to maintain consistent settings and
    preferences when switching between different devices. The
    implementation uses a cloud-based storage system with conflict
    resolution to handle simultaneous updates from multiple
    devices.
    

To make this easier, configure your code editor to display a vertical ruler at the 72-character mark, or enable soft wrapping at that column. Consistent practice will quickly build the muscle memory needed for this important formatting rule.

5. Use the Body to Explain What and Why vs How

While the subject line provides a concise summary, the commit message body is where you provide crucial context. The most valuable habit you can build is to use this space to explain the what and why of a change, not the how. The how is already detailed in the code diff; duplicating that information is redundant. The why, however, provides the priceless context that the code alone cannot.

This approach transforms the commit history from a simple log of changes into a rich, searchable documentation of the project's decision-making process. Future developers, including your future self, can quickly understand the business justification, the problem being solved, or the specific trade-offs made without needing to decipher complex code logic from scratch. This practice is rooted in strong engineering communication principles and is widely adopted across the open-source community.

Why It's a Best Practice

The code shows how a problem was solved, but it rarely reveals the problem's origin, the alternatives considered, or the business driver behind the change. Explaining the "what" and "why" addresses these gaps, making maintenance, debugging, and future development significantly more efficient. It helps team members review pull requests more effectively and provides a historical record of intent.

Key Insight: Treat your commit body as the missing piece of documentation. If a developer five years from now reads your code, the commit message should explain the reasoning that isn't immediately obvious from the implementation itself.

Practical Implementation

Focus on the problem and the rationale for your solution. Link to external resources like issue trackers to provide even deeper context.

  • Bad (Focuses on "How"): Iterated through the user array and changed the timeout variable from 30 to 60 in the API config file.

  • Good (Explains "What" and "Why"): `Increase API timeout from 30s to 60s

    Customer support reported multiple timeout errors during peak hours when processing large datasets. Analysis showed that 95% of requests complete within 45 seconds, but the longest 5% need up to 55 seconds. Increasing to 60s will reduce customer complaints while maintaining reasonable boundaries. See ticket JIRA-123.`

  • Bad (Vague): Switched to OAuth

  • Good (Explains "What" and "Why"): `Replace custom authentication with OAuth 2.0

    The existing custom auth system lacks proper security features like token refresh and scope limitations. Adopting OAuth 2.0 provides better security, simplifies third-party integrations, and reduces our maintenance burden, aligning with new company-wide security standards.`

6. Use Conventional Commits Format

For teams seeking maximum clarity and automation, adopting the Conventional Commits specification is a game-changing upgrade to your workflow. This specification provides a lightweight but powerful convention for structuring commit messages. It enforces a standard format that makes messages machine-readable, which unlocks powerful automated workflows like changelog generation and semantic versioning.

The core idea is to prefix every commit subject line with a type, an optional scope, and a colon. This structure immediately communicates the intent of a commit. Common types include feat for a new feature, fix for a bug fix, docs for documentation changes, and refactor for code restructuring. This systematic approach transforms your commit history into a highly organized and queryable database of changes.

Why It's a Best Practice

Conventional Commits bring structure and predictability to your project's history. This format makes it easy to scan the log and understand the nature of changes without reading the full details. It’s especially powerful for automating semantic versioning, where a feat commit can trigger a minor version bump and a fix can trigger a patch release. Breaking changes, indicated by a ! after the type/scope (e.g., feat!: ...), can automatically trigger a major version bump.

Key Insight: Conventional Commits turn your Git history into a structured dataset. This data can then be used by tools like semantic-release to automate package releases and changelog generation, saving significant manual effort.

Practical Implementation

The standard format is <type>(<scope>): <description>. Here are examples demonstrating its application:

  • New Feature: feat(auth): add Google OAuth2 integration
  • Bug Fix: fix(api): resolve memory leak in user service
  • Documentation: docs(readme): update installation instructions
  • Breaking Change: feat(users)!: change user API response format

To streamline adoption, consider using tools like Commitizen to guide developers through creating compliant messages via an interactive prompt. Integrating commitlint with a commit-msg Git hook can also enforce the standard, rejecting non-compliant messages before they enter the history. This structured approach is a key part of an effective code review process, which you can learn more about in this comprehensive code review checklist.

7. Reference Issues and Pull Requests

Modern software development rarely happens in a vacuum; code changes are almost always tied to a specific task, bug report, or user story. One of the most powerful habits you can adopt is to explicitly link your commits to these external items, such as Jira tickets or GitHub issues. This creates a traceable path from the initial requirement or problem report directly to the code that addresses it, providing invaluable context for future developers.

By including these references, you bridge the gap between your project management tools and your version control history. Anyone reviewing the code months or years later can instantly understand the "why" behind a change. Platforms like GitHub and GitLab even leverage special keywords to automatically link and close issues when a commit is merged, streamlining project workflows and ensuring that progress is accurately tracked.

Why It's a Best Practice

Referencing issues and pull requests transforms your commit log from a simple list of changes into a rich, interconnected history of the project's development. It provides crucial business context, helping team members understand the purpose of a commit without needing to dig through old emails or project boards. This practice is essential for code archeology, debugging, and performing effective code reviews. For a deeper dive into related best practices, you can explore more on creating effective pull requests on blog.pullnotifier.com.

Key Insight: Treat your commit message as the permanent link between a business requirement and its technical implementation. A simple reference like Fixes #4321 can save hours of future investigation.

Practical Implementation

Consistently referencing issues requires establishing a team-wide format. Here’s how to do it effectively, along with good and bad examples:

  • Bad (No Context): Fix user login validation

  • Good (Linked): Fix user login validation (Resolves #1234)

  • Bad (Vague): Update dashboard

  • Good (Multi-reference): Update user dashboard layout (Fixes PROJ-123, See also: PROJ-124)

  • Bad (Just the code): feat(payment): add Stripe integration

  • Good (With Body Context): feat(payment): Add Stripe integration

    Implements the payment processing requirements outlined in issue #456. Related to PR #789.

Using keywords like Resolves, Fixes, or Closes will automatically close the corresponding issue on platforms like GitHub when the commit is merged into the main branch. GitHub supports nine keywords in total: close, closes, closed, fix, fixes, fixed, resolve, resolves, and resolved — all case-insensitive. This means even past-tense forms like Fixed #123 work, making this a flexible and efficient practice.

8. Document Trade-offs and Alternatives Considered

While section 5 covers explaining the what and why, this practice goes a step further: document the trade-offs you weighed and the alternatives you rejected. This is the information that's hardest to reconstruct later and most valuable during future maintenance. When a developer revisits your code and wonders "why didn't they just do X instead?", your commit message should already answer that question.

This mindset shifts your commit from a simple explanation into a mini design decision record. It captures the reasoning that typically lives only in Slack threads, meeting notes, or a developer's memory — all of which are lost over time. This is closely related to providing clear feedback in pull requests, as both skills rely on empathy and context. Learn more about how to write clear PR feedback.

Why It's a Best Practice

The most common question during code archeology isn't "what does this do?" — it's "why was it done this way?" Documenting rejected alternatives prevents future developers from wasting time re-exploring paths you've already ruled out. It also builds a culture of thoughtful decision-making and strong technical communication across the team.

Key Insight: Your audience is a competent developer who is smart but lacks the specific context you had while making the change. Assume they'll understand the code, but not the constraints, trade-offs, or business pressures that shaped your decision.

Practical Implementation

Focus on what you considered, what you chose, and why. Here are examples that show the difference:

  • Bad (No trade-off context): Optimize user profiles

  • Good (Documents the decision):

    Cache user profiles with Redis to reduce database load
    
    Profile data was being fetched on every page load, causing
    slow response times on the dashboard. Considered three
    approaches:
    - In-memory cache: Fast but not shared across instances
    - CDN caching: Overkill for authenticated user data
    - Redis: Shared across instances, supports TTL
    
    Chose Redis with a 5-minute TTL as the best balance of
    performance and data freshness.
    
  • Bad (No alternatives): Update dependencies

  • Good (Explains the rationale and trade-off):

    Switch from moment.js to date-fns for bundle size
    
    Moment.js was adding 200KB to our bundle. Evaluated
    date-fns, dayjs, and Temporal API as replacements.
    Temporal is not yet widely supported. Dayjs has a smaller
    API surface. Date-fns provides the same functionality with
    tree-shaking support, reducing our bundle by ~150KB.
    Updated all date formatting calls to the new API.
    

By treating your commit message as a mini design decision record, you create an invaluable historical record that empowers your entire team.

Quick Reference: All 8 Practices at a Glance

PracticeComplexityKey Advantage
Use the Imperative MoodLowAligns with Git's internal style; consistent, scannable history
Keep Subject Under 50 CharactersLowPrevents truncation; enforces clarity across all tools
Separate Subject from BodyLowEnsures correct Git parsing and tool compatibility
Wrap Body at 72 CharactersMediumFits within 80-column terminals with indentation
Explain What & Why, Not HowMediumAdds irreplaceable context for maintenance and debugging
Use Conventional CommitsMedium-HighEnables automated changelogs and semantic versioning
Reference Issues and PRsLow-MediumCreates traceability between requirements and code
Document Trade-offs and AlternativesMediumPrevents re-exploring rejected paths; captures decision context

From Chore to Craft: Mastering Your Commit History

Moving from inconsistent, brief commit messages to a well-structured, informative log is a significant upgrade for any development team. It’s the difference between a cluttered, confusing project diary and a clear, searchable historical record. Adopting the git commit message best practices we've covered isn't about enforcing rigid, arbitrary rules. Instead, it’s a strategic investment in communication, clarity, and long-term project maintainability. Each well-written commit becomes a breadcrumb, leaving a trail that makes debugging, code archeology, and onboarding new team members exponentially easier.

The true value of this discipline emerges over time. Imagine jumping into a legacy module six months from now. A history filled with messages like feat: Add user authentication endpoint and a detailed body explaining the choice of JWT over sessions is far more valuable than a simple wip or fixed bug. The former empowers any developer to understand the context instantly, while the latter forces them to decipher the code from scratch, wasting valuable time and mental energy.

Unlocking Team Velocity and Code Quality

The benefits extend far beyond individual understanding. A consistent and descriptive commit history directly impacts team velocity and collaboration.

  • Accelerated Code Reviews: When a pull request is composed of atomic commits, each with a clear purpose articulated in its message, reviewers can grasp the changes much more efficiently. They can review commit by commit, understanding the logical progression of the feature or fix without needing to ask for clarification.
  • Simplified Debugging: Tools like git bisect become superpowers when your commit history is clean. git bisect performs a binary search through your commits to find the exact one that introduced a bug. It works best when each commit is atomic and functional — if commits bundle unrelated changes, bisect can't isolate the problem. A clean history turns a potentially hours-long bug hunt into a swift, targeted fix.
  • Automated Changelog Generation: By adhering to a specification like Conventional Commits, you unlock the ability to automatically generate accurate, user-friendly changelogs for every release. This eliminates a tedious manual task and ensures stakeholders are always informed.

Making Best Practices Your Team's Default

The key to successfully implementing these guidelines is consistency. To make this transition smooth and sustainable, start with a team discussion. Agree on which conventions matter most to your workflow and document them. From there, leverage automation to make the right way the easy way.

Set up a shared commit.template to pre-fill the structure in every developer's editor. Implement a commit-msg Git hook using tools like commitlint to provide instant feedback and enforce formatting standards before a commit is even created. This combination of shared agreement and automated enforcement removes the cognitive load and turns best practices into muscle memory. By weaving these habits into your daily workflow, you transform the act of writing a commit message from a mundane chore into a deliberate act of craftsmanship that pays dividends for the entire lifecycle of your project.


Clear commits are the first step, but keeping your team in sync requires bridging the gap between your repository and your communication channels. PullNotifier elevates your workflow by delivering concise, actionable pull request updates directly to Slack, ensuring reviews happen faster and your team stays focused. Stop context switching and start shipping faster by trying PullNotifier today.