Rain Lag

The Two-Column Coding Ledger: A Simple Way to Capture What You Tried vs. What Worked

Learn how a simple two-column note-taking pattern can transform your debugging sessions into a clear, reusable record of what you tried, what actually worked, and why.

Introduction

Most developers have lived this scene:

You spend three hours debugging a nasty issue. You try a dozen things. Eventually, something finally works. A week later, a similar bug appears—and you can’t remember what you did last time.

You vaguely recall grep-ing logs, tweaking configs, or adding print statements, but the exact chain of attempts? Gone.

The problem isn’t just memory. It’s that most of us don’t have a simple, repeatable way to record what we tried vs. what actually worked. Our notes—if we take any—are linear streams of consciousness, Slack messages, or half-finished TODOs that are painful to revisit.

Enter the Two-Column Coding Ledger.

This is a low-friction note pattern that helps you:

  • Separate actions and attempts from results and insights
  • Treat each debugging or coding session like a mini bug report
  • Build a concise, reusable record of your work
  • Reduce thrash and avoid repeating the same unproductive steps

If you’ve ever seen the Cornell note-taking method, this will feel familiar—just adapted to software engineering.


What Is the Two-Column Coding Ledger?

At its core, the Two-Column Coding Ledger is a structured note layout you maintain during a coding or debugging session.

You divide your page (or digital note) into two vertical sections:

  • Left column: Attempts / Actions – what you did, tried, or changed
  • Right column: Outcomes / Insights – what happened, what you learned, and what you decided

In Markdown, it might look like this:

| Attempt / Action | Outcome / Insight | |-------------------------------------------|---------------------------------------------------| | Restarted service with debug logging on. | Logs show repeated 500 errors from auth service. | | Disabled new feature flag `X_BETA_LOGIN`. | Errors stop; bug tied to new login flow feature. | | Reviewed recent auth PR #1234. | Found missing null check on user profile object. |

Or simply:

  • Attempt: Restarted service with debug logging on.
    Outcome: Logs show repeated 500 errors from auth service.

  • Attempt: Disabled new feature flag X_BETA_LOGIN.
    Outcome: Errors stop; bug tied to new login flow feature.

You fill this in as you go—not afterward from memory. It’s meant to be a living ledger of the session, not a reconstructed story.


Why Two Columns Work So Well

This pattern is simple, but it has powerful side effects.

1. Clear separation of process and result

Most raw debugging notes blur together:

"Tried X. Then Y. Then restarted. Then changed env var. Still broken. Maybe it’s the cache?"

With a two-column layout, you’re forced to label:

  • What exactly you did
  • What exactly happened because of it

That separation makes it dramatically easier to:

  • Spot which actions were useless noise
  • See which steps actually moved you closer to the fix
  • Reconstruct the cause–effect chain later

2. Similar in spirit to Cornell notes

Cornell notes use a structured layout:

  • A main notes area
  • A left-hand cue column for keywords/questions
  • A bottom summary section

The spirit is the same: structure helps compression.

In the Two-Column Coding Ledger:

  • The left column is your chronological stream of attempts/actions
  • The right column is your distilled interpretation/insights

This built-in structure nudges you to condense your thinking as you go, not just dump raw logs.

3. A personal debugging black box recorder

Using a two-column ledger turns each session into a mini bug report:

  • What you suspected (implicitly in your attempts)
  • What you tried and why
  • What happened next
  • What finally worked

Later, when you or a teammate needs to:

  • Reproduce the issue
  • Understand the root cause
  • Document a fix or write a postmortem

…you already have a concise, chronological trace of the investigation.


How to Use the Ledger in a Debugging Session

You can start using this pattern with almost no setup. Here’s a simple workflow.

Step 1: Start with a mini problem statement

At the top of your note, write:

  • Problem: One-sentence description of the issue.
  • Context: Environment, branch, or relevant conditions.

Example:

Problem: API returns 500 on `POST /login` in staging. Context: Staging, build 1.4.2, feature flag X_BETA_LOGIN enabled.

This anchors all your attempts.

Step 2: Log each attempt as you make it

For every meaningful action, add a row or entry:

  • Attempt: what you did (command, change, check)
  • Optional Why: (short note of your hypothesis)
  • Outcome: what you observed (logs, behaviors, metrics)

Example:

| Attempt / Why | Outcome / Insight | |---------------------------------------------------------|-------------------------------------------------------| | Checked Nginx logs for /login requests. | No errors; upstream is returning 500s. | | Inspected app logs around request timestamp. | Stack trace shows `NullPointerException` in AuthSvc. | | Hypothesis: missing field in JWT claims. | | | Logged decoded JWT in AuthSvc. | All expected fields present; hypothesis incorrect. |

Notice how the failed hypotheses are still valuable. Later, they show what didn’t cause the bug.

Step 3: Mark what actually worked

When you reach a fix or decisive insight, make it visually clear:

  • Add a or FIX: label in plain text
  • Bold the outcome
  • Add a small "Conclusion" section at the end

Example:

| Attempt / Why | Outcome / Insight | |------------------------------------------------------|--------------------------------------------------------| | Added null check on `user.profile` in AuthSvc. | **✔ Login succeeds; 500 errors disappear.** | | Deployed fix to staging and reran regression tests. | **All tests passing; no new errors detected.** |

Then summarize:

Conclusion: 500 errors on /login were caused by missing null check on `user.profile` in AuthSvc. Triggered only when beta login feature flag was enabled.

Now your ledger is ready to be turned into a ticket comment, incident report, or knowledge base entry.


How This Improves Focus and Reduces Thrash

When you write down each attempt and its outcome, you:

  • See in real time when you’re cycling through the same ideas
  • Become more deliberate about why you’re trying something
  • Stop relying on "maybe I haven’t tried restarting everything yet"-style thrash

Because you’re externalizing your process, you’re less likely to:

  • Repeat the same command 10 times hoping for a different result
  • Bounce randomly between logs, code, and dashboards
  • Lose track of which configs or flags you’ve already toggled

The two-column format encourages a loop of:

Hypothesis → Attempt → Observation → Insight

…instead of chaotic trial-and-error.


Long-Term Benefits: Your Personal QA and Learning Tool

Used regularly, the ledger becomes more than just per-session notes—it becomes a personal quality-assurance and learning system.

1. Reveal patterns in your mistakes

Review a few weeks or months of ledgers and look for:

  • Repeated classes of bugs (off-by-one, nulls, race conditions, misconfigured env vars)
  • Common assumptions that turn out to be wrong
  • Areas of the codebase that frequently cause trouble

This helps you target:

  • Where tests are missing
  • Where documentation is unclear
  • Which modules need refactoring or extra safeguards

2. Identify your most effective techniques

You’ll also see which moves tend to pay off:

  • Adding logs in specific places
  • Writing minimal reproduction tests
  • Using certain profiling or tracing tools

Over time, you can refine a personal debugging playbook based on what actually works for you—not generic advice.

3. Improve team communication

The ledger is easy to share:

  • Paste into a Jira ticket
  • Drop into a Slack thread
  • Attach to an incident report

Instead of: "I poked around and eventually fixed it," you can provide:

  • A clear history of what was tried
  • Evidence for the root cause
  • A ready-made narrative for a postmortem

This builds trust and makes it easier for others to:

  • Reproduce issues
  • Learn from your approach
  • Avoid duplicating dead-end attempts

Where to Keep Your Ledger

You don’t need special tools. Use whatever fits your workflow:

  • A Markdown file per issue in your repo
  • A dedicated "Debug Log" note in Notion, Obsidian, or Evernote
  • A simple text file in your project directory
  • A paper notebook with a vertical line down the page

The only requirement is that it’s:

  • Fast to open when you start debugging
  • Easy to append to as you work
  • Searchable or browsable later

Conclusion

The Two-Column Coding Ledger is a tiny habit with outsized impact:

  • It separates what you tried from what actually worked
  • It turns each debugging session into a mini bug report
  • It creates a personal history of your mistakes, insights, and effective strategies

You don’t need a new tool or elaborate template. Just start your next debugging session with two headings:

Attempt / Action | Outcome / Insight

Fill them in as you go. In a few weeks, you’ll have not only fewer repeated mistakes and less thrash, but also a rich, searchable log of how you actually solve problems.

And the next time a "mysterious" bug reappears, you won’t be starting from scratch—you’ll be turning to your own ledger.

The Two-Column Coding Ledger: A Simple Way to Capture What You Tried vs. What Worked | Rain Lag