Skip to content

A Personal Knowledge Management System for Developers

Design a sustainable knowledge workflow with Zettelkasten principles, progressive summarization and spaced repetition to retain and connect concepts.

4 min read
Knowledge graph visualization showing interconnected notes on programming concepts with bidirectional links forming clusters around architecture, testing, and performance topics

Developers consume massive amounts of technical information—documentation, blog posts, conference talks, code reviews, debugging sessions—but most of it evaporates within weeks. You've read the same article about database indexing three times because nothing stuck. A personal knowledge management (PKM) system fixes this not by saving everything, but by processing what matters and creating connections that surface when you need them.

The goal isn't a comprehensive archive. It's a thinking tool that helps you make better technical decisions by connecting what you've learned across contexts.

The Processing Pipeline

Raw information is worthless in storage. Value comes from processing: extracting key insights, connecting them to what you already know, and making them findable when relevant.

tstypescript
// ❌ The collector's trap — save everything, process nothing
interface RawNote {
  source: string;
  content: string; // Full copy of the article
  savedAt: Date;
  tags: string[];
  // Never looked at again after saving
}
tstypescript
// ✅ Progressive processing pipeline
interface CapturedNote {
  id: string;
  source: string;
  capturedAt: Date;
  status: "inbox" | "processing" | "permanent";
  rawHighlights: string[];
}
 
interface ProcessedNote {
  id: string;
  title: string; // Your own words, not the source title
  insight: string; // One core idea in 2-3 sentences
  evidence: string; // Supporting example or code snippet
  connections: string[]; // IDs of related notes
  applicationContext: string; // When would I use this?
  source: string;
  processedAt: Date;
}
 
// The processing step is where learning happens
function processCapture(
  raw: CapturedNote,
  existingNotes: ProcessedNote[]
): ProcessedNote {
  return {
    id: generateId(),
    // Rewrite in your own words — forces understanding
    title: "Index-only scans eliminate heap fetches",
    // Distill to the core insight
    insight:
      "A covering index includes all columns needed by a " +
      "query, allowing Postgres to satisfy the query entirely " +
      "from the index without reading the heap table. This " +
      "eliminates random I/O on large tables.",
    // Concrete evidence or example
    evidence: `
      CREATE INDEX idx_orders_covering
      ON orders (customer_id)
      INCLUDE (total, status, created_at);
      -- Query reads only from index, not table
    `,
    // Link to existing knowledge
    connections: findRelatedNotes(existingNotes, [
      "database-indexing",
      "query-optimization",
      "io-patterns",
    ]).map((n) => n.id),
    // When does this matter?
    applicationContext:
      "When queries on large tables have high heap fetch " +
      "counts in EXPLAIN ANALYZE, check if a covering " +
      "index can serve the query from index alone.",
    source: raw.source,
    processedAt: new Date(),
  };
}

The processing step is non-negotiable. It's where you transform passive reading into active understanding. Writing the insight in your own words forces you to actually understand the concept—you can't paraphrase something you don't comprehend.

Zettelkasten Linking for Technical Knowledge

The Zettelkasten method's core innovation is bidirectional linking between atomic notes. For developers, this creates a network where database concepts connect to performance patterns connect to monitoring strategies connect to incident response.

tstypescript
// Atomic note structure — one idea per note
interface Zettel {
  id: string;
  title: string;
  content: string;
  links: Array<{
    targetId: string;
    relationship: string; // Why these are connected
  }>;
  sequences: string[]; // Ordered note chains for topics
  tags: string[]; // Entry points, not categories
}
 
// Example note network for "database performance"
const notes: Zettel[] = [
  {
    id: "2023-12-a1",
    title: "Index-only scans eliminate heap table reads",
    content:
      "Covering indexes include all columns a query needs...",
    links: [
      {
        targetId: "2023-09-b3",
        relationship:
          "Covering indexes are a specific technique for " +
          "reducing the I/O patterns described in this note",
      },
      {
        targetId: "2023-11-c2",
        relationship:
          "EXPLAIN ANALYZE output shows whether index-only " +
          "scan is used, connecting to query analysis methods",
      },
    ],
    sequences: ["database-performance"],
    tags: ["postgresql", "indexing", "performance"],
  },
  {
    id: "2023-09-b3",
    title: "Random I/O vs sequential I/O cost difference",
    content:
      "Random reads are 10-100x slower than sequential...",
    links: [
      {
        targetId: "2023-12-a1",
        relationship: "Index-only scans avoid random I/O",
      },
      {
        targetId: "2023-08-d1",
        relationship: "SSD vs HDD changes the cost ratio",
      },
    ],
    sequences: ["database-performance", "systems-fundamentals"],
    tags: ["io", "performance", "hardware"],
  },
];
 
