Dynamic Apex means writing code that operates on runtime-determined types and metadata, rather than hard-coded references.
Dynamic SOQL:
String objectName = 'Account';
String fieldList = 'Id, Name, Industry';
String query = 'SELECT ' + fieldList + ' FROM ' + objectName + ' WHERE Industry = :ind';
String ind = 'Tech';
List<sObject> results = Database.query(query);Use when the object/field names aren't known at compile time. Always parameterise user input — concatenating raw user input into the query opens SOQL injection vulnerabilities.
Dynamic DML:
SObject newRec = Schema.getGlobalDescribe().get(objectName).newSObject();
newRec.put('Name', 'Dynamic Record');
insert newRec;Dynamic describe:
Map<String, Schema.SObjectField> fields = Schema.getGlobalDescribe()
.get(objectName).getDescribe().fields.getMap();
for (String fieldName : fields.keySet()) {
Schema.DescribeFieldResult fdr = fields.get(fieldName).getDescribe();
System.debug(fieldName + ' = ' + fdr.getType());
}Type.forName for runtime instantiation:
Type t = Type.forName('AccountTriggerHandler');
Object handler = t.newInstance();Used in framework code that needs to dispatch to handlers by name.
When to use:
- Generic framework code that operates on any object — trigger frameworks, audit utilities.
- Configurable behaviour where the field/object is named in Custom Metadata.
- Multi-org apps where customers add their own fields.
- Schema introspection tools.
When NOT to use:
- Performance-critical paths — dynamic dispatch is slower than direct method calls.
- When the type is known — direct typed references are more readable, type-safe, and refactor-friendly.
Common pitfalls:
- SOQL injection — never concatenate untrusted input. Use bind variables (
:varName) orString.escapeSingleQuotes(). - FLS bypass — Dynamic SOQL doesn't automatically respect FLS. Use
Security.stripInaccessible()to filter. - Test coverage — dynamic code paths are easy to miss in tests.
Modern guidance: prefer typed Apex; reach for dynamic only when you genuinely need it.