Skip to main content

SF-0313 · Concept · Easy

What is debugging and How do you debug apex?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Debugging is the systematic process of finding why code doesn’t do what you expected. In Apex, debugging means reproducing the bad behaviour, capturing the platform’s debug log for that execution, and reading the log (often with help from Developer Console’s Log Inspector or VS Code’s Apex Replay Debugger) to identify the line, variable value, or governor limit that’s off.

The debugging workflow

  1. Set a trace flag for the user or class whose execution you want to capture.
  2. Reproduce the issue (clicking the button, running the test, calling the API).
  3. Open the resulting debug log — Developer Console, VS Code, or sf apex tail-log.
  4. Inspect the log: SOQL fired, DML statements, governor counters, debug output, stack trace of any exception.
  5. Form a hypothesis, add targeted System.debug statements or a checkpoint, repeat.

The tools

1. System.debug — the everyday workhorse

System.debug('account count = ' + accs.size());
System.debug(LoggingLevel.WARN, 'unexpected null: ' + a.Industry);

Outputs land in the debug log. Cheap and fast — the most common debugging tool by far.

2. Developer Console — Log Inspector

Open a captured log → switch to the Log Inspector view. You get:

  • Source — your Apex code with hit counts and timing markers.
  • Stack Tree — visual call hierarchy.
  • Execution Log — raw timeline.
  • Variables — values at each scope.
  • Limits — running totals of SOQL, DML, CPU, heap.

For “why is this slow?” or “why is this hitting the SOQL limit?”, the Log Inspector beats reading raw logs.

3. VS Code Apex Replay Debugger

Capture a log, then replay it inside VS Code with a real debugger interface:

sf apex tail-log
# reproduce the issue
sf apex log get --log-id <id> --output-dir ./logs

In VS Code: open the log → >SFDX: Launch Apex Replay Debugger with Current File. You get breakpoints, step over/into/out, variable inspection — just like debugging a local language.

The breakpoints are virtual — they reference lines in the recorded log, not live execution. But for most “why did this happen?” investigations, it’s the most powerful Apex debugger available.

4. Checkpoints (Developer Console)

Set a checkpoint on a line of Apex (click in the gutter). The next time that line executes, the platform captures a deep dump of variables and heap state. Then in Developer Console: Debug → View Checkpoints to inspect. Faster than scattering System.debug everywhere.

Limit: up to 5 checkpoints per user, captured once each until reset.

5. sf apex tail-log

Streams new debug logs to your terminal as they’re captured. Great for “I’m about to hit save in the UI, show me what fires”:

sf apex tail-log --target-org my-sandbox
# ... reproduce the issue in another window ...

6. Test class output with assertions

@isTest
static void shouldComputeTax() {
    Order o = new Order(100);
    System.assertEquals(118, o.totalWithTax(), 'tax should be 18%');
}

When the assertion fails, the test report gives you the line number, expected vs actual value, and stack — often enough information to identify the bug without any other tools.

Where to start when something’s broken

A quick triage flowchart:

  1. Did it throw an unhandled exception? Read the stack trace. The line number is usually exact; the cause is usually within 5 lines.
  2. Did the DML insert/update silently the wrong values? Add System.debug before/after DML or query the records post-save.
  3. Did a trigger fire too many times or not enough? Static counter or Limits.getDmlStatements() inspection at known checkpoints.
  4. Is performance bad? Log Inspector → Limits panel; find the SOQL or CPU usage outlier.
  5. Async job behaving strangely? Use Apex Replay Debugger on the captured log — async logs are no different from sync logs.

Common newcomer mistakes

  • Forgetting to set the trace flag — running the code, getting nothing, blaming the platform.
  • Trace flag expired — flags last 24 hours by default. Re-enable from Setup → Debug Logs.
  • Wrong user — the trace flag is for a specific user; if your scheduled job runs as the system user, you need a trace flag for that user.
  • Truncated logs — Salesforce caps a single debug log at 20 MB. Heavy logging at FINEST can hit this; scope the level to the part of the code you care about.

What interviewers are really looking for

The naming check: “use System.debug and read the log.” Strong signals: (1) the trace flag has to be set before logs are captured, (2) the Log Inspector and Apex Replay Debugger are the right tools beyond raw System.debug, (3) checkpoints as a real-debugger alternative to scattering debug statements, (4) Limits.getX() for governor-limit triage, (5) sf apex tail-log for live streaming during reproduction. Mention logs are capped at 20 MB and FINEST level can truncate — a senior signal that you’ve debugged at scale.

Verified against: Apex Developer Guide — Debugging Apex, Apex Replay Debugger. Last reviewed 2026-05-17.