← Back to Field Notes

Duplicate Management Rules That Don't Annoy Users

·9 min read

The first time a duplicate rule cost me a weekend, it wasn't even my rule. A marketing team flipped their Lead duplicate rule from Alert to Block on a Thursday afternoon, ahead of a campaign, because someone in a governance meeting had decided that duplicates were "unacceptable". Web-to-lead kept posting. The integration user kept getting DUPLICATE_VALUE errors it was never built to handle. By Monday we'd quietly dropped somewhere north of 2,000 inbound leads that never made it into the org, and the marketing lead wanted to know why the campaign "didn't work".

Native duplicate management is one of the few data-quality features Salesforce ships that actually works well out of the box. The problem is almost never the engine. It's the configuration — rules that block the person least able to fix the problem, fuzzy matching that fires on records that aren't duplicates, and alerts so frequent that users learn to click through them without reading. This is what I've settled on after tuning duplicate management across a dozen or so orgs.

Matching Rules Are Where the Whole Thing Lives or Dies

People conflate the two objects, so it's worth being precise. A matching rule decides what counts as a duplicate. A duplicate rule decides what to do about it. Almost every complaint I've ever heard about duplicate management — "it blocks me constantly", "it never catches the obvious ones" — traces back to a matching rule that was set up once and never looked at again.

The single biggest mistake is using fuzzy matching on everything. Fuzzy is brilliant for company names and personal names, where "Bob" and "Robert" or "Qantas Airways" and "Qantas Airways Ltd" should collapse together. It's terrible on fields where a one-character difference is meaningful. I've seen a matching rule use fuzzy matching on an address field and merge two genuinely different sites because the street numbers were close. Reserve fuzzy for the fields that actually vary in the wild, and use exact matching everywhere the value should be identical or nothing.

Here's a matching rule I'd trust on an Account. Fuzzy on the name, exact on the website domain, and blanks explicitly excluded from matching so a record with an empty website doesn't collide with every other blank-website record in the org.

<?xml version="1.0" encoding="UTF-8"?>
<MatchingRules xmlns="http://soap.sforce.com/2006/04/metadata">
    <matchingRules>
        <fullName>Account_Name_And_Domain</fullName>
        <label>Account Name And Domain</label>
        <matchingRuleItems>
            <blankValueBehavior>NullNotAllowed</blankValueBehavior>
            <fieldName>Name</fieldName>
            <matchingMethod>FuzzyCompanyName</matchingMethod>
        </matchingRuleItems>
        <matchingRuleItems>
            <blankValueBehavior>NullNotAllowed</blankValueBehavior>
            <fieldName>Website</fieldName>
            <matchingMethod>Exact</matchingMethod>
        </matchingRuleItems>
        <ruleStatus>Active</ruleStatus>
    </matchingRules>
</MatchingRules>

The NullNotAllowed on both fields is the detail that saves you. The default behaviour treats blanks as matchable, which is how you end up with a duplicate rule that flags every prospect who hasn't filled in a website yet. Set it deliberately on every field, every time.

Alert Beats Block Almost Every Time

This is the opinion I'll defend hardest. Default your duplicate rule action to Alert, not Block. Block is the setting that feels responsible in a governance meeting and causes damage in production.

The reasoning is about who gets punished. When a duplicate rule blocks a save, it stops the person creating the new record — and that person is almost never the one who created the original mess. A sales rep entering a lead they were handed at an event doesn't know, and shouldn't have to know, that someone in another business unit loaded a near-identical account eight months ago. Block turns a data-quality problem that belongs to the organisation into a wall in front of one user trying to do their job. Alert shows them the potential match, lets them decide, and records the decision. In my experience you catch the vast majority of real duplicates that way, without the collateral damage.

Block earns its place in exactly two situations. The first is where you have a genuine unique key — a government identifier, a membership number, an external ID from a system of record — and a second record with that key is definitionally wrong. The second is regulatory, where creating a duplicate has a compliance consequence and a hard stop is the point. Outside those, if you're reaching for Block, you're usually trying to fix a process problem with a technical barrier, and the barrier loses.

Show the Duplicate, Don't Just Deny It

An alert that says "this might be a duplicate" and nothing else is barely better than a block. Users can't act on information they can't see. The duplicate rule lets you choose which fields display on the matched record in the alert, and this is the part most admins skip. Show the fields a human needs to make the call — the account owner, the created date, the last activity, the record status. A rep will happily abandon a duplicate lead when they can see the original is owned by a colleague and was touched last week. They'll fight you when all they see is a red banner.

