From Splink to Tilores: A 5-Step Migration Playbook
TL;DR: Splink gets you to a proven proof-of-concept; Tilores gets you to production. If your Splink batch job already works but downstream teams now need real-time answers, you don’t have to start over. This five-step playbook (understand your Splink logic, map it to Tilores rules, ingest a sample, dry-run against ground truth, then go live) lets you carry your matching design across without re-doing the hard thinking. We use the public FEBRL benchmark dataset throughout so every step is concrete and reproducible.
See it on your data: Book a Demo to walk through real-time entity resolution on your own records, or Get the Evaluation Build to try it locally.
Why migrate from Splink at all?
Splink is the most credible open-source entity resolution library available today: probabilistic, explainable, free, and battle-tested inside the UK Ministry of Justice. If your job is batch deduplication or a research linkage study, it is an excellent choice (here is what we love about Splink), and our own reproducible benchmark shows it is fast: 1,000,000 records deduplicated in 9.24 seconds as an embedded DuckDB library, for zero infrastructure cost.
The reason to migrate is rarely accuracy. It is a change in requirements. A team proves entity resolution works with Splink, then someone asks: “Can we check whether this new record is a duplicate right now, without waiting for the next batch?” That question is architecturally unanswerable with a one-off pair list. It is the moment a batch tool hits its wall, and the moment a real-time API earns its keep.
Tilores is a real-time entity resolution API. It resolves and links records at ingestion and returns the resolved entity context at query time over a GraphQL API. It sits next to your existing MDM, CDP, warehouse, or KYC/AML stack and does not replace any of them. The matching logic you already designed in Splink maps cleanly onto it. This playbook shows how.
When does this migration actually make sense?
Know the trigger before you start. The question isn’t whether Tilores is better than Splink. Splink is the right R&D and batch tool; Tilores is the right production and real-time tool.
| You should migrate when… | You should stay on Splink when… |
|---|---|
| Downstream systems need a live answer at the moment a record arrives (KYC at onboarding, fraud at signup, point-of-care lookup). | Your use case is pure batch and will stay that way. |
| You want incremental ingestion — a single new record via API, not a full re-run. | You need to publish Fellegi-Sunter model parameters in a research paper. |
| You need a persistent, searchable entity index instead of a static pair list. | A hard zero-budget constraint rules out any commercial tool. |
| Operating the batch infrastructure (Spark/DuckDB jobs, scheduling, monitoring) has become a burden. | You enjoy and have the in-house expertise to run the pipeline. |
Many teams run both: Splink for prototyping and ground-truth labelling, Tilores for production deployment. The playbook below assumes you’ve decided the production trigger applies.
Step 1: Understand your Splink matching logic
You can’t migrate logic you can’t articulate. Before touching Tilores, write down three things from your Splink settings, because every one of them has a direct counterpart on the other side.
- Comparisons. Which fields do you match on, and with which comparison functions? In the FEBRL benchmark, both tools match on six fields (
first_name,last_name,date_of_birth,city,postcode, andphone) using fuzzy comparisons like Jaro-Winkler and Levenshtein for the name and address fields. - Blocking rules. Which SQL blocking rules limit the candidate-pair space? This is the load-bearing part of any Splink config. Note them carefully: blocking is why Splink is fast, and also where most of its missed matches come from. Records that never share a blocking bucket can never be compared.
- Threshold. What match-probability cutoff turns a candidate pair into a confirmed link? In the benchmark we used probability ≥ 0.80.
Capture this as a plain table: field, comparison type, blocking role, threshold. That table is your migration spec.
Step 2: Map fields and comparisons to a Tilores rule config
Here is the key mental shift. Splink is probabilistic: it learns m and u parameters and sums match weights into a single probability. Tilores is rule-based: you enumerate explicit linking rules over comparison functions, and a pair links when any rule fires, with links merged transitively in the entity graph. There is no blocking gate that can pre-emptively prevent a match. That is why, in the benchmark, Tilores recovered 19,345 more true duplicates (recall 0.9900 vs 0.7965).
The mapping is more direct than it looks:
| Splink concept | Tilores equivalent |
|---|---|
Fields under comparisons | Fields in your Tilores schema |
| A comparison function (Jaro-Winkler, Levenshtein) | A comparison/matcher in a linking rule |
| Blocking rule (a constraint on candidates) | Not needed — instead, you author the positive rules that should link records |
| Match threshold | Rule selectivity — how strict each rule’s conditions are |
The shift in thinking is from “which candidate pairs do I allow, and what score clears the bar?” to “what concrete combinations of evidence mean these are the same entity?” For FEBRL, that becomes a small set of overlapping rules: for example a name-plus-date-of-birth rule, a name-plus-city rule, and a postcode-plus-phone rule. Because they overlap, corruption in one field doesn’t block a match when another field stays clean. Use normalising transformers (case, whitespace, phonetics) on inputs so comparisons behave consistently. The exact configuration used for the benchmark is published in full in the public repo; start from it rather than from a blank file. For the full rule-config and matcher reference, see the Tilores API docs.
Step 3: Ingest a sample dataset
Don’t start with all 1,000,000 records. Take a representative sample of 10,000 to 50,000 rows, including known duplicate pairs from your ground truth, and ingest it through the Tilores API. Resolution happens at ingestion: as each record lands, Tilores evaluates your rules against the existing index and links it into the right entity, assigning a stable entity ID.
This is also where the operational difference becomes tangible. With Splink you re-run the batch to incorporate new data; with Tilores you send one record (the benchmark wraps each JSONL row and submits it via batch-graphql) and it is resolved against everything already ingested. Once the sample is in, you can query it immediately. The benchmark verifies matches with a single lookup by record id, and its resolved entity’s other members are the linked duplicates. That is exactly the “is this a duplicate, right now?” question:
query($id: ID!) {
entityByRecord(input: { id: $id }) {
entity {
id
records { id }
}
}
}
To match an incoming record by its field values rather than a known id, Tilores also exposes a search query that returns candidate entities with a score and hitScore, plus recordInsights for the matched-record evidence. See the Tilores API docs for its exact shape.
Step 4: Dry-run and compare against your Splink output
This is the step that earns trust internally. Resolve the sample with Tilores, export the linked pairs, and compare them against (a) your Splink output and (b) your ground truth. You already have the harness for this if you ran Splink — the evaluation protocol is the same: count true positives, false positives, and false negatives against the known pairs, then compute precision, recall, and F1.
On the full FEBRL benchmark, the comparison looked like this:
| Metric | Splink (DuckDB) | Tilores | Δ |
|---|---|---|---|
| F1 | 0.8867 | 0.9949 | +10.8pp |
| Precision | 0.9999 | 0.9998 | ~tie |
| Recall | 0.7965 | 0.9900 | +19.4pp |
Expect precision to be near-identical, since both approaches are cautious about false matches. The gap you should see is in recall, and the pairs Tilores finds that Splink missed are almost always records where a blocking field was corrupted. Inspect those specific pairs: they are the concrete, defensible evidence that the migration improves coverage on your data. If a rule is over-linking, tighten its conditions; if it’s under-linking, add an overlapping rule. Iterate on the sample until the numbers and the eyeballed pairs both satisfy you. Every number above is reproducible from the public repo.
Step 5: Go live with the real-time API and incremental ingest
Once the dry-run holds up, scale the ingest to the full dataset and switch on the two capabilities a pair list could never give you:
- Real-time search at query time. Downstream systems query the resolved index instead of waiting for a batch. On the benchmark’s 1M-record index, search latency was 8.6ms p95 on LAN, fast enough to sit in an onboarding or point-of-care flow.
- Incremental ingestion. New records arrive one at a time through the API and are resolved against the live index immediately. No full re-run, no stale golden records between batches.
Keep your Splink job if it still earns its place. Many teams keep it for periodic ground-truth labelling and re-validation, and re-run the dry-run comparison whenever rules change. Migration doesn’t mean deletion; it means the production path is now real-time, with batch relegated to the R&D role it’s best at.
What does this migration not give you for free?
Honesty matters here as much as in the benchmark. Two things are worth weighing before you commit.
Probabilistic explainability. Splink produces a per-pair match weight and can show, in a waterfall chart, how much each field contributed. Tilores gives you rule provenance, meaning which rule caused a link. That is excellent for “show me why” but is not the same as a per-comparison weight breakdown. For some regulated audit workflows that distinction matters; evaluate it against your compliance requirements.
Cost. Splink is free; Tilores is a commercial product. The trade is real-time capability, incremental ingestion, and removing the operational burden of running the matching infrastructure yourself. Whether that trade is worth it depends on whether you need production real-time answers, which is exactly the trigger in Step 0 of this playbook.
FAQ
Do I have to re-design my matching logic from scratch in Tilores?
No. The fields and comparison functions you chose in Splink carry over almost directly to a Tilores rule config. The main shift is conceptual: instead of blocking rules plus a probability threshold, you author explicit overlapping linking rules that fire when records share enough evidence to be the same entity.
Will my accuracy change after migrating?
On the FEBRL benchmark, precision stayed effectively tied while recall rose substantially (+19.4pp), because Tilores has no blocking gate that can pre-emptively prevent a match. Your own results depend on your data and rule config — which is exactly why Step 4 dry-runs against your ground truth before you go live.
Can I keep using Splink after migrating?
Yes, and many teams do. Splink remains an excellent tool for batch prototyping and for generating ground-truth labels. A common pattern is Splink for R&D and re-validation, Tilores for the production real-time path.
How does real-time resolution actually work in Tilores?
Tilores resolves and links each record at ingestion and maintains a persistent, queryable entity index. At query time you search that index and get back resolved entities with fields like score, hitScore, and recordInsights. It sits next to your existing MDM, CDP, warehouse, or KYC system rather than replacing it.
Where can I see the benchmark and rule config used in this guide?
Everything — the FEBRL data generator, both tools’ configs (including the exact Tilores rules), and the pre-computed results — is in the public repository at github.com/tilotech/tilores-splink-benchmark. The full methodology is written up in our Splink vs. Tilores benchmark post.
Is Tilores a replacement for my data warehouse or MDM?
No. Tilores is a real-time entity resolution API that supplies the resolved entity key those systems lack. It complements your warehouse, MDM, CDP, or KYC/AML stack — you keep them and add live resolution alongside.
See what resolved entity data does for your business — and your AI.