The Commit Preview Habit: How Mental Dry-Runs Catch Bugs Before CI Does
Learn how to mentally “dry-run” your commits, use your git diff as a narrative, and combine careful mental simulation with fast CI to catch bugs earlier and ship cleaner code.
Introduction
Most developers treat CI as the primary bug detector: push code, wait for the pipeline, then fix whatever breaks. It works—until it doesn’t. Flaky tests, slow pipelines, and subtle logic bugs can all slip through, and a broken main branch quickly becomes everyone’s problem.
There’s a simple habit that dramatically changes this dynamic: the commit preview habit.
Instead of relying on CI to tell you whether your change is safe, you mentally dry-run each commit before pushing. You walk through the diff line by line, simulate how the code will execute, and ask, “What could this break?”
This is not about being paranoid. It’s about being deliberate. When you treat every commit as something you must be able to justify and mentally execute, your code gets cleaner, your CI runs become more predictable, and your team’s trust in the main branch increases.
What Is the Commit Preview Habit?
The commit preview habit is a simple, repeatable practice:
- Make a small, focused change.
- Review the diff as if it were someone else’s code.
- Mentally simulate the execution of the new or changed code.
- Ask, for every hunk: what could break because of this?
- Refine until the diff looks clean and you can’t find more issues.
- Only then commit and push, using CI as confirmation, not discovery.
You’re not just eyeballing changes. You’re performing a mental dry-run of the commit: imagining real inputs, actual execution paths, existing dependencies, and weird edge cases.
This habit turns your git diff into a powerful quality gate that lives in your brain, not just in your CI pipeline.
Mentally Dry-Run Your Code: Line by Line
The core of the habit is intentional mental simulation.
When you look at a diff, don’t just read it—run it in your head:
- "What values can reach this line?"
- "If this condition is false, where do we go?"
- "What happens on the first element? The last one? An empty list?"
- "What if this API returns null / throws / times out?"
Focus on Specific Bug Patterns
While dry-running, deliberately hunt for:
-
Logic errors
Are conditions reversed? Are you mixing up AND/OR? Are you reusing variables incorrectly? -
Off-by-one issues
Indexing loops from0vs1, inclusive vs exclusive bounds, slicing arrays—these are classic mental-simulation catches. -
Hidden assumptions
Are you assuming non-empty input, sorted lists, non-null fields, or stable external APIs? What happens when those assumptions fail? -
State changes
Are you mutating shared objects, global state, or caches in ways that can cause side effects elsewhere?
A Simple Example
Suppose your diff shows:
- for (int i = 0; i < items.Count; i++) { - Process(items[i]); - } + for (int i = 1; i <= items.Count; i++) { + Process(items[i]); + }
A quick glance might think, "Loop still runs, no big deal." A mental dry-run says:
- On the first iteration,
i = 1→ skipsitems[0]. - On the last iteration,
i = items.Count→items[i]throws an index error.
A fast CI run might catch this if you have good tests. Your mental dry-run catches it before you even commit.
Treat Your Diff as a Narrative, Not Just a Patch
One of the most powerful mindset shifts is to read your diff like a story:
- Who are the main characters? (functions, modules, external systems)
- What changed in their behavior?
- What other parts of the system depend on that behavior?
Read the diff as if you are code reviewing someone else’s work. That distance makes it easier to challenge assumptions:
- "Why did we remove this null check?"
- "This function used to return an empty list; now it can return null. Who calls it?"
- "We changed a timeout from 3s to 30s—what does that do to user experience?"
Ask yourself "What could break?" at every change:
- At function boundaries: Return types, error handling, nullability.
- At data boundaries: Parsing, serialization, schema changes.
- At behavior boundaries: Timeouts, retries, concurrency, caching.
At the end of the diff, you should be able to summarize the narrative in one or two sentences:
This commit changes user login to log additional metrics, without modifying authentication behavior or error handling.
If your mental summary doesn’t match the diff—or you can’t summarize it clearly—your change is probably too big or too fuzzy. Split it or refine it.
Avoid "Oops" Commits: Refine Locally First
A common anti-pattern: commit, push, CI fails, then follow up with a stream of "oops" or "fix build" commits.
This is a symptom of using CI as your first line of defense instead of your last.
Instead:
- Run your mental dry-run.
- Adjust your code until the diff looks intentional and coherent.
- Run tests locally (even a focused subset) when possible.
- Only then commit and push.
This has several benefits:
- Your commit history tells a clean story, instead of documenting your trial-and-error.
- Code review becomes faster and more pleasant.
- CI infrastructure is used for validation, not as an editor with unit tests.
Your goal is not perfection. Your goal is that most CI failures should surprise you—and then lead you to improve your mental model.
CI as Confirmation, Not Discovery
When you integrate the commit preview habit with CI, the relationship changes:
- Your mental dry-run is the first gate.
- Your local tests are the second gate.
- Your CI pipeline becomes the third gate: independent confirmation.
This mindset produces a powerful feedback loop:
- CI is green most of the time because you’ve already caught obvious issues.
- When CI fails, you learn: your mental model missed something. You refine it.
- Over time, your internal simulator gets better at predicting real-world behavior.
A practical rule:
Treat a green CI run as "I correctly predicted the behavior of my code," not just "The code seems fine."
That subtle shift nudges you to care about understanding your system, not just passing tests.
Refuse to Merge While CI Is Red
There’s a cultural element here: build integrity.
Adopt and enforce a simple policy:
No merges while CI is red. Ever.
This reinforces:
- The idea that green build + mental preview = definition of done.
- Respect for teammates’ time: no one else has to debug your broken main.
- That red builds are exceptions to be fixed immediately, not background noise.
If your team treats red builds as normal, your brain stops trusting CI, and the commit preview habit loses one of its key feedback sources.
Keep Tests Fast and Reliable
The commit preview habit works best when paired with fast, trustworthy tests. Otherwise you’ll be tempted to skip CI or ignore failures.
Aim for:
- Fast feedback: Unit and integration tests that run in minutes, not hours.
- High signal: Minimize flaky tests that randomly fail.
- Clear ownership: When tests fail, it should be obvious where to look and who should care.
When tests are fast and reliable, your daily rhythm becomes:
- Implement change.
- Mentally dry-run and review diff.
- Run local tests (or targeted suites).
- Push and let CI confirm.
This becomes automatic—the habit disappears into muscle memory. Your brain and your CI cooperate instead of competing.
Making the Habit Stick
To build the commit preview habit:
- Start small. Pick one commit today and deliberately dry-run it before you push.
- Use checklists. Before committing, quickly ask: logic errors? off-by-one? nulls? dependencies?
- Timebox it. Spend 1–3 minutes per commit on mental simulation. It’s cheap insurance.
- Reflect on CI failures. When CI catches something, ask: "Could I have seen this in the diff?" If yes, update your mental checklist.
With repetition, your brain will start running these simulations automatically. The cost decreases; the benefit compounds.
Conclusion
The commit preview habit is not a replacement for tests or CI. It’s the missing layer in between: a deliberate mental pass that turns your git diff into a story you understand and can defend.
By mentally dry-running each commit, treating the diff as a narrative, avoiding "oops" commits, using CI as confirmation, and insisting on green builds, you:
- Catch logic bugs and off-by-one errors earlier.
- Ship cleaner, more intentional commits.
- Build stronger trust in your codebase and your team’s process.
You already read your diffs. The difference is whether you skim them or simulate them.
Next time you’re about to hit git commit, pause for a moment and run the code in your head. Let CI confirm what you already know: this change is ready.