Home / apex / apex-with-without-sharing-decision
apex-with-without-sharing-decision
Choosing the sharing keyword on an Apex class: with sharing vs without sharing vs inherited sharing, how it flows through called methods, and when WITH USER_MODE overrides it. NOT for enforcing CRUD/FLS on the records themselves — use apex/apex-stripinaccessible-and-fls-enforcement. NOT for granting row access via __Share records — use apex/apex-managed-sharing.
Open the skill folder on GitHub
Apex With / Without / Inherited Sharing Decision
Activate when an author is creating a new Apex class, refactoring one, or reviewing a PR and the question is the narrow one: what sharing keyword goes on this class right now? This is a code-author decision, distinct from the org-level sharing-mechanism choice in standards/decision-trees/sharing-selection.md.
Before Starting
Gather this context before deciding:
- Entry point. Is the class invoked from an LWC
@AuraEnabledmethod,a REST
@HttpGet, a batchDatabase.executeBatch, a trigger handler, a Flow@InvocableMethod, or chained from another Apex class? - Record visibility intent. Should the user see only what their
sharing rules permit, or does the operation legitimately need to read and write records they cannot normally see (e.g., audit aggregation, approval routing, compliance scrubbing)?
- Caller context. If this class will be called from other Apex,
remember that
with/without sharingis inherited from the calling class — your keyword may be irrelevant if awithout sharingcaller invokes you, unless you explicitly declarewith sharing. - Annotations on the class.
@AuraEnabled,@RemoteAction, and@RestResourceclasses are user-facing entry points and must default towith sharingunless you have a documented elevation reason.
Core Concepts
The three keyword choices
with sharing / without sharing / inherited sharing differ on whose sharing rules apply to direct SOQL queries. Field-Level Security and CRUD are a separate enforcement concern (WITH USER_MODE, Security.stripInaccessible; WITH SECURITY_ENFORCED only on classes at API ≤ 66.0 — it is removed in 67.0 and does not compile there) and are not controlled by these keywords.
| Keyword | Sharing applied to direct SOQL | Default for what entry points | When to use |
|---|---|---|---|
with sharing | Running user's sharing rules enforced; invisible records filtered out | @AuraEnabled, @RemoteAction, @RestResource (must default here) | Any user-facing entry point unless you have a documented elevation reason |
without sharing | Running user's sharing rules ignored for this class's own queries | None — never a safe default | Audit aggregation, approval routing, compliance scrubbing — deliberate elevation only |
inherited sharing | Adopts the caller's mode; defaults to with sharing when called directly from Lightning/REST/Aura | Reusable utilities, selectors, base service classes | Shared service/selector code that should respect whatever the entry point demanded |
without sharing is a record-visibility keyword, not a blanket system context. It does not turn off FLS or CRUD — and at API 67.0+ database operations enforce the running user's FLS and object permissions by default with no keyword at all, so an elevation that genuinely needs them off states it per statement (WITH SYSTEM_MODE, AccessLevel.SYSTEM_MODE).
Sharing is inherited through method calls
A class that explicitly declares with sharing keeps its own mode no matter who calls it. What a bare callee does depends on its own apiVersion (see "Bare class behavior" below): at API ≤ 66.0 the outermost class on the call stack governs it, so a with sharing controller makes the bare service run with sharing while a without sharing controller makes the same service run without sharing. At API 67.0+ the bare callee no longer follows the caller down — it runs with sharing on its own. Either way the callee's behavior is decided somewhere other than its source, so always declare an explicit keyword on shared service / selector layers.
Where inherited sharing fits
Reusable utilities, selectors, and base service classes that should respect whatever the entry point demanded. inherited sharing is the explicit way to say "I do not want to override the caller's choice, but I also do not want to be ambiguous about it." It also produces a safe default (with sharing) when called from Lightning / REST entry points — unlike a bare class with no keyword.
Bare class (no keyword) behavior — version-gated
The bare-class default inverted in Summer '26. The gate is the apiVersion in the class's own .cls-meta.xml, not the org's release — a Summer '26 org runs a class pinned to 58.0 with the old behavior.
- API 67.0+ — a class with no keyword runs
with sharing,regardless of who called it. The rule reaches across a call chain: once any class in the chain is saved at 67.0+, the chain from there runs
with sharing— a bare 67.0 callee is not pulled down towithout sharingby awithout sharingcaller. - API ≤ 66.0 — a bare class is not equivalent to
with sharing;it ran effectively as
without sharing, except when called from a Lightning context (@AuraEnabled,@RemoteAction), which has run itwith sharingsince API v34.
Never ship a bare class at either version — pick one of the three keywords explicitly so reviewers can audit intent, and so an apiVersion bump cannot silently flip a class that needs elevation into enforcing sharing. Canonical version table: