Blog

The Apex inner loop in VS Code

Run tests, step through a live debugger, and watch coverage in the editor — with no scratch org, no sandbox, and no deploy step between you and a result.

Short version

Install the Nimbus CLI and the VS Code extension, open your existing SFDX project, and click Run Test above any @isTest method. Results appear inline in milliseconds. No org authorization, no deploy, no Docker — the whole loop runs on your laptop.

The problem isn't Apex. It's the loop around it.

Writing Apex is fine. What wears you down is everything between saving a file and knowing whether it works: deploy to a sandbox, wait, open the Developer Console or run sf apex run test, wait again, read the result, switch back to your editor. A one-line change costs a context switch and a few minutes.

Every other ecosystem closed this gap years ago — you save, tests run, you see red or green without leaving the file. Apex never had that, because the runtime lived in the org and the org lived in the cloud.

Nimbus is a local Apex runtime. It executes your classes, triggers, SOQL, and DML against an embedded PostgreSQL database that ships inside the binary — so the same suite that runs in your org runs on your machine, in milliseconds. The VS Code extension wires that runtime into the editor. This post walks through the resulting inner loop.

Step 1 — Install the CLI

One line, no admin rights, no Docker, no JVM:

bash
# macOS / Linux
curl -fsSL https://testnimbus.dev/install.sh | bash

# Homebrew
brew install nimbus-solution/nimbus/nimbus

# Windows (Scoop)
scoop install nimbus

Verify it:

bash
nimbus --version

Step 2 — Install the VS Code extension

Search Nimbus in the Extensions view, or install from the VS Code Marketplace. It's also on Open VSX, so it works in Cursor, Windsurf, and VSCodium too.

There's no "connect to org" step and nothing to authorize. The extension talks to a local background daemon that starts on its own the first time you run something. Open your existing SFDX project — the same force-app/main/default layout you already have — and you're set. Nimbus reads sfdx-project.json to find your source.

Why no org?

This is the point of Nimbus. Your Apex, schema, and triggers come from your project source, not a live connection. If you want production org schema — custom objects, fields, record types — pull it once with nimbus sync; after that the loop is fully offline. You never deploy to test.

Step 3 — Run tests from the file you're editing

Every @isTest class gets inline action buttons — CodeLens — above the code. No command palette, no sidebar hunting:

  • Run Test / Debug Test above each test method.
  • Run All Tests and Validate above the class.
  • The result lands next to the method with its execution time — ✓ 83ms — and failures point at the file and line.

Prefer the palette or keyboard? The commands are all there: Nimbus: Run Test Method, Nimbus: Run Test Class, Nimbus: Run Tests in Current File, Nimbus: Run All Tests. Tests also show up in VS Code's native Test Explorer, discovered automatically.

Non-test classes get Run and Debug buttons above each public method, with parameter prompts and return values shown inline — useful for poking at a method without writing a harness.

Step 4 — Execute anonymous Apex

For a quick check — a SOQL query, a DML round-trip, a bit of throwaway logic — run Nimbus: Execute Anonymous Apex from the command palette, or use the CLI:

bash
# Run a snippet
nimbus exec -c "System.debug([SELECT COUNT() FROM Account]);"

# Or a file
nimbus exec scripts/backfill.apex

System.debug output, exceptions, and governor-limit usage come straight back in the terminal — no trace flag, no downloaded log.

Step 5 — Tighten the loop with watch mode

Toggle watch mode from the status bar or Nimbus: Toggle Watch Mode. Every .cls or .trigger save re-runs the affected tests, and the status bar tracks state live: Watch: force-app idle, 3/10 tests… running, Watch: all passed or Watch: 2 failed after.

Pair it with a split editor — test on the left, implementation on the right. Save the implementation, watch the test turn green. That's the loop the rest of software engineering has had for a decade, finally applied to Apex. From the CLI it's nimbus test:watch.

Step 6 — Debug live, not from a log

Click Debug Test and you get a real debugger — breakpoints, step over and into, a call stack, and variable inspection — running against the live local execution. This is not the Replay Debugger's deploy-run-download-replay dance; you're stepping through code as it runs, on your machine.

After any run, View Trace opens a side panel with a call tree, a timeline waterfall, and a filterable log, plus a variable inspector that updates as you move through the trace — so even without a breakpoint you can see exactly what executed and where the time went.

Step 7 — Coverage and governor limits, in the editor

Turn on coverage with the nimbus.coverage.enabled setting and each run annotates the gutter directly: green for executed, red for missed, an orange half-circle for a partially-taken branch. Method-level percentages show inline — // 75% (3/4 lines) — so you know where coverage is thin without opening a report. Branch coverage goes past Salesforce's built-in line metric and tracks both sides of every if, switch, and ternary.

The Nimbus sidebar adds three views: Test History (every run, with a 30-day pass-rate trend and flaky-test detection), Governor Limits (per-method SOQL / DML / CPU / heap consumption, color-coded), and a Schema Explorer that browses the local database tables, row counts, and relationships.

A note on mocking and managed packages

Nimbus runs standard Apex, so standard test patterns work as written — no special mock configuration file. HTTP callouts use Test.setMock with your HttpCalloutMock implementation exactly as in the org. For code that depends on a managed package you don't have source for, generate stubs with nimbus stub add <namespace>; see Managed Packages for the details.

Settings worth knowing

Everything lives under the nimbus.* namespace in settings.json or the Settings UI. A few you'll reach for early:

  • nimbus.coverage.enabled — collect coverage on every run.
  • nimbus.parallel — number of parallel test workers (default 4).
  • nimbus.validateOnSave — validate Apex syntax as you save.
  • nimbus.governor.showCodeLens — governor-limit usage above each method.

Project-level configuration (multi-currency behaviour, seed data, managed-package settings) goes in a nimbus.properties file — run nimbus config init to generate a documented starter.

The result

Save a file, see the test result next to it, step through a failure with a live debugger, and never open a browser tab. That's the inner loop — and it runs entirely on your laptop, with the SFDX project you already have. When something the local runtime doesn't yet support turns up, the failure names it directly, and a scratch org is still there for the edges.