- SalesforceChaCha
- Posts
- 💃 Apex Transactions Explained 🕺
💃 Apex Transactions Explained 🕺
Salesforce’s execution truth
Good morning, Salesforce Nerds! An Apex transaction is Salesforce’s fundamental unit of work.
Every meaningful data change happens inside one. 📥️
When a transaction starts, Salesforce creates an execution bubble and invites every relevant participant inside: triggers, record-triggered Flows, Apex, validation rules, workflow actions, and database operations.
A transaction begins whenever Salesforce needs to persist data.
That includes UI saves, API calls, record-triggered Flows, triggers firing, or Apex invoked by automation. 👈️
Once started, Salesforce tracks every field change and decision until it either commits or rolls everything back.
Here’s the architectural reframe that matters: Flow and Apex are peers, not layers stacked on top of each other. 💯
They execute within the same transactional boundary.
If one fails, they all fail. ❌ If one commits, they all commit. ✅
That single idea explains more “weird Salesforce behavior” than any debugging trick ever will.

TABLE OF CONTENTS
💃 Apex Transactions Explained 🕺
COMMIT OR VANISH
ALL OR NOTHING
Salesforce transactions are atomic. They follow a strict all-or-nothing rule. 📐
If an unhandled exception escapes execution, Salesforce will:
Rolls back every DML operation
Discards all field changes
Leaves the database exactly as it was
Handled exceptions behave differently.
If you catch an exception and continue, Salesforce assumes you know what you’re doing and keeps the transaction alive. 😅
Savepoint sp = Database.setSavepoint();
try {
insert records;
riskyOperation();
} catch (Exception e) {
Database.rollback(sp);
}This distinction is subtle and critical.
Many teams assume “an error occurred” equals “rollback happened.” 🤔
But, that’s only true if the exception is not handled.
Savepoints are your escape hatch when you want controlled failure without detonating the entire transaction. 💣️
WHO RUNS WHEN
THE EXECUTION PARADE
Salesforce executes automation in a precise and repeatable order. 🔄
Highlights, simplified:
Load record values
Before-save record-triggered Flows
Before triggers
Validation rules
After-save record-triggered Flows
After triggers
Workflow, processes, Flow actions
Commit
A few architectural consequences follow: 🏗️
Before-save Flows run before before-triggers
After-save Flows run after triggers
Autolaunched Flows invoked from Apex stay inside the same transaction
Everything above happens before commit
This is why moving logic between Flow and Apex can change outcomes even when the logic is identical. 🧠
Timing matters as much as code.
SCHRODINGER MEETS TRAFFIC
ISOLATION AND CONCURRENCY
Inside a transaction, Salesforce supports read-your-own-writes. ✍️
Insert a record, and you can immediately query it again.
insert acct;
Account a = [SELECT Id FROM Account WHERE Id = :acct.Id];That record exists, but only inside this specific transaction. ‼️
Outside the transaction, it does not. Until commit. 🤯
This means:
Other users cannot see it
Other transactions cannot query it
Other automations cannot depend on it
Salesforce behaves like a READ COMMITTED database.
No dirty reads. No half-finished data. ⛔️ This isolation protects consistency at massive scale.
Now, add concurrency. 😰
Salesforce does not provide implicit record locking for business logic.
Two transactions can:
Query the same “next available” record
Both believe they’re first
Both attempt updates
Congratulations, you’ve discovered a race condition. 🏁
Architects must design for this explicitly:
Use
FOR UPDATEwhen appropriateIntroduce queue or semaphore objects
Design idempotent logic
Avoid “SELECT then INSERT” assumptions
Salesforce optimizes for throughput, not serialized execution. Safety is a design responsibility. 🦺
NEW TRANSACTION, NEW RULES
ASYNC MEETS NEW REALITY
Asynchronous Apex always runs in a separate transaction: ✨
@futureQueueable
Batch (each execute chunk)
Scheduled Apex
Platform Event and CDC-triggered Flows
These execute after the original transaction commits.
They do not share: 🙅
Savepoints
Rollback behavior
Pending data visibility
This separation is intentional. 👈️
It gives you clean commit boundaries, retry safety, and eventual consistency.
It also means assumptions from synchronous logic no longer apply. ❎
Every async entry point is a fresh execution universe.
ARCHITECTURE, NOT HOPE
DESIGNING WITH INTENT
Once you internalize Salesforce’s transaction model, good architecture becomes easier:
Treat Flow and Apex as co-equal participants
Never assume visibility outside your transaction
Use savepoints deliberately
Design for concurrency, not luck
Push long-running work async
Prefer explicit coordination patterns over timing assumptions
Salesforce isn’t unpredictable. It’s consistent. 🔥
Teams struggle when they design as if transactions are suggestions instead of contracts.
Master transactions, and your automations stop being fragile. 💪
They become deliberate, scalable, and boring in the best possible way.
SOUL FOOD
Today’s Principle
"Transactions are the basic unit of correctness in a system."
and now....Salesforce Memes



What did you think about today's newsletter? |