Rain Lag

The Three‑Commit Story: Turning Messy Coding Sessions into Clear Progress

How to use three deliberate commits as tiny, self‑contained stories that bring structure to your coding sessions, improve Git history, and turn every block of work into visible progress.

Introduction

You sit down to code for “just an hour.” Three hours later, you’ve changed ten files, tried three different approaches, fixed an unrelated bug, and your Git history is a graveyard of wip, oops, and fix stuff commits.

The code might work, but your progress is hard to see. Reviewing what happened is painful. Future‑you (or your teammates) will have no idea why things changed the way they did.

There’s a better pattern: the three‑commit story.

This is a simple practice that turns fuzzy goals into tiny, trackable units of progress—using just three deliberate commits per focused session. It helps you:

  • Avoid sprawling, meandering coding sessions
  • Keep your Git history readable and reviewable
  • Turn every block of work into a meaningful, documented story

Let’s break down how it works.


What Is a Three‑Commit Story?

A three‑commit story is a tiny, self‑contained unit of progress that you can complete in a single focused session (typically 30–120 minutes).

It has three characteristics:

  1. Small scope – one clearly defined improvement (e.g., “add search to the products list,” “fix pagination bug,” “extract email sending to a service”).
  2. Three intentional commits – you structure your work into three steps, each captured as a commit.
  3. Readable narrative – the three commits together tell a story of what you did and why.

Instead of approaching a task as “implement search,” you approach it as:

  1. Plan & scaffold: Set up structure, tests, and placeholders.
  2. Implementation: Make it actually work.
  3. Cleanup & refactor: Polish, simplify, and document.

Each of these becomes one commit, forming a compact, understandable story in your Git history.


The Three Deliberate Commits

A three‑commit story follows a simple template. You can adapt the labels, but the flow remains the same.

1. Plan / Scaffold Commit

Goal: Prepare the ground without fully implementing the feature.

Typical changes:

  • Add or update tests that express the desired behavior
  • Introduce new files, classes, or functions as placeholders
  • Add TODO comments or stubs where logic will go
  • Wire up basic routing or configuration

Example commit message:

feat: scaffold product search endpoint - add /products/search route - define ProductSearchRequest and response DTO - add failing integration test for search by name

Why it matters:

  • Forces you to clarify what you’re about to build
  • Gives you a clear target (tests, types, or contracts)
  • Keeps implementation separate from design and setup

2. Implementation Commit

Goal: Make it work. This is where the main logic lands.

Typical changes:

  • Implement the previously stubbed methods
  • Make tests pass
  • Add essential error handling and happy‑path behavior

Example commit message:

feat: implement product search by name - query products by name (case-insensitive) - support partial matches with LIKE - ensure existing filters still apply

Why it matters:

  • Keeps the “meaty” change in one place
  • Easier to review and debug because intent is clear
  • If something breaks, this is the first commit you inspect

3. Cleanup / Refactor Commit

Goal: Polish and stabilize what you just built.

Typical changes:

  • Refactor for readability and maintainability
  • Remove duplication or dead code
  • Improve naming and structure
  • Update docs or comments

Example commit message:

refactor: simplify product search query - extract query builder to ProductSearchSpecification - rename params for clarity - add comments for search behavior

Why it matters:

  • Keeps refactors separate from behavior changes
  • Makes diffs smaller and more meaningful
  • Encourages you not to skip cleanup because it has its own slot

You end the session with a three‑act story: scaffold → implement → refine.


Writing Clear, Action‑Oriented Commit Messages

Three‑commit stories work best when each commit message is short, specific, and action‑oriented.

Use this simple structure:

  1. Summary line (max ~72 characters)
  2. Optional body for details, each bullet or line explaining what and why

Examples:

feat: add optimistic locking to order updates - prevent double-submit from overwriting changes - surface concurrency error to the UI
fix: handle empty query string in product search - return 400 instead of 500 on invalid query - add regression test for empty search

Guidelines:

  • Start with a verb: add, fix, remove, refactor, document, rename.
  • Be concrete: implement product search pagination, not misc changes.
  • Use the body to explain why for non‑obvious decisions.

