Approach · workflow · in production
Commit Often, Push Deliberately
I use 'Commit Often, Push Deliberately' to maintain a detailed local history of iterative work, ensuring every remote push represents a stable, tested, and deployable state.
The 'Commit Often, Push Deliberately' approach separates the rapid, iterative capture of work in local commits from the more considered, verified act of pushing changes to a remote repository. This distinction is fundamental to how I operate Total Ventures, allowing for granular version control without introducing instability into shared branches or deployment pipelines.
What it is
This workflow involves frequent local commits—sometimes every few minutes—to save progress, experiment, or capture small, atomic changes. These commits are often messy, incomplete, or experimental. They are a personal scratchpad. The 'deliberate push' part means that before any changes leave my local machine for a remote like GitHub, they are squashed, rebased, and thoroughly tested. The goal is to present a clean, linear, and fully functional history to the remote, where each commit could theoretically stand alone as a working feature or fix. This is particularly important when working with agent-generated code, where initial outputs from Claude Code or Gemini might require several local iterations and corrections before they are ready for integration.
Why I do this way
My primary reason for this separation is stability. As a solo operator, every push to `main` often triggers a Vercel deployment or a Firebase function update. I cannot afford to break production with half-baked code. Frequent local commits provide a safety net; I can revert to any point in my local history without affecting anyone else or triggering unnecessary CI/CD cycles. This also allows me to experiment freely, knowing that failed attempts are contained locally. It's a key part of my strategy to Systemize Corrections, ensuring that agent-introduced bugs are caught and fixed locally before they ever hit a shared branch. This approach also complements my Monorepo as Operating System setup, where changes across multiple packages need careful coordination before being pushed.
How it works in practice
My typical workflow starts with a new branch for a feature or fix. As I build, I commit locally after every logical step, even minor ones. For example, if I'm integrating a new Stripe webhook for Inky, I might have separate local commits for: 'add webhook endpoint', 'parse event payload', 'handle `checkout.session.completed`', 'update user subscription in Firebase'. These are small, focused, and often contain `console.log` statements or temporary debugging code. Before pushing, I use `git rebase -i` to combine these into one or two coherent commits, clean up messages, and remove any debugging artifacts. I then run all relevant local tests and, critically, perform a CI Local Before CI Remote check using `pnpm build` and `pnpm test` within my monorepo. Only when everything is green and the commit history is clean do I push to the remote. This ensures that when a push happens, it's a high-confidence event.
Where this breaks down
This workflow requires discipline. Forgetting to rebase or squash before pushing can lead to a messy remote history, which defeats the purpose. It also adds a small cognitive overhead compared to simply committing and pushing immediately. In scenarios requiring rapid, collaborative iteration on a shared branch, this approach might introduce friction if team members aren't aligned on the rebase-before-push policy. For my solo studio, where I am the only one pushing to shared branches, these breakdowns are manageable and the benefits outweigh the costs. The biggest risk is human error in the rebase process itself, which can be mitigated with careful attention and `git reflog` knowledge.
FAQs
- How do you handle large changes that take days?
- For multi-day features, I still commit often locally. If I need to switch contexts, I might create a temporary remote branch for backup, but the main work stays local until it's ready for a clean, squashed push to the integration branch.
- What if I need to share work quickly with an external collaborator?
- If immediate sharing is necessary, I'll create a dedicated feature branch, push my current local commits (even if messy) to it, and share that branch. This is an exception, not the rule, and the branch is usually deleted after review.
- Doesn't rebasing rewrite history and cause problems?
- Yes, rebasing rewrites history, which is why I only do it on my local feature branches before pushing to a shared remote. Once pushed to `main` or a shared integration branch, history is never rewritten to avoid issues for others.
I run this and four other brands. Want to see the operator playbook in detail?
Get the Builder's Playbook →