skills/sf-soql-constraints/SKILL.md
Enforce SOQL/SOSL safety rules, selectivity, and governor limit compliance. Use when writing or reviewing ANY SOQL query, SOSL search, or database operations. Do NOT use for Apex structure, LWC, or Flow.
npx skillsauth add jiten-singh-shahi/salesforce-claude-code sf-soql-constraintsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill auto-activates when writing, reviewing, or optimizing any SOQL query, SOSL search, or Apex database operation. It enforces query safety rules, selectivity requirements, and governor limit compliance for all database operations.
Hard rules for every SOQL query, SOSL search, and Apex database operation. Violations cause governor limit failures, security vulnerabilities, or production outages. See @../_reference/SOQL_PATTERNS.md for selectivity thresholds and @../_reference/GOVERNOR_LIMITS.md for per-transaction budgets.
Never place SOQL or SOSL inside a loop. Every iteration consumes one
of the per-transaction SOQL query budget (see @../_reference/GOVERNOR_LIMITS.md). Query
once before the loop, store results in a Map<Id, SObject>, then iterate.
Never write a non-selective query on objects with >200,000 rows. The query optimizer will full-table-scan and the query fails in trigger context. Every WHERE clause must target an indexed field below the selectivity threshold (see @../_reference/SOQL_PATTERNS.md, Selectivity Thresholds table).
Never use FIELDS(ALL) or FIELDS(CUSTOM) in triggers, service classes,
or production paths. Select only the fields the calling code actually
reads. FIELDS() directives are for exploration and debugging only.
Never hardcode Salesforce record IDs. IDs differ between orgs and
sandboxes. Use Schema.SObjectType, Custom Metadata, or Custom Labels to
resolve IDs at runtime.
Never omit LIMIT on unbounded queries. Any query that could return an
unknown number of rows must include a LIMIT clause to stay within the
per-transaction row limit (see @../_reference/GOVERNOR_LIMITS.md).
Never concatenate user input into dynamic SOQL strings. This creates
SOQL injection vulnerabilities. Use bind variables (:variable) for inline
SOQL or Database.queryWithBinds() for dynamic SOQL.
Never use a leading wildcard in LIKE filters (LIKE '%term%').
Leading wildcards prevent index use and cause full table scans. Use SOSL
(FIND) for full-text search instead.
Never use != or NOT IN as the sole WHERE filter. These operators
are non-optimizable and always produce table scans (see @../_reference/SOQL_PATTERNS.md,
Optimizable vs Non-Optimizable Operators).
Never omit security enforcement on user-facing queries. Queries
triggered by user actions (LWC, Aura, VF, REST endpoints) must include
WITH USER_MODE or equivalent FLS/CRUD enforcement.
Never load all records just to count them. Use SELECT COUNT() FROM
or aggregate queries instead of querying records and calling .size().
Always bulkify database operations. Collect IDs in a Set<Id>, query
once with WHERE Id IN :idSet, and store results in a Map<Id, SObject>.
Always use bind variables (:variable) in inline SOQL. For dynamic
SOQL, always use Database.queryWithBinds() with a bind map.
Always use WITH USER_MODE (see @../_reference/API_VERSIONS.md for minimum version) on queries executed in
user-facing contexts (LWC controllers, Aura controllers, VF controllers,
REST resources). Use WITH SYSTEM_MODE only for documented system
processes (batch jobs, integrations) with explicit justification.
Always filter on indexed fields. Prefer Id, Name, OwnerId,
RecordTypeId, CreatedDate, SystemModstamp, lookup/master-detail
fields, or External ID fields. See @../_reference/SOQL_PATTERNS.md, Standard Indexed
Fields table for the full list.
Always add LIMIT when only one record is expected (LIMIT 1) or
when displaying a bounded list.
Always use relationship queries (parent-to-child subqueries or child-to-parent dot notation) instead of separate queries when fetching related data. Subqueries do not count as separate SOQL queries.
Always use SOSL instead of LIKE for text search across objects.
SOSL uses the search index and is far more efficient than LIKE on
large-volume objects.
Always validate object and field names via Schema.getGlobalDescribe()
before building dynamic SOQL. Never trust external input for object or
field names.
Always test triggers and services with 200 records (the standard trigger batch size) to validate bulk safety against governor limits.
| Problem | Correct Pattern |
|---|---|
| SOQL inside for loop | Query before loop, store in Map<Id, SObject> |
| SELECT FIELDS(ALL) FROM Account in service class | SELECT Id, Name FROM Account -- explicit fields only |
| WHERE Description LIKE '%keyword%' | FIND 'keyword' IN ALL FIELDS RETURNING Account(Id, Name) (SOSL) |
| WHERE Custom_Field__c = 'value' on non-indexed field (LDV) | Add indexed field to WHERE, or request custom index |
| String query = '...WHERE Name = \'' + input + '\'' | [SELECT Id FROM Account WHERE Name = :input] or Database.queryWithBinds() |
| List<Account> all = [SELECT Id FROM Account]; Integer c = all.size(); | Integer c = [SELECT COUNT() FROM Account]; |
| Hardcoded WHERE Id = '001xx000003DGXXX' | WHERE Id = :accountId with runtime-resolved variable |
| No WITH USER_MODE on LWC controller query | Add WITH USER_MODE to enforce FLS + sharing |
| Separate queries for parent and child records | Use subquery: SELECT Id, (SELECT Id FROM Contacts) FROM Account |
| WHERE Status__c != 'Closed' as only filter | Add a selective indexed filter: WHERE RecordTypeId = :rtId AND Status__c != 'Closed' |
Do not memorize raw numbers -- always check @../_reference/GOVERNOR_LIMITS.md for the authoritative table. Key constraint categories that shape every query decision:
Use Limits.getQueries() / Limits.getLimitQueries() to check remaining
budget at runtime before issuing additional queries.
development
Update Salesforce platform reference docs with latest release features and deprecation announcements. Use when SessionStart hook warns docs are outdated or a new Salesforce release has shipped. Do NOT use for Apex or LWC development.
development
Use when syncing documentation after Salesforce Apex code changes. Update README, API docs, and deploy metadata references to match the current org codebase.
development
Use when managing context during long Salesforce Apex development sessions. Suggests manual compaction at logical intervals to preserve deploy and org context across phases.
tools
Visualforce development — pages, controllers, extensions, ViewState, JS Remoting, LWC migration. Use when maintaining VF pages, building PDFs, or planning VF-to-LWC migration. Do NOT use for LWC, Aura, or Flow.