The other half of "don't just deny it" is giving people a path to the real record. If your alert surfaces a match, make sure the matched record is actually reachable — that the user has at least read access through your sharing model. I've watched a duplicate rule flag a match to a record the user couldn't open, which is the most frustrating possible outcome: told you're wrong, denied the ability to see why.

The Bypass Every Integration Needs

Interactive duplicate rules and automated data loads are a bad combination, and the marketing weekend I opened with is the standard way you find out. Any integration user or bulk process that writes records needs a deliberate answer to duplicate rules, not the default behaviour.

For inserts you control from Apex, you can allow the save explicitly rather than letting the rule reject the DML:

Database.DMLOptions dml = new Database.DMLOptions();
dml.DuplicateRuleHeader.AllowSave = true;

List<Lead> inbound = buildLeadsFromPayload(payload);
Database.SaveResult[] results = Database.insert(inbound, dml);

That doesn't mean you've given up on quality — it means you've moved the decision to a place where you can handle it properly instead of throwing an unhandled error back at a webhook. Before the insert, or on a schedule, run the matching yourself and route the likely duplicates somewhere a human reviews them:

List<Datacloud.FindDuplicatesResult> matches =
    Datacloud.FindDuplicates.findDuplicates(inbound);

for (Datacloud.FindDuplicatesResult res : matches) {
    for (Datacloud.DuplicateResult dup : res.getDuplicateResults()) {
        for (Datacloud.MatchResult match : dup.getMatchResults()) {
            if (!match.getMatchRecords().isEmpty()) {
                // flag for review rather than blocking the load
                queueForStewardReview(match.getMatchRecords());
            }
        }
    }
}

Datacloud.FindDuplicates runs your active matching rules against records without saving anything, which makes it the right tool for pre-screening a load or building a review queue. If you're calling this inside an Apex trigger, keep it in the handler where the rest of your dedup logic lives — I keep this sort of thing in a dedicated service class behind the trigger framework rather than sprinkled through the trigger itself. And when you're screening large volumes, watch your query shape; the same discipline I cover in SOQL query patterns that won't kill your governor limits applies to any batch matching you write by hand.

Tune the Rules by Watching What They Actually Do

Duplicate management is not a set-and-forget feature, and the good news is that it tells you how it's doing if you ask. Every time a duplicate rule fires and a user proceeds anyway, Salesforce writes a DuplicateRecordSet with DuplicateRecordItem children. Report on those. A rule that fires 400 times a month and gets overridden 380 times isn't protecting your data — it's training your users to ignore warnings.

On one org I tuned, the Contact matching rule was fuzzy on both first and last name, and it was flagging something like 35% of new contacts, almost all of them false positives — different people who happened to share a common surname. We tightened it to exact on last name plus fuzzy on first name plus exact on email domain, and the flag rate dropped to under 5% while the real catch rate went up, because users started trusting the alerts again. I won't pretend it was a clean win: we never fully solved the contacts who share an email address, like a couple using one household address, and in the end we added a manual monthly review for those and moved on. Two years later that review still runs.

When Native Duplicate Management Isn't Enough

The honest limits are worth stating plainly. Native duplicate management fires at create and edit time only — it does nothing about the duplicates already sitting in your org. If you inherit a database with years of accumulated mess, matching rules won't clean it; you need a separate dedup and merge exercise, and if that mess arrived via a migration, that's a data-loading problem you should have caught at the door, which is a whole discipline of its own that I've written about in the data migration playbook.

The other ceiling is cross-object and scale. Native matching works within a single object, so if your definition of a duplicate spans a Lead and an existing Account, or you need matching across millions of records with survivorship rules about which value wins, you're into custom Apex or a dedicated tool like a dedup app from the AppExchange. That's a real cost, and I only reach for it when the native feature has genuinely run out of room — which, tuned properly, is later than most people assume. If you want a second pair of eyes on a rule set before you decide it can't do the job, that's the kind of review I do.

A blocking duplicate rule makes your data look clean by making your org hard to use. A well-tuned alert makes the org easy to use and lets your data get clean on its own, one honest decision at a time. I know which one I'd rather explain to the sales team on a Monday.

Liked this?

New essays in your inbox when I publish. No cadence, no spam.