Skip to content
Muhammad AR Zihad Muhammad AR Zihad

Learning and logs

Muhammad AR Zihad Muhammad AR Zihad

Learning and logs

  • PHP
  • Laravel
  • SQL
  • System Design
  • Privacy Policy
  • PHP
  • Laravel
  • SQL
  • System Design
  • Privacy Policy
Close

Search

Portfolio
Latest logs
How PHP works with Nginx, PHP-FPM workers, Laravel, CPU cores, RAM, and database
October 6, 2026
How PHP Actually Works: PHP-FPM, Workers, CPU Cores, and Concurrency
Sherlock Holmes inspired illustration of evidence-based software debugging
September 30, 2026
Debugging Like Sherlock Holmes: Start With Evidence, Not Assumptions
YAGNI and over-engineering illustrated through the Room of Requirement
September 28, 2026
Your Software Is Not the Room of Requirement
AI-generated code and software engineering ownership illustration
September 28, 2026
AI Writes Faster. But Who Understands the Code?
Ship of Theseus illustration representing technical debt in software development
September 24, 2026
The Ship of Theseus and Technical Debt
Home/Software Engineering/Debugging Like Sherlock Holmes: Start With Evidence, Not Assumptions
Sherlock Holmes inspired illustration of evidence-based software debugging
Software Engineering

Debugging Like Sherlock Holmes: Start With Evidence, Not Assumptions

By Muhammad AR Zihad
September 30, 2026 5 Min Read
0

A production bug appears. Payments are failing, users are reporting issues, and the team starts looking for answers.

Someone says, “It looks like a database issue.” Another person thinks the payment provider may be having problems. Someone else suggests rolling back the latest deployment.

Within a few minutes, there are already several theories on the table. The problem is that there is still very little evidence.

This is where one of Sherlock Holmes’s most famous observations becomes surprisingly relevant to software engineering:

“It is a capital mistake to theorize before one has data.”

Holmes was talking about investigation, but the same principle applies almost perfectly to debugging. When we decide too early what the problem is, we often stop investigating objectively. Instead of using facts to test a theory, we start looking for facts that support the theory we already have.

That is how debugging slowly turns into guessing.

When Debugging Becomes Guessing

The pattern is very common.

A bug report arrives. You inspect the code and notice something that looks suspicious. You change it, the tests pass, and the fix is deployed.

A few hours later, the same problem happens again.

So another part of the code is changed. Then another.

Each individual change may look reasonable, but without enough evidence, we are effectively experimenting with the system until something happens to work.

A better debugging process starts with questions rather than solutions.

For a payment failure, for example, I would first want to know whether the request actually reached our server. Is the problem affecting every user or only a specific group? Did we send the request to the payment provider? What response did the provider return? Did the database transaction complete successfully? Did the problem start immediately after a recent deployment?

Once those questions have answers, many possible causes disappear automatically.

Instead of having ten theories, we may suddenly have two.

That is the real value of evidence in debugging. It reduces the search space.

Sometimes the Most Important Clue Is What Did Not Happen

There is another interesting lesson from Sherlock Holmes that is particularly useful when debugging distributed systems.

Holmes did not only pay attention to what happened. He also paid attention to what should have happened but did not.

In Silver Blaze, a horse disappears during the night. One of the important clues is that the guard dog did not bark. That absence of a reaction mattered because a stranger entering the area should normally have caused the dog to react.

Software systems often leave similar clues.

Suppose a payment request was sent successfully, but the expected webhook never arrived.

A database transaction started, but there is no committed log afterward.

A job was pushed to the queue, but there is no log showing that a worker ever picked it up.

An API request entered one service, but there is no trace of the downstream service receiving it.

When debugging, we naturally search for error messages and exceptions. Those are useful, but sometimes the strongest clue is simply the absence of something that should have happened.

A useful debugging question is therefore not only:

“What happened?”

It is also:

“What was supposed to happen next, and why didn’t it?”

That question can be especially powerful in systems involving queues, webhooks, background workers, external APIs, database transactions, or multiple services.

Mitigation and Root Cause Analysis Are Different Problems

Of course, production incidents are not detective stories. If customers cannot make payments, the team cannot spend hours investigating while the system remains broken.

The first priority is usually to reduce the impact.

If the issue started immediately after a deployment and rolling back is safe, rollback may be the most sensible first action. Restoring the service is often more important than understanding the exact cause in that moment.

But mitigation and root cause analysis are not the same thing.

If rolling back a deployment makes the problem disappear, we have learned something useful: some part of the recent change was related to the incident.

We still do not necessarily know which part.

Maybe a new query caused database contention. Maybe a configuration changed. Maybe an API payload changed. Maybe a background job started behaving differently under production traffic.

Rollback can restore stability, but it does not automatically explain the failure.

That investigation still needs to happen.

Fixing the Symptom Is Not Always Fixing the Problem

Another common debugging mistake is treating the symptom instead of the cause.

Imagine a service starts timing out after five seconds. One quick solution is to increase the timeout to thirty seconds.

