The Complete Guide to Writing Better Git Commits for MERN Stack Developers

Today

The Complete Guide to Writing Better Git Commits

As developers, we write hundreds of commits throughout a project's lifetime. Yet, many of us treat commit messages as an afterthought. A well-written commit message is like leaving breadcrumbs for your future self (and your team) - it tells the story of why your code changed, not just what changed.

In this guide, I'll show you how to write professional commit messages with real examples from MERN stack and Next.js development.

Why Good Commits Matter

Imagine joining a project and seeing this in the git history:

- fixed stuff
- updates
- asdfasdf
- working now
- final fix (for real this time)

Now compare that to this:

- feat: add user authentication with JWT
- fix: resolve memory leak in WebSocket connection
- refactor: extract API logic into service layer
- chore: upgrade Next.js to version 14

Which project would you rather work on? Good commits:

The Conventional Commits Format

The most widely-used standard is Conventional Commits. Here's the structure:

<type>: <short description>

[optional body]

[optional footer]

Commit Types

Let me break down the most common types with real scenarios from MERN stack development:

feat: - New Features

When to use: Adding something NEW that users or clients will see or interact with.

Real examples:

feat: add user profile page with avatar upload
feat: implement dark mode toggle
feat: add pagination to blog posts
feat: create checkout page with Stripe integration
feat: add email verification on signup
feat: implement real-time notifications with Socket.io
feat: add product search with filters
feat: create admin dashboard for order management
feat: implement OAuth login with Google
feat: add markdown support for blog posts

Think: "Would I demo this to a client or add it to release notes?" If yes → feat:

fix: - Bug Fixes

When to use: Correcting something that's broken or not working as intended.

Real examples:

fix: resolve 404 error on refresh in Next.js dynamic routes
fix: correct login redirect loop
fix: prevent duplicate orders on double-click
fix: resolve CORS error in production API
fix: correct profile image not displaying after upload
fix: resolve MongoDB connection timeout in deployment
fix: fix JWT token expiration handling
fix: correct cart total calculation for discounted items
fix: resolve hydration mismatch in SSR components
fix: prevent memory leak in useEffect cleanup

Think: "Was something not working correctly before?" If yes → fix:

refactor: - Code Improvements

When to use: Restructuring or improving code WITHOUT changing its external behavior.

Real examples:

refactor: convert class components to functional hooks
refactor: extract API calls into separate service layer
refactor: split UserController into smaller modules
refactor: move validation logic to middleware
refactor: convert callback functions to async/await
refactor: reorganize folder structure for better scalability
refactor: simplify authentication logic using custom hook
refactor: replace prop drilling with Context API
refactor: optimize database queries with aggregation pipeline
refactor: extract repeated code into utility functions

Think: "Does the app do the exact same thing, just with cleaner code?" If yes → refactor:

chore: - Maintenance Tasks

When to use: Setup, configuration, dependencies, tooling - things that don't affect the app's functionality.

Real examples:

chore: add .gitignore with node_modules and .env
chore: update Next.js to version 14
chore: configure ESLint and Prettier rules
chore: add Husky pre-commit hooks
chore: setup MongoDB connection pooling
chore: configure environment variables for production
chore: add Docker configuration files
chore: update package dependencies to latest versions
chore: setup CI/CD pipeline with GitHub Actions
chore: configure Vercel deployment settings
chore: add TypeScript configuration

Think: "Is this about tooling, setup, or dependencies - not app features?" If yes → chore:

Other Useful Types

docs:     # Documentation only changes
docs: add API documentation for payment endpoints
docs: update README with deployment instructions
 
style:    # Formatting, missing semicolons (no code change)
style: format code with Prettier
style: fix indentation in components folder
 
test:     # Adding or updating tests
test: add unit tests for authentication service
test: add integration tests for checkout flow
 
perf:     # Performance improvements
perf: optimize image loading with lazy loading
perf: add Redis caching for frequently accessed data
 
build:    # Build system or external dependencies
build: configure Webpack for production optimization
build: add compression plugin to build process

Real Day in MERN Development

Let me show you what a typical development day looks like with proper commits:

Monday Morning - Project Setup

git commit -m "chore: initialize Next.js project with TypeScript"
git commit -m "chore: install and configure Tailwind CSS"
git commit -m "chore: setup MongoDB Atlas connection"
git commit -m "chore: configure environment variables"

Monday Afternoon - Authentication

git commit -m "feat: create user registration API endpoint"
git commit -m "feat: add login page with form validation"
git commit -m "feat: implement JWT token generation"

Tuesday - Bug Fixes & Features

git commit -m "fix: resolve password hashing issue in signup"
git commit -m "feat: implement protected routes with middleware"
git commit -m "feat: add user profile page"

Wednesday - Products Feature

git commit -m "feat: add product listing page with MongoDB queries"
git commit -m "feat: create shopping cart functionality"
git commit -m "refactor: extract product card into reusable component"
git commit -m "feat: implement cart persistence with localStorage"

Thursday - Checkout & Refinements

git commit -m "fix: correct cart not persisting after page refresh"
git commit -m "feat: add checkout page with order summary"
git commit -m "refactor: move all API routes to /api/v1 structure"
git commit -m "feat: integrate Stripe payment processing"

