Introduction
Production is down. A critical bug is costing money by the minute. You need to deploy a fix now, but you also need to keep your team's Git workflow intact. The hotfix workflow is a branching strategy that lets you ship urgent fixes safely while maintaining code quality and keeping your main development branch clean. In this article, we'll walk through the hotfix workflow: Git & GitHub best practices explained, showing you exactly how to branch from production, write minimal fixes, get them reviewed quickly, and merge them back into your main line.
Why hotfix workflows matter in production
Every team eventually faces the scenario: a bug hits production and users are affected. The pressure is real, but panic-driven commits are how serious issues compound. A structured hotfix workflow gives you a controlled way to respond quickly without bypassing quality gates. You get speed without sacrificing safety.
The core idea is simple: create a dedicated branch from your production tag, isolate the fix, merge it back to production and to your main development branch, then close the hotfix branch. This ensures that your fix doesn't get lost when main eventually merges into production, and it creates an audit trail for incident investigations.
Branch from production — Start from the deployed tag
The first rule of hotfix branching is to start from exactly what's running in production. Not from main. Not from some developer's feature branch. From the tag that represents your live deployment.
When you tag a release, you're marking a specific commit as the version deployed to production. This tag is your anchor point. Everything in that tag is known to be live; everything since is experimental.
Here's how to create a hotfix branch correctly:
# First, fetch the latest tags from remote
git fetch origin --tags
# List recent tags to find the production version
git tag --list --sort=-version:refname | head -10
# Create hotfix branch from the production tag
git checkout -b hotfix/payment-validation v1.8.2
# Verify you're on the right commit
git log -1 --oneline
The branch name follows a convention: hotfix/ prefix plus a brief, kebab-case description. This signals to everyone on the team that this branch is urgent and temporary. When you push it, CI/CD pipelines can recognize the prefix and apply faster, more lenient checks if needed (though you should never skip security scanning).
Real-world scenario: Your payment processor is rejecting valid card numbers due to a regex validation bug introduced in v1.8.2. Customers are losing revenue. You fetch the v1.8.2 tag, branch from it, and start fixing the validation logic immediately—without waiting for the main branch to stabilize.
Minimal fix — Only what is needed, with a test
The temptation in a crisis is to fix everything. Resist it. A hotfix branch should contain exactly one logical change: the minimal code required to resolve the issue, plus a test that proves the fix works.
Why minimal? Because every line of code in production carries risk. The smaller your change, the easier it is to review, the faster it is to deploy, and the simpler it is to revert if something unexpected happens.
Here's a practical example. Say the bug is a null pointer exception in a payment validation method:
# Make a single, focused change
git add src/services/PaymentValidator.ts src/tests/PaymentValidator.test.ts
# Commit with a clear message that references the issue
git commit -m "fix: handle null cardholderName in payment validation
Fixes #4521. The regex validator was not checking for null before
calling match(). Added explicit null check and unit test.
"
Notice the commit message format. It references the issue, explains the root cause, and shows why the fix is necessary. Later, when someone investigates this hotfix, they'll understand the decision-making instantly.
Write a test first, if possible. Even in a hotfix, especially in a hotfix, you need proof that the bug is fixed. A single unit test that reproduces the original issue and passes with your change is invaluable. If the test already existed and was failing, even better—now it passes.
# Example: a minimal test that validates the fix
# test/PaymentValidator.test.ts
describe('PaymentValidator', () => {
it('should handle null cardholderName gracefully', () => {
const payment = { cardholderName: null, cardNumber: '4111111111111111' };
const result = validator.validate(payment);
expect(result.valid).toBe(false);
expect(result.error).toContain('cardholderName');
});
});
This single test takes 30 seconds to write and saves you from deploying a regression. In production work, this is non-negotiable.
Fast review and deploy — Expedited but still reviewed
Now you push the hotfix branch and open a pull request. This is not a moment to skip code review. It's a moment to do code review differently—faster, but still thorough.
git push origin hotfix/payment-validation
On GitHub, create a pull request from your hotfix branch to your production or release branch (often called main or release). Add a label like hotfix or urgent so your team knows this needs immediate attention.
Key points for hotfix PR reviews:
- Scope the review: The reviewer should focus on whether the fix addresses the issue and doesn't introduce new risks. They shouldn't request refactoring or style improvements.
- Check the test: Verify that the test reproduces the original issue and that it passes. If no test exists, this is a red flag.
- Verify the diff: Look at the full diff. A hotfix should be small. If it's more than 50-100 lines, ask questions.
- Use branch protection smartly: Your repository should require at least one approval on all PRs, even hotfixes. But don't require three reviewers for a hotfix. One senior engineer is enough.
Real-world scenario: You have on-call rotations. When a critical issue is reported, the on-call engineer creates the hotfix branch and pushes it to GitHub. They notify the team lead via Slack. Within 10 minutes, the team lead reviews the fix, approves the PR, and the CI/CD pipeline automatically deploys it to production. The entire process is logged in Git.
Configure your GitHub branch protection rules to allow urgent bypasses only for tagged team members, and always log those bypasses in your incident post-mortem.
Merge back — Make sure main gets the fix too
Once your hotfix is live on production, you're not done. If you don't merge the fix back into your main development branch, the bug will reappear the next time you release from main. This is a common mistake that leads to "why is this bug back?" moments.
After your hotfix PR is merged to production, create a second PR from the hotfix branch to your main development branch:
# Ensure you're up to date
git fetch origin
# Check out the hotfix branch
git checkout hotfix/payment-validation
# Create a PR against main (do this via GitHub UI or CLI)
# The PR will show the same minimal diff
If your main branch has diverged from production, this back-merge PR might have conflicts. Resolve them carefully. The fix should apply cleanly to main; if it doesn't, there's a deeper architectural issue to investigate post-incident.
Merge the back-merge PR with the same rigor as the production merge. Once it's merged, you can delete the hotfix branch:
# Delete locally
git branch -d hotfix/payment-validation
# Delete remotely
git push origin --delete hotfix/payment-validation
Now your fix is in production, in main, and in your Git history. If a customer reports the issue again, your logs will show when and how it was fixed.
Common mistakes
Branching from main instead of the production tag: This defeats the purpose. You'll be testing your fix against unreleased code, which might have other issues. Always start from the deployed tag.
Skipping tests to save time: A hotfix without a test is a rollback waiting to happen. Add a test, even if it takes an extra 5 minutes. It pays for itself the first time it catches a regression.
Forgetting to merge back to main: This is the most common mistake. The hotfix works in production, everyone relaxes, and then main gets released without the fix. Add a checklist item to your incident response template: "Did we back-merge to main?"
Treating hotfix as an excuse to skip code review: Fast review is not no review. A single senior engineer should review every hotfix, even if it takes 10 minutes. The risk of a bad fix is higher than the risk of a delayed deployment.
Making the hotfix branch too large: If your fix is touching more than one service or more than 100 lines, you might be solving the wrong problem. Break it down or consider if this is really a hotfix or a planned release.
Key takeaways
- Branch from the production tag, not main. This ensures you're fixing the exact code that's live.
- Keep the fix minimal and add a test. Small, tested changes are easier to review and safer to deploy.
- Review hotfixes quickly but thoroughly. Speed doesn't mean skipping review; it means focused, efficient review by one senior engineer.
- Always merge back to main. A hotfix that only lives in production will resurface when main is released.
- Delete the hotfix branch after merging. Keep your repository clean and your workflow clear.
- Automate as much as possible. Use CI/CD to run tests and branch protection to enforce review, so humans can focus on the fix itself.
- Document the incident and the fix. Post-mortem reviews of hotfixes often reveal systemic issues that prevent future fires.






Comments
Loading comments…