Duplicate prevention in Salesforce is a layered defense, not a single setting. The interview answer that wins is one that names the layers, knows when each fires, and acknowledges the trade-offs.
The 60-second answer
For most orgs, the answer is: (1) Matching Rule to define what “duplicate” means, (2) Duplicate Rule to decide what happens — block vs warn — and against which user/record-type scope, (3) a Unique flag on the natural-key field for hard database-level enforcement, and (4) a trigger for cross-object or fuzzy logic Duplicate Rules can’t express. For data loads, run a matching-rule report beforehand and use Data Loader’s “Match using external ID” flow.
The four layers and when each fires
| Layer | When it fires | What it does | Limits |
|---|---|---|---|
| Unique field (database) | On insert/update | Hard block, all paths | Exact-match only, no fuzzy |
| Validation rule | On insert/update | Hard block, all paths | Only sees the current record; no SOQL cross-record |
| Matching + Duplicate Rule | On UI create/clone, optionally on API | Warn or block; allows override | Standard objects + custom; fuzzy match available |
| Apex trigger | On any insert/update | Anything you can code | You own the bulkification |
The right answer for a given scenario depends on where the duplicates come from: UI users, API integrations, data loads, or all three.
Layer 1 — Matching Rule + Duplicate Rule
This is the native, no-code answer and the one interviewers expect first.
Matching Rule = “what does duplicate mean here?” — for example, “Account is a duplicate if Name is fuzzy-matched AND Billing City is exact.” Use the standard matching rules for Accounts, Contacts, and Leads; build custom matching rules for custom objects.
Duplicate Rule = “what do we do about it?” — block, allow with alert, or allow with report. You can scope it to certain record types, certain users, or certain conditions.
Matching Rule (Account):
Name → Fuzzy Company Name
AND
BillingCity → Exact
Duplicate Rule:
Action on Create → Block + show alert
Action on Edit → Allow + show alert
Record-level security → Enforce sharing rules
Bypass duplicate alerts → checked for Data Loader profile
Gotcha: Duplicate Rules fire on UI insert by default. To enforce on API too, check “Run duplicate rules when records are saved via API” in the Duplicate Rule. Without this, Bulk API loads silently bypass duplicate detection.
Layer 2 — Unique field at the database level
Mark a custom field as Unique (case-sensitive or insensitive). The platform enforces this at the database tier — no user, no API, no trigger can bypass it. Best for true natural keys: external IDs, SKUs, email addresses on a Contact when business says no two contacts can share one.
Field: External_ID__c Type: Text(255) Unique: ✓ External ID: ✓
Gotcha: Unique + External ID together unlock Data Loader’s upsert flow — the loader matches on this key and updates instead of inserting duplicates. This is the cleanest answer for migration scenarios.
Layer 3 — Trigger for cross-object or fuzzy logic
When the dedup logic spans objects (e.g., “no two Opportunities for the same Account in the same month”) or needs SOQL the Matching Rule can’t express, a trigger is the right tool.
trigger PreventDuplicateOpps on Opportunity (before insert, before update) {
Set<Id> acctIds = new Set<Id>();
for (Opportunity o : Trigger.new) {
if (o.AccountId != null) acctIds.add(o.AccountId);
}
Map<Id, Set<String>> existingKeys = new Map<Id, Set<String>>();
for (Opportunity o : [
SELECT Id, AccountId, Name, CloseDate
FROM Opportunity
WHERE AccountId IN :acctIds
AND Id NOT IN :Trigger.newMap?.keySet()
]) {
Id k = o.AccountId;
String monthKey = o.CloseDate.year() + '-' + o.CloseDate.month();
if (!existingKeys.containsKey(k)) existingKeys.put(k, new Set<String>());
existingKeys.get(k).add(monthKey + ':' + o.Name);
}
for (Opportunity o : Trigger.new) {
String monthKey = o.CloseDate.year() + '-' + o.CloseDate.month();
Set<String> keys = existingKeys.get(o.AccountId);
if (keys != null && keys.contains(monthKey + ':' + o.Name)) {
o.addError('Duplicate opportunity for this account in this month.');
}
}
}
Notes: bulkify (one SOQL outside the loop), exclude records currently being updated (Trigger.newMap.keySet()), and never addError() after DML — do it in before context.
Layer 4 — Pre-load matching for bulk imports
For migrations, prevent duplicates before they enter the system:
- Profile the source data —
COUNT(*) GROUP BY emailto find dup candidates. - Run Salesforce’s “Find Duplicates” in the Matching Rule report.
- Use Data Loader upsert keyed on a Unique External ID — never
Insertfor migrated data. - Set the “Bypass duplicate alerts” permission for the migration user only if you’ve pre-cleaned the source.
Anti-patterns to avoid
- Validation rule with SOQL — validation rules can’t query other records. People sometimes attempt fake formulas with
VLOOKUP()— it works on Custom Settings only, not records. - Trigger that doesn’t exclude the current record set — query existing dups but forget to exclude
Trigger.newMap.keySet(); record matches itself on update, fires false positive. - Relying only on UI alerts — Bulk API silently bypasses Duplicate Rules unless “Run on API” is checked.
- Unique field with case sensitivity off when business uses mixed-case identifiers (turns
AcmeandACMEinto one record — usually wrong for SKUs, usually right for emails). - Letting trigger errors block a multi-row insert — use
addError()per record, notaddError()on the trigger.
How to answer in 30 seconds
“Layer the defenses. Native Matching + Duplicate Rules for fuzzy UI dedup, Unique fields with External ID for the database-level guarantee, an Apex trigger for cross-object or SOQL-heavy logic, and pre-load matching for migrations. Make sure Duplicate Rules are flagged to run on API, not just UI.”
How to answer in 2 minutes
Add the trigger pattern (bulkified SOQL, exclude current set), the Data Loader upsert flow keyed on External ID, the API-bypass gotcha on Duplicate Rules, and the difference between a Validation Rule (single-record) and a trigger (cross-record). Mention that Duplicate Rules respect sharing — a duplicate the user can’t see won’t be flagged unless “Enforce sharing rules” is off.
Likely follow-up questions
- What’s the difference between a Matching Rule and a Duplicate Rule?
- How would you find duplicates that already exist in production data?
- Can you write a Validation Rule that prevents duplicates?
- What happens to duplicate detection in a Bulk API 2.0 load?
- How would you merge duplicate Accounts programmatically?
Verified against: Salesforce Help — Duplicate Management, Matching Rules, SOAP API — Upsert. Last reviewed 2026-05-19.