George Ongoro.

Insights, engineering, and storytelling. Exploring the intersection of technology and creativity to build the future of the web.

Navigation

Home FeedFor YouAboutContact

Legal

Privacy PolicyTerms of ServiceAdmin Portal

Stay Updated

Get the latest engineering insights delivered to your inbox.

© 2026 George Ongoro. All rights reserved.

System Online
    Homedevops-deployment

    Why Tiny Commits Make Debugging Easier

    January 27, 202614 min read
    devops-deployment
    Why Tiny Commits Make Debugging Easier
    Share:

    Table of Contents

    Size vs. Completeness of commitsHow Perfect Git History Becomes Your Debugging SuperpowerThe Step-by-Step Habit - Crafting Atomic Commits Daily1. Just Code First2. Review What Changed3. Identify Logical Units4. Stage Changes Piece by Piece5. Commit with Clear Messages6. Repeat Until Everything is Committed"But My Feature Was Too Big for One Commit!"How Small Commits Transform Code ReviewsGit Commands That Make This Possiblegit commit --amendgit rebase -i (Interactive Rebase)git add -N (Intent to Add)Changing Your Mindset: From "Code Then Commit" to "Commit as You Think"When Atomic Commits Aren't PerfectWhy I am Writing This

    I used to be one of those developers who'd hammer away at code for hours, then commit everything with a message like "Fixed stuff" or "Updates." It felt efficient at the time. Why waste precious coding flow on making a bunch of tiny commits when I could just bundle everything together at the end?

    Then I had to debug a production issue.

    The bug appeared somewhere in the last 2 days of work, buried among 47 commits that ranged from "quick fix" to "updated tests." I hours manually checking out commits, copy pasting logs to LLMs, running tests, and trying to figure out what broke. 6 hours for a bug that, once found, took 15 minutes(or less) to fix.

    That's when I learned the hard way that commit size matters. A lot.

    Size vs. Completeness of commits

    Let's clear something up right away: atomic commits aren't just about being small. I've seen plenty of small commits that are absolutely useless for debugging. A commit that says "WIP" and contains half a feature? Small, but not atomic. A commit that fixes a typo in one file while also refactoring an entire module in another? That's two changes crammed into one commit.

    An atomic commit does exactly one thing. According to the definition that finally made it click for me, an atomic commit represents the smallest possible, meaningful change that does exactly one thing and nothing more, while leaving the codebase in a working state.

    Here's the key word: meaningful. An atomic commit is as small as possible but still complete. Your tests should pass. The app should run. Someone reading the commit six months from now should be able to understand what changed and why without needing a decoder ring.

    Think about it like this. In physics, you can't split an atom (well, you can, but that's nuclear fission and we're trying to keep things simple here). Similarly, an atomic commit contains all the essential elements to stand on its own, like different subatomic particles that together form an atom.

    A good atomic commit:

    • Addresses one logical unit of change
    • Can be reverted without side effects
    • Doesn't mix concerns (no "fix bug AND refactor code" commits)
    • Has tests that pass
    • Can be described in a single, clear sentence

    How Perfect Git History Becomes Your Debugging Superpower

    When you have a clean commit history made up of atomic commits, something magical happens: git bisect actually works the way it's supposed to.

    If you haven't used git bisect before, here's the idea. You tell Git "this commit is good" (no bug) and "this commit is bad" (bug exists). Git then uses binary search to check out commits in between, and you test each one until you find the exact commit that introduced the problem.

    The math is beautiful. For 100 commits, git bisect requires approximately 7 tests, and for 1000 commits, about 10 tests. Compare that to manually checking commits one by one, which could take hours or days.

    But bisect only works well if your commits are actually atomic. If each commit mixes three different changes together, bisect might point you to the right commit, but you'll still have no idea which of those three changes caused the bug.

    The Step-by-Step Habit - Crafting Atomic Commits Daily

    "Okay," you're thinking, "this sounds great in theory, but I'm in the middle of coding. How am I supposed to remember to make tiny commits?"

    Here's my workflow, which I borrowed from developers who recommend not worrying about the process while coding, but breaking up work logically when you reach the end:

    1. Just Code First

    Don't interrupt your flow trying to commit every three lines. When you're in the zone, stay in the zone. Build the feature, fix the bug, write the code however it comes out.

    2. Review What Changed

    Once you've reached a stopping point (or the end of your feature), run git status and git diff to see everything you changed. Take a moment to understand the full scope.

    Or Maybe, you could use Gitaid instead, i created it just for his purpose, It reviews your diff and then generates a commit message that covers everything you changed and it uses Gemini. See the Repository README.md for more info.

    👉 Gitaid on Github

    3. Identify Logical Units

    Look at your changes and ask: what are the independent pieces here? Maybe you:

    • Updated a function's logic
    • Added tests for that function
    • Fixed a typo in documentation
    • Refactored a utility file

    Those are four commits, not one.

    4. Stage Changes Piece by Piece

    This is where git add -p becomes your best friend. Instead of staging entire files, this command lets you stage individual "hunks" of changes interactively.

    Here's what happens when you run it:

    git add -p myfile.js
    

    Git shows you each chunk of changes and asks what you want to do:

    Stage this hunk [y,n,q,a,d,s,e,?]?
    

    The options you'll use most:

    • y - yes, stage this hunk
    • n - no, skip this hunk
    • s - split this hunk into smaller pieces
    • e - manually edit the hunk (for surgical precision)
    • q - quit (stop staging)

    The s option is incredibly useful. When Git groups unrelated changes into a single hunk, you can split it at unchanged lines to create separate hunks. This lets you commit just the validation code separate from the logging code, even though they're in the same file.

    5. Commit with Clear Messages

    Each commit message should answer: "What did this change do?" Not "what files did I touch" (Git tracks that), but the actual logical change.

    Good commit messages:

    • "Add validation for negative payment amounts"
    • "Extract database connection logic to utility function"
    • "Fix race condition in cache invalidation"

    Bad commit messages:

    • "Update files"
    • "Fix bug"
    • "Changes"

    6. Repeat Until Everything is Committed

    Keep using git add -p and git commit until git status shows a clean working directory. Each commit should be one logical change.

    "But My Feature Was Too Big for One Commit!"

    This is the most common pushback I hear, and it's actually proof that you're thinking about atomic commits the right way. If a feature genuinely can't fit in one commit, that's perfect. It shouldn't.

    Let me show you a real example. Last month, I built a user notification system for a School Management System. If I'd committed it all at once, the commit would have included:

    • Database migration for notifications table
    • New API endpoints
    • Email template system
    • Frontend components
    • WebSocket integration for real-time updates
    • Tests for all of the above

    That's insane. Nobody can review that effectively, and if anything breaks, good luck figuring out what.

    Instead, I broke it into a series of atomic commits:

    1. "Add notifications database schema"
    2. "Create notification model with basic CRUD operations"
    3. "Add API endpoints for fetching notifications"
    4. "Implement email notification service"
    5. "Add email templates for system notifications"
    6. "Create notification bell component"
    7. "Add notification list view"
    8. "Integrate WebSocket client for real-time notifications"
    9. "Connect real-time notifications to notification bell"
    10. "Add notification tests"

    Each commit built on the previous one. Each commit could be tested independently. And when we discovered a bug in the WebSocket integration, git bisect took me straight to commit 8.

    The secret is thinking about your feature as a series of steps, not as one giant blob. What's the smallest complete piece you can build first? Start there. Then build the next smallest piece.

    This actually makes development easier, not harder. By forcefully working in atomic commits, you approach work by simplifying it into smaller steps, which is the very core of our job as developers. You're not trying to hold the entire feature in your head at once. You're solving one small, manageable problem at a time.

    How Small Commits Transform Code Reviews

    Code reviews with atomic commits are night and day different from reviews with giant commits.

    Think about being a reviewer for a second. You open a pull request, and it shows "47 files changed, 2,847 additions, 1,203 deletions." Your heart sinks. This is going to take an hour. You grab coffee, settle in, and try to understand what the heck is going on across 47 files worth of changes.

    Now imagine the same pull request, but with 12 atomic commits. Each commit message clearly states what it does:

    • "Extract user validation to separate service"
    • "Add caching layer for user queries"
    • "Update user routes to use new validation"

    You can review commit by commit. Five minutes on the first commit, seven on the second. If you find an issue in commit 3, your feedback is specific: "In the validation commit, the email regex doesn't handle plus signs." The developer knows exactly which commit to revise.

    I've also noticed reviewers give better feedback on atomic commits. When faced with a massive changeset, reviewers often focus on surface-level stuff like formatting because it's too exhausting to dig into the logic. But with small, focused commits, they can actually think about the architecture and point out real issues.

    Git Commands That Make This Possible

    You've already seen git add -p, but there are a few more commands that make working with atomic commits practical:

    git commit --amend

    Made a tiny mistake in your last commit? Or forgot to add one small change? Don't create a "fix typo" commit. Amend the original:

    git commit --amend
    

    This rewrites the last commit to include your staged changes. You can also use it to fix commit messages with git commit --amend -m "Better message".

    Important: only amend commits that haven't been pushed to a shared branch. Rewriting history that others have pulled causes headaches.

    git rebase -i (Interactive Rebase)

    This is where things get powerful. Interactive rebase lets you rearrange, combine, edit, or delete commits.

    git rebase -i HEAD~5
    

    This opens an editor showing your last 5 commits:

    pick a1b2c3d Add user validation
    pick e4f5g6h Fix typo in validation  
    pick i7j8k9l Update tests
    pick m0n1o2p Add logging
    pick q3r4s5t Fix logging bug
    

    You can change pick to other commands:

    • reword - change the commit message
    • edit - pause here to modify the commit
    • squash - combine this commit with the previous one
    • fixup - like squash, but discard this commit's message
    • drop - delete this commit entirely

    Squash melds a commit into the previous one, allowing you to concatenate commit messages, while fixup does the same but discards the commit's message.

    Want to combine those two fix commits into their original commits? Change the file to:

    pick a1b2c3d Add user validation
    fixup e4f5g6h Fix typo in validation
    pick i7j8k9l Update tests  
    pick m0n1o2p Add logging
    fixup q3r4s5t Fix logging bug
    

    Save and close. Git will automatically squash those fixup commits, leaving you with three clean commits.

    git add -N (Intent to Add)

    Here's a weird edge case: you created a new file and want to use git add -p on it, but Git doesn't see new files in patch mode. The solution:

    git add -N newfile.js
    git add -p newfile.js
    

    The -N flag tells Git "I intend to add this file" without actually staging its contents. Now git add -p can work with it.

    Changing Your Mindset: From "Code Then Commit" to "Commit as You Think"

    The hardest part of adopting atomic commits isn't the Git commands. It's changing how you think about commits.

    I used to think of commits as save points. Finish the day? Commit. Need to switch branches? Commit. That's treating Git like Ctrl+S.

    Atomic commits are different. They're communication. Each commit tells a story about one specific change to the codebase. Reading through commit history should be like reading a well-written novel, where each chapter advances the plot clearly.

    When you start thinking this way, you realize commits aren't a chore. They're documentation. They're a debug tool. They're a time machine that lets you understand why code exists.

    I'll be honest: it took me about three weeks of conscious practice before atomic commits felt natural. The first week was annoying. I kept forgetting to break up my changes. The second week, I'd finish a feature and spend 20 minutes using git add -p to separate everything out.

    By the third week, something clicked. I started thinking in atomic chunks while coding. "Okay, I'm going to add this validation function. That's one commit. Then I'll wire it up to the API. That's another commit." The workflow became automatic.

    Now, I literally can't work any other way. Huge commits feel dirty. When I see a 500-line commit in a code review, my eye twitches.

    When Atomic Commits Aren't Perfect

    Let me be balanced here. Atomic commits aren't a silver bullet, and there are situations where they can be awkward:

    Large Refactoring: Sometimes you need to rename a function used in 50 files. That's technically one logical change, but it touches everything. You can still keep it atomic (one commit for the rename), but reviewers might grumble about the size.

    Experimental Work: When you're spiking on an idea and don't know if it'll work, atomic commits can feel like premature optimization. It's okay to commit messily while exploring, then clean up history with interactive rebase before pushing.

    Emergency Hotfixes: When production is on fire, nobody cares about commit quality. Fix it fast, commit it, deploy it. You can always clean up the commits later with rebase before they hit main.

    Learning New Territory: If you're working with a completely new library or framework, your commits might be messier because you don't yet understand the logical units. That's fine. Clean them up once you understand what you built.

    The goal isn't perfection. The goal is communication and maintainability.

    Why I am Writing This

    Here's the thing that finally made atomic commits stick for me: they're not just about debugging. They're about respecting everyone who touches your code, including future you.

    Small focused commits lead to an infinitely better code review process, allowing reviewers to tackle reviews in smaller chunks and provide more targeted feedback. They make git blame useful instead of showing "Updated files" from six months ago. They make cherry-picking possible when you need to port a fix to another branch.

    Most importantly, atomic commits force you to think clearly about what you're building. You can't make atomic commits if you don't understand the logical structure of your changes. The practice of breaking work into atomic pieces makes you a better developer because it makes you think more clearly about software architecture.

    When I first started doing this, a senior developer told me something I didn't fully understand at the time: "The code you write is temporary, but your commits are forever."

    I get it now. Code gets refactored, rewritten, deleted. But commits stick around. They're the permanent record of how and why the codebase evolved. Making them clear and atomic is one of the highest-leverage things you can do for long-term code quality.

    The debugging benefits alone make atomic commits worth it. But trust me, once you've worked this way for a few months, you'll never want to go back. Your git history will be cleaner, your code reviews will be smoother, and when that next production bug appears, you'll track it down in minutes instead of days.

    Small commits. Big impact. Give it a try. Also try gitaid

    Rate this post
    Share:
    George Ongoro
    George Ongoro

    Blog Author & Software Engineer

    I'm George Ongoro, a passionate software engineer focusing on full-stack development. This blog is where I share insights, engineering deep dives, and personal growth stories. Let's build something great!

    View Full Bio

    Related Posts

    Comments (0)

    Join the Discussion

    Please login to join the discussion