Apex classes carry an API Version (declared in metadata, e.g., 60.0 for Spring '24). Salesforce releases new API versions every release; older versions remain supported indefinitely (in practice, 3+ years).
Why version matters:
- Each class compiles against its declared API version's behaviour.
- Salesforce updates platform behaviour in newer versions while preserving old behaviour for old versions.
- Classes calling each other can have different versions; behavioural quirks of the older version's class persist.
Common version-related issues:
- A class on API v40 that uses an older method signature; calling code on v60 sees the legacy behaviour.
- A new feature only available at API v55+ — old class can't use it without bumping its version.
- A breaking change in a newer API version — old classes are insulated, new classes adopt the new behaviour.
Strategy in customer orgs:
- Bump API versions deliberately — when you upgrade a class, decide whether to bump its API version. Bumping may unlock new features but may also introduce subtle behaviour changes.
- Read release notes for each API version — Salesforce documents API-version-related changes.
- Test thoroughly when upgrading versions — particularly anything date/time, sharing, or aggregation-related.
- Don't upgrade for the sake of upgrading — old API versions are stable; only upgrade when you need a feature or fix.
Strategy in managed packages:
This is harder. Managed packages have:
- Package version — your own internal version (1.0, 1.1, 1.2, 2.0).
- API version per class — what each class compiled against.
- Subscriber API version — what's installed in customer orgs.
When you publish a new package version:
- Classes can be added, modified, deprecated.
- `global` method signatures cannot change — backward-compat forever.
- `public` methods are flexible internally; not visible outside the package.
- API version of a packaged class affects how it behaves in subscriber orgs.
Deprecation patterns:
- `@deprecated` annotation on a class or method — marks it but doesn't break it.
- Document migration path in release notes for each version.
- Long deprecation windows — typically 2-3 major versions before removal.
Forward compatibility tools:
- Stable interfaces — define
global interfacethat doesn't change; concrete classes evolve behind it. - Versioned URLs for REST endpoints (
/v1/,/v2/). - Feature flags via Custom Metadata to toggle new behaviour while keeping old code path.
Tooling:
- Salesforce DX tracks API versions in
sfdx-project.json(default version) and per-class metadata. - Lightning Inspector / Setup -> Apex Classes show installed class versions.
A mature versioning strategy: the codebase's classes are mostly current API version, with a clear list of "legacy classes pinned to older API version because of specific behaviour we depend on" maintained as technical debt.
