Git Workflow Best Practices for Solo Developers

Working alone does not mean skipping Git discipline. A clean Git history is a log of your thinking, a safety net for experiments, and a foundation for eventual collaboration. These practices will make you a better developer even when no one else is reviewing your commits.

Git Version Control Workflow Best Practices

1. Always Work on Feature Branches

Even as a solo developer, committing everything to main is a bad habit. When you need to interrupt one feature to fix a bug, a branch-based workflow lets you switch contexts cleanly.

# Create and switch to a feature branch
git checkout -b feature/add-user-auth

# Work, commit, test...

# Merge back to main when done
git checkout main
git merge --no-ff feature/add-user-auth
git branch -d feature/add-user-auth

The --no-ff flag forces a merge commit even when a fast-forward is possible. This preserves the history of when the feature branch existed, making the log easier to read later.

Use short-lived feature branches for every meaningful change.
Commit directly to main for every change.

2. Write Meaningful Commit Messages

A good commit message answers "why", not just "what". The diff already shows what changed. The commit message should explain your reasoning.

# Bad commit messages
git commit -m "fix"
git commit -m "changes"
git commit -m "wip"

# Good commit messages (imperative mood, under 72 chars for subject)
git commit -m "Fix null pointer error when user has no profile photo"
git commit -m "Add rate limiting to the /login endpoint"
git commit -m "Refactor auth middleware to support multiple JWT issuers"

For complex changes, add a body after a blank line:

git commit -m "Switch from HS256 to RS256 for JWT signing

HS256 requires sharing the secret with third-party services that
need to verify tokens. RS256 lets them use the public key instead,
keeping the private key secure on the auth server only."

3. Commit Early, Commit Often

Small, focused commits are easier to review, easier to revert, and easier to understand. Each commit should represent a single logical change — not two hours of work lumped together.

Commit after completing each small unit of work: a function, a test, a config change.
Build a feature over three days and make one giant commit with dozens of changed files.

If you have accumulated a mess of small WIP commits, use interactive rebase before merging to main to clean them up:

git rebase -i HEAD~5  # Squash/edit the last 5 commits

4. Use git stash for Context Switching

When you are mid-feature and need to urgently fix a bug on main, git stash saves your uncommitted changes without creating a commit.

# Save current work
git stash push -m "half-done user settings page"

# Switch to main and fix the bug
git checkout main
git checkout -b hotfix/login-redirect
# fix, commit, merge...

# Return to your feature branch
git checkout feature/user-settings
git stash pop  # Restore your saved changes

5. Prefer Rebase for Keeping Branches Current

When your feature branch falls behind main, you have two options: merge main into your branch, or rebase your branch onto main. Rebasing gives a cleaner linear history.

# Instead of: git merge main
git fetch origin
git rebase origin/main

# If conflicts arise, resolve them, then:
git rebase --continue
Rebase commits that have already been pushed to a shared remote — this rewrites history and breaks your collaborators' branches.
Rebase local-only commits freely to keep history clean before pushing.

6. Use .gitignore Religiously

Commit a .gitignore at the start of every project. Never commit build artifacts, dependencies, secrets, or editor configs.

# Common .gitignore entries
node_modules/
.env
.env.local
dist/
build/
*.pyc
__pycache__/
.DS_Store
*.log
coverage/

If you accidentally committed a file that should be ignored:

# Remove it from tracking without deleting locally
git rm --cached .env
git commit -m "Remove accidentally committed .env file"

7. Tag Releases

Tags create permanent named snapshots of your repository at a specific commit. Use them for every release so you can always check out exactly what was deployed to production.

# Create an annotated tag
git tag -a v1.2.0 -m "Release version 1.2.0: adds OAuth login"

# Push tags to remote (they are not pushed by default)
git push origin --tags

# Check out a specific release tag
git checkout v1.1.0

8. Know How to Recover from Mistakes

Git has excellent recovery tools. The most powerful is git reflog, which records every position HEAD has been at in the last 90 days — including commits you thought you lost.

# See the full history of where HEAD has been
git reflog

# Recover a "deleted" commit
git checkout -b recovery-branch abc1234

# Undo the last commit but keep the changes staged
git reset --soft HEAD~1

# Undo the last commit and unstage changes
git reset HEAD~1

Summary

Good Git hygiene takes five minutes to learn and saves hours of confusion over the life of a project. Use branches for every feature, write commit messages that explain why, use stash for context switching, rebase to keep history clean, and always have a .gitignore. Your future self reading the log will thank you.

Related Snippets