# Apex class and trigger best practices

*Platform Developer Study Guide · Process Automation and Logic · Last updated October 15, 2026 · https://www.certifyforce.com/study-guide/platform-developer/process-automation-and-logic/apex-class-and-trigger-best-practices*

Write triggers the way the exam expects: context variables, one trigger per object with a handler class, bulkified SOQL and DML, before-trigger field updates, addError, change detection with Trigger.oldMap, recursion guards and no hardcoded configuration.

- Certification: Salesforce Platform Developer
- Exam section: Process Automation and Logic
- Study guide: https://www.certifyforce.com/study-guide/platform-developer

**Official exam objective:**

> Given a scenario, follow best practices to write Apex classes and triggers.

## What the exam expects

- Read trigger context variables and know which exist, and which are writable, in each event.
- Consolidate logic into one trigger per object that dispatches to a handler class.
- Bulkify: no SOQL or DML inside loops; sets, maps and one DML on a collection.
- Choose a before trigger for same-record field updates and `addError()` to block a save.
- Detect real field changes with `Trigger.oldMap` and stop unwanted re-entry with a guard.
- Keep configuration such as record type Ids out of code.

Key terms: **context variable** (an implicit `Trigger` member such as `Trigger.new`), **handler class** (the class a thin trigger delegates to), **bulkification** (code that works for 1 or 200 records within limits), **recursion guard** (logic that stops a trigger re-running its work in the same transaction).

## Key facts

| Fact | Detail | Source |
|---|---|---|
| Trigger.new editable | Only in before triggers; after triggers throw a runtime error | [Context Variable Considerations](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables_considerations.htm) |
| Trigger.old and oldMap | Only in update and delete triggers | [Trigger Context Variables](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm) |
| Trigger.newMap | Before update, after insert, after update, after undelete | [Trigger Context Variables](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm) |
| Trigger batch size | 200 records per invocation; Trigger.size is the current batch | [Trigger Context Variables](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm) |
| Multiple triggers, same object and event | Execution order not guaranteed | [Record-Triggered Automation decision guide](https://architect.salesforce.com/docs/architect/decision-guides/guide/record-triggered) |
| Limits a loop breaks | 100 synchronous SOQL queries, 150 DML statements | [Bulk Apex Triggers](https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_bulk) |
| addError from an Apex DML statement | One error rolls back the whole operation | [Trigger Exceptions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_exceptions.htm) |
| Static variable scope | One transaction; persists across trigger invocations in it | [Static and Instance Methods and Variables](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_static.htm) |
| Sharing keywords on triggers | Not allowed; triggers typically run without sharing | [Sharing Keywords](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_keywords_sharing.htm) |

## Check yourself

- Why is Trigger.newMap unavailable in a before insert trigger?
- When should field updates on the triggering records go in a before trigger?
- Why does the one-trigger-per-object practice exist?
- How can a trigger remember across its own re-invocations in one transaction that work is done?

## Sources

- [Trigger and Bulk Request Best Practices](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_bestpract.htm)
- [Bulk Apex Triggers](https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_bulk)
- [Trigger Context Variables](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables.htm)
- [Context Variable Considerations](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables_considerations.htm)
- [Record-Triggered Automation decision guide](https://architect.salesforce.com/docs/architect/decision-guides/guide/record-triggered)
- [Triggers](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm)
- [Trigger Exceptions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_exceptions.htm)
- [Static and Instance Methods, Variables, and Initialization Code](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_static.htm)
- [Use the with sharing, without sharing, and inherited sharing Keywords](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_keywords_sharing.htm)
- [DescribeSObjectResult Class](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_sobject_describe.htm)
- [Custom Metadata Types](https://help.salesforce.com/s/articleView?id=platform.custommetadatatypes_overview.htm&type=5)
- [Get Started with Apex Triggers](https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_intro)

## About the full unit

The "Core concepts", "Exam traps" and "Worked scenario" sections of this unit, its 16 linked practice questions, and unlimited timed mock exams are included with the Platform Developer pack (USD $19): https://www.certifyforce.com/practice-exams
