Salesforce record id written as a literal
ID |
apex.correctness.hardcoding_id |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Api Usage |
Language |
Apex |
Description
Reports a string literal shaped like a 15- or 18-character Salesforce id (a 0 or a key prefix, mixed case and digits).
Rationale
Record ids are assigned per org. The id of a profile, a record type, a queue or a custom setting in production is a different id in every sandbox, in every scratch org and in every subscriber org that installs the package. Code holding one compiles everywhere and is wrong everywhere else: the query returns nothing, the assignment points at a record that does not exist, and the failure is an empty result or a null dereference rather than anything that names the id. Sandbox refreshes re-issue ids too, so the same code can start failing in a sandbox that used to work.
The following code illustrates the pattern detected by this rule:
public class ProfileLookup {
// FLAGGED: Salesforce record id written as a literal
private static final Id SYSTEM_ADMIN = '00e5g000001ABCDAA4';
public List<User> admins() {
// FLAGGED: Salesforce record id written as a literal
return [SELECT Id FROM User WHERE ProfileId = '00e5g000001ABCD'];
}
public List<User> adminsByName() {
Profile p = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1];
return [SELECT Id FROM User WHERE ProfileId = :p.Id];
}
public String label() {
return 'System Administrator';
}
public String developerName() {
return 'business_account_type';
}
}
Remediation
Resolve the record by a stable name at runtime - query the profile by Name, the record type by developer name, the queue by DeveloperName - and cache the id in a static for the length of the transaction. Where an id genuinely has to be configured per org, put it in Custom Metadata or a Custom Setting so it can be set per environment without a deployment.