If you omit both with sharing and without sharing on an Apex class, the class inherits the sharing context of whatever called it. When it’s the entry-point class — invoked from a trigger, anonymous Apex, a button, or a scheduled job with no calling sharing context — it defaults to without sharing, meaning the running user’s record-level sharing is not enforced. That’s the trap candidates miss: “no keyword” doesn’t mean “secure by default.”
The three behaviors
Apex sharing keywords on a class:
| Keyword | What it does |
|---|---|
with sharing | Enforces the running user’s record-level sharing — they only see/edit records they have access to per OWD + sharing |
without sharing | Ignores the running user’s record sharing — Apex sees all records the schema allows |
inherited sharing | Adopts the caller’s sharing mode — explicit version of the “no keyword” default |
| no keyword | Implicit inherited sharing — but with one nasty default for entry-point classes |
The implicit-default trap
When you omit the keyword:
- If called from a class with
with sharing, the no-keyword class runs with sharing. Fine. - If called from a class with
without sharing, the no-keyword class runs without sharing. Fine. - If it’s the entry point (trigger, anonymous, button, scheduled job, REST endpoint), the no-keyword class runs without sharing by default. Record-level security is bypassed.
The last case is what bites teams. A controller for an LWC, a @AuraEnabled method, a custom REST resource — if you forget the keyword and a low-privilege user invokes it, your code can read and write records they shouldn’t see.
Why inherited sharing exists
Salesforce added the inherited sharing keyword precisely to fix the implicit-default trap. When you write inherited sharing:
- Called from a
with sharingcaller → runs with sharing. - Called from a
without sharingcaller → runs without sharing. - Entry-point invocation → defaults to
with sharing, not without.
That last bullet is the security upgrade. inherited sharing is the recommended choice for Apex you expect to be reused as both an entry point and a callee — typically @AuraEnabled controllers.
Quick example
// BAD: implicit default
public class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> all() {
return [SELECT Id, Name FROM Contact];
}
}
// Called from LWC by a user with Private OWD on Contact:
// returns EVERY contact in the org, ignoring their sharing.
// GOOD: explicit inherited sharing
public inherited sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> all() {
return [SELECT Id, Name FROM Contact];
}
}
// Called from LWC: returns only contacts the user can see.
Common interview follow-ups
- Why isn’t the default just
with sharing? Historical baggage. The default predates the security review that found this risky. - Where does inner-class sharing come from? An inner class inherits its outer class’s sharing mode regardless of its own keyword. Avoid putting business logic in inner classes if sharing matters.
- Does this apply to anonymous Apex from Developer Console? Anonymous Apex always runs with the user’s sharing context but typically as a sysadmin — so the practical effect is “see everything.”
Verified against: Apex Developer Guide — Using the with sharing, without sharing, and inherited sharing Keywords. Last reviewed 2026-05-17.