Proof-carrying deduplication · built for the AI era
It's just a little
duplicate code.
Until it's a full rewrite.
You'll fix it later. You won't. It compounds. Silent, harmless-looking. Until the refactor you keep dodging is a ground-up rewrite: a senior engineer, a lost year, a bill nobody budgeted for. And AI writes it faster than any team can catch.
dupelint is the only engine that proves duplication away instead of guessing. And the first with the math to catch Type-5 — subsumption — soundly, where every other tool approximates it or throws it away.
read-only · never modifies your files · operated by ROI PIPE LLC
the actual output, not a mockup
Proven on CPython
Over-detect. Then prove down to certainty.
One pass across the CPython standard library and tooling. The net flags everything that could be a clone; the sound sieve discards everything it cannot prove. Nothing reaches a verdict without an unconditional proof.
What it solves
Speed up, slim down, stop the rewrite.
Duplicate code is the one debt that compounds on its own. And AI now writes it faster than any team can catch. Remove it, with proof, and the payoff lands in five places at once.
Reuse what exists. AI stops re-deriving the primitives it already built. Less to write, review, and test.
One source of truth. Not the same fix in eight places, and the one you'd have missed.
Don't regenerate what exists or reload bloat into context. The savings compound on every run.
AI has no memory of what it wrote. dupelint is the one it lacks: ask before it writes, sweep after it merges.
Zero false positives, by proof. Consolidate with confidence, on a branch behind your tests.
The clone taxonomy
Five types, ordered by how hard they are to prove.
We make detection sound across all five types — zero false positives, where the field only approximates. The standard taxonomy stops at four; the fifth, subsumption, others detect only to discard. We’re the first to make it sound.
Identical modulo whitespace and comments.
First detection by J. Johnson · 1993
Identical up to consistent renaming of identifiers.
First detection by B. Baker · 1993-95
Statements inserted or removed; the shared run still proven equivalent.
First detection by Baxter et al. · 1998
Same behavior, different code. No text overlap required.
First detection by Komondoor & Horwitz · 2001
One construct silently re-deriving what another already computes.
First detection by Ronald C. Smart · 2026
A short history
The grid is the summary. Here is who first caught each type — and how.
Johnson brought clone detection into being. His method reduced source to fingerprints — compact hashes of text windows — and matched them to surface exact repetitions across a codebase, scaling to over 300 MB of legacy source. It set the founding idea: find duplication by structure you can compute, not by eye. A 1994 follow-up carried it into substring matching for change tracking.
Baker made detection survive a rename. Her dup tool introduced parameterized matches: two fragments count as duplicate even when identifiers and constants are systematically substituted, found fast with suffix trees. It is why a copy-pasted block with every variable renamed can no longer hide — the renamed, or Type-2, clone.
Baxter and colleagues moved from text to trees. Their method hashed abstract-syntax-tree subtrees into buckets and compared within them, catching near-miss clones — fragments where statements were inserted, deleted, or edited between two otherwise-shared runs. The gapped clone.
Komondoor and Horwitz dropped the requirement of any textual overlap at all. Using program dependence graphs and slicing, they matched code by what it computes rather than how it reads — the same behavior written two different ways. The semantic clone: the hardest of the classic four to prove.
For over two decades the taxonomy stood at four. Smart added the fifth: subsumption, where a larger construct silently re-derives everything a smaller one already computes. The containment relation itself was not new — earlier tools spotted it approximately and discarded it as redundant noise. The contribution is to catch it soundly, with zero false positives, and turn it into a reuse: the outer construct simply calls the one that already exists. The certainty is yours; the method is ours.
How it decides
Math first. The model only speaks where math can't.
Deterministic by design
The verdict step is compute, not a model. The same input gives the same answer, every time. A sound verdict is reserved for an unconditional proof, so it carries no false positives.
Two bases, never blurred
sound. An unconditional proof, safe to act on: apply on a branch, behind your tests. bounded. Proven only within a stated domain; flagged for you to verify. The two are never folded together.
Read-only & secret-safe
It reads code as structure and never modifies your files. Verdicts are proven statically, not by running your code. The only execution is a sandboxed check of pure-arithmetic functions, with no file, network, or import access, used only to rule out false matches. Secret-looking files are rejected on ingest, and the output is data: a verdict and a suggested rewrite you choose to apply.
The answer, not the recipe
A response says these are equivalent or inner ⊑ outer. It never carries how that was decided. You get the verdict; the derivation stays ours.
Safe by design
It reads. You decide. Your tests confirm.
dupelint never modifies your code. It returns a verdict and a suggested rewrite. Scanning an entire codebase changes nothing. Applying is yours, done the safe way.
Never modifies your files. A scan reads your code and changes nothing on your side; verdicts come from static analysis, not from running it.
The output is data: a verdict and a suggested rewrite. You apply it, on your terms.
Branch → apply → run your tests → review → merge. Never straight to production.
Anything the proof can't close is flagged for review. Never marked sound, never guessed.
Full terms of use → dupelint.com/terms
Privacy
We never store your code.
Your code is read in memory to compute the verdict, then dropped. It is never written to disk, and no copy is kept. Here's the full lifecycle of a scan, limits included.
Encrypted over TLS on every request. Your code never travels in the clear.
Held in RAM only, for the moment of the scan, read to compute the verdict. Never written to disk, and swap is off so it cannot leak there.
When the scan finishes, your code is released from memory. No stored copy, on disk or off.
Processed in EU data centers, under GDPR. Never in US data centers.
Full transparency: we store nothing and never write your code to disk. We also won't overclaim. RAM holds the bytes only until they are overwritten, and we don't own the physical host, so we don't call it "unrecoverable." Hardware-encrypted memory (confidential computing) is on our roadmap to seal the in-scan window itself.
Transparency
Where it runs, and who else is involved.
You can find most of this out yourself. We'd rather state it up front.
Scans run on servers we operate at Hetzner Online GmbH, a German company, in their German data centers. Processing stays in the EU under GDPR. Hetzner is ISO 27001 certified and we hold an Article 28 data processing agreement with them. They provide the hardware and network — they hold no account on our machines and no access to scan contents.
The verdict is computed, not inferred. Your code is never sent to a language model, an inference API, or any third party. There is no model provider in the processing chain, so there is nothing to opt out of and no training question to ask.
Who else is involved
| Vendor | Purpose | Data | Region |
|---|---|---|---|
| Hetzner Online GmbH | Compute and hosting for scan execution | Code held in memory during the scan, never written to disk | Germany |
| Stripe, Inc. | Billing and payment processing | Billing identity, payment method | United States |
Your code goes to exactly one of these: the machines we run in Germany. Stripe sees billing details only and never touches code. Nothing in the scan path leaves the EU. Vendors used for account email and SMS are listed in our privacy policy — none of them sit in the scan path. We publish a changelog entry whenever this list changes.
Roadmap
Proven, not promised. One unit at a time.
Python is in development, and already proving itself: its functions and classes are proven today, matched across all five clone types, sound, with zero false positives. More units are landing one at a time, and every language ships the same way — proven, or not at all. Not a demo: see it run across the entire CPython standard library, the real output.
See the CPython scan ↗A unit ships only when all five clone types are sound for it, and each language grows unit by unit. Expand a language to see what’s proven, what’s next, and what’s planned.
01Python
in development
Code units
- Functions & methodsproven
- Classesproven
- Module-level codeproven
- Statement blocksproven
- Constants & data structuresproven
- Whole filesproven
- Lambdas & comprehensionsin progress
Sub-function constructs
- Decoratorsplanned
- Exception handlersplanned
- Context managers (with)planned
- Loop & branch bodiesplanned
- Match / case armsplanned
- Signatures & defaultsplanned
- Type aliasesplanned
- String literalsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
__init__ assignments, ABC/Protocol stubs, and @property pairs are detected but never flagged for removal — mandatory duplication.
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
02Shell
planned
Code units
- Functionsplanned
- Pipelines & command blocksplanned
- Conditionals & loopsplanned
- Case branchesplanned
- Here-docsplanned
- Traps & handlersplanned
- Constants & variablesplanned
- Whole scriptsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
03SQL
planned
Code units
- Queriesplanned
- CTEs & viewsplanned
- Subqueries & joinsplanned
- Stored procedures & functionsplanned
- Triggersplanned
- Constraints (FK · check · unique)planned
- Indexesplanned
- Migrationsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
04Rust
planned
Code units
- Functions & methodsplanned
- Structs & enumsplanned
- Traits & impl blocksplanned
- Modules & whole filesplanned
Constructs
- Macros (macro_rules!)planned
- Match armsplanned
- Closuresplanned
- Derive attributesplanned
- Generics & lifetimesplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
05TypeScript / JavaScript
planned
Code units
- Functions & methodsplanned
- Classesplanned
- Interfaces & type aliasesplanned
- Enums & namespacesplanned
- Modules & whole filesplanned
Constructs
- JSX components & hooksplanned
- Arrow functions & closuresplanned
- Mapped & conditional typesplanned
- Statement blocksplanned
- Object literals & destructuringplanned
- Constants & object literalsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. .d.ts declarations and barrel re-exports are detected but never flagged for removal — mandatory duplication.
06HTML · CSS · XML · YAML · Markdown
planned
Units
- Rules & selectorsplanned
- Media queriesplanned
- Mixins & variablesplanned
- Components & templatesplanned
- Config keys & valuesplanned
- Whole documentsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
07PHP
planned
Code units
- Functions & methodsplanned
- Classesplanned
- Traits & interfacesplanned
- Namespaces & whole filesplanned
Constructs
- Closures & arrow functionsplanned
- Exception handlersplanned
- Statement blocksplanned
- Constants & arraysplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
08Go
planned
Code units
- Functions & methodsplanned
- Structsplanned
- Interfacesplanned
- Packages & whole filesplanned
Constructs
- Func literals & closuresplanned
- Statement blocksplanned
- Struct tagsplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Go’s if err != nil is detected but never flagged for removal — mandatory duplication.
09Java
planned
Code units
- Methodsplanned
- Classesplanned
- Interfacesplanned
- Enums & recordsplanned
- Whole filesplanned
Constructs
- Annotationsplanned
- Lambdas & streamsplanned
- Exception handlersplanned
- Statement blocksplanned
- Constants & fieldsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Getters/setters and POJO/DTO boilerplate are detected but never flagged for removal — mandatory duplication.
10C#
planned
Code units
- Methodsplanned
- Classes & structsplanned
- Interfacesplanned
- Enums & recordsplanned
- Whole filesplanned
Constructs
- Propertiesplanned
- Attributesplanned
- LINQ & lambdasplanned
- Statement blocksplanned
- Constants & fieldsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Auto-property and record boilerplate is detected but never flagged for removal — mandatory duplication.
11Kotlin
planned
Code units
- Functions & methodsplanned
- Classes & data classesplanned
- Interfacesplanned
- Objects & companionsplanned
- Whole filesplanned
Constructs
- Extension functionsplanned
- Lambdasplanned
- When branchesplanned
- Statement blocksplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Getters/setters and data-class boilerplate are detected but never flagged for removal — mandatory duplication.
12Swift
planned
Code units
- Functions & methodsplanned
- Classes & structsplanned
- Protocols & extensionsplanned
- Enumsplanned
- Whole filesplanned
Constructs
- Closuresplanned
- Property wrappersplanned
- Switch casesplanned
- Statement blocksplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
13Dart
planned
Code units
- Functions & methodsplanned
- Classesplanned
- Mixins & extensionsplanned
- Enumsplanned
- Whole filesplanned
Constructs
- Closuresplanned
- Async & streamsplanned
- Statement blocksplanned
- Constants & collectionsplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
14Lua
planned
Code units
- Functionsplanned
- Tables & metatablesplanned
- Whole chunksplanned
Constructs
- Closuresplanned
- Conditionals & loopsplanned
- Statement blocksplanned
- Constants & tablesplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound.
15C
planned
Code units
- Functionsplanned
- Structs & unionsplanned
- Enums & typedefsplanned
- Whole filesplanned
Constructs
- Macros (#define)planned
- Control blocksplanned
- Statement blocksplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Header-vs-implementation declarations are detected but never flagged for removal — mandatory duplication.
16C++
planned
Code units
- Functions & methodsplanned
- Classes & structsplanned
- Templatesplanned
- Namespaces & whole filesplanned
Constructs
- Macrosplanned
- Lambdasplanned
- Statement blocksplanned
- Constants & dataplanned
Cross-construct
- Same logic, different constructplanned
- Granularity roll-upplanned
Every unit is matched across all five clone types — exact · renamed · gapped · semantic · subsumption — and nothing ships until it is proven sound. Header-vs-implementation declarations are detected but never flagged for removal — mandatory duplication.
For coding agents
Built for the agent loop.
Generation is fast and forgetful. It reinvents what it already wrote. dupelint is the primitive that catches it, with an answer solid enough to act on. On a branch, behind your tests.
Query before it writes
A worker checks whether the code it’s about to write — a function or a class — already exists before emitting it. Prevent the duplicate at the source.
Supervisor sweep
A coordinator scans merged output in parallel, catching cross-worker duplication no single worker can see.
Post-task refactor
When the task closes, batch-scan and consolidate. A typical repo clears in under a minute.
Stop shipping duplicate code.
Point dupelint at your codebase, or wire it into your agents. The verdict is sound, the proof is ours, and your code stays slim.
Built on Python
dupelint runs on CPython, and we proved it on CPython's own source. As a proud PSF Supporting Member, we back the Python Software Foundation, the nonprofit that stewards the language everything here is built on.