Flex Engineers
4 min read

Why We Keep a Version-Controlled Interview & Algorithms Prep Repo

Data StructuresJavaScriptTypeScriptComputer ScienceTechnical MentoringAlgorithm Development

The problem with "I'll remember it"

Most engineers accumulate interview and algorithms knowledge the same way: a scratch file here, a bookmarked article there, a half-finished LeetCode folder that gets abandoned once the pressure is off. Six months later, when a client's technical interview round comes up, or a hiring loop needs a second interviewer, it's all gone — rebuilt from scratch under time pressure.

We got tired of that cycle across the team, so we built a shared repo for it. Not a gist, not a Notion doc — an actual git repository with the same rigor we apply to a production codebase: versioned files, a consistent naming convention, runnable code, and a structure that scales as we add more topics.

What's actually in it

The repo is organized as numbered, topic-scoped files rather than one giant notes dump. That numbering matters — it encodes a study order (fundamentals before frameworks, frameworks before infra) and makes it trivial for any engineer to see what's covered at a glance:

01. JS.md            06. NodeJS.md
02-03. TS.md          07-08. Git.md
04. React.md          09-10. Docker.md
05. NextJS.md         11. ReactNative.md
DSA/                  CLRS/

Two of these deserve more explanation because they're not just markdown:

DSA/ is a small, standalone Node package (its own package.json) with hand-written implementations of the structures that actually come up in interviews — linked lists, doubly linked lists, stacks, queues, trees, hash tables, graphs, heaps, a recursive/randomized BST, and the standard sort family (bubble, insertion, selection, merge, quick), plus a dynamic programming file. Every implementation is written to run standalone via node, with console.log walkthroughs at the bottom of the file rather than a test framework — the point isn't test coverage, it's being able to node "10. RBST.js" and watch the tree mutate step by step.

#deleteNode(value, currentNode) {
  if (currentNode === null) return null;
  if (value < currentNode.value) {
    currentNode.left = this.#deleteNode(value, currentNode.left);
  } else if (value > currentNode.value) {
    currentNode.right = this.#deleteNode(value, currentNode.right);
  } else {
    if (currentNode.left === null && currentNode.right === null) return null;
    else if (currentNode.left === null) currentNode = currentNode.right;
    else if (currentNode.right === null) currentNode = currentNode.left;
    else {
      let subTreeMin = this.minValue(currentNode.right);
      currentNode.value = subTreeMin;
      currentNode.right = this.#deleteNode(subTreeMin, currentNode.right);
    }
  }
  return currentNode;
}

That's BST node deletion with the three-case branch (leaf, one child, two children via in-order successor) — the kind of thing that's easy to explain and surprisingly easy to get subtly wrong under interview pressure if you haven't typed it out recently.

The second file worth calling out is DSA/index.js. It's a mirror of every class and function in the package — same signatures, same private methods, all with empty bodies:

class RBST {
  constructor() {}
  #rinsert(value, currentNode = this.root) {}
  rInsert(value) {}
  #deleteNode(value, currentNode) {}
  deleteNode(value) {}
  BFS() {}
  DFSPreOrder() {}
  DFSInOrder() {}
  DFSPostOrder() {}
}

This is deliberate: it's a self-quiz any engineer on the team can use. To check whether an implementation was actually retained rather than just read once, an engineer opens index.js and fills in the blanks from memory, then diffs the attempt against the real file. It's the same principle as closed-book practice exams — recognition isn't recall, and this file forces recall.

CLRS/ is a chapter-by-chapter set of notes following Introduction to Algorithms — from "The Role of Algorithms in Computing" through Red-Black Trees, Dynamic Programming, Graph Algorithms, NP-Completeness, and Approximation Algorithms. It's the theoretical backbone underneath the DSA/ implementations: the CLRS notes cover why an algorithm has a given complexity; the DSA folder is how it's actually coded in JavaScript.

Design decisions that weren't accidental

A few choices in how this repo is structured are worth calling out, because they generalize beyond interview prep:

DecisionWhy
Numbered filenames (01., 02., …)Encodes study order without needing a separate index; sorts correctly in any file explorer or ls
Split TS across two files (02. and 03.)Basic vs. advanced type-system topics don't belong in the same reading pass
DSA/ is its own npm packageKeeps runnable code isolated from prose notes — no risk of a require reaching into unrelated markdown
Tables for feature comparisons (var/let/const, scope/hoisting/reassignment)A table answers "which one applies here" in one glance; prose buries the answer in a paragraph
Empty-body mirror file (index.js)Converts a reference doc into an active recall tool, for free, with zero extra tooling

None of this required a framework, a static site generator, or tooling of any kind — it's git, markdown, and Node, used with intent. That's the point: good structure doesn't need heavy infrastructure, it needs a consistent convention applied without exception, and it needs to be a shared team asset rather than something each engineer rebuilds alone.

Takeaway

This repo isn't a product, but it's a good proxy for how our team approaches any codebase: impose structure early, make the "how do you verify this" question answerable without re-deriving it from scratch, and prefer boring, durable tools (plain files, git history, a numbering scheme) over anything that adds ceremony without adding clarity. The same instinct — organize first, automate the boring parts, keep everything diffable — is what shows up in the production systems we build for clients.