Here’s a clean, realistic future method that handles the most common interview ask: re-query records by Id, transform them, and post to an external system.
The code
public class AccountSyncService {
/**
* Sync accounts to the downstream ERP system.
* Called from triggers, batch jobs, or controllers.
*/
@future(callout=true)
public static void syncToErp(List<Id> accountIds) {
if (accountIds == null || accountIds.isEmpty()) return;
// 1) Re-query for fresh data (future methods cannot take sObjects)
List<Account> accounts = [
SELECT Id, Name, AccountNumber, Industry, AnnualRevenue, BillingCountry
FROM Account
WHERE Id IN :accountIds
];
if (accounts.isEmpty()) return;
// 2) Build the payload
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:ERP_Credentials/v1/accounts/bulk');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setTimeout(60000);
req.setBody(JSON.serialize(accounts));
// 3) Send and handle response
try {
HttpResponse resp = http.send(req);
if (resp.getStatusCode() >= 200 && resp.getStatusCode() < 300) {
logSuccess(accounts);
} else {
logFailure(accounts, resp.getStatusCode(), resp.getBody());
}
} catch (CalloutException e) {
logFailure(accounts, 0, e.getMessage());
}
}
private static void logSuccess(List<Account> accs) {
List<Erp_Sync_Log__c> logs = new List<Erp_Sync_Log__c>();
for (Account a : accs) {
logs.add(new Erp_Sync_Log__c(
Account__c = a.Id,
Status__c = 'Success',
Synced_At__c = Datetime.now()
));
}
insert logs;
}
private static void logFailure(List<Account> accs, Integer status, String body) {
List<Erp_Sync_Log__c> logs = new List<Erp_Sync_Log__c>();
for (Account a : accs) {
logs.add(new Erp_Sync_Log__c(
Account__c = a.Id,
Status__c = 'Failed',
Error_Code__c = status,
Error_Body__c = body?.left(32000),
Synced_At__c = Datetime.now()
));
}
insert logs;
}
}
How a trigger invokes it
trigger AccountTrigger on Account (after insert, after update) {
Set<Id> idsToSync = new Set<Id>();
for (Account a : Trigger.new) {
Account old = Trigger.isUpdate ? Trigger.oldMap.get(a.Id) : null;
Boolean changed = Trigger.isInsert
|| a.Name != old.Name
|| a.AnnualRevenue != old.AnnualRevenue;
if (changed) idsToSync.add(a.Id);
}
if (!idsToSync.isEmpty()) {
AccountSyncService.syncToErp(new List<Id>(idsToSync));
}
}
The points an interviewer is checking
| Point | What it looks for |
|---|---|
static | Required — future methods cannot be instance methods |
void | Required — future methods cannot return values |
@future(callout=true) | Allows HTTP callouts |
List<Id> parameter | Avoids the sObject restriction |
| Re-query inside | Guarantees fresh data |
| Named Credential | No hardcoded secrets |
| Bulkified | One call for many records, not 200 calls |
| Logged outcomes | You can’t return the response, so persist it |
A minimal version
If they want it shorter:
public class HelloFuture {
@future
public static void sayHi(List<Id> ids) {
List<Contact> cs = [SELECT Id, FirstName FROM Contact WHERE Id IN :ids];
for (Contact c : cs) System.debug('Hi ' + c.FirstName);
}
}
Verified against: Apex Developer Guide — Future Methods. Last reviewed 2026-05-17.