Data as of Aug 25, 2026 · Based on 319 AI responses from ChatGPT Search and Google AI Mode · See how Parse measures this
Brands AI recommends here
Named in 39% of answers
Named in 36% of answers
Named in 30% of answers
Named in 21% of answers
As a developer, treat CRM data quality as a **system-design problem**, not a cleanup task. The goal is to make invalid or duplicate data difficult to create in the first place, while continuously detecting anything that slips through. ## 1. Define what “duplicate” means Don't rely on a single field like `name`. Build…
As a developer, treat CRM data quality as a system-design problem, not a cleanup task. The goal is to make invalid or duplicate data difficult to create in the first place, while continuously detecting anything that slips through.
Don't rely on a single field like name. Build matching rules around the entity.
For example:
Think in terms of:
exact match → almost certainly duplicate
strong match → probably duplicate
fuzzy match → possible duplicate, needs review
no match → create normally
CRM platforms themselves commonly use a combination of matching rules and duplicate rules, rather than one simplistic equality check.
Two records shouldn't be considered different merely because their formatting differs.
For example:
" [email protected] "
"[email protected]"
should normalize to the same value.
Typical normalization:
def normalize_email(email):
return email.strip().lower()
def normalize_phone(phone):
return digits_only(phone) # ideally normalize to E.164
def normalize_name(name):
return collapse_whitespace(name.strip().lower())
def normalize_domain(domain):
domain = domain.strip().lower()
return domain.removeprefix("www.")
Do the normalization before matching, and preferably store a canonical/normalized representation so you don't have to repeatedly recompute it.
Be careful with aggressive normalization, though. For example, don't assume that removing punctuation or modifying email addresses is always semantically safe.
This is one of the most important protections for API integrations.
Instead of:
POST /contacts
blindly creating a contact every time, establish an external identity:
source_system = "salesforce"
external_id = "003xxxxxxxx"
and enforce:
UNIQUE(source_system, external_id)
Then your integration can safely retry:
receive event
↓
check source + external ID
↓
already exists? → update
doesn't exist? → create
This prevents duplicates caused by retries, webhook redelivery, batch jobs, and race conditions.
Application-level checks alone aren't enough.
This is unsafe:
if not contact_exists(email):
create_contact(email)
Two workers can execute the check simultaneously:
Worker A: doesn't exist
Worker B: doesn't exist
Worker A: INSERT
Worker B: INSERT
Instead, enforce uniqueness at the database level:
CREATE UNIQUE INDEX ux_contact_email
ON contacts (normalized_email);
Then handle the constraint violation or use an atomic upsert:
INSERT INTO contacts (...)
VALUES (...)
ON CONFLICT (normalized_email)
DO UPDATE SET
name = EXCLUDED.name;
For more complex CRM matching, you may not be able to make the entire fuzzy rule a database constraint—but you should make hard identifiers unique wherever possible.
Every create/update endpoint that can be retried should ideally be idempotent.
For example:
PUT /customers/{external_customer_id}
is generally safer for integrations than:
POST /customers
where the caller has no way to indicate that the customer already exists.
For asynchronous systems, also consider an idempotency table:
idempotency_key
request_hash
result_id
created_at
so the same request can safely be processed multiple times.
Don't allow every application to write arbitrary CRM data.
Create a validation layer that checks things such as:
required fields
valid email
valid phone
valid country/state
allowed enum values
valid dates
maximum lengths
referential integrity
business rules
Prefer controlled values over free text:
❌ "Canada", "CA", "canada", "CAN"
✅ country_code = "CA"
This is especially important because inconsistent fields make later matching and reporting much harder. Salesforce and HubSpot both recommend validation and standardized fields as part of data-quality controls.
Don't automatically merge based on something like:
first_name + last_name
because:
John Smith
could legitimately represent hundreds of people.
Instead, calculate a match score:
email exact +100
phone exact +70
company domain exact +50
name similarity +30
address similarity +20
Then establish thresholds:
≥ 100 → automatic duplicate
70–99 → block/warn
40–69 → review queue
< 40 → probably unrelated
The actual values should be determined from your data and false-positive/false-negative costs.
CRM systems such as Salesforce support configurable matching criteria, including fuzzy matching, rather than requiring exact equality.
Even excellent duplicate detection fails if two requests arrive simultaneously.
For high-risk operations, use one or more of:
For example:
Request A ─┐
├──> atomic upsert(customer_id)
Request B ─┘
rather than:
Request A → SELECT → INSERT
Request B → SELECT → INSERT
Duplicates will still happen. Make merging deterministic.
Define:
primary record
secondary record(s)
field survivorship rules
relationship reassignment
activity/history handling
audit trail
rollback strategy
For example:
Email:
primary if verified
otherwise newest verified value
Phone:
verified > unverified
Address:
newest validated address
Owner:
preserve primary owner
Activities:
move all to primary
External IDs:
preserve mappings from both records
Never simply delete the duplicate. You can lose relationships, activities, integration mappings, or audit information.
Salesforce, for example, supports duplicate sets, reports, and merge workflows specifically to manage this process.
Create automated checks for:
duplicate rate
invalid email rate
missing required fields
orphaned relationships
stale records
failed integrations
conflicting customer identities
records created without external IDs
merge frequency
For example:
SELECT normalized_email, COUNT(*)
FROM contacts
GROUP BY normalized_email
HAVING COUNT(*) > 1;
Put these metrics on a dashboard and alert when they cross thresholds.
Also run periodic reconciliation jobs. Data naturally degrades over time, so prevention alone isn't sufficient.
For a modern CRM integration, I'd aim for this:
External systems
│
▼
API / ingestion layer
│
├── schema validation
├── normalization
├── authentication
└── idempotency
│
▼
Identity / deduplication service
│
├── exact identifiers
├── fuzzy matching
└── confidence score
│
▼
Transactional database
│
├── unique constraints
├── foreign keys
└── audit fields
│
▼
CRM
│
├── validation rules
├── duplicate warnings/blocking
└── controlled user input
│
▼
Data-quality monitoring
│
├── duplicate reports
├── reconciliation jobs
└── remediation/merge queue
Prevent → detect → resolve → measure.
Don't build a giant nightly deduplication script and call the problem solved. Put identity, validation, uniqueness, and idempotency into the write path, then use periodic detection and reconciliation as a safety net. That's much more scalable and keeps bad data from propagating into downstream analytics and integrations.
As a developer, ensuring data quality and preventing duplicates in a CRM requires a multi-layered defense strategy—`handling it at the database, application, and integration layers` . Here is how you can systematically lock down your CRM data integrity: - Database Constraints & Schema Enforcement: Enforce uniqueness…
As a developer, ensuring data quality and preventing duplicates in a CRM requires a multi-layered defense strategy—handling it at the database, application, and integration layers . Here is how you can systematically lock down your CRM data integrity:
If you'd like, let me know:
I can give you a more targeted architectural blueprint or code patterns for your stack.
No sources cited
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach. As a developer, you shouldn't just rely on out-of-the-box UI rules; you need to enforce integrity at the database, application, and pipeline levels. Here is a structured blueprint to bulletproof your CRM data: 1.…
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach. As a developer, you shouldn't just rely on out-of-the-box UI rules; you need to enforce integrity at the database, application, and pipeline levels.
Here is a structured blueprint to bulletproof your CRM data:
Never trust that the client-side application or user input will send clean data.
first_name, last_name , and company_domain).Exact matching is rarely enough because users mistype names or use different email aliases.
Inbound webhooks, marketing automation tools (like HubSpot or Marketo), and CSV imports are primary sources of dirty data.
Data hygiene degrades over time, so automation is key.
If you'd like, let me know:
I can dive deeper into specific implementation patterns for your setup.
No sources cited
As a developer, treat CRM data quality as an **application integrity problem**, not just a cleanup task. The strongest approach is to prevent bad records at ingestion, make duplicate detection deterministic where possible, and continuously monitor quality. ## 1. Define what “duplicate” means Create explicit matching…
As a developer, treat CRM data quality as an application integrity problem, not just a cleanup task. The strongest approach is to prevent bad records at ingestion, make duplicate detection deterministic where possible, and continuously monitor quality.
Create explicit matching rules for each CRM object.
For example:
Avoid using names alone as a unique identifier—two legitimate people can have the same name.
CRM platforms such as Salesforce and HubSpot use combinations of matching criteria and configurable duplicate rules rather than relying solely on exact record equality.
Never compare raw user input directly.
For example:
" [email protected] "
↓
"[email protected]"
Typical normalization:
Inc, Inc., Incorporated)Then perform matching against the normalized representation.
A useful database pattern is to retain both:
email = "[email protected]"
email_normalized = "[email protected]"
That lets you preserve the user's original data while making matching reliable.
This is one of the most important developer practices.
Don't rely exclusively on:
if not exists(email):
create_contact()
Two requests can execute that check simultaneously.
Instead, use a unique constraint/index wherever the business rule genuinely requires uniqueness:
CREATE UNIQUE INDEX ux_contact_email
ON contacts(email_normalized);
Then handle the resulting conflict gracefully.
For identifiers that are unique only within a tenant/account, use a composite constraint:
UNIQUE (tenant_id, external_customer_id)
The database should be the final line of defense.
This is especially important for CRM integrations, webhooks, imports, and background jobs.
Give external systems a stable identifier:
source = "stripe"
external_id = "cus_12345"
Then make:
(source, external_id)
unique.
Your integration becomes:
record = find_by(source, external_id)
if record exists:
update(record)
else:
create(record)
Even better, use an upsert where your database/CRM supports it.
This prevents retries from creating multiple copies of the same customer.
Don't validate only the UI.
CRM data can enter through:
Put critical validation in a shared service/domain layer so every path gets the same rules.
For example:
validate_contact(input)
normalize_contact(input)
check_duplicate(input)
upsert_contact(input)
Required fields, formats, enumerations, and business rules should be enforced at the system boundary. Data-quality guidance from Salesforce similarly recommends validation at entry and regular auditing rather than relying solely on cleanup.
Don't automatically merge everything that looks similar.
A useful model:
Email exact match = 100
Phone exact match = 80
Name + company exact match = 60
Fuzzy name + company = 40
Same company domain = 30
Then:
score >= 90 → automatically merge/block
70–89 → flag for review
<70 → allow
The exact thresholds depend on your data.
This is safer than aggressive fuzzy matching, which can accidentally combine two legitimate customers.
When duplicates are merged, decide which data wins.
For example:
email → verified value wins
phone → most recently verified value
job_title → most recently updated value
created_at → earliest value
owner → explicit business rule
consent → most restrictive applicable value
Don't let the merge process arbitrarily choose one record.
Document these rules before running large-scale deduplication.
For APIs, I'd implement:
Request
↓
Schema validation
↓
Normalization
↓
Idempotency check
↓
Duplicate detection
↓
Business validation
↓
Transactional upsert
↓
Audit/event log
For bulk imports, don't insert directly into production tables.
Use a staging pipeline:
CSV/API
↓
staging table
↓
validation
↓
normalization
↓
deduplication
↓
review/rejection
↓
production CRM
This makes large imports much safer.
Create dashboards/alerts for things like:
For example:
duplicate_rate < 0.5%
email_validity > 98%
required_field_completeness > 99%
integration_failure_rate < 0.1%
Set thresholds appropriate to your business.
Continuous monitoring matters because CRM data naturally becomes stale and inconsistent over time; Salesforce recommends scheduled health checks alongside real-time prevention.
For every merge or significant correction, record:
who
what changed
when
why
source
old value
new value
For example:
{
"operation": "merge",
"survivor_id": "C123",
"merged_id": "C987",
"reason": "exact_email_match",
"actor": "dedupe-service",
"timestamp": "..."
}
This makes mistakes reversible and gives you confidence when automating deduplication. Some CRM platforms provide duplicate-management history/audit capabilities for exactly this reason.
If I were designing a CRM integration from scratch, I'd aim for:
┌──────────────┐
Web/API ─────────►│ │
Import ──────────►│ Data Quality │
Webhook ─────────►│ Gateway │
Integration ─────►│ │
└──────┬───────┘
│
┌──────────▼──────────┐
│ Normalize + Validate│
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Idempotency / Match │
└──────────┬──────────┘
│
┌─────▼─────┐
│ Upsert │
└─────┬─────┘
│
┌──────────▼──────────┐
│ CRM / Source of │
│ Truth │
└─────────────────────┘
The key principle: don't build a giant deduplication job and hope it keeps the CRM clean. Prevent duplicates at write time, enforce hard uniqueness in the database, make integrations idempotent, and continuously measure data quality. That's much more scalable than periodic cleanup.
Document these rules before running large-scale deduplication.
For APIs, I'd implement:
Request
↓
Schema validation
↓
Normalization
↓
Idempotency check
↓
Duplicate detection
↓
Business validation
↓
Transactional upsert
↓
Audit/event log
For bulk imports, don't insert directly into production tables.
Use a staging pipeline:
CSV/API
↓
staging table
↓
validation
↓
normalization
↓
deduplication
↓
review/rejection
↓
production CRM
This makes large imports much safer.
Create dashboards/alerts for things like:
For example:
validate_contact(input)
normalize_contact(input)
check_duplicate(input)
upsert_contact(input)
Required fields, formats, enumerations, and business rules should be enforced at the system boundary. Data-quality guidance from Salesforce similarly recommends validation at entry and regular auditing rather than relying solely on cleanup.
Don't automatically merge everything that looks similar.
A useful model:
As a developer, treat CRM data quality as an **engineering constraint**, not just a cleanup task. The strongest approach is to prevent bad data at ingestion, enforce invariants in the backend, and continuously monitor what gets through. ### 1. Define what “good data” means For each CRM object—contacts, accounts,…
As a developer, treat CRM data quality as an engineering constraint, not just a cleanup task. The strongest approach is to prevent bad data at ingestion, enforce invariants in the backend, and continuously monitor what gets through.
For each CRM object—contacts, accounts, opportunities, etc.—define rules for:
Put these rules in a data dictionary/schema rather than relying on tribal knowledge.
Prefer immutable identifiers over names.
For example:
contact_id → internal UUID
external_id → ID from your source system
email_normalized → normalized email
If an upstream system provides a customer ID, make that your idempotency/deduplication key whenever possible.
At the database layer, enforce uniqueness with actual constraints rather than merely checking in application code. A database UNIQUE constraint prevents concurrent requests from creating the same value.
CREATE UNIQUE INDEX ux_customer_external_id
ON customers(external_system, external_id);
Don't compare raw strings:
[email protected]
[email protected]
Normalize them first:
def normalize_email(email):
return email.strip().lower()
Similarly, normalize phone numbers to a canonical international format, standardize country/state codes, and normalize company domains.
Normalization improves matching; Microsoft specifically recommends it as part of customer deduplication.
Don't rely on one fuzzy rule.
A practical hierarchy is:
High confidence
external_customer_id == external_customer_id
High confidence
normalized_email == normalized_email
Medium confidence
normalized_phone + company_id
Lower confidence
name + company + address
Avoid using name alone—different people can legitimately have the same name. Microsoft's guidance makes the same point when describing matching rules.
CRM platforms use this same general concept: Salesforce, for example, separates matching rules from duplicate-handling rules.
This is particularly important for APIs, webhooks, imports, and integrations.
Instead of:
POST /contacts
always meaning "create another contact," give events a unique ID:
{
"event_id": "evt_12345",
"customer_id": "cust_987",
"email": "[email protected]"
}
Store event_id and reject/replay safely if you've already processed it.
This prevents retries such as:
Webhook arrives
→ timeout
→ sender retries
→ CRM creates second record
Don't assume your UI is the only entry point.
Validate data coming from:
A good architecture is:
┌─ Web UI
├─ API
├─ Import
└─ Integration
│
▼
┌─────────────────┐
│ Normalize │
│ Validate │
│ Deduplicate │
│ Idempotency │
└────────┬────────┘
▼
┌─────────────────┐
│ CRM / Database │
│ UNIQUE constraints
└─────────────────┘
The key is defense in depth: frontend validation improves UX, but backend/database enforcement protects the actual data.
I'd use confidence tiers:
| Match | Action |
|---|---|
| Exact external ID | Auto-link |
| Exact normalized email | Auto-link/merge according to policy |
| Phone + company match | Flag/review |
| Name + company similarity | Flag/review |
| Name alone | Don't merge |
For automatic merges, define survivorship rules beforehand—for example, which phone number, owner, lifecycle stage, or address wins when records conflict.
Create metrics such as:
duplicate_rate
missing_email_rate
invalid_email_rate
missing_company_rate
stale_contact_rate
orphaned_record_rate
failed_validation_rate
Then monitor them over time.
For example:
SELECT
COUNT(*) AS total_contacts,
COUNT(*) FILTER (WHERE email IS NULL) AS missing_email,
COUNT(*) FILTER (
WHERE email IS NOT NULL
GROUP BY LOWER(TRIM(email))
HAVING COUNT(*) > 1
) AS duplicate_emails
FROM contacts;
You want dashboards and alerts—not a six-month cleanup project.
Salesforce recommends scheduled health checks alongside real-time duplicate prevention, while HubSpot similarly recommends validation at entry plus recurring audits.
For every integration, document:
System A ID → CRM ID
System B ID → CRM ID
Maintain a mapping table if necessary:
source_system | source_id | crm_id
--------------+-----------+-------
Stripe | cus_123 | 8f2...
Salesforce | 003... | 8f2...
ERP | C-9981 | 8f2...
This gives every external system a path to the same CRM entity, instead of each integration independently deciding whether to create a customer.
Even excellent prevention won't eliminate legacy duplicates.
A safe process is:
Detect
↓
Score
↓
Review high-confidence matches
↓
Select surviving record
↓
Merge/re-parent relationships
↓
Audit/log the operation
↓
Recalculate metrics
Keep an audit trail of merges. Ideally, merging should be reversible or at least recoverable from an audit/event log.
Modern CRM systems expose similar mechanisms: HubSpot supports unique-value properties, record IDs, duplicate management, and duplicate-management audit history.
Prevent → Enforce → Detect → Remediate → Monitor
If I were designing a CRM from scratch, I'd prioritize:
That combination is much more reliable than simply running a deduplication script periodically.
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach. You cannot rely solely on users to enter clean data; you need to enforce constraints at every layer of…
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach. You cannot rely solely on users to enter clean data; you need to enforce constraints at every layer of ingestion.
Here is how you can build a robust data-quality and de-duplication engine:
To tailor this specifically to your stack, could you tell me:
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach, `combining automated database constraints, middleware validation, and continuous data…
Ensuring data quality and preventing duplicates in a CRM requires a multi-layered architectural approach, combining automated database constraints, middleware validation, and continuous data hygiene.
Here is how you can implement a robust strategy:
Would you like me to dive deeper into fuzzy matching algorithms or provide an example of implementing idempotency for CRM webhook ingestion?
As a developer, treat CRM data quality as an **engineering constraint**, not a periodic cleanup project. The strongest approach is to prevent bad data at ingestion, detect duplicates before writes, and continuously monitor what gets through. ### 1. Define what “valid” means For each CRM object—contacts, accounts,…
As a developer, treat CRM data quality as an engineering constraint, not a periodic cleanup project. The strongest approach is to prevent bad data at ingestion, detect duplicates before writes, and continuously monitor what gets through.
For each CRM object—contacts, accounts, leads, etc.—define:
Validation at the point of entry is much cheaper than discovering bad data later.
Don't compare raw values directly.
For example:
" [email protected] " → "[email protected]"
"(703) 555-0199" → "+17035550199"
"Acme, Inc." → "acme"
" ACME INC " → "acme"
Create reusable normalization functions for:
Be careful not to over-normalize. For example, don't strip information that distinguishes two legitimate people or companies.
A good matching strategy typically has three tiers:
High confidence — automatically block
normalized_email == normalized_email
or a CRM-specific immutable external/customer ID matches.
Medium confidence — warn/review
same_phone
AND similar_name
or:
same_company_domain
AND similar_company_name
Low confidence — flag for investigation
similar_name
AND similar_address
CRM platforms commonly use matching criteria plus separate rules that determine whether to warn, block, or allow the record. Salesforce, for example, explicitly separates matching rules from duplicate rules and supports fuzzy matching.
This is one of the biggest developer wins.
Instead of:
POST /contacts
with no identity strategy, have integrations provide a stable external identifier:
source_system = "billing"
external_id = "cust_12345"
Then enforce uniqueness on:
(source_system, external_id)
Your integration becomes:
if external_id exists:
update existing record
else:
create record
This prevents retrying an API request from creating another customer.
For distributed systems, also consider an idempotency key for create operations and database-level uniqueness constraints where your CRM architecture permits them.
A check like:
SELECT id FROM contacts WHERE email = ?
followed by:
INSERT INTO contacts ...
has a race condition: two requests can both see “no record” and then both insert.
Use a database-level unique constraint where appropriate:
UNIQUE(normalized_email)
or, for integrations:
UNIQUE(source_system, external_id)
Application-level validation provides a good user experience; database constraints provide the final safety net.
Never simply delete one duplicate.
Have a deterministic survivor/merge strategy:
This matters because a false-positive merge can be more damaging than having two records temporarily. Salesforce specifically distinguishes intentional duplicates, unintentional duplicates, and disconnected records for this reason.
Create dashboards/alerts for things such as:
| Metric | Example target |
|---|---|
| Duplicate rate | < 0.5% |
| Required-field completeness | > 98% |
| Invalid email rate | < 1% |
| Records failing validation | < 1% |
| Unmatched integration records | 0 |
| Duplicate creation rate | Trending toward 0 |
Also monitor new duplicates per day, not just total duplicates. A growing total tells you there's a problem; a rising creation rate tells you the prevention mechanism is failing.
Regular auditing is recommended because CRM data naturally becomes stale and inconsistent over time.
Your CRM probably receives data from:
Don't assume the UI's validation protects you. Every integration boundary should normalize, validate, identify, and deduplicate.
A useful architecture is:
External source
↓
Normalize
↓
Validate schema/business rules
↓
Resolve identity / find duplicate
↓
Create OR update
↓
CRM
↓
Quality monitoring + audit log
Even excellent prevention won't eliminate historical problems. Run periodic jobs that:
For example, HubSpot's current duplicate tooling compares properties such as email, phone, name, ZIP code, and company name and supports custom duplicate rules and merge-history auditing.
I'd implement the policy as:
Normalize → Validate → Identify → Deduplicate → Write → Audit → Monitor
And make identity resolution a shared service/library rather than letting every integration invent its own matching logic. That prevents the classic situation where the Salesforce integration considers two people identical while the billing integration creates two separate customers.
If you're building this for Salesforce, HubSpot, Dynamics, or a custom CRM, the exact implementation differs substantially, but the architecture above remains a good foundation.
As a developer, ensuring data quality and preventing duplicates in a CRM `requires a multi-layered strategy spanning database constraints, ingestion pipelines, and application logic` . Relying on user discipline alone always fails—you have to bake integrity directly into your…
As a developer, ensuring data quality and preventing duplicates in a CRM requires a multi-layered strategy spanning database constraints, ingestion pipelines, and application logic . Relying on user discipline alone always fails—you have to bake integrity directly into your architecture.
Here is a structured blueprint to bulletproof your CRM data:
If you want to dive deeper, let me know:
I can provide code snippets or platform-specific implementation patterns tailored to your setup.
As a developer, treat CRM data quality as an **engineering/data-governance problem**, not just a cleanup task. The strongest approach is to prevent bad data at ingestion, enforce invariants in the database/integration layer, and continuously monitor quality. ### 1. Define what makes a record unique For each CRM…
As a developer, treat CRM data quality as an engineering/data-governance problem, not just a cleanup task. The strongest approach is to prevent bad data at ingestion, enforce invariants in the database/integration layer, and continuously monitor quality.
For each CRM object, explicitly define its identity rules.
For example, for a Contact:
Don't automatically merge based solely on names—legitimate people can share them.
Create canonical versions of fields used for matching:
Email:
" [email protected] " → "[email protected]"
Phone:
"(201) 555-0199" → "+12015550199"
Company:
"Acme, Inc." → "acme"
Name:
"José García" → normalized/canonical representation
Keep the original value if you need it for display or auditing, but use the canonical value for matching.
Every integration should have a stable external identifier:
source_system = "billing"
external_id = "cus_123456"
Then make your ingestion operation effectively:
UPSERT contact
WHERE source_system = 'billing'
AND external_id = 'cus_123456';
Avoid:
Search → if nothing found → INSERT
as your only protection. Two requests can execute concurrently and both see "nothing found."
Instead, enforce uniqueness at the database/API layer:
UNIQUE(source_system, external_id)
and handle the resulting conflict by updating/retrying rather than creating another record.
Use multiple levels:
| Layer | Purpose |
|---|---|
| Unique constraint | Guarantees exact uniqueness |
| Normalization | Makes equivalent values comparable |
| Deterministic matching | Finds obvious duplicates |
| Fuzzy matching | Finds spelling/format variations |
| Review queue | Handles ambiguous matches |
| Merge process | Consolidates confirmed duplicates |
CRM platforms such as Salesforce explicitly separate matching rules (how records are identified as potential duplicates) from duplicate rules (what to do when a match is found). Their matching system can also normalize fields and use fuzzy matching.
This is one of the biggest developer-side improvements.
For every inbound event, maintain an idempotency key such as:
integration + event_type + event_id
Store processed event IDs:
stripe + customer.updated + evt_123
If the same webhook arrives three times, the first request changes the CRM; the other two become no-ops.
Also make retries safe. Assume every API call can be delivered more than once.
Don't allow every integration to invent its own interpretation of CRM data.
Have a shared validation layer:
Inbound data
↓
Schema validation
↓
Normalization
↓
Required-field validation
↓
Identity/duplicate resolution
↓
Business-rule validation
↓
CRM write
Reject or quarantine malformed records rather than silently inserting questionable data.
I'd use three outcomes:
score >= 0.95 → automatically merge/link
0.70–0.95 → human review
< 0.70 → treat as a new record
The actual thresholds should be determined from your data and false-positive/false-negative costs.
A false positive is particularly dangerous: incorrectly merging two real customers can corrupt ownership, history, attribution, permissions, and reporting.
Never simply delete the losing record and throw away its information.
Keep:
That gives you an audit trail and makes mistakes reversible.
Create metrics and alerts for things such as:
duplicate_rate
null_rate_by_field
invalid_email_rate
invalid_phone_rate
orphaned_reference_rate
failed_ingestion_rate
records_without_external_id
manual_merge_rate
For example:
duplicate_rate > 0.5%
→ alert
invalid_email_rate > 2%
→ investigate source integration
new records without external_id > 0
→ block deployment/integration
This turns data quality into something you can observe rather than periodically discovering during a painful cleanup.
Your automated tests should specifically cover:
A good target architecture is:
┌───────────────┐
Sources ────────►│ Validation │
└───────┬───────┘
↓
┌───────────────┐
│ Normalization │
└───────┬───────┘
↓
┌───────────────┐
│ Identity / │
│ dedup service │
└───────┬───────┘
↓
┌───────────────┐
│ Unique keys + │
│ CRM write │
└───────┬───────┘
↓
┌───────────────┐
│ Monitoring + │
│ audit trail │
└───────────────┘
If you're working specifically with Salesforce, its Matching Rules + Duplicate Rules + duplicate jobs provide much of the duplicate-detection framework, while you should still make your custom integrations idempotent and enforce stable external IDs. Salesforce also supports organization-wide duplicate jobs and duplicate record sets for ongoing cleanup.
The key principle is: don't build a deduplication script and call the problem solved. Build the system so creating a duplicate is difficult, detectable, auditable, and recoverable.