Pattern Mine
Discovers recurring patterns buried in your codebase โ similar logic written three different ways, duplicated validation in five controllers, the same error...
ๆ่ฝ่ฏดๆ
name: pattern-mine version: 1.0.0 description: > Discovers recurring patterns buried in your codebase โ similar logic written three different ways, duplicated validation in five controllers, the same error handling copy-pasted across twelve files. Surfaces what should be shared but isn't, and what IS shared but shouldn't be. The difference between a codebase that grew and one that was cultivated. author: J. DeVere Cooley category: everyday-tools tags:
- patterns
- duplication
- refactoring
- code-quality
metadata:
openclaw:
emoji: "โ๏ธ"
os: ["darwin", "linux", "win32"]
cost: free
requires_api: false
tags:
- zero-dependency
- everyday
- refactoring
Pattern Mine
"A pattern isn't repeated code. It's a repeated decision โ and every repeated decision is a decision that should have been made once."
What It Does
Your codebase has patterns. Some are intentional (design patterns, conventions, shared utilities). Most are accidental โ the same logic independently invented by different developers at different times, slightly different each time, all slowly diverging.
Pattern Mine excavates these buried patterns and brings them to the surface:
- Convergent patterns: Different code doing the same thing (should be unified)
- Divergent patterns: Same code doing different things (should be separated)
- Emerging patterns: A pattern forming but not yet crystallized (candidate for abstraction)
- Fossilized patterns: Old patterns still followed long after the reason died
The Four Mining Operations
Operation 1: Convergent Pattern Detection
"Three developers independently wrote the same thing"
Not just copy-paste detection (your linter does that). Pattern Mine finds semantically equivalent code with different syntax โ code that does the same thing but looks different.
EXAMPLE โ Found: 3 independent implementations of "retry with backoff"
LOCATION 1: src/api/client.ts:45
async function fetchWithRetry(url, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try { return await fetch(url); }
catch (e) { await sleep(1000 * Math.pow(2, i)); }
}
throw new Error('Failed after retries');
}
LOCATION 2: src/services/payment.ts:112
const retry = async (fn, max = 3) => {
let lastError;
for (let attempt = 1; attempt <= max; attempt++) {
try { return await fn(); }
catch (err) { lastError = err; await delay(attempt * 2000); }
}
throw lastError;
};
LOCATION 3: src/workers/email.ts:67
function withRetry(operation, retries = 5) {
return operation().catch(err => {
if (retries <= 0) throw err;
return new Promise(r => setTimeout(r, 1000))
.then(() => withRetry(operation, retries - 1));
});
}
ANALYSIS:
โโโ All three implement retry-with-backoff
โโโ Different: max attempts (3, 3, 5), backoff strategy (exp, linear, fixed)
โโโ Different: error handling (generic throw, preserve last, re-throw)
โโโ None are configurable enough to replace the others
โโโ RECOMMENDATION: Extract shared retry utility with configurable
attempts, backoff strategy, and error handling
Operation 2: Divergent Pattern Detection
"Same abstraction, different behavior โ the abstraction is lying"
Finds code that looks like it follows a pattern but actually deviates in meaningful ways:
EXAMPLE โ Found: UserValidator diverges from pattern
PATTERN: All validators in src/validators/ follow:
โโโ validate(input) โ { valid: boolean, errors: string[] }
โโโ Throw on null input
โโโ Return empty errors array on success
DIVERGENCE: UserValidator
โโโ validate() returns { isValid: boolean, messages: string[] }
โ โโโ Different property names: 'valid'โ'isValid', 'errors'โ'messages'
โโโ Returns null on null input (doesn't throw)
โโโ Returns undefined errors on success (not empty array)
โโโ Every consumer of UserValidator has special-case handling
RECOMMENDATION: Align UserValidator with the common pattern.
Estimated consumer cleanup: 8 files.
Operation 3: Emerging Pattern Detection
"This is about to become a pattern โ should it be one?"
Finds code that is repeated 2-3 times but hasn't yet become an abstraction. This is the sweet spot for extraction โ enough repetition to justify it, but not yet so much that extraction requires touching dozens of files.
EXAMPLE โ Emerging: Permission check + audit log (2 occurrences, likely growing)
src/routes/admin.ts:
if (!user.hasRole('admin')) {
auditLog.write({ action: 'ADMIN_ACCESS_DENIED', userId: user.id });
throw new ForbiddenError('Admin access required');
}
src/routes/billing.ts:
if (!user.hasRole('billing')) {
auditLog.write({ action: 'BILLING_ACCESS_DENIED', userId: user.id });
throw new ForbiddenError('Billing access required');
}
ANALYSIS:
โโโ Pattern: role check โ audit denied access โ throw forbidden
โโโ Occurrences: 2 (and a third route is being written this sprint)
โโโ Variation: only the role name and audit action differ
โโโ RECOMMENDATION: Extract requireRole(user, role) middleware
before the third copy appears
Operation 4: Fossilized Pattern Detection
"Everyone follows this pattern. Nobody remembers why."
Finds patterns that are consistently followed but serve no current purpose:
EXAMPLE โ Fossilized: Defensive null checks after non-nullable call
PATTERN FOUND IN 23 LOCATIONS:
const user = await getUser(id); // getUser now always returns User or throws
if (!user) { // This branch is unreachable
throw new NotFoundError(); // getUser throws NotFoundError itself
}
HISTORY:
โโโ getUser() used to return null for missing users (pre-2024)
โโโ Rewritten to throw NotFoundError directly (commit a8f3d2e, 2024-03)
โโโ Null checks were not removed after rewrite
โโโ New code copied the pattern from old code (cargo cult)
RECOMMENDATION: Remove 23 unreachable null checks.
Safe to remove: YES (getUser's contract guarantees non-null return).
The Mining Process
Phase 1: EXTRACTION
โโโ Parse all source files into structural representations
โโโ Identify functional blocks (functions, methods, handlers, middleware)
โโโ For each block, extract:
โ โโโ Input/output signature
โ โโโ Core operations performed
โ โโโ Error handling strategy
โ โโโ Side effects
โ โโโ Dependencies
โโโ Build a similarity matrix between all blocks
Phase 2: CLUSTERING
โโโ Group blocks by semantic similarity (not just syntactic)
โโโ For each cluster:
โ โโโ How many instances? (2-3 = emerging, 4+ = established)
โ โโโ How consistent? (identical = convergent, varied = divergent)
โ โโโ How old? (all recent = emerging, all old = fossilized)
โ โโโ Trend? (growing = emerging, stable = established, declining = fossilized)
โโโ Filter noise: single-line patterns, framework boilerplate, trivial duplication
Phase 3: ANALYSIS
โโโ For convergent patterns:
โ โโโ What's the canonical form? (most common variant)
โ โโโ What are the meaningful variations? (configurable vs. copy-paste error)
โ โโโ Extraction difficulty (how coupled is each instance?)
โ โโโ Extraction benefit (how much code eliminated ร frequency of change)
โโโ For divergent patterns:
โ โโโ Which instance is "wrong"? (or is the pattern itself wrong?)
โ โโโ Impact of divergence (confuses developers? causes bugs?)
โ โโโ Alignment difficulty
โโโ For emerging patterns:
โ โโโ Is abstraction justified yet? (rule of three)
โ โโโ What would the interface look like?
โ โโโ Will this pattern keep growing?
โโโ For fossilized patterns:
โโโ When did the justification die?
โโโ Is removal safe?
โโโ How many instances to clean up?
Phase 4: MINE REPORT
โโโ Patterns discovered, by type
โโโ Extraction/cleanup recommendations, prioritized by:
โ โโโ Bug risk (divergent patterns first)
โ โโโ Development velocity (most-duplicated convergent patterns)
โ โโโ Code health (fossilized patterns for cleanup)
โ โโโ Timeliness (emerging patterns before they spread)
โโโ Estimated effort for each recommendation
Output Format
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PATTERN MINE โ
โ Codebase: acme-platform โ
โ Files scanned: 347 / Patterns found: 18 โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ โ
โ CONVERGENT (should unify): 6 patterns โ
โ โโโ Retry with backoff ........... 3 variants, 3 files โ
โ โ Extraction saves: ~45 lines, unifies behavior โ
โ โโโ API response formatting ...... 4 variants, 12 files โ
โ โ Extraction saves: ~120 lines, fixes 2 inconsistencies โ
โ โโโ Input sanitization ........... 3 variants, 8 files โ
โ โ โ One variant misses XSS case (security risk) โ
โ โโโ Date parsing from API ........ 2 variants, 6 files โ
โ โโโ Pagination parameter handling 3 variants, 9 files โ
โ โโโ Cache key generation ......... 2 variants, 4 files โ
โ โ
โ DIVERGENT (should align): 3 patterns โ
โ โโโ Validator return types ....... UserValidator deviates โ
โ โโโ Error response shape ........ /admin routes differ โ
โ โโโ Logging level usage ......... warn vs error inconsistentโ
โ โ
โ EMERGING (watch / extract soon): 4 patterns โ
โ โโโ Role check + audit log ....... 2 locations (growing) โ
โ โโโ Optimistic lock + retry ...... 2 locations โ
โ โโโ Feature flag gating .......... 3 locations (new pattern)โ
โ โโโ Webhook dispatch + logging ... 2 locations โ
โ โ
โ FOSSILIZED (safe to remove): 5 patterns โ
โ โโโ Null check after non-nullable 23 locations, 0 risk โ
โ โโโ IE11 polyfill conditionals ... 7 locations, 0 risk โ
โ โโโ Legacy encoding detection .... 4 locations, 0 risk โ
โ โโโ Manual promise wrapping ...... 3 locations (use async) โ
โ โโโ Explicit bind(this) in arrow 12 locations (no-op) โ
โ โ
โ TOP RECOMMENDATION: โ
โ Extract API response formatter (12 files, 4 variants). โ
โ Highest ROI: most duplicated ร most frequently changed. โ
โ Estimated effort: 3 hours. Eliminates 120 lines + 2 bugs. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
When to Invoke
- Before any refactoring effort โ know what patterns exist before restructuring
- When onboarding (understand the codebase's actual patterns, not just the documented ones)
- During sprint planning for cleanup work (prioritized extraction targets)
- When a code review reveals "we have this pattern everywhere"
- After a new developer joins and writes code that almost matches existing patterns
- Quarterly, as a health check (are patterns converging or diverging?)
Why It Matters
Unmined patterns are a hidden tax on every developer who reads, writes, or modifies the code. Every time someone writes retry logic from scratch because they didn't know a retry utility exists (or because the existing three retry utilities are all slightly different), the codebase gets a little bigger, a little more inconsistent, and a little harder to understand.
Pattern Mine doesn't tell you to DRY everything. It tells you where DRY matters and where it doesn't โ so you abstract the right things at the right time.
Zero external dependencies. Zero API calls. Pure structural and semantic analysis.
ๅฆไฝไฝฟ็จใPattern Mineใ๏ผ
- ๆๅผๅฐ้พ่พAI๏ผWeb ๆ iOS App๏ผ
- ็นๅปไธๆนใ็ซๅณไฝฟ็จใๆ้ฎ๏ผๆๅจๅฏน่ฏๆกไธญ่พๅ ฅไปปๅกๆ่ฟฐ
- ๅฐ้พ่พAI ไผ่ชๅจๅน้ ๅนถ่ฐ็จใPattern Mineใๆ่ฝๅฎๆไปปๅก
- ็ปๆๅณๆถๅ็ฐ๏ผๆฏๆ็ปง็ปญๅฏน่ฏไผๅ