// Find notes by traversing connections
function getRelatedInsights(
  startId: string,
  notes: Zettel[],
  depth: number = 2
): Zettel[] {
  const visited = new Set<string>();
  const result: Zettel[] = [];
 
  function traverse(id: string, currentDepth: number) {
    if (currentDepth > depth || visited.has(id)) return;
    visited.add(id);
 
    const note = notes.find((n) => n.id === id);
    if (!note) return;
 
    result.push(note);
    for (const link of note.links) {
      traverse(link.targetId, currentDepth + 1);
    }
  }
 
  traverse(startId, 0);
  return result;
}

Spaced Repetition for Technical Retention

Passive review doesn't work. Spaced repetition schedules reviews at increasing intervals, testing recall when you're about to forget.

tstypescript
// Spaced repetition scheduler for technical concepts
interface FlashCard {
  id: string;
  front: string; // Question or prompt
  back: string; // Answer or explanation
  interval: number; // Days until next review
  easeFactor: number; // How easy this card is (2.5 default)
  nextReview: Date;
  noteId: string; // Link back to knowledge base
}
 
function scheduleReview(
  card: FlashCard,
  quality: 0 | 1 | 2 | 3 | 4 | 5
): FlashCard {
  // SM-2 algorithm (simplified)
  let { interval, easeFactor } = card;
 
  if (quality < 3) {
    // Failed — reset interval
    interval = 1;
  } else {
    if (interval === 0) interval = 1;
    else if (interval === 1) interval = 6;
    else interval = Math.round(interval * easeFactor);
  }
 
  // Adjust ease factor
  easeFactor = Math.max(
    1.3,
    easeFactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
  );
 
  const nextReview = new Date();
  nextReview.setDate(nextReview.getDate() + interval);
 
  return { ...card, interval, easeFactor, nextReview };
}
 
// Convert processed notes to flashcards
function noteToCards(note: ProcessedNote): FlashCard[] {
  return [
    {
      id: `${note.id}-concept`,
      front: `What is the core insight about: ${note.title}?`,
      back: note.insight,
      interval: 0,
      easeFactor: 2.5,
      nextReview: new Date(),
      noteId: note.id,
    },
    {
      id: `${note.id}-application`,
      front: `When would you apply: ${note.title}?`,
      back: note.applicationContext,
      interval: 0,
      easeFactor: 2.5,
      nextReview: new Date(),
      noteId: note.id,
    },
  ];
}

The Weekly Review Workflow

The system only works with a consistent review rhythm. A 30-minute weekly review keeps the pipeline flowing and connections growing.

markdownmarkdown
## Weekly PKM Review Checklist (30 minutes)
 
### Inbox Processing (15 min)
- [ ] Review all items in inbox (articles, highlights, notes)
- [ ] For each item: process into a permanent note OR delete
- [ ] Empty the inbox completely — no "I'll process this later"
 
### Connection Building (10 min)
- [ ] For each new permanent note, find 2-3 connections
- [ ] Write the relationship description (why connected)
- [ ] Check if any existing notes should link to the new ones
 
### Spaced Repetition (5 min)
- [ ] Review today's due flashcards
- [ ] Create cards for any new notes that have
      application-worthy insights
 
### Monthly: Note Quality Audit
- [ ] Pick 10 random notes — are they still useful?
- [ ] Merge notes that overlap significantly
- [ ] Archive notes that are outdated or wrong
- [ ] Update connections that no longer make sense
tstypescript
// Automate the weekly review reminders
interface ReviewMetrics {
  inboxCount: number;
  dueFlashcards: number;
  orphanNotes: number; // Notes with no connections
  staleNotes: number; // Notes not touched in 6 months
}
 
function generateReviewSummary(
  notes: ProcessedNote[],
  cards: FlashCard[]
): ReviewMetrics {
  const now = new Date();
  const sixMonthsAgo = new Date(
    now.getTime() - 180 * 24 * 60 * 60 * 1000
  );
 
  return {
    inboxCount: notes.filter(
      (n) => n.source && !n.connections.length
    ).length,
    dueFlashcards: cards.filter(
      (c) => c.nextReview <= now
    ).length,
    orphanNotes: notes.filter(
      (n) => n.connections.length === 0
    ).length,
    staleNotes: notes.filter(
      (n) => n.processedAt < sixMonthsAgo
    ).length,
  };
}

Key Takeaways

A PKM system's value comes from processing, not collecting—rewriting insights in your own words forces understanding and makes information retrievable, while raw highlights never get read again. Bidirectional links between atomic notes create a knowledge network where database concepts connect to performance patterns connect to incident response, surfacing relevant context when you need it. Spaced repetition using the SM-2 algorithm reviews concepts at increasing intervals, testing recall at the point of forgetting rather than wasting time re-reading familiar material. The weekly review habit is the system's heartbeat: 30 minutes of inbox processing, connection building, and flashcard review keeps information flowing from capture through permanent knowledge. Tags serve as entry points, not categories—use links and sequences to organize knowledge, since rigid categories force notes into single buckets while links allow the same insight to surface in multiple contexts.

Wilfredo Rujel

Wilfredo Rujel

Full Stack Software Engineer

Share this postX