API version 67.0 updates to better represent system/user mode distinctions

This commit is contained in:
James Simone 2026-07-28 10:18:05 -04:00
parent 7baeb07b36
commit 039893ecf8
No known key found for this signature in database
GPG Key ID: 2DF6F3268D65188D
5 changed files with 11 additions and 5 deletions

View File

@ -183,7 +183,7 @@ public class ComplexLogic {
## Example 6: Class Without Explicit Sharing Declaration
**Problem:** Classes without sharing default to "without sharing" — security risk.
**Problem:** Classes in API versions 66.0 and below without sharing default to "without sharing" — security risk. If the API version is 67.0 or higher, the below example does not apply.
**Sample violating code:**
```apex

View File

@ -13,7 +13,7 @@
]
```
**AST evidence:** `ModifierNode` has `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` — all `false()` means no sharing keyword declared (implicit without sharing).
**AST evidence:** `ModifierNode` has `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` — all `false()` means no sharing keyword declared (implicit without sharing in API versions 66.0 and below. In API versions 67.0 and higher, the default changes to implicit with sharing).
Added `@Nested = false()` to exclude inner classes (which inherit from parent).

View File

@ -117,6 +117,7 @@ If any constraint would be violated in generated code, **stop and explain the pr
- `SELECT *` does not exist in SOQL -- always specify the exact fields needed
- Apply `LIMIT` clauses to bound result sets; use `ORDER BY` for deterministic results
- When querying Custom Metadata Types (objects ending with `__mdt`), do NOT use SOQL — use the built-in methods (`{CustomMdt__mdt}.getAll().values()`, `getInstance()`, etc.)
- Queries executed in `without sharing` keyword classes with API versions 67.0 and up will throw when the running user does not have the proper field or object-level security. If API versions are being updated, ensure queries are safeguarded properly. Only by passing the `System.AccessLevel.SYSTEM_MODE` class to queries, or by using the `WITH SYSTEM_MODE` modifier after a query's `WHERE` clause will ensure backwards compatibility
### Caching
@ -126,7 +127,7 @@ If any constraint would be violated in generated code, **stop and explain the pr
### Security
- Default to `with sharing`; document justification for `without sharing` or `inherited sharing`
- `WITH USER_MODE` in SOQL and `AccessLevel.USER_MODE` for `Database` DML for CRUD/FLS enforcement
- `WITH USER_MODE` in SOQL and `AccessLevel.USER_MODE` for `Database` DML for CRUD/FLS enforcement — these are the defaults for _all_ Apex classes with API versions of 67.0 or higher
- Validate dynamic field/operator names via allowlist or `Schema.describe`
- Named Credentials for all external credentials/API keys
- `AuraHandledException` for `@AuraEnabled` user-facing errors (no internal details)
@ -153,7 +154,7 @@ Before finalizing, verify: CRUD/FLS enforced (SOQL + DML) · explicit sharing ke
- Add guard clauses for null/empty inputs at the top of every public method; match style to context: `return` early in private/trigger-handler methods, `throw` exceptions in public APIs, `record.addError()` in validation services
- Return empty collections instead of `null`
- Use safe navigation (`?.`) for chained property access
- Never dereference `map.get(key)` inline unless presence is guaranteed; use `containsKey`, assignment+null check, or safe navigation first
- Never dereference `map.get(key)` inline unless presence is guaranteed; use `containsKey`, assignment + null check, or safe navigation first
- Use null coalescing (`??`) for default values
- Prefer `String.isBlank(value)` over manual checks like `value == null || value.trim().isEmpty()`
@ -220,7 +221,7 @@ Method-level format:
### Code Structure & Architecture
- Single responsibility per class; max 500 lines -- split when exceeded
- Return Early: validate preconditions at method top, return/throw immediately
- Return early: validate preconditions at method top, return/throw immediately
- Extract private helpers for methods over ~40 lines
- Use Dependency Injection (constructor/method params) for testability
- Prefer composition and narrow interfaces over deep inheritance; extend via new implementations, not modifications

View File

@ -189,6 +189,10 @@ Deliverables per test class:
- `{ClassName}Test.cls` + `{ClassName}Test.cls-meta.xml` (match API version of class under test; default `66.0`)
- `TestDataFactory.cls` + `TestDataFactory.cls-meta.xml` (if not already present)
## API version considerations
Apex running in API versions 66.0 and below defaults to using the `without sharing` keyword when not explicitly specified and runs in system mode by default; Apex running in API versions 67.0 and higher defaults to running in user mode, which means that upgrading API versions to something that crosses the 67.0 boundary can cause downstream test failures without code having explicitly changed. This is especially true in test classes, where it's rare for the `sharing` keywords to have been declared at all. Whenever API versions are upgraded across this critical boundary, additional uplift may be necessary to ensure tests are properly passing.
## Reference Files
Load on demand for detailed patterns:

View File

@ -91,6 +91,7 @@ Cover:
| "Uncommitted work pending" error in callout test | DML and HTTP callouts cannot be mixed in the same test context without `Test.startTest()` wrapping |
| Mock not taking effect in test | Ensure `Test.setMock()` is called before the code that makes the callout |
| `@TestSetup` data missing in test method | `@TestSetup` data is committed per test method — re-query it; do not store in static variables |
| API version 67.0 and higher without necessary access level checks | Ensuring user running test has the proper field and object-level permissions when performing DML or querying |
---