Friday - Polish & Deploy

git commit -m "fix: resolve Next.js Image optimization errors"
git commit -m "perf: add image lazy loading for product gallery"
git commit -m "chore: add README with setup instructions"
git commit -m "chore: update dependencies before deployment"
git commit -m "docs: add API documentation"

Writing the Perfect Commit Message

The Subject Line

Rules:

  1. Use imperative mood - "add" not "added" or "adds"
  2. Keep it under 50 characters
  3. Don't end with a period
  4. Capitalize the first letter after the type
  5. Be specific but concise

Good:

feat: add user authentication with JWT
fix: prevent crash when email field is empty
refactor: extract validation logic to utils

Bad:

feat: added some new features for users
fixed bug
update
changed files

The Body (Optional but Valuable)

Add a body when you need to explain the why behind your changes:

fix: prevent memory leak in WebSocket connection
 
The connection wasn't being properly closed when users 
navigated away from the chat page, causing memory to 
accumulate over time. Now explicitly closing connections 
in the useEffect cleanup function.
 
This resolves the performance degradation reported by 
users after extended app usage.

When to add a body:

The Footer (For Special Cases)

feat: add user profile deletion
 
Allow users to permanently delete their accounts and 
all associated data in compliance with GDPR requirements.
 
BREAKING CHANGE: API endpoint /api/user now requires 
authentication header for all requests

Quick Decision Tree

Not sure which type to use? Follow this:

┌─ Is it visible to users or adds functionality?
│  └─ Yes → feat:
│
├─ Is it fixing something broken?
│  └─ Yes → fix:
│
├─ Is it improving code without changing behavior?
│  └─ Yes → refactor:
│
├─ Is it tests?
│  └─ Yes → test:
│
└─ Is it tooling, config, or dependencies?
   └─ Yes → chore:

Best Practices & Tips

1. Commit Often, Commit Early

Don't wait until you have 500 lines changed. Make small, logical commits:

# ❌ Bad - too much in one commit
git commit -m "feat: add entire user authentication system"
 
# ✅ Good - broken into logical pieces
git commit -m "feat: create user registration endpoint"
git commit -m "feat: add login form with validation"
git commit -m "feat: implement JWT token generation"
git commit -m "feat: add protected route middleware"

2. One Concern Per Commit

# ❌ Bad - mixing concerns
git commit -m "feat: add login page and fix cart bug and update packages"
 
# ✅ Good - separate commits
git commit -m "feat: add user login page"
git commit -m "fix: resolve cart total calculation bug"
git commit -m "chore: update React and Next.js versions"

3. Test Before You Commit

Your code should work before you commit it. Don't commit broken code (except on feature branches with WIP notation).

4. Use Commit Templates

Create a .gitmessage template:

# ~/.gitmessage
# <type>: <subject>
# 
# <body>
# 
# <footer>
 
# Type: feat, fix, refactor, chore, docs, style, test, perf
# Subject: imperative mood, 50 chars max, no period
# Body: explain what and why (72 chars per line)
# Footer: breaking changes, issue references

Then configure git:

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

5. Leverage Git Hooks

Use Husky to enforce commit conventions:

npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
 
# package.json
{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}

Tools to Help You

Commitizen

Interactive CLI for writing commits:

npm install -g commitizen
commitizen init cz-conventional-changelog --save-dev --save-exact

Then use git cz instead of git commit

VSCode Extensions

Automated Changelog Generation

Tools like standard-version can generate changelogs from your commits:

npm install --save-dev standard-version
 
# Creates CHANGELOG.md automatically
npm run release

Common Mistakes to Avoid

1. Vague Messages

 git commit -m "updates"
 git commit -m "fixes"
 git commit -m "changes"
 git commit -m "fix: resolve CORS error in production API"

2. Too Much in One Commit

 git commit -m "feat: add entire e-commerce system"
 Split into: product listing, cart, checkout, payment

3. Mixing Types

 git commit -m "feat: add login and fix navbar bug"
 Separate into feat and fix commits

4. Irrelevant Details in Subject

 git commit -m "fix: updated the LoginComponent.tsx file on line 45"
 git commit -m "fix: resolve authentication redirect loop"

Real-World Examples from Open Source

Let's look at commits from popular projects:

Next.js:

feat: add support for React Server Components
fix: resolve hydration mismatch in app directory
perf: optimize bundle size for production builds
docs: update deployment guide for Vercel

React:

feat: add useId hook for SSR-safe unique IDs
fix: prevent memory leak in useEffect
refactor: simplify reconciliation algorithm
test: add integration tests for Suspense

Conclusion

Good commit messages are a hallmark of professional developers. They:

Start small - focus on getting the type and short description right. As you build the habit, add bodies to complex changes. Your future self will thank you!

Quick Reference Card

feat:     New feature for users
fix:      Bug fix
refactor: Code improvement (no behavior change)
chore:    Tooling, config, dependencies
docs:     Documentation only
style:    Formatting (no code change)
test:     Adding or updating tests
perf:     Performance improvement
 
Format:   type: imperative description under 50 chars
Body:     Explain WHY (optional)
Footer:   Breaking changes, issues (optional)

Resources:

Happy committing! 🚀


What are your commit message tips? Share them in the comments below!