with sharing. Bypassing security with without sharing is something you must explicitly declare in your code. Legacy classes (API v66 and below) retain their old behavior.
INSUFFICIENT_ACCESS error on a field update. Nothing broke—the class simply runs in user mode by default now. It was over-reaching silently for years, and API v67 turned the lights on.
Skyline updates its Apex classes to API v67. A background batch job that quietly processed every record for years suddenly processes fewer rows. Nothing in the code changed—the platform default did.
The Old/Bad Way: Responding with panic in one of two wrong ways: either freezing every class on an old API version forever, or recompiling everything to API v67 in one weekend without proper test coverage and uncovering errors in production.
Why This Fails: Legacy code often depended on system-mode permissions it never requested explicitly. Freezing API versions buries tech debt, while blind mass upgrades break functionality.
The New/Good Way:
- Understand the new default: on API v67+, database operations execute in user mode by default.
- Migrate class by class, running comprehensive unit tests first.
- When a background process truly requires full access, request it explicitly using system-mode controls (such as
AccessLevel.SYSTEM_MODE) and document why.
The Payoff: Security is now the default setting. System power is an exception you must ask for by name.
Core Security Concepts in API v67
This is the largest security shift in Apex history, consisting of three interconnected changes anchored to API version 67.0 and later:
- User Mode Operations by Default: Database operations—including SOQL, SOSL, DML, and
Databasemethods—run in USER MODE by default. They enforce object permissions, FLS, and sharing rules automatically. Earlier versions defaulted to system mode. - Default Class Sharing: A class compiled at API v67 without a sharing keyword defaults to
with sharing. Bypassing sharing requires explicit declaration. - Deprecation of Legacy Syntax: The
WITH SECURITY_ENFORCEDclause is completely removed and fails compilation at v67+. - Explicit Elevation: Elevated access remains available but must be declared explicitly:
WITH SYSTEM_MODEin SOQL,as systemin DML,AccessLevel.SYSTEM_MODEinDatabasemethods, or an explicitwithout sharingon the class. - Inheritance Trap: In an inheritance chain, if any class in the hierarchy is on API v67+, an omitted sharing declaration across the chain defaults to
with sharing. - Triggers Context: Triggers always run in system mode regardless of API version. Trigger handler classes must declare their own explicit sharing and access contexts.
Unmarked class = WITH sharing | Unmarked operation = USER mode | SECURITY_ENFORCED = Removed | Triggers = System mode.
- Rule: Starting from API v67, say it to bypass it. Unmarked classes mean
with sharing; unmarked operations mean user mode. - Gain: Secure execution is now the default. New code enforces user permissions, field security, and sharing rules automatically.
- Price: This is an execution behavior change. Legacy code assuming system mode will throw
INSUFFICIENT_ACCESSerrors once saved under API v67. - Limits: Applies only to classes saved at API v67 or later.
WITH SECURITY_ENFORCEDno longer compiles. Triggers run in system mode, requiring explicit handler declarations. - Operation-Level Control: Adding
WITH USER_MODEor usingAccessLevelparameters overrides the class keyword for that single operation. - Migration Strategy: Make every class sharing declaration explicit before upgrading API version numbers across an org.
Core Q&A
with sharing. You must explicitly state when you want to bypass security.
A: Before API v67, Apex assumed code had already checked permissions. Operations ran in system mode, ignoring CRUD, FLS, and sharing. An unmarked class ran without sharing at its entry point, requiring developers to remember to add security manually.
From API v67, database operations run in user mode, automatically enforcing object permissions, FLS, and record sharing. Unmarked classes default to with sharing, and WITH SECURITY_ENFORCED no longer compiles. System power still exists, but must be written explicitly: without sharing on the class, WITH SYSTEM_MODE in queries, or AccessLevel.SYSTEM_MODE in Database methods.
Scenario-Based Follow-Ups
A1: The operation-level setting always wins for that specific operation.
- In a
without sharingclass, a query usingWITH USER_MODEenforces object permissions, FLS, and sharing rules for that single query. - In a
with sharingclass running a plain system-mode query, the class keyword enforces row-level record sharing, but the system query does not enforce CRUD or FLS. Users can still receive field values forbidden by their profile. - Sharing keywords control ROWS only. User mode controls ROWS, OBJECTS, and FIELDS.
A2: Treat this as a behavior migration rather than a simple version bump:
- Step 1: Make Sharing Explicit: Add explicit sharing declarations (
with sharing,without sharing, orinherited sharing) across all existing classes to avoid inheritance chain defaults. - Step 2: Upgrade in Phased Waves: Upgrade utilities and selectors first, and integration entry points last. Run test suites after every wave.
- Step 3: Error Triage: Triage errors into two buckets: if the code was over-reaching, keep user mode and fix permissions; if it is legitimate system work, declare
without sharingorSYSTEM_MODEexplicitly. - What to Refuse: Refuse to fix user-mode errors by broadly expanding profile or permission set access, as this exposes data across list views, reports, and APIs.
A3: Legacy classes on v66 and below retain old behaviors, making unannotated code ambiguous. Writing explicit keywords communicates developer intent clearly to reviewers and prevents unexpected behavior in inherited sharing contexts.
Q1. OWD is Private, yet a user sees a record they shouldn't. Where do you look?
A1. Check the record's Sharing Hierarchy first, then user permissions for "View All" overrides.
Q2. Which tool is used when one team must see LESS data than everyone else?
A2. A Restriction Rule, which is the only security tool that subtracts access.
Q3. An Apex class has no sharing keyword and was saved at API v67. What mode does it run in?
A3. It runs with sharing in user mode by default.