Dynamic Apex is code that doesn’t know at compile time which object or fields it will work with. Instead of writing Account a = new Account(...), you ask the runtime for the schema, construct sObjects generically, and build SOQL strings on the fly. It’s how reusable utilities, packaged frameworks, and metadata-driven controllers work without hardcoding every object name.
What it lets you do
Three things you can’t do with static Apex:
- Discover schema at runtime — list every SObject in the org, every field on a given SObject, every picklist value on a field.
- Construct sObjects without knowing the type —
Schema.getGlobalDescribe().get('Account').newSObject()returns a genericSObjectyou canput()fields onto. - Query objects whose names are computed —
Database.query('SELECT Id FROM ' + objectName)accepts a string and returns results.
The Schema namespace
The entry point to dynamic Apex is Schema.getGlobalDescribe(), which returns a Map<String, Schema.SObjectType> of every accessible SObject keyed by API name.
// Get a token for an SObject by name
Schema.SObjectType accountType = Schema.getGlobalDescribe().get('Account');
// Describe its fields
Schema.DescribeSObjectResult describe = accountType.getDescribe();
Map<String, Schema.SObjectField> fields = describe.fields.getMap();
// Iterate field metadata
for (String fieldName : fields.keySet()) {
Schema.DescribeFieldResult fd = fields.get(fieldName).getDescribe();
System.debug(fd.getLabel() + ' (' + fd.getType() + ') updatable=' + fd.isUpdateable());
}
// Construct a record generically
SObject record = accountType.newSObject();
record.put('Name', 'Acme');
record.put('Industry', 'Technology');
insert record;
Dynamic SOQL
Database.query(String) and Database.queryWithBinds(String, Map<String, Object>, AccessLevel) execute SOQL built at runtime.
String objectName = 'Account';
String fieldList = 'Id, Name, Industry';
String filter = 'Industry = :industry';
Map<String, Object> binds = new Map<String, Object>{
'industry' => 'Technology'
};
String soql = 'SELECT ' + fieldList +
' FROM ' + objectName +
' WHERE ' + filter +
' WITH USER_MODE';
List<SObject> results = Database.queryWithBinds(soql, binds, AccessLevel.USER_MODE);
The WITH USER_MODE clause (Winter ‘23+) tells the runtime to enforce the running user’s FLS and sharing — the modern replacement for WITH SECURITY_ENFORCED.
Warning: Concatenating user input directly into a dynamic SOQL string is a SOQL injection vulnerability. Always use bind variables (
:variable) orDatabase.queryWithBinds()for any value that originates from outside the controller.
Dynamic DML
Once you have a generic SObject, DML works the same way as on a specific type:
List<SObject> records = new List<SObject>();
for (String name : new List<String>{ 'Acme', 'Beta', 'Gamma' }) {
SObject r = Schema.getGlobalDescribe().get('Account').newSObject();
r.put('Name', name);
records.add(r);
}
insert records;
Common dynamic Apex patterns
Field-level security check before query
Schema.DescribeFieldResult fd = Account.SSN__c.getDescribe();
if (fd.isAccessible()) {
List<Account> accs = [SELECT Id, SSN__c FROM Account];
}
Picklist values for an LWC
@AuraEnabled(cacheable=true)
public static List<String> getIndustryValues() {
List<String> values = new List<String>();
Schema.DescribeFieldResult fd = Account.Industry.getDescribe();
for (Schema.PicklistEntry pe : fd.getPicklistEntries()) {
if (pe.isActive()) {
values.add(pe.getValue());
}
}
return values;
}
Object-agnostic deduplicator
public static void dedupBy(String objectName, String externalIdField, List<SObject> records) {
String soql = 'SELECT Id, ' + externalIdField + ' FROM ' + objectName +
' WHERE ' + externalIdField + ' IN :keys';
Set<String> keys = new Set<String>();
for (SObject r : records) keys.add((String) r.get(externalIdField));
Map<String, SObject> existing = new Map<String, SObject>();
for (SObject r : Database.queryWithBinds(soql, new Map<String, Object>{'keys' => keys}, AccessLevel.SYSTEM_MODE)) {
existing.put((String) r.get(externalIdField), r);
}
// ... merge or skip duplicates
}
Performance and limit considerations
Dynamic Apex uses the same governor budget as static code — describe calls have no separate quota in modern API versions, but each Database.query() counts as one SOQL statement. The cost is readability and compile-time safety: every field access becomes a string, so renames in Setup silently break dynamic code that won’t fail until runtime.
What interviewers are really looking for
Naming the namespace Schema and quoting Database.query() covers the surface. The real signal is whether you understand when to choose dynamic over static: packaged code, metadata-driven utilities, schema explorers, FLS-enforcing helpers. Mention SOQL injection risk and the bind-variable defense, and you’ve covered the security angle interviewers love to probe.
Verified against: Apex Developer Guide — Dynamic Apex, Dynamic SOQL. Last reviewed 2026-05-17 for Spring ‘26 release.