Queueable Apex

Queueable Apex Practice Problems

9 free Salesforce Apex challenges · Full problem statements, best-practice notes, and step-by-step hints · Solve live on ApexArena

This guide walks through 9 Queueable Apex for chainable, stateful asynchronous processing. Each entry below includes the full problem statement, the governor-limit and best-practice constraints it's testing, and a numbered approach to solving it — then you can open the same problem in ApexArena's browser-based Apex editor and get instant pass/fail feedback against real test cases.

On this page
  1. 1. Queueable: Recalculate Opportunity Health Score
  2. 2. Queueable: Chained Jobs — Account Tier then Contact Tag
  3. 3. Queueable: Mass Opportunity Stage Update with Retry Logic
  4. 4. Scenario: Asynchronously Enrich a New Case With Account Health Data
  5. 5. Queueable Apex: Generate a Customer Risk Assessment After Data Import
  6. 6. Queueable Apex: Process Large Files in Sequential Chunks
  7. 7. Queueable Apex: Recalculate Dependent Records After Configuration Change
  8. 8. Queueable Apex: Execute Independent Processing Steps With Failure Isolation
  9. 9. Queueable Apex: Build a Job Status Tracker for Long-Running Processing
Medium QueueableAsync Apex

1. Queueable: Recalculate Opportunity Health Score

Problem #210 · Salesforce Apex Coding Challenge

Problem Statement

Write OpportunityHealthScoreQueueable that implements Queueable and recalculates a custom Health_Score__c field on a set of Opportunities.

Scoring Rules

  • +30 if Amount ≥ 50,000
  • +20 if CloseDate ≥ today (not in the past)
  • +25 if StageName is 'Proposal/Price Quote' or 'Value Proposition'
  • +50 if StageName is 'Closed Won'

Requirements

  • Implement Queueable.
  • Constructor accepts Set<Id> opportunityIds.
  • execute(QueueableContext context): guard empty input, query Opps, compute score, bulk update.

Best Practices

  • SOQL and DML outside loops.
  • Guard with isEmpty() before DML.
  • Enqueue: System.enqueueJob(new OpportunityHealthScoreQueueable(ids));
Approach
  • 1Class signature: public class OpportunityHealthScoreQueueable implements Queueable
  • 2execute() signature: public void execute(QueueableContext context)
  • 3Guard: if (opportunityIds == null || opportunityIds.isEmpty()) return;
  • 4Enqueue from trigger/class: System.enqueueJob(new OpportunityHealthScoreQueueable(myIds));
Hard QueueableAsync Apex

2. Queueable: Chained Jobs — Account Tier then Contact Tag

Problem #211 · Salesforce Apex Coding Challenge

Problem Statement

Implement the chained Queueable pattern with two classes:

  1. AccountTierQueueable — sets Revenue_Tier__c on Accounts, then chains to ContactTagQueueable.
  2. ContactTagQueueable — sets Contact_Segment__c = 'Processed' on related Contacts.

Requirements

  • Both classes implement Queueable.
  • Both accept Set<Id> accountIds via constructor.
  • AccountTierQueueable.execute(): update Revenue_Tier__c per scoring rules, then call System.enqueueJob(new ContactTagQueueable(accountIds)) guarded by !Test.isRunningTest().
  • ContactTagQueueable.execute(): set Contact_Segment__c = 'Processed' on all Contacts whose AccountId IN :accountIds.

Best Practices

  • Guard chaining with if (!Test.isRunningTest()) — you cannot enqueue from within a Queueable during test execution without wrapping in Test.startTest()/stopTest().
  • All SOQL and DML outside loops.
Approach
  • 1Both classes must implement Queueable and have matching constructors.
  • 2Chain with: if (!Test.isRunningTest()) { System.enqueueJob(new ContactTagQueueable(accountIds)); }
  • 3Test.isRunningTest() guard is essential — you cannot enqueue a job from within a Queueable in test context without startTest/stopTest.
  • 4In tests, call ContactTagQueueable directly to test it independently of the chain.
Hard QueueableAsync Apex

3. Queueable: Mass Opportunity Stage Update with Retry Logic

Problem #225 · Salesforce Apex Coding Challenge

Problem Statement

Write OpportunityStageUpdateQueueable that updates a batch of Opportunities to a target StageName. If the DML fails, re-enqueue itself up to MAX_RETRIES = 3 times.

Requirements

  • Implement Queueable.
  • Constructor: (List<Id> opportunityIds, String targetStage, Integer retryCount).
  • Declare private static final Integer MAX_RETRIES = 3.
  • execute(): guard empty input and blank stage; skip records already at the target stage; wrap DML in try-catch; on failure re-enqueue if retryCount < MAX_RETRIES guarded by !Test.isRunningTest().

Best Practices

  • Skip records that are already at the target stage — avoids unnecessary DML.
  • Log the attempt number in the error message for debugging.
  • Re-enqueue with retryCount + 1 — never modify the field directly before the retry call.
Approach
  • 1Null-safe retryCount in constructor: this.retryCount = retryCount != null ? retryCount : 0;
  • 2Guard blank stage: if (String.isBlank(targetStage)) return;
  • 3Skip already-correct records: if (opp.StageName != targetStage) { ... toUpdate.add(opp); }
  • 4Re-enqueue: System.enqueueJob(new OpportunityStageUpdateQueueable(opportunityIds, targetStage, retryCount + 1));
Hard Queueable Apex

4. Scenario: Asynchronously Enrich a New Case With Account Health Data

Problem #446 · Salesforce Apex Coding Challenge

Business Scenario

When a Case is created, support agents want to immediately see a snapshot of the related Account's tier and revenue directly on the Case — without a slow synchronous lookup blocking the save. Do the enrichment asynchronously via Queueable Apex.

Requirements

  • Write a class CaseEnrichmentQueueable implementing Queueable, constructed with a Set<Id> caseIds.
  • execute() re-queries the given Cases, collects their related Account Ids, and looks up each Account's Tier__c and AnnualRevenue.
  • Copies those values onto Account_Tier_Snapshot__c and Account_Revenue_Snapshot__c on each Case, bulk-updated.

Best Practices

  • Queueable jobs receive only serializable constructor state (like a Set<Id>) — never pass live SObject records expecting them to reflect the latest committed data; re-query inside execute() instead.
  • A Map<Id, Account> built from one bulk query gives O(1) lookups per Case instead of querying per record.
Approach
  • 1Re-query Cases by caseIds inside execute() — never assume constructor-passed data is still fresh by the time the async job runs.
  • 2Build a Map from one bulk query so each Case can look up its Account's data in O(1).
  • 3Guard c.AccountId != null and accountsById.containsKey(...) before reading account fields for a Case.
Hard Queueable Apex

5. Queueable Apex: Generate a Customer Risk Assessment After Data Import

Problem #597 · Salesforce Apex Coding Challenge

Problem Statement

Write a queueable class RiskAssessmentQueueable that, given a Set<Id> of Account Ids passed into its constructor, queries those accounts' AnnualRevenue and sets Risk_Level__c to 'High' when revenue is below 50,000, 'Medium' below 500,000, and 'Low' otherwise.

This runs after a bulk data import, so it must be bulk-safe for any number of Ids.

Approach
  • 1Store the Set passed to the constructor in a private field so execute() can use it.
  • 2implements Queueable — the execute(QueueableContext) method is where the real work happens.
Hard Queueable Apex

6. Queueable Apex: Process Large Files in Sequential Chunks

Problem #598 · Salesforce Apex Coding Challenge

Problem Statement

Write a queueable class ChunkProcessorQueueable that processes a List<String> of file line records in fixed-size chunks, chaining itself to process the next chunk until the whole list is done — never trying to process more than one chunk of work in a single transaction.

Constructor takes the full List<String> and a starting offset. CHUNK_SIZE is 200.

Approach
  • 1Slice out this chunk's range with a plain for-loop from the offset up to offset + CHUNK_SIZE (bounded by the list's actual size).
  • 2At the end of execute(), if there is more work left, enqueue a new instance of the SAME class with the next offset — only when not already running inside another queued job (System.isQueueable()).
Hard Queueable Apex

7. Queueable Apex: Recalculate Dependent Records After Configuration Change

Problem #599 · Salesforce Apex Coding Challenge

Problem Statement

Write a queueable class PricingRecalcQueueable that, given a discount percentage passed into its constructor, re-prices every Opportunity with IsClosed = false by applying that discount to Amount, storing the result in Discounted_Amount__c.

Approach
  • 1Store the discount percent from the constructor in a field, e.g. discountPercent.
  • 2discountedAmount = amount * (1 - discountPercent / 100).
Expert Queueable Apex

8. Queueable Apex: Execute Independent Processing Steps With Failure Isolation

Problem #600 · Salesforce Apex Coding Challenge

Problem Statement

Write a queueable class IsolatedStepRunnerQueueable that updates a list of independent Account Ids' Processed__c flag to true such that a DML failure on one record never prevents the others from being processed — without issuing DML inside a loop. Use bulk DML with partial-success mode (allOrNothing = false) and inspect the returned Database.SaveResult list to isolate exactly which records failed.

Track which Ids failed in a List<Id> field so Database.Stateful preserves it, and log every failure's message.

Approach
  • 1implements Queueable, Database.Stateful so the failedIds list survives (Queueable itself only runs once here, but Stateful is what the org would require if this were later split into chunks).
  • 2Database.update(records, false) runs ONE bulk DML statement and never throws for an individual bad record — that IS the real isolation mechanism, not a try/catch per record in a loop (which is itself the classic "DML in a loop" governor-limit anti-pattern).
  • 3The returned List is index-aligned with the records list you passed in, so results[i] corresponds to accountIds[i].
Expert Queueable Apex

9. Queueable Apex: Build a Job Status Tracker for Long-Running Processing

Problem #601 · Salesforce Apex Coding Challenge

Problem Statement

Write a queueable class TrackedImportQueueable that processes a batch of Lead Ids and records its own progress in a Job_Status__c record: Total_Count__c, Processed_Count__c, and Status__c ('Running' while working, 'Completed' once every Id has been processed).

The constructor accepts the Job_Status__c record's Id (already created by the caller) alongside the Lead Ids to process.

Approach
  • 1Store both the jobStatusId and the leadIds in constructor-assigned fields.
  • 2Update the Job_Status__c record ONCE at the end of execute() with the final counts and status — not once per lead.

Practice All 9 Problems Free

Write and run real Apex code right in your browser — instant pass/fail feedback, best-practice linting, and governor limit monitoring. No Salesforce org needed.

Create Free Account → Explore All Problems
More Practice Topics
About ApexArena

ApexArena is a free, browser-based Salesforce Apex coding practice platform covering every major topic tested on the Salesforce Platform Developer I (PD1) and Platform Developer II (PD2) certification exams. All problems run directly in your browser with instant pass/fail feedback, best-practice linting (SOQL in loops, DML in loops, empty catch blocks), and governor limit monitoring — no Salesforce Developer Edition org required.

Related tutorials: Apex Triggers · SOQL · Batch Apex · Interview Q&A · Governor Limits