Duplicated code
The same logic copied across files, drifting apart one tweak at a time.
Debtlint finds duplicated and near-duplicate logic across your codebase, before AI-generated copy-paste quietly turns into technical debt. Fast, programmatic, language-agnostic.
Coding assistants are fast, and forgetful. Existing utilities get rewritten, business patterns get repeated, logic fragments across files. Nobody notices until the same bug has to be fixed in five places.
The same logic copied across files, drifting apart one tweak at a time.
One defect, many hiding places. Fixing it once is never enough.
Every feature touches more places than it should. Velocity quietly drops.
Onboarding slows, reviews get longer, refactors get riskier.
The obvious answers don't hold up on a real codebase. Each one trades away precision, portability or speed.
Brittle. Rename a variable or reformat a line and the match disappears.
Too sensitivePrecise, but bound to one language and its parser. Every new language is a new project.
Language-lockedPairwise comparison explodes as the repository grows. It simply doesn't scale.
Too expensiveByte Pair Encoding learns the recurring patterns of your codebase, then Winnowing fingerprints the token stream. The result: tolerant to small edits, independent of any grammar, and fast enough for every pull request.
A compact pipeline, inspired by compression and plagiarism-detection research, tuned for source code.
{ "src": ".", "excludes": ["target", "node_modules", ".git"] }
Token 129 appears in n files, at n places, a recurring structure, found without understanding the language.
[ { "file": "src/pricing.py", "start_line": 34, "end_line": 42 }, { "file": "src/pricing.py", "start_line": 42, "end_line": 50 }, { "file": "src/pricing.py", "start_line": 50, "end_line": 58 } ]
Every duplicated block comes with its file and line range. A human-readable summary by default, JSON for CI and agents. Illustrative output.
No LLM calls, no tokens to buy, no latency to wait on. Debtlint is deterministic, local and cost-efficient. Run it on every commit.
No grammar to maintain. If it's text, it's in scope.
Decide how small a duplicate has to be to matter with --shingle-size. Drag the slider.
--shingle-sizeSave and reuse your vocabulary, export encoded sequences, tune every threshold, and build on an open Apache 2.0 core.
Drop the GitHub Action into a workflow and every pull request gets a clear, actionable comment.
Ship an agent skill that reruns Debtlint after each generation and refactors until the report is clean.
Analyze every pull request and get the report right where reviewers look.
name: Check duplication on: pull_request: branches: [main] jobs: debtlint: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - uses: actions/checkout@v4 - name: Run debtlint uses: PoCInnovation/Debtlint@v1 with: vocab-path: .debtlint-vocab.json
Duplicated copies, file and line range, right in the PR conversation.
3 duplicated copies
def calculate_price_001(price, quantity): total = price * quantity tax = total * 0.20 final_price = total + tax print(f"Total: {final_price}") return final_price
def calculate_price_002(price, quantity): total = price * quantity tax = total * 0.20 final_price = total + tax print(f"Total: {final_price}") return final_price
Generation becomes codebase-aware. The agent checks what already exists, removes what it just duplicated, and only stops when the report is clean, or after three iterations.
debtlint --report-format json
Classic clone detectors such as JSCPD are great tools. Debtlint takes a different path, one designed for AI-assisted codebases.
| JSCPD | ||
|---|---|---|
| Detection | Token matching with language-specific tokenizers | BPE vocabulary learned from your code + Winnowing fingerprints |
| Language support | Per-format support, extended release by release | Language-agnostic by design, no parser needed |
| Sensitivity | Detection modes and minimum thresholds | Fine-grained: --shingle-size, --window-size, --min-tokens |
| Pull request feedback | Reports via CI scripting | Native GitHub Action posting a PR comment |
| AI agents | Integrate by hand | Ready-to-use agent skill and JSON output |
| Install | npm | cargo install debtlint · single native binary |
Comparison based on default setups and public documentation at the time of writing.
cargo install debtlintdebtlint.config.json{ "src": ".", "excludes": ["target", "node_modules", ".git"] }
debtlint --save-vocab .debtlint-vocab.jsondebtlint --load-vocab .debtlint-vocab.json| Flag | Default | Description |
|---|---|---|
--vocab-size | 1000 | Max vocabulary size (base + merges) |
--min-frequency | 2 | Min pair frequency to create a merge |
--load-vocab | n/a | Load a vocabulary JSON, skip BPE training |
--save-vocab | n/a | Save the trained vocabulary as JSON |
--output-encoded | n/a | Write encoded token sequences as JSON |
--shingle-size | 50 | Shingle size in tokens (Winnowing) |
--window-size | 3 | Sliding window size in shingles |
--min-tokens | 20 | Minimum duplicated block length in tokens |
--report-format | text | text or json |
A student-driven software lab. Debtlint is the result of a PoC project led by four people.
Install Debtlint, add the Action to your next pull request, and make technical debt visible before it spreads.