Most Salesforce developers never touch the performance tools sitting right inside their org — not because the tools are hidden, but because nobody needs them until data volume turns a fast query into a query timeout. By then, the fix is reactive instead of planned.
This is the first post in a series on tools that let you measure and fix code performance before it breaks in production — including cases where the code was written by AI. Part 1 covers the most foundational of these: the Query Plan Tool.
What the Query Plan Tool Does
In large enterprise orgs, data volumes climb into the millions of records. A SOQL query that runs instantly against a sandbox with a thousand rows can hit a query timeout against a million-row production table.
The Query Plan Tool tells you, upfront, how a query will actually be executed — whether the optimizer will use an index, fall back to a full table scan, or rely on sharing rules — so you can fix a bad query plan before it ships, not after it fails.
How to Enable It
- Open the Developer Console.
- Go to Help → Preferences.
- Enable “Enable Query Plan.”
Once enabled, a Query Plan tab appears next to the Query Editor.
Key Terms You Need to Know
| Term | What It Means |
|---|---|
| Cardinality | Estimated number of records the leading operation will return |
| Fields | The indexed field(s) the optimizer chose to use, if any |
| Leading Operation Type | How the optimizer plans to execute the query — via an index, sharing rules, a table scan, or other internal optimizations |
| Cost | A score representing query efficiency (more below) |
| sObject Cardinality | Total approximate record count for the queried object |
| sObject Type | The primary object the query runs against |
Salesforce continuously runs statistics on your objects so the optimizer can calculate these costs using real, current data distribution — not guesswork.
How Query Cost Is Actually Calculated
Here’s the part most developers skip past without understanding — and it’s the part that matters most.
Imagine a custom object, Support_Ticket__c, with 1 million records. You run:
SELECT Id, Subject__c FROM Support_Ticket__c
WHERE Priority__c = 'High' AND Status__c = 'Closed'
The optimizer doesn’t just run the query — it checks current data distribution and scores multiple possible execution paths.
Plan A: Index on Priority__c
600,000 of the 1 million tickets are marked “High.” A custom index is only considered selective if it returns under 10% of the table. 600,000 records is 60% of the data — six times over the threshold. Cost: 6.0 (60% ÷ 10%). Verdict: non-selective, unsafe to use.
Plan B: Index on Status__c
Only 30,000 of 1 million tickets are “Closed” — 3% of the table, well under the 10% threshold. Cost: 0.3 (3% ÷ 10%). Verdict: highly selective — this is the plan the optimizer should pick.
| Leading Operation Type | Fields | Cost | Verdict |
|---|---|---|---|
| Index | Status__c | 0.3 | Winner — highly selective, jumps straight to 30K records |
| TableScan | null | 1.0 | Fallback baseline — scans all 1M records |
| Index | Priority__c | 6.0 | Loser — six times over the selectivity threshold |
The rule of thumb: anything with a Cost under 1.0 is selective and safe. Anything at or above 1.0 means you’re not getting real index benefit.
If you want the platform-level detail on how selectivity thresholds are derived, Salesforce publishes a query optimization cheatsheet (search for it by title if the link has moved).
Using It in Practice
- Enable the tool in Developer Console (steps above).
- Run your actual query in the Query Editor — don’t bother substituting in literal record IDs for
WHEREclauses; ID fields are indexed by default, so they’re not your risk area. Focus your attention on non-ID fields inWHEREclauses. - Read the output: sObject cardinality, leading operation type, and cost.
- Target a cost below 1.0. If you’re above it, look for a more selective field to filter or index on.
A few field-level gotchas worth knowing up front: the platform maintains standard indexes on most objects for RecordTypeId, Division, CreatedDate, SystemModstamp, Name, Email (on Contacts and Leads), all foreign-key relationship fields (lookups and master-detail), and the record Id itself. LastModifiedDate is the one that’s not indexed by default — if your query relies heavily on filtering by it, plan for that early rather than discovering it after a timeout in production.
The Goal
The point of this tool isn’t to memorize terminology — it’s to get every query you ship below a cost of 1.0, so it scales with your org’s data instead of breaking when data volume grows.
Next in this series: Perspective Manager, the Apex Log Analyzer VS Code extension, and Scale Center — three more tools for diagnosing performance once a query plan alone isn’t enough to tell you what’s slow.