Tested in Summer ’26 on September 10, 2026. Salesforce changes often. If something here no longer matches your org, tell us and we will retest it.

The answer, briefly: use WITH USER_MODE on your queries and AccessLevel.USER_MODE on your Database calls. The syntax most tutorials still teach, WITH SECURITY_ENFORCED, no longer compiles. Running it in a Summer ’26 org returns, verbatim: “WITH SECURITY_ENFORCED is no longer supported, use WITH USER_MODE instead.”

How Apex access enforcement got hereThree eras of enforcing CRUD and field-level security in Apex, and which syntax still compiles.01Manual checks: isAccessible() and isUpdateable(), object by object, fieldby field02WITH SECURITY_ENFORCED: queries only, throws rather than filters, no DMLcover03WITH SECURITY_ENFORCED no longer compiles — “use WITH USER_MODE instead”04WITH USER_MODE on SOQL: object permissions, field-level security andsharing05AccessLevel.USER_MODE on Database calls: the same enforcement on DML06Security.stripInaccessible when you want fields removed rather than anexceptionVerified in a Summer '26 Developer org: USER_MODE compiles and returns rows, SECURITY_ENFORCEDfails at compile time with that exact message. Any class you edit and redeploy will fail untilit is migrated.
The syntax most tutorials teach no longer compiles.

Apex runs in system context, which is the whole problem

Apex ignores object permissions, field-level security and sharing by default. with sharing on the class restores record-level sharing, and it does nothing at all for object or field permissions — a distinction that catches people who believe one keyword covered everything.

So a controller that queries a field the running user cannot see will happily return it, and your Lightning component will happily display it. Nothing errors. That is the failure mode: not a crash, a disclosure.

The three eras of enforcement, and where we are now

Manual checks. Schema.sObjectType.Account.isAccessible() and isUpdateable() per object, per field, by hand. Verbose, easy to forget, and still what a lot of older code does.

WITH SECURITY_ENFORCED. A SOQL clause that checked field and object access for the query. Better, and limited: it only covered the query, it threw rather than filtered, and it did not apply to DML.

WITH USER_MODE and AccessLevel.USER_MODE. The current answer, and the only one that still compiles. User mode applies object permissions, field-level security and sharing rules, on both queries and DML.

Verified in a Summer ’26 Developer org on 10 September 2026: WITH USER_MODE compiles and returns rows; Database.query(..., AccessLevel.USER_MODE) compiles and returns rows; WITH SECURITY_ENFORCED fails to compile with the message quoted above.

What the current syntax looks like

// Query in the user's context: object, field and sharing all apply.
List<Account> accounts = [SELECT Id, Name, AnnualRevenue FROM Account WITH USER_MODE];

// Dynamic SOQL takes the access level as an argument.
List<Account> more = Database.query('SELECT Id FROM Account', AccessLevel.USER_MODE);

// DML too — this is the part SECURITY_ENFORCED never covered.
Database.insert(newAccounts, AccessLevel.USER_MODE);
Database.update(changed, AccessLevel.USER_MODE);

The DML line is the upgrade that matters most. Before user mode, a class could enforce access perfectly on the way in and then write a field the user had no permission to edit.

When you want filtering rather than an exception

User mode throws when the running user lacks access. Sometimes you want the operation to continue with the inaccessible fields removed — a list view that shows what it can, rather than failing entirely.

SObjectAccessDecision decision = Security.stripInaccessible(AccessType.READABLE, records);
List<Account> safe = decision.getRecords();
System.debug(decision.getRemovedFields());

stripInaccessible returns the records with unreadable fields blanked, and tells you which fields it removed. That second part is genuinely useful in a log: it turns “a user says a field is empty” into a fact.

What to do with the code you already have

You cannot leave WITH SECURITY_ENFORCED in place. It will not compile, so any deployment touching that class fails. The migration is mechanical, and the ordering matters:

  1. Find every occurrence. A search across your metadata, not a memory of where it was used.
  2. Replace with WITH USER_MODE, one class at a time.
  3. Run the tests, and expect new failures. User mode is stricter. It enforces sharing as well as field access, so a test whose data was created by a system-context setup can now legitimately see nothing.
  4. Decide per failure whether the test data was unrealistic or the code was over-permissive. Both happen, and the second one is the reason to do this.

Do not switch a whole codebase to user mode in one deployment. The failures are informative and you want to read them, not race them.

Where user mode is the wrong choice

Some code is supposed to run in system context: a batch job recalculating a rollup across records nobody owns, an integration that must write regardless of the calling user, a trigger maintaining data integrity. Forcing user mode there breaks legitimate work.

The test is not “is this Apex” but “is this acting on behalf of a person”. A controller behind a Lightning component: user mode. A nightly recalculation: system mode, deliberately, with a comment saying why.

Frequently asked

Does with sharing still matter? Yes, for sharing, and only for sharing. It is not a substitute for user mode and never was.

Will this break my managed package? Packaged code compiled against an older version is not recompiled by an upgrade, but any class you edit and redeploy is. That is why this arrives as a deployment failure rather than a runtime one.

Is user mode slower? There is a cost to enforcing sharing on a query, and it is the same cost the platform pays for a user running a report. If it matters at your volume, that is a selectivity problem before it is an enforcement one.

What about SOSL? It takes WITH USER_MODE too, and the same reasoning applies.