The errors disappear.

Technically, the incident may look fixed.

But the most important question remains unanswered:

Why did the service become slow in the first place?

Maybe a database query lost an index. Maybe an external API became slower. Maybe a new feature introduced an N+1 query problem. Maybe a queue that normally handles expensive processing asynchronously is no longer working.

Increasing the timeout might be a perfectly reasonable temporary mitigation, but if we stop there, the real problem remains inside the system.

Eventually it may return in a different form.

A More Reliable Debugging Process

For me, a practical debugging process looks something like this:

  1. Reduce the impact first.
    Restore service when possible through rollback, feature flags, traffic controls, or another safe mitigation.
  2. Collect facts.
    Check logs, metrics, traces, database state, external provider responses, and the scope of the failure.
  3. Build a hypothesis.
    Use the evidence to identify a plausible explanation.
  4. Test the hypothesis.
    Try to prove or disprove it using reproducible behavior, logs, data, or controlled experiments.
  5. Discard wrong theories quickly.
    A failed hypothesis is useful because it removes one possibility from the investigation.
  6. Fix the root cause.
    Once the cause is understood, make the smallest reliable change that addresses it.

The actual code change may eventually be only two lines.

Finding the correct two lines is usually the difficult part.

Good debugging is therefore not mainly about how quickly we can modify code. It is about how efficiently we can move from uncertainty to evidence, from evidence to a hypothesis, and from a hypothesis to a verified cause.

The next time something breaks in production, perhaps the first question should not be:

“What should I change to fix this?”

A better starting point might be:

“What do I actually know so far, and what was supposed to happen but didn’t?”

That small change in mindset can turn debugging from a guessing game into an investigation.

Post Views: 27

Tags:

DebuggingObservabilityProduction IncidentsRoot Cause Analysis
Author

Muhammad AR Zihad

Follow Me
Other Articles
YAGNI and over-engineering illustrated through the Room of Requirement
Previous

Your Software Is Not the Room of Requirement

How PHP works with Nginx, PHP-FPM workers, Laravel, CPU cores, RAM, and database
Next

How PHP Actually Works: PHP-FPM, Workers, CPU Cores, and Concurrency

No Comment! Be the first one.

    Leave a Reply Cancel reply

    Your email address will not be published. Required fields are marked *

    New ones

    • How PHP works with Nginx, PHP-FPM workers, Laravel, CPU cores, RAM, and database
      How PHP Actually Works: PHP-FPM, Workers, CPU Cores, and Concurrency
      by Muhammad AR Zihad
    • Sherlock Holmes inspired illustration of evidence-based software debugging
      Debugging Like Sherlock Holmes: Start With Evidence, Not Assumptions
      by Muhammad AR Zihad
    • YAGNI and over-engineering illustrated through the Room of Requirement
      Your Software Is Not the Room of Requirement
      by Muhammad AR Zihad
    • AI-generated code and software engineering ownership illustration
      AI Writes Faster. But Who Understands the Code?
      by Muhammad AR Zihad
    • Ship of Theseus illustration representing technical debt in software development
      The Ship of Theseus and Technical Debt
      by Muhammad AR Zihad

    Categories

    • Backend Engineering
    • PHP
    • Software Engineering

    You May Have Missed

    How PHP works with Nginx, PHP-FPM workers, Laravel, CPU cores, RAM, and database
    Backend Engineering PHP

    How PHP Actually Works: PHP-FPM, Workers, CPU Cores, and Concurrency

    Muhammad AR Zihad
    By Muhammad AR Zihad
    October 6, 2026
    Sherlock Holmes inspired illustration of evidence-based software debugging
    Software Engineering

    Debugging Like Sherlock Holmes: Start With Evidence, Not Assumptions

    Muhammad AR Zihad
    By Muhammad AR Zihad
    September 30, 2026
    YAGNI and over-engineering illustrated through the Room of Requirement
    Software Engineering

    Your Software Is Not the Room of Requirement

    Muhammad AR Zihad
    By Muhammad AR Zihad
    September 28, 2026
    AI-generated code and software engineering ownership illustration
    Software Engineering

    AI Writes Faster. But Who Understands the Code?

    Muhammad AR Zihad
    By Muhammad AR Zihad
    September 28, 2026
    Ship of Theseus illustration representing technical debt in software development
    Software Engineering

    The Ship of Theseus and Technical Debt

    Muhammad AR Zihad
    By Muhammad AR Zihad
    September 24, 2026

    Categories

    • PHP
    • Laravel
    • SQL
    • System Design
    • Privacy Policy

    Tags

    AI-Generated Code AI Assisted Coding Backend Development Code Review Concurrency CPU Debugging Engineering Ownership Laravel Maintainability Nginx Node.js Observability OPcache Over-Engineering Performance PHP-FPM Production Incidents Root Cause Analysis Software Architecture System design Technical debt YAGNI

    Copyright 2026 — Muhammad AR Zihad. All rights reserved.