The result is a Git history that reads like a sequence of clear, small actions.


Using Consistent Prefixes to Signal Intent

Consistent prefixes make patterns jump out when scanning history. Popular choices include:

  • feat: – new feature or behavior
  • fix: – bugfix or defect resolution
  • refactor: – code changes that don’t change behavior
  • docs: – documentation only
  • test: – test‑related changes
  • chore: – tooling, config, or maintenance

In the three‑commit story, you might see patterns like:

  • feat: scaffold product search
  • feat: implement product search by name
  • refactor: simplify product search query

Or for a bugfix:

  • test: add regression test for pagination bug
  • fix: correct page offset calculation
  • refactor: extract pagination logic into helper

These prefixes:

  • Make it easy to filter commits by type
  • Help reviewers understand the purpose of each change
  • Enable automation tools to generate meaningful changelogs

Treating Git History as Documentation

When you adopt three‑commit stories, your Git history stops being a junk drawer and starts becoming usable documentation.

Benefits:

Easier Code Reviews

Reviewers can:

  • Walk through the story commit by commit
  • See the design, implementation, and cleanup stages separately
  • Comment on smaller, focused diffs

Faster Debugging

When a bug appears, you can:

  • git blame a line and see a clear commit message
  • Inspect the three‑commit story around that area
  • Understand why a change was made, not just what changed

Clearer Knowledge Sharing

New team members can read history to learn how the system evolved:

  • “Oh, this is when we added optimistic locking.”
  • “Here’s how we approach refactors without changing behavior.”

Each three‑commit story is a tiny chapter in your project’s documentation.


Turning Commit History into Automation Fuel

Well‑structured commit history is not just nice for humans—it’s gold for automation.

With consistent prefixes and clear messages, you can wire up tools like GitHub Actions, GitLab CI, or other pipelines to:

  • Generate changelogs from feat: and fix: commits
  • Build API docs when docs: or certain feat: commits land
  • Draft release notes automatically from commit history

For example, a release pipeline might:

  1. Scan commits since the last tag
  2. Group them by type (feat, fix, refactor)
  3. Produce a markdown changelog section for the next release

Suddenly, your disciplined three‑commit stories feed directly into your project’s communication and release process.


Making Three‑Commit Stories a Habit

Habits stick best when they’re simple and repeatable. Here’s how to build this one.

1. Start With a Tiny Scope

Ask yourself before coding:

What’s a unit of progress I can complete in one focused session?

If it feels big, slice it smaller. Aim for stories you can finish without feeling rushed.

2. Name Your Three Commits Up Front

Before you start, jot down something like:

  • feat: scaffold user password reset
  • feat: implement password reset email flow
  • refactor: tidy password reset handlers

This gives you a mini roadmap and makes it easier to stop scope creep.

3. Resist Mixing Stages

While implementing, you’ll notice refactors. Instead of doing them immediately:

  • Drop a TODO comment
  • Or note them in your session notes
  • Handle them in the third, dedicated cleanup commit

This keeps each commit focused and easier to review.

4. Practice, Don’t Police

This is a technique, not a religion. Sometimes you’ll need:

  • A fourth commit to fix a test
  • A single small commit for a trivial change

Use the three‑commit story as a guiding pattern, not a rigid rule. The goal is clarity and progress, not perfection.


Conclusion

Messy coding sessions happen when goals are fuzzy and changes blend together. The three‑commit story gives you a simple structure:

  1. Plan & scaffold the change
  2. Implement the behavior
  3. Cleanup & refactor the result

Combined with clear, action‑oriented commit messages and consistent prefixes, each session becomes a small, well‑told story in your Git history.

Over time, you’ll notice:

  • Less chaos in your branches
  • Faster, clearer code reviews
  • Easier debugging and onboarding
  • A Git history that doubles as living documentation

Try it in your next session: pick a tiny goal, sketch your three commits, and see how it changes the way you work. One small story at a time, you’ll turn messy progress into clear, visible momentum.

The Three‑Commit Story: Turning Messy Coding Sessions into Clear Progress | Rain Lag