Rubber Duck Debugging / 2 of 5 / 1 dilihat
Nine Step Framework for Systematic Debugging
A 9-Step Framework for Systematic Debugging: From Context to Question
One of the main reasons debugging feels slow and exhausting isn't that the bug itself is hard — it's that we don't have a clear structure for chasing it down. So we jump around: check one part of the code, then suddenly switch to another part because "maybe it's here too," never actually following one line of reasoning all the way through.
The framework below is a simple way to force debugging into a straight line — and as a bonus, the end result doubles as a bug report that's ready to share with whoever needs to know.
CONTEXT
↓
EXPECTED
↓
ACTUAL
↓
REPRODUCTION
↓
FLOW
↓
EVIDENCE
↓
INVESTIGATION
↓
HYPOTHESIS
↓
QUESTION1. Context — What You're Working On
Before talking about the bug, state what you're actually building. It sounds trivial, but it's often skipped — and it's exactly what lets anyone else (or you, five minutes from now) immediately grasp the scale of the problem.
"I'm working on infinite scroll on the content list screen."
2. Expected — What Should Happen
Clearly state what's supposed to happen when everything works correctly.
"Once the list reaches the last page, the app stops calling the API for more."
3. Actual — What's Really Happening
Compare that directly against what's actually occurring. Avoid jumping to a cause here — just describe the symptom.
"After the last page is reached, the app keeps calling the next page over and over."
4. Reproduction — Steps to Trigger It Again
If you can't reliably reproduce the bug, you're not ready for serious investigation yet, because there's no way to actually verify whether any fix you try worked.
1. Open the content list screen.
2. Scroll to the last item.
3. Scroll once more.
4. The API still gets called.5. Flow — The Program Path Involved
Sketch the data or control flow roughly. No need to go down to the line level — just enough to show which components are involved.
RecyclerView
↓
onLoadMore()
↓
ViewModel
↓
Repository
↓
API6. Evidence — Proof, Not a Feeling
This is the step that separates fact-based debugging from guess-based debugging. Evidence can be Logcat output, a stack trace, an API response, a screenshot, a screen recording, a database value, network inspector output, or a debugger session.
"I think the API is wrong" is not evidence. "The API response shows nextPage: 0 when it should be null" is.
7. Investigation — What You've Already Checked
Note down what you've already looked at, even if the result was "turns out this part is fine." This matters so you — or whoever's helping — don't waste time re-checking the same thing.
8. Hypothesis — A Guess Backed by Evidence
Only after context, evidence, and investigation are in place should you form a hypothesis. A good hypothesis can always be traced back to the evidence supporting it.
"I suspect the stop condition still relies oncurrentPage < totalPage, when it should instead follow thenextPagevalue returned by the API."
9. Question — A Genuinely Specific Ask
This is the payoff of the whole process: a clear question, not just "why is this broken?"
"Should the pagination stop condition follow the API'snextPagevalue, or should we keep computing it on the Android side withcurrentPage?"
Common Mistakes When Applying This Framework
A few mistakes show up again and again once people start trying this out:
Jumping straight to Hypothesis with no Evidence. This is the most common one. It's tempting to say "it's probably the ViewModel" before a single piece of evidence has been gathered. A guess with no evidence behind it isn't a hypothesis — it's just a guess wearing technical vocabulary.
Mixing Actual with a conclusion. Writing "the API is broken" under Actual is a mistake — that's already a conclusion, not a symptom. What should go there is "the API response returned HTTP 500," and the conclusion "the API is broken" only belongs under Hypothesis once it's actually backed by evidence.
Reproduction steps that aren't specific enough. Something like "open the app, then the error shows up" isn't enough. Good reproduction steps should be followable by someone else exactly as written, including starting conditions — login state, network connectivity, specific device — that might affect the outcome.
Skipping Flow because it "feels obvious." This is usually exactly where the first wrong point gets found. The flow that feels obvious in your head often differs from what the program actually executes — especially with async calls, callbacks, or coroutines with unpredictable timing.
Why This Framework Still Matters When You're Debugging Alone
This framework looks like it's built for communicating with other people, but its biggest payoff shows up when you're debugging entirely on your own. The moment you try to fill in the "Evidence" section and it comes up empty — no proof, just a guess — that's a clear signal you're not ready to conclude anything, let alone change any code.
This 9-step framework will be the backbone of the rest of this series. In upcoming posts, we'll go through Android components one at a time — reading Logcat and stack traces properly, and working through specific cases like an empty RecyclerView, broken pagination, or a state that never updates — all mapped back onto these nine steps.