Skip to main content

Command Palette

Search for a command to run...

@future: The Quick Fix — Async Apex

Not every async problem is a bulk problem. Sometimes you just need one operation out of the way — and @future is the simplest tool Salesforce gives you to do it.

Updated
5 min read
P
Software Developer trying to balance work and passion

Sometimes you don't have 50,000 records. You just have one operation that can't run right now — either because of where it's being called from, or because it needs to reach out to an external system. That's exactly the gap @future methods fill.


What is a @future Method?

@future is an annotation over a method that tells Salesforce to run it asynchronously — pushed out of the current execution context and processed in the background when resources are available. Typically used for long-running operations, callouts to external web services, or any operation you want isolated from the main transaction.


Signature

public with sharing class FutureDemo {
    @future
    public static void myFutureMethod() {
        // your operations
    }
}

Dos and Don'ts

Before writing a single line inside a @future method, know these rules cold:

  • ✅ Must always return void

  • ✅ Must be static

  • ✅ Can accept primitive types — String, Integer, Boolean, Id, List<Id> etc.

  • ❌ Cannot accept sObjects or any non-primitive type as a parameter

Why no sObjects? By the time the method is invoked and by the time it actually executes, the record could have been modified by something else. Your @future method would be holding stale data and could overwrite newer values when it runs. That's why developers pass Id instead and query the record fresh inside the method at runtime.


When Do You Actually Need It?

1. The Mixed DML Problem

This is the most common reason developers reach for @future without even realising it.

Salesforce doesn't allow you to modify a setup object and a non-setup object in the same transaction.

  • Setup objects: User, UserRole, Profile, Group and similar

  • Non-setup objects: Account, Contact, Lead and similar

Try it and you get:

System.DmlException: MIXED_DML_OPERATION

Without @future — this throws the error:

// Setup Object — Insert User
Profile p = [SELECT Id FROM Profile WHERE Name='Standard User' LIMIT 1];
UserRole r = [SELECT Id FROM UserRole LIMIT 1];
User u = new User(
    Alias = 'testu', Email='testuser@example.com',
    EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
    LocaleSidKey='en_US', ProfileId = p.Id, UserRoleId = r.Id,
    TimeZoneSidKey='America/Los_Angeles',
    UserName='testuser' + DateTime.now().getTime() + '@test.com'
);
insert u;

// Non-Setup Object — Insert Coupon
Promotion pr = [SELECT Id FROM Promotion LIMIT 1];
Coupon c = new Coupon(CouponCode = 'ERROR_TEST', PromotionId = pr.Id);
insert c; // BOOM — Mixed DML error

With @future — fixed:

public class FutureDemo {
    @future
    public static void createUser() {
        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User' LIMIT 1];
        UserRole r = [SELECT Id FROM UserRole LIMIT 1];
        User u = new User(
            Alias = 'testu', Email='testuser@example.com',
            EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
            LocaleSidKey='en_US', ProfileId = p.Id, UserRoleId = r.Id,
            TimeZoneSidKey='America/Los_Angeles',
            UserName='testuser' + DateTime.now().getTime() + '@test.com'
        );
        insert u;
    }
}
// Non-Setup Object runs now
Promotion pr = [SELECT Id FROM Promotion LIMIT 1];
Coupon c = new Coupon(CouponCode = 'ERROR_TEST', PromotionId = pr.Id);
insert c;

// Setup Object pushed to background — separate transaction
FutureDemo.createUser();

The two DML operations now run in separate transactions. No error.

2. Callouts to External Systems

By default Salesforce doesn't allow HTTP callouts from triggers or certain other contexts. Adding callout=true to the annotation unlocks it.

@future(callout=true)
public static void syncWithExternalSystem() {
    HttpRequest req = new HttpRequest();
    req.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
    req.setMethod('GET');
    Http http = new Http();
    try {
        HttpResponse res = http.send(req);
        System.debug('Response: ' + res.getBody());
    } catch(Exception e) {
        System.debug('Callout failed: ' + e.getMessage());
    }
}

Before running this — make sure the endpoint is added in Setup → Remote Site Settings. Salesforce only allows callouts to endpoints explicitly authorised there.


Limits

Three specific ones worth knowing:

  1. You can call a @future method from a regular synchronous method — but you cannot call another async method from inside a @future method. No chaining whatsoever.

  2. During test execution, @future methods don't run immediately. They behave asynchronously until Test.stopTest() is called — at that point all queued async calls execute synchronously. Keep this in mind when writing test classes.

  3. In a 24-hour period, Salesforce allows either 250,000 @future method calls or (number of user licences × 200) — whichever is greater.


Seeing It Run — Apex Jobs

Once it fires track it in Setup → Apex Jobs. Status will show Queued then Completed. Unlike Batch Apex there's no chunk breakdown — it's a single job entry per method call.


That's @future Methods

Simple annotation, two real use cases, one hard limit on chaining — and a very good reason never to pass sObjects as parameters.

When you hit a Mixed DML wall or need to fire a callout from a trigger, this is your first reach.

Async Apex — From the Ground Up

Part 3 of 6

A series revisiting Asynchronous Apex from first principles. Covers the why behind Governor Limits, and walks through all four async tools — Future Methods, Batch Apex, Queueable Apex, and Scheduled Apex — with real examples, gotchas, and the things most tutorials skip. Written by a developer, for developers who want to actually understand what's happening under the hood.

Up next

Scheduled Apex: The Alarm Clock

Batch handles volume. @future handles one-offs. But when you need something to run every Monday at 8 AM automatically — that's Scheduled Apex.