๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
ๅฐ้พ™่™พๅฐ้พ™่™พAI
๐Ÿค–

Pattern Mine

Discovers recurring patterns buried in your codebase โ€” similar logic written three different ways, duplicated validation in five controllers, the same error...

ไธ‹่ฝฝ50
ๆ˜Ÿๆ ‡0
็‰ˆๆœฌ1.0.0
ๅ†™ไฝœๅˆ›ไฝœ
ๅฎ‰ๅ…จ้€š่ฟ‡
๐Ÿ”—API

ๆŠ€่ƒฝ่ฏดๆ˜Ž


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:

  1. Convergent patterns: Different code doing the same thing (should be unified)
  2. Divergent patterns: Same code doing different things (should be separated)
  3. Emerging patterns: A pattern forming but not yet crystallized (candidate for abstraction)
  4. 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ใ€๏ผŸ

  1. ๆ‰“ๅผ€ๅฐ้พ™่™พAI๏ผˆWeb ๆˆ– iOS App๏ผ‰
  2. ็‚นๅ‡ปไธŠๆ–นใ€Œ็ซ‹ๅณไฝฟ็”จใ€ๆŒ‰้’ฎ๏ผŒๆˆ–ๅœจๅฏน่ฏๆก†ไธญ่พ“ๅ…ฅไปปๅŠกๆ่ฟฐ
  3. ๅฐ้พ™่™พAI ไผš่‡ชๅŠจๅŒน้…ๅนถ่ฐƒ็”จใ€ŒPattern Mineใ€ๆŠ€่ƒฝๅฎŒๆˆไปปๅŠก
  4. ็ป“ๆžœๅณๆ—ถๅ‘ˆ็Žฐ๏ผŒๆ”ฏๆŒ็ปง็ปญๅฏน่ฏไผ˜ๅŒ–

็›ธๅ…ณๆŠ€่ƒฝ