All three run async, but they handle state differently — and that affects what is possible.
`@future` — stateless. Method signature accepts only primitive types and collections of primitives (Id, String, Integer). Cannot accept sObjects directly. Why: when the future fires, original sObjects may be stale. Salesforce avoids inconsistency by disallowing sObject parameters.
Queueable — stateful. The class is instantiated with member variables, and the entire instance state is preserved into the async execution. You can pass sObjects, custom Apex classes, complex types through the constructor.
apex public class MyJob implements Queueable { private List<Account> accs; public MyJob(List<Account> accs) { this.accs = accs; } public void execute(QueueableContext ctx) { for (Account a : accs) { /* */ } } }
Batch Apex — chunk-stateful with Database.Stateful marker. By default, each execute() call gets a fresh instance. With Database.Stateful, member variables persist across executes.
Implications:
- `@future` — limited usefulness when complex data needed. Fire-and-forget callouts and primitives.
- Queueable — modern choice for most async work. Pass any data, chain jobs, simpler to test.
- Batch — chunks large data with cross-chunk state via
Database.Stateful.
Recommendation: prefer Queueable as default async; reach for @future only for simple callouts (legacy patterns); use Batch for >10k records.
