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.”
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:
- Find every occurrence. A search across your metadata, not a memory of where it was used.
- Replace with
WITH USER_MODE, one class at a time. - 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.
- 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.
