Overview
Process Automation and Logic is where the Platform Developer I exam tests whether you can actually write Apex. It covers the capabilities of declarative automation in Flow, Apex declarations (variables, constants, methods, modifiers and interfaces), control flow, SOQL, SOSL and DML with their return types, trigger and class best practices, governor limits, the save order of execution with recursion and cascading, exception handling, and combining Flow with Apex.
The section exists because Apex runs inside a shared, limited transaction that also contains flows, validation rules and other triggers. Code that works for one record in a test can fail on a 200-record data load, re-fire itself through a workflow field update, or roll back an entire batch because of one bad row. It builds directly on the data model and declarative-first thinking from Developer Fundamentals and supplies the logic that User Interface components call and that Testing, Debugging, and Deployment must cover.
How it's tested
Expect code reading and scenario design. Many questions present a short Apex snippet, trigger or query and ask what it returns, how many times something executes, or which limit it breaks. Others describe a requirement and ask for the best automation design, often with two or three answers to select. Read code line by line and count queries and DML inside loops.
-
Predict the output
A snippet with a loop, switch, SOQL assignment or DML call is shown, and you choose its result, return type or the exception it throws.
-
Find the limit problem
Code works for one record but contains SOQL or DML inside a loop, and you identify the failure under bulk load and the bulkified fix.
-
Order and automation design
A requirement involves triggers, flows and validation rules, and you place them in the save order or choose the right before or after context and tool.
A trigger loops over Trigger.new and runs a SOQL query for each Contact's Account. A data load inserts 300 contacts. Triggers process records in chunks of 200, so the 101st query in the first chunk exceeds the synchronous limit of 100 and throws an uncatchable LimitException. The fix collects AccountIds into a Set, runs one query into a Map, and reads from the map inside the loop.
Objectives 9
Official objectives from the Salesforce outline, in outline order.
2.1 Identify the capabilities of the declarative process automation features.What Flow and approval processes can automate before you reach for Apex.
- What it means
- Flow is Salesforce's primary declarative automation tool: record-triggered flows (before-save for fast same-record updates, after-save for related records and actions), screen flows for guided user input, schedule-triggered flows, autolaunched flows called from Apex or other flows, and platform event-triggered flows. Approval processes handle multi-step approvals, and Workflow Rules and Process Builder are retired in favor of Flow.
- What to understand
- Know what Flow can do without code: create, update and delete records, send emails, call invocable Apex, run on a schedule, and run asynchronously after save. Also know where it stops: complex data processing, sophisticated error handling and some integration patterns still call for Apex.
- How it shows up
- A requirement such as updating a field on the same record before save, or creating a task when an opportunity closes, is described and you choose the right flow type or decide Apex is needed.
- Common confusion
- Candidates pick an after-save flow to update fields on the triggering record, when a before-save flow does it faster with no extra DML.
2.2 Understand how to declare variables, constants, methods, modifiers, Apex interfaces, and the appropriate use cases for each.Apex declarations, access modifiers, static versus instance, and interfaces.
- What it means
- Apex variables are strongly typed and default to null, including Integer and Boolean. Constants use static final. Access modifiers are private (the default), protected, public and global; static members belong to the class and persist for the transaction, while instance members belong to an object. Interfaces declare method signatures that a class must implement, as with Database.Batchable, Queueable, Schedulable and Comparable.
- What to understand
- Know which modifier fits: global only when code must be visible outside the namespace (managed packages, web services), public within the namespace, private by default for members. Know that virtual and abstract allow extension and that a class implementing an interface must implement every method it declares.
- How it shows up
- A snippet declares a variable without initializing it and adds to it, or asks which modifier lets a managed package expose a method, or which interface a class needs to be scheduled.
- Common confusion
- Candidates assume an uninitialized Integer is 0; it is null, and arithmetic on it throws a NullPointerException.
2.3 Given a scenario, assess how Apex control flow statements will execute and when to apply different control flow statements.if/else, switch, loops, break and continue, and SOQL for loops.
- What it means
- Apex supports if/else, switch on (with when values and when else) for Integer, Long, String, sObject and Enum values, traditional for loops, list or set iteration loops, while and do-while loops, and break and continue. A SOQL for loop iterates over query results in chunks to reduce heap use.
- What to understand
- Be able to trace a loop and give the exact count or final value, including do-while running at least once and continue skipping the rest of one iteration. Know that a SOQL for loop is the memory-efficient way to process large query results, though it still counts as one query.
- How it shows up
- A snippet with nested loops, a switch statement or a do-while condition asks for the printed value or the number of iterations.
- Common confusion
- Candidates treat do-while like while and miss that the body runs once before the condition is checked.
2.4 Given a scenario, write SOSL, SOQL, and DML statements in Apex, and assess what they will return.Query syntax, return types, bind variables and DML options.
- What it means
- SOQL queries one object (plus related objects through relationships) and returns List<sObject>, a single sObject when assigned to one, an Integer for COUNT(), or AggregateResult[] for aggregate functions. SOSL searches text across multiple objects and returns List<List<sObject>>. DML statements (insert, update, upsert, delete, undelete, merge) are all-or-none; Database methods such as Database.insert(records, false) allow partial success and return result objects.
- What to understand
- Know the exact return types, that assigning a SOQL query with zero rows to a single sObject variable throws a QueryException, that bind variables (:name) safely pass Apex values into queries, and that Database.SaveResult tells you which rows failed.
- How it shows up
- A snippet shows a query or DML call and asks what it returns, or a requirement says valid records must save even if some fail, pointing to Database methods with allOrNone set to false.
- Common confusion
- Candidates use SOQL to search a phrase across several objects, which is SOSL's job, or expect SOSL to return a flat list.
2.5 Given a scenario, follow best practices to write Apex classes and triggers.Bulkified, logic-less triggers with handler classes and correct context use.
- What it means
- Best practice is one trigger per object that delegates to a handler class, code bulkified to handle up to 200 records per trigger chunk, no SOQL or DML inside loops, collections and maps for lookups, and correct use of trigger context variables such as Trigger.new, Trigger.oldMap and Trigger.isBefore.
- What to understand
- Update fields on the triggering records in a before trigger by assigning to Trigger.new, with no DML; changes to other records belong in an after trigger with explicit DML. Trigger.new is read-only in after triggers, and Trigger.old is always read-only.
- How it shows up
- A trigger is shown with a query inside a loop or a DML update on Trigger.new in a before trigger, and you identify the flaw or choose the corrected design.
- Common confusion
- Candidates write update Trigger.new in a before trigger, which is unnecessary and throws an error, instead of just assigning field values.
2.6 Given a scenario, understand governor limits and their implications on Apex transactions.Per-transaction limits on queries, DML, rows, CPU and heap.
- What it means
- Key synchronous limits per transaction: 100 SOQL queries (200 asynchronous), 150 DML statements, 50,000 rows retrieved by SOQL, 10,000 rows processed by DML, 20 SOSL queries, 10 seconds of CPU time (60 asynchronous) and 6 MB heap (12 MB asynchronous). Exceeding any limit throws a LimitException that cannot be caught and rolls back the transaction.
- What to understand
- All code in one transaction shares the same limits, including triggers fired by your DML and other automation. The Limits class reports consumption, and asynchronous Apex gets higher limits for heavier work.
- How it shows up
- Code is shown processing records in a loop, and you identify which limit fails first at a given volume, or choose a bulkified or asynchronous fix.
- Common confusion
- Candidates wrap code in try/catch to handle a limit breach; LimitException cannot be caught, so only redesign avoids it.
2.7 Describe the relationship between Apex transactions, the save order of execution, and the potential for recursion and/or cascading.What runs, in what order, when a record is saved, and how logic re-fires.
- What it means
- When a record is saved, Salesforce runs system validation, before-save record-triggered flows, before triggers, custom validation rules and duplicate rules, saves the record without committing, then runs after triggers, assignment and auto-response rules, any workflow field updates, after-save record-triggered flows, roll-up summary updates to parents, and finally commits and runs post-commit logic such as emails and asynchronous jobs.
- What to understand
- Cascading happens when a save causes other saves, for example an after trigger updating related records or a roll-up summary sending the parent through its own save. Recursion happens when logic re-fires itself, such as an after update trigger updating its own records; use static variables or sets of processed IDs to stop it before the maximum trigger depth error.
- How it shows up
- A scenario asks whether a validation rule sees a value set in a before trigger, or why a trigger runs twice after a field update, and you answer from the order of execution.
- Common confusion
- Candidates think validation rules run before before triggers; custom validation runs after before triggers, so values set in a before trigger are validated.
2.8 Implement exception handling in Apex, including custom exceptions as needed.try/catch/finally, built-in exception types, custom exceptions and addError.
- What it means
- Apex uses try, catch and finally blocks with built-in exceptions such as DmlException, QueryException, NullPointerException and ListException. A custom exception is a class that extends Exception and whose name ends in Exception, thrown with the throw keyword. In triggers, addError on a record or field blocks the save and shows a message to the user.
- What to understand
- Catch specific exception types before the generic Exception, use finally for cleanup, and do not swallow errors silently. Know that LimitException cannot be caught and that addError in a before trigger is the standard way to reject a record with a friendly message.
- How it shows up
- A snippet with nested try/catch asks which block runs, or a requirement asks how to stop a record from saving with a custom message.
- Common confusion
- Candidates name a custom exception without the Exception suffix or forget to extend Exception, both of which fail to compile.
2.9 Given a scenario, use declarative functionality and Apex together to automate business logic.Invocable Apex in Flow, Apex launching flows, and splitting work by tool.
- What it means
- Flow and Apex combine in both directions: a flow calls Apex through a method annotated @InvocableMethod (one per class, static, public or global, taking and returning lists), with @InvocableVariable for structured inputs, and Apex can start an autolaunched flow through the Flow.Interview class.
- What to understand
- Design so each tool does what it does best: Flow for orchestration and admin-maintained rules, Apex for complex logic, heavy processing or callouts. Invocable methods receive inputs as lists so that Flow can bulkify calls when many records trigger the same flow.
- How it shows up
- An admin's flow needs a calculation or callout that Flow cannot perform, and you choose an invocable method signature or identify why an existing method is not visible in Flow Builder.
- Common confusion
- Candidates write an invocable method that takes a single record instead of a List, or put two invocable methods in one class.
High-yield concepts
The terms, rules and formulas this domain turns into questions most often.
-
Bulkification
Writing Apex so it handles many records per execution by working with collections, querying once outside loops and issuing one DML statement per object.
- Remember
- Triggers receive up to 200 records per chunk, so code must never assume Trigger.new has one record.
- Tested as
- Almost every trigger question tests it, directly or through a limit failure.
- Trap
- Code that passes a single-record test but fails on a data load because it queries or saves inside a loop.
-
SOQL return types
A query returns List<sObject>; assigned to one sObject variable it must return exactly one row; COUNT() returns an Integer; aggregate functions return AggregateResult[].
- Remember
- Assigning a zero-row result to a single sObject throws System.QueryException rather than returning null.
- Tested as
- 'What is returned' and 'what happens' snippet questions.
- Trap
- Expecting a null value or an empty object when the query matches nothing.
-
SOSL
Salesforce Object Search Language searches text across multiple objects and fields with FIND ... RETURNING, using the search index.
- Remember
- SOSL returns List<List<sObject>>, one inner list per object in RETURNING, in the order listed.
- Tested as
- Chosen when a scenario searches a term across several objects, such as a phone number in Accounts, Contacts and Leads.
- Trap
- Using several SOQL LIKE queries where one SOSL search fits, or casting the result to a flat list.
-
DML statement vs. Database method
DML statements such as insert are all-or-none and throw a DmlException on any failure; Database.insert(records, false) saves valid rows and reports failures in SaveResult objects.
- Remember
- Use allOrNone false when some records may fail and the rest must still be saved.
- Tested as
- Scenarios about partial success in bulk loads or integrations.
- Trap
- Wrapping an insert statement in try/catch and expecting the good rows to be saved anyway.
-
Governor limits (synchronous)
Per transaction: 100 SOQL queries, 150 DML statements, 50,000 rows queried, 10,000 rows in DML, 20 SOSL queries, 10,000 ms CPU and 6 MB heap.
- Remember
- Asynchronous Apex raises some limits, including 200 SOQL queries, 60,000 ms CPU and 12 MB heap.
- Tested as
- Counting queries or DML in a loop to find the first limit hit.
- Trap
- Assuming each trigger or each record gets its own limits rather than sharing one transaction's budget.
-
LimitException
The exception thrown when code exceeds a governor limit, which ends the transaction and rolls back its changes.
- Remember
- It cannot be caught by try/catch, so prevention through bulkification or asynchronous processing is the only fix.
- Tested as
- Distractor answers propose catching it.
- Trap
- Choosing an answer that handles a limit breach with exception handling.
-
Before vs. after triggers
Before triggers run before the record is saved and can change field values on Trigger.new without DML; after triggers run after the save, when record IDs and system fields are available.
- Remember
- Use before for validation and same-record field updates, after for work on related records or anything needing the new record's ID.
- Tested as
- Context-selection questions and snippet errors.
- Trap
- Running DML on Trigger.new in a before trigger, or editing Trigger.new in an after trigger, both of which fail.
-
Order of execution
On save: before-save flows, before triggers, validation rules and duplicate rules, save, after triggers, assignment and workflow rules, after-save flows, roll-up summary updates, commit, then post-commit logic.
- Remember
- Before-save flows run before before triggers, and custom validation rules run after before triggers.
- Tested as
- Questions about which automation sees which value and why logic fires twice.
- Trap
- Placing validation rules ahead of before triggers, or assuming emails and asynchronous jobs run before commit.
-
Recursion control
Recursion occurs when automation causes the same trigger to run again in the same transaction, such as an after update trigger updating its own records.
- Remember
- A static Boolean or a static Set<Id> of processed records in a handler class prevents repeated processing, because static variables last for the transaction.
- Tested as
- Scenarios where a trigger runs twice or hits the maximum trigger depth.
- Trap
- Using an instance variable as the guard, which resets each time a new handler object is created.
-
Custom exception
A class that extends Exception and whose name ends with Exception, used to signal application-specific errors.
- Remember
- Throw it with throw new MyAppException('message'); constructors for message and cause are inherited automatically.
- Tested as
- Syntax questions and best-practice error handling.
- Trap
- Omitting the Exception suffix or the extends Exception clause.
-
@InvocableMethod
An annotation that exposes a static Apex method to Flow and other automation as an Apex action.
- Remember
- Only one per class; the method must be public or global and static, and its input and output are lists to support bulk calls.
- Tested as
- The standard bridge when a flow needs logic Flow cannot perform.
- Trap
- Declaring a single sObject parameter or adding a second invocable method to the same class.
-
Static vs. instance members
Static variables and methods belong to the class and persist for the whole transaction; instance members belong to each object created with new.
- Remember
- Static variables are the tool for transaction-wide state such as recursion guards and caches.
- Tested as
- Declaration questions and recursion design.
- Trap
- Expecting a static variable to keep its value across separate transactions.
Side by side
Concepts candidates mix up, compared on the point that decides the answer.
SOQL vs. SOSL
| Concept | Meaning | Key difference | Common trap |
|---|---|---|---|
| SOQL | Structured query on one object and its relationships | Returns List<sObject>, Integer or AggregateResult[]; precise filters with WHERE | Using LIKE across several objects instead of one search |
| SOSL | Text search across multiple objects using the search index | Returns List<List<sObject>> in RETURNING order | Treating the result as a flat list of records |
Before-save flow vs. before trigger vs. after trigger
| Concept | Meaning | Key difference | Common trap |
|---|---|---|---|
| Before-save flow | Declarative update of fields on the triggering record | Runs first in the save order, with no DML needed | Using an after-save flow for same-record updates |
| Before trigger | Apex that runs before the save and can modify Trigger.new | Validation and same-record changes with code-level logic | Issuing DML on Trigger.new |
| After trigger | Apex that runs after the save, before commit | Record IDs are available; related-record changes need explicit DML | Trying to modify Trigger.new directly |
DML statement vs. Database method
| Concept | Meaning | Key difference | Common trap |
|---|---|---|---|
| DML statement | insert, update, upsert, delete as language statements | All-or-none; any failure throws DmlException | Expecting valid rows to save when one fails |
| Database method | Database.insert(records, allOrNone) and similar | allOrNone false saves valid rows and returns result objects | Ignoring SaveResult and missing which rows failed |
Exam traps
Each trap is an answer that looks right. Learn the clue that should change your mind.
-
Catch the limit
- What candidates pick
- Wrap the loop in try/catch so the transaction survives a governor limit error.
- Why it looks right
- Exception handling is the usual way to deal with runtime failures.
- Why it's wrong
- LimitException cannot be caught; the transaction ends and rolls back regardless of handlers.
Clue to watch for The scenario mentions bulk volumes, data loads or 'Too many SOQL queries'.
-
DML on Trigger.new
- What candidates pick
- Add update Trigger.new at the end of a before update trigger to save field changes.
- Why it looks right
- In most code, changes are not saved until you issue DML.
- Why it's wrong
- In a before trigger, changes to Trigger.new are saved automatically; explicit DML on those records is redundant and throws an error.
Clue to watch for The trigger context is before insert or before update and the changes target the same records.
-
Null for no rows
- What candidates pick
- Check if the queried Account is null after Account a = [SELECT ... LIMIT 1].
- Why it looks right
- Many languages return null when nothing is found.
- Why it's wrong
- Assigning a zero-row SOQL result to a single sObject throws a QueryException before the null check runs; query into a List and check isEmpty().
Clue to watch for A single sObject variable receives a query that might match no records.
-
After-save for same record
- What candidates pick
- An after-save record-triggered flow to set a field on the record that triggered it.
- Why it looks right
- After-save flows can do everything a before-save flow can and more.
- Why it's wrong
- Updating the triggering record after save requires another DML and re-runs the save order; a before-save flow does it faster with no DML.
Clue to watch for The only change is to fields on the triggering record itself.
-
Instance-variable guard
- What candidates pick
- A non-static Boolean in the handler class to stop a trigger from running twice.
- Why it looks right
- It looks like a normal flag that remembers state.
- Why it's wrong
- Each trigger invocation can create a new handler instance, so the flag resets; recursion guards need static variables that persist for the transaction.
Clue to watch for The trigger runs again because of a workflow field update or its own DML.
-
SOQL for searching
- What candidates pick
- Several SOQL queries with LIKE to find a term in Accounts, Contacts and Leads.
- Why it looks right
- SOQL is the query language candidates practice most.
- Why it's wrong
- SOSL is designed for text search across multiple objects in one call and returns results grouped by object.
Clue to watch for A free-text term searched across multiple objects or fields.
-
All-or-none by default
- What candidates pick
- An insert statement inside try/catch to keep the good records from a mixed batch.
- Why it looks right
- Catching the DmlException seems to isolate the failures.
- Why it's wrong
- The insert statement rolls back every row when any row fails; Database.insert(records, false) is needed for partial success.
Clue to watch for The requirement says valid records must be saved even if some fail.
Answer framework
For any Apex or automation scenario in this section, work through these steps before picking an answer.
- Decide whether Flow can meet the requirement on its own, and prefer a before-save flow for same-record field updates.
- If Apex is needed, pick the context: before trigger for validation and same-record changes, after trigger for related records or new IDs.
- Scan the code for SOQL or DML inside loops and count consumption against the transaction limits at the stated record volume.
- Check return types and failure modes: single-row assignments, SOSL nested lists, and all-or-none versus partial-success DML.
- Place every piece of automation in the save order and add a static guard wherever logic could re-fire itself through cascading updates.
Practice question
Want more like this? The Salesforce PD1 practice test has questions for every domain.
Checklist
0 of 13
Tick each one you can do without notes. Your ticks stay in this browser.
Study strategy
Give this section the most time, since it carries the most weight. Start with Apex syntax basics: declarations, modifiers, static versus instance, interfaces and control flow, and trace small snippets by hand until you can predict output without running them. Anonymous Apex in the Developer Console is the fastest way to check yourself.
Then move to SOQL, SOSL and DML, writing each query type and printing what it returns. Memorize the return types and the zero-row QueryException, because these are easy points that are easy to lose.
Next, build triggers. Write one handler-based trigger, deliberately put a query in a loop, insert 201 records from anonymous Apex and read the error. Then fix it with sets and maps. Learn the main governor limit numbers at this point, while they mean something.
The order of execution and recursion are the most underestimated areas: draw the save sequence from memory and explain where before-save flows, validation rules and after-save flows fall. Finish with exception handling and invocable methods. Afterwards, practice code-reading questions under time pressure and review every miss by re-running the snippet.
FAQ
How much of the Platform Developer I exam is Process Automation and Logic?
It is 28% of the exam, the largest of the four sections. Applied to 60 scored questions that suggests about 17 questions as a planning estimate; Salesforce does not publish per-section counts.
Which governor limits should I memorize?
Know the synchronous per-transaction limits of 100 SOQL queries, 150 DML statements, 50,000 queried rows, 10,000 DML rows, 10 seconds of CPU time and 6 MB heap, plus the higher asynchronous values for SOQL, CPU and heap. Questions usually require counting usage in a loop against these numbers.
Do I need to know Flow on a developer exam?
Yes. The section starts with declarative process automation, and many design questions expect a before-save or after-save flow where it meets the requirement. You also need to know how Flow calls Apex through invocable methods.
What is the difference between SOQL and SOSL?
SOQL queries specific objects with precise filters and returns a list of records. SOSL searches text across multiple objects using the search index and returns a list of lists, one per object in the RETURNING clause.
How much of the order of execution do I need?
Know the main sequence: before-save flows, before triggers, validation and duplicate rules, save, after triggers, assignment and workflow rules, after-save flows, roll-up summaries, commit, then post-commit actions. Most questions ask which step sees a value or why logic runs twice.
How do I stop a trigger from running recursively?
Use a static variable in a handler class, such as a Boolean flag or a Set of processed record IDs, because static variables persist for the entire transaction. Check it before processing and update it afterwards.
Are there code snippets on the exam?
The exam guide frames many objectives as 'given a scenario', and this section involves predicting what Apex statements, queries and DML return. Practicing by reading and running short snippets is the most direct preparation.
Other CRM & Business Applications certifications
Where the same subject shows up in other exams. Cards marked "Same subject" open the matching domain.
Test your Domain 2 knowledge
You have covered Flow capabilities, Apex syntax, queries and DML, trigger best practices, governor limits and the order of execution. See whether you can predict what code does under timed conditions.
Keep going
- Training Training Built on your Weak areas
- Practice test Questions by domain, with explanations
- Mock exam Full length, timed like the real exam
- Readiness test Find your weakest domain in minutes
- Cheat sheet Key terms and formulas on one page
- Ask the Salesforce PD1 AI tutor Get any concept explained your way