This guide walks through 16 asynchronous @future methods for callouts and decoupling long-running work from triggers. 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.
Write a @future(callout=true) method that fires when a Case
is closed and POSTs the case details as JSON to an external REST endpoint.
@future(callout=true)Implement a Queueable Apex chain that processes large datasets by self-chaining — each execution enqueues the next batch until all records are processed.
This pattern overcomes the 50,000 record SOQL limit by processing in chunks.
Queueable and Database.AllowsCalloutsPublish a Platform Event (Order_Completed__e) from an Apex trigger
when an Order record's Status changes to Activated.
Write a subscriber trigger on the platform event that creates a follow-up Task
record for the account owner.
Order_Id__c (Text)Account_Id__c (Text)Amount__c (Number)Implement a Schedulable Apex class that runs nightly to delete
Case records that have been Closed for more than
90 days.
Schedulable interfaceStatus = 'Closed' and
ClosedDate < 90 days agoDatabase.delete(records, false) to allow partial successUse System.schedule() inside a separate method to register the job
for daily execution at midnight.
Build a Queueable Apex class that makes an HTTP callout and chains to another Queueable on success.
Requirements for ContactSyncQueueable:
Queueable and Database.AllowsCallouts interfacesList<Id> contactIds and Integer retryCountexecute():
'https://api.example.com/contacts/sync'Sync_Status__c = 'Synced'Sync_Status__c = 'Failed' on all contactsSystem.enqueueJob() for chainingWrite an Apex class AccountRatingAsync with a
@future method updateRatings(Set<Id> accountIds)
that queries each Account and sets:
AnnualRevenue > 1,000,000 → Rating = "Hot"AnnualRevenue >= 100,000 → Rating = "Warm"A @future method runs asynchronously and cannot accept SObject parameters — only primitives and collections of primitives (e.g. Set<Id>).
Write an Apex class WeeklyLeadDigest that implements
Schedulable. In its execute(SchedulableContext ctx)
method, query all Leads created in the last 7 days and send a weekly
digest email with the count to a sales address.
Schedulable interfaceCreatedDate >= LAST_N_DAYS:7Messaging.sendEmail with the lead count in the bodyWrite a Queueable Apex class ContactTaggerQueueable that:
List<Id> of Contact IDs in its constructorexecute(QueueableContext ctx), queries those Contacts
along with their Account's IndustryContact.Description to
"Industry: " + account.IndustryEvery problem above runs in a real in-browser Apex editor with instant pass/fail feedback and best-practice linting. No Salesforce org needed.
Create Free Account →Write a Batch Apex class StaleOpportunityBatch that closes
all open Opportunities whose CloseDate is in the past by
setting StageName = 'Closed Lost'.
Database.Batchable<SObject>start(): return a QueryLocator for open, past-close-date Oppsexecute(): update StageName to "Closed Lost"finish(): log completion with System.debugYour marketing operations team runs a nightly job to archive leads that have been
stuck in Status = 'Open' for more than 30 days without conversion.
Write a Batch Apex class LeadArchiverBatch that:
Lead records where Status = 'Open'
and CreatedDate <= 30 days agoStatus = 'Archived' on each lead in the batchfinish() using System.debugDatabase.Batchable<SObject>start(): use Database.getQueryLocatorexecute(): bulk-safe — collect changes in a list, single DML updatefinish(): System.debug a completion messageDate.today().addDays(-30) to compute the cutoff date.Database.executeBatch() and asserts status changed.After an integration runs, your team needs to asynchronously update a custom field
Tier__c on each Account based on its AnnualRevenue.
Write a Queueable Apex class AccountTierQueueable that accepts a
Set<Id> of account IDs in its constructor and, when executed:
AnnualRevenueTier__c based on revenue: <$50K → 'Bronze', <$250K → 'Silver',
<$1M → 'Gold', ≥$1M → 'Platinum'Queueable with void execute(QueueableContext ctx)Set<Id> accountIds@future when you need to pass SObject collections or chain jobs.System.enqueueJob(new AccountTierQueueable(ids)).Test.startTest() / stopTest() — the job executes synchronously in tests.When leads are inserted via an API integration, you need to asynchronously calculate and store a numeric score on each lead so the process does not slow down the synchronous transaction.
Write an Apex class LeadScoringService with a @future
method calculateScore(Set<Id> leadIds) that:
LeadSource and Industry on the given leadsLead_Score__c using this matrix:@future methods must be static, return void, and accept only primitive types or collections of primitives.if (!System.isFuture() && !System.isBatch()) guard.Write an Apex class ContactAddressSyncUtil with a
@future method syncMailingAddressFromAccount(Set<Id> accountIds)
that copies the Account's billing address to all its Contacts' mailing address fields.
@future, public static void.Set<Id>.Map<Id, Account> to avoid nested loops.BillingCity → MailingCity, BillingState → MailingState,
BillingPostalCode → MailingPostalCode, BillingCountry → MailingCountry.isEmpty().@future methods cannot accept SObject parameters — use Set<Id>.Write LeadEnrichmentUtil with a @future(callout=true) method
enrichLeadFromExternalAPI(Id leadId) that calls an external REST API
to enrich a Lead's company data.
@future(callout=true)leadId == null.HttpRequest: endpoint, method GET, Accept header, timeout 10 s.new Http().send(req) inside a try-catch(Exception e).JSON.deserializeUntyped, set
Company, Industry, LeadSource = 'API Enriched',
then update the Lead.System.debug.try-catch(Exception e) — network can fail.getStatusCode() == 200 before trusting the body.req.setTimeout(10000) — prevents governor limit hits.Write LeadScoreResetUtil with a @future method
resetLeadScoresForCampaign(Set<Id> campaignIds) that resets
Lead_Score__c to 0 for all Leads who are members of the given Campaigns.
@future annotation; parameter must be Set<Id>.campaignIds is null or empty.CampaignMember to find Lead Ids.Set<Id> leadIds, guard empty, then query Lead.Lead_Score__c = 0 on all found Leads.update in try-catch(DmlException e).leadIds.isEmpty() before the second SOQL to avoid empty IN clause.Write AccountCreditCheckUtil with a @future(callout=true)
method runCreditCheck(Id accountId) that calls an external credit API
and updates Credit_Score__c and Credit_Status__c on Account.
Credit_Status__c = 'Approved''Review''Declined'@future(callout=true); guard null accountId.CalloutException separately — set Credit_Status__c = 'Callout Error' and return early.Credit_Status__c = 'API Error'.try-catch(Exception e).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 ProblemsApexArena 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