Skip to main content

Capture Patterns

"I keep solving the same class of problem. How do I turn that into something reusable?"

A pattern generalizes a solved problem into a named, reusable solution. Use it when the same class of problem can recur β€” not just to document a single incident.

Patterns vs. lessons​

DimensionLessonPattern
ScopeA specific incidentA recurring class of problems
VoiceNarrative ("we found…")Prescriptive ("when X, do Y")
NameDescriptive titleA noun phrase that names the technique
ReuseDocuments what happenedGuides future decisions

A lesson is raw material. A pattern is the distillation. Use /kmgraph:capture-lesson --category patterns when the insight applies beyond the incident that produced it.

Example:

  • Lesson: "CI builds failed because the connection pool was exhausted by parallel runners."
  • Pattern: Connection Pooling β€” maintain a shared pool of reusable connections; do not allocate one connection per request.

The command​

A pattern entry is appropriate when all of the following are true:

  • The same problem can recur in different contexts or projects.
  • The solution is transferable without deep rework.
  • The insight is not obvious from standard documentation.

If any condition fails, create a lesson instead.

You've observed the problem at least once and can name the solution in a noun phrase β€” then run:

/kmgraph:capture-lesson --category patterns

When prompted, provide the pattern name as the title and paste the filled template as the body. The command will write the entry to the active knowledge graph and index it for full-text search.

Confirm with /kmgraph:recall [pattern name] β€” the entry should appear.

Name the pattern​

Choose a noun phrase that describes the technique, not the symptom:

  • Good: "Connection Pooling", "Retry with Exponential Backoff", "Centralized Resource Pooling"
  • Weak: "Fix for CI timeouts", "What we did with Postgres"

The name is the primary search key. It must be recognizable out of context.

Template​

## [Pattern Name]

**Problem:** [One sentence. What recurring situation does this solve?]

**Solution:** [One sentence. What is the canonical response?]

**When to use:**
- [Trigger condition 1]
- [Trigger condition 2]

**Quick Reference:**
- [Key configuration value or threshold]
- [Key constraint or warning]
- [Monitoring recommendation]

**Cross-References:**
- **Lesson:** [[path/to/originating-lesson.md]]
- **ADR:** [[path/to/related-adr.md]]

Every field is required. A missing "When to use" section is the most common reason a pattern becomes useless six months later.

If the pattern was derived from a specific lesson, open that lesson file and add a cross-reference in its Cross-References section:

## Cross-References
- **Pattern:** [[patterns/connection-pooling.md]]

Bidirectional linking is required for the graph traversal in /kmgraph:recall to surface the connection. Maintain links in both directions: in the pattern file, list every lesson that instantiates or informs the pattern under Cross-References; in each lesson file, list the derived pattern under its Cross-References section. Use the wiki-link format [[relative/path.md]] for all internal references β€” absolute URLs and bare filenames are not indexed by the graph traversal engine.


Reference​

Good pattern vs. weak pattern​

Weak pattern entry:

## Performance Fix

**Problem:** Things were slow.
**Solution:** We made it faster.
**When to use:** When slow.
**Quick Reference:** (none)
**Cross-References:** (none)

Problems: vague problem statement, no trigger conditions, no actionable reference, no links.

Strong pattern entry:

## Connection Pooling

**Problem:** Creating a new database connection per request is expensive
and fails under concurrent load.

**Solution:** Maintain a shared pool of pre-allocated connections;
application instances borrow from and return to the pool.

**When to use:**
- Application handles >100 requests/second
- Database connection overhead is measurable (>5ms per request)
- Connection limit constraints exist at the database layer

**Quick Reference:**
- Pool size: 10–20 per application instance
- Idle timeout: 30–60 seconds
- Max wait time: 5–10 seconds
- Monitor: pool utilization, wait time, timeout errors

**Cross-References:**
- **Lesson:** [[debugging/ci-connection-pool-exhaustion.md]]
- **ADR:** [[decisions/ADR-012-pgbouncer.md]]

The strong version names a specific condition, states a testable solution, gives numeric guidance, and links to the evidence.

STAR format​

Structure knowledge narratives to answer:

  • Situation: What was the context?
  • Task: What needed to be done?
  • Action: What did you do?
  • Result: What happened?

Review checklist​

Before finalizing a pattern (or any knowledge entry):

Content:

  • Title is descriptive and searchable
  • Problem/context clearly explained
  • Solution includes enough detail to replicate
  • Key insights explicitly stated
  • Examples included (code, config, diagrams)

Structure:

  • Follows template for document type
  • Sections in logical order
  • Headers used for navigation
  • Lists/bullets for scanability

Connections:

  • Cross-references to related knowledge
  • Keywords/tags for search
  • Category appropriate
  • Links use correct format ([[relative/path.md]])

Quality:

  • Written for future-you (6 months later)
  • Specific enough to be useful
  • Concise enough to be readable
  • Free of sensitive data

Common mistakes​

1. Too abstract​

Weak:

# Lesson: Performance Optimization
We optimized the system and it got faster.

Strong:

# Lesson: Database Query N+1 Problem
Reduced API response time from 5s to 300ms by converting
15 sequential queries to a single JOIN query.

2. Missing context​

Weak:

Changed max_connections to 100.

Strong:

Increased PostgreSQL max_connections from 50 to 100.

**Why:** 5 app instances * 20 pool size = 100 connections needed.
**Impact:** Eliminated "too many connections" errors.

3. No replication steps​

Weak:

Fixed the bug by adding validation.

Strong:

**Fix:**
1. Add validation schema in `config/validation.js`
2. Import in request handler: `const { validate } = require('./validation')`
3. Call before processing: `if (!validate(req.body)) return 400`
4. Add test in `tests/validation.test.js`

4. Weak cross-references​

Weak:

Related to some other docs.

Strong:

## Cross-References
- **Pattern:** [[patterns.md#input-validation]]
- **Gotcha:** [[gotchas.md#validation-bypass]]
- **Related Lesson:** [[debugging/validation-regression.md]]
- **ADR:** [[ADR-008-validation-strategy.md]]