@future was Apex’s first async mechanism and the design has aged. Most of its limitations are why Queueable was introduced. Knowing the full list is interview territory.
The full list
| Limitation | Detail |
|---|---|
| Void return only | Must return void. No way to receive a result back. |
| Static methods only | @future must be applied to a static method. |
| Primitive args only | Accepts primitives, Ids, and collections of primitives. No sObjects, no custom classes. |
| No chaining | A future method cannot call another @future method. |
| No invocation from Batch | Batch jobs cannot call @future methods. |
| 50 invocations per transaction | Hard cap on how many @future calls a single transaction can queue. |
| Daily org-wide limit | Up to 250,000 async invocations per 24 hours (or 200 × org licenses, whichever is greater). |
| No JobId monitoring | @future returns nothing. The AsyncApexJob row exists, but you have no JobId from the call site. |
| No callouts without annotation | Must declare @future(callout=true) for HTTP callouts. |
| Callout time limit | Maximum 120 seconds total for callouts inside the method. |
Not testable without Test.startTest | Future invocations run synchronously between Test.startTest() and Test.stopTest() — outside that block they don’t run during a test. |
The “no chaining” rule in practice
public class FutureChainTest {
@future
public static void first() {
second(); // throws System.AsyncException
}
@future
public static void second() { /* never runs */ }
}
The platform throws AsyncException: Future method cannot be called from a future or batch method. The fix is Queueable, which can call other Queueables.
The 50-per-transaction limit, explained
If a single transaction calls 51 @future methods (even different methods), the 51st throws LimitException: Too many future calls: 50. Bulkify by passing a Set<Id> to one method invocation rather than calling per record.
// BAD
for (Account a : Trigger.new) {
StripeService.push(a.Id); // 200 calls if Trigger.new has 200 records
}
// GOOD
StripeService.push(Trigger.newMap.keySet()); // 1 call
Why this matters for the interview
When the interviewer asks “what are the limitations of future methods?”, they’re often setting up the follow-up “so what would you use instead?” The answer is Queueable, and the reasons map one-to-one to the limitations above.
Limitation of @future | Queueable equivalent |
|---|---|
| No complex args | Accepts any serializable type |
| No chaining | System.enqueueJob from inside execute() chains the next job |
| No JobId | System.enqueueJob returns the JobId |
| No monitoring | Track via AsyncApexJob SOQL on JobId |
Common interview follow-ups
- Can
@futurebe called from a Schedulable? — Yes. Schedulable →@futureis allowed. - Can
@futurebe called from a Queueable? — Yes. Queueable →@futureis allowed. The block is@future→@futureand Batch →@future. - What’s the workaround for “future from future”? — Replace the inner future with a Queueable, or convert the whole flow to Queueable chaining.
Verified against: Apex Developer Guide — Future Methods, Future Method Limits. Last reviewed 2026-05-17.