with sharing respects the running user's record access. without sharing acts like God mode to see everything. inherited sharing copies whatever the caller was using. Default habit: use with sharing, and go without only on purpose.
with sharing returns only invoices Asha can see. A nightly stats job marked without sharing counts ALL invoices on purpose (and documented). A shared utility class marked inherited sharing behaves like whoever called it.
🎬 Real-Life Example: The Component That Leaked Other Drivers' Parcels
A "My Deliveries" Lightning Web Component (LWC) goes live. A driver scrolls past his own list—and keeps scrolling into everyone else's deliveries.
The Old/Bad Way: The Apex controller class had no sharing keyword. Depending on how it was entered, it ran in system context, ignoring sharing rules completely. SOQL returned every row in the org.
Why this is bad: This is a silent data leak. No error, no log entry, and nothing failed. The code worked perfectly—for the wrong audience.
The New/Good Way:
- Declare
with sharingon every class that serves the UI so queries use the running user's access. - Mark shared utility classes as
inherited sharingso they adopt whatever the caller declared. - Reserve
without sharingfor narrow, documented system jobs with an explanatory comment right above the class.
The payoff: The sharing keyword decides whose eyes the query uses. Declare it explicitly on every class—silence is the vulnerability.
Concept
- Class-level keywords control whether Apex respects record-level sharing.
- They do NOT enforce Create, Read, Update, Delete (CRUD) or Field-Level Security (FLS).
with sharingenforces the running user's record access.without sharingruns with full system visibility of records.inherited sharingadopts the caller's mode and defaults to WITH sharing when it is the entry point.- An unmarked class inherits its caller's mode, but on API v66 and earlier, it defaults to WITHOUT sharing at the entry point.
- From API v67 onwards, an unmarked class defaults to WITH sharing at the entry point.
- The sharing mode is decided by the class where the code runs, not where the transaction started.
- A single
without sharingutility class can quietly widen the data exposure of an entire call chain.
with sharing = enforce user's rows. UNMARKED at entry = WITHOUT sharing up to v66 (danger!), but WITH sharing from v67+. inherited sharing = inherits, but SAFE (with) at entry point. → State it explicitly; never rely on defaults.
| Keyword | Behavior | At Entry Point |
|---|---|---|
with sharing |
Enforces record sharing | Enforced |
without sharing |
Ignores record sharing | Ignored |
inherited sharing |
Takes caller's mode | Defaults to WITH (Safe) |
| (unmarked) | Takes caller's mode | WITHOUT ≤v66 (Risk) / WITH v67+ |
🧭 360 Card — with / without / inherited sharing
- Rule: Every class declares its mode.
inherited sharingfor services;with sharingfor user-facing entry points. - Gain: A single word at the top of a class explicitly defines visibility so reviewers can audit it at a glance.
- Price: These keywords cover records only. They do nothing for object or field permissions.
- Limits: Mode is set by the entry point. Unmarked classes ran WITHOUT sharing up to API v66, but default to WITH from v67+. Inherited sharing is safe at entry points and flexible mid-chain.
- Mirror — without sharing: Full visibility for real system work like logging or cross-user roll-ups, but a silent data leak if used on a UI controller.
- At volume:
with sharingadds a sharing evaluation to every query. On massive datasets, filter selectively first.
Core Q&A
Q: What is your default sharing declaration policy in a codebase, and where are the legitimate 'without sharing' exceptions?
🎯 Say this first: Default to 'with sharing' everywhere; reserve 'without sharing' only for named, reviewed service classes performing system work like logging or cross-user rollups.
A: Every class must explicitly declare a mode.
- No unmarked classes—enforce this in code reviews.
- Default to
inherited sharingfor services and utilities (safe at entry points, flexible mid-chain). - Apply
with sharingon controllers and anything marked@AuraEnabled. - Reserve
without sharingfor isolated system operations: writing Share records, cross-user rollups, deduplication/matching that requires full visibility, and system logging. - Isolation is key: wrap privileged queries in small
without sharinginner or helper classes to minimize elevated access risk.
Follow-ups (Scenario-based)
Q1: An @AuraEnabled method on a 'with sharing' controller calls an unmarked helper class, which queries Cases. Whose visibility applies—and what if the helper is called from an unmarked Batch class?
A1: It depends entirely on the entry point:
- When called from the controller, the unmarked helper inherits
with sharing, enforcing user visibility. - When called from an unmarked Batch class on API v66 or earlier, the batch defaults to
without sharing, which the helper inherits (exposing all data). - This context-dependent shift is why unmarked classes are banned in clean codebases.
- Using
inherited sharingsolves this by failing safe towith sharingwhen invoked as an entry point.
Q2: An AI reporting feature reads task and delivery data to generate summaries for managers. Which sharing modes should its service layer use, and why?
A2: Run user-facing surfaces with with sharing:
- Keep LWC controllers and user-initiated reports under
with sharingso AI-generated summaries never leak unauthorized records. - Generative AI summaries can exfiltrate sensitive data far more subtly than standard list views.
- For aggregated metrics, calculate team-level data within an isolated
without sharingservice, but output only pre-aggregated, de-identified metrics to a dedicated reporting object. - Re-gate access to that reporting object through declarative sharing rules.