The trigger goes on Account — the object whose record creation is the cause. Contact creation is the effect. Apex triggers fire on the object the event happens to, so the new-Account event must be observed on Account.
Why Account, not Contact
A common confusion: “the records I’m creating are Contacts, so the trigger goes on Contact, right?” No. The trigger fires in response to the event you care about — and the event is Account being inserted. Inside that trigger, you insert Contacts as a side effect. Contacts don’t have their own “parent was just inserted” event to listen for.
The trigger
trigger AccountAutoContacts on Account (after insert) {
List<Contact> toCreate = new List<Contact>();
for (Account a : Trigger.new) {
for (Integer i = 1; i <= 3; i++) {
toCreate.add(new Contact(
FirstName = 'Auto',
LastName = a.Name + ' Contact ' + i,
AccountId = a.Id
));
}
}
if (!toCreate.isEmpty()) {
insert toCreate;
}
}
Key points:
after insert, notbefore insert. We need the Account’s record Id to populate theAccountIdlookup on each Contact. Inbefore insert, Account Ids don’t exist yet.- One
insert toCreateat the end — not three per Account in a loop. Bulkified, even though the spec said “3 contacts per account.” Trigger.newis the typed list of just-inserted Accounts.
What if Data Loader inserts 200 Accounts?
Each Trigger.new invocation can carry up to 200 records. The code above scales linearly:
- 200 Accounts × 3 Contacts = 600 new Contacts.
- One
insertstatement, 600 rows of DML — well under the 10,000-row DML limit.
The same code would still work for a 50,000-row Data Loader insert: the platform fires the trigger in 250 chunks of 200, and each chunk inserts up to 600 Contacts on its own.
Production polish — delegate to a handler
The above is fine for an interview answer. In production, push the logic into a handler so the trigger file stays at one line:
trigger AccountTrigger on Account (after insert) {
new AccountTriggerHandler().run();
}
public class AccountTriggerHandler {
public void run() {
if (Trigger.isInsert && Trigger.isAfter) {
createDefaultContacts(Trigger.new);
}
}
private void createDefaultContacts(List<Account> accounts) {
List<Contact> toCreate = new List<Contact>();
for (Account a : accounts) {
for (Integer i = 1; i <= 3; i++) {
toCreate.add(new Contact(
FirstName = 'Auto',
LastName = a.Name + ' Contact ' + i,
AccountId = a.Id
));
}
}
if (!toCreate.isEmpty()) insert toCreate;
}
}
Could this be Flow instead?
In 2026, the configuration-first preference would actually steer this toward a Record-Triggered Flow on Account, after-save, that creates three Contact records. The benefits: no Apex test class required, admins can edit it, and it’s visible in Setup. The Apex answer remains correct as a fallback when:
- The 3-contact rule depends on complex logic that’s awkward in Flow.
- You need a guarantee of bulk performance under heavy data load.
- The team has standardised on Apex for parent → child creation.
What interviewers are really looking for
Stating “trigger on Account” is the table-stakes answer. The senior signal is: (1) explain why the trigger goes on the cause, not the effect, (2) name after insert specifically and explain that before would lack the AccountId, (3) bulkify with a single insert at the end, (4) acknowledge that Record-Triggered Flow could solve this without Apex. Mention error handling — wrap the insert in Database.insert(records, false) and surface addError on the parent if you want partial-success behaviour.
Verified against: Apex Developer Guide — Triggers. Last reviewed 2026-05-17.