How this is builtGroundworkHow this is built
3 / 26 written chapters

B3 · Part 1 · Foundations

Where everything lives

Ten directories, the rule each one follows, and the places the rules bend.

6 min read1 diagram6 sections
06On this page
  1. The map
  2. Which way the imports point
  3. Inside app/
  4. Inside components/ and lib/
  5. The content directory in detail
  6. The files that are still large

The map

The repository holds 552 tracked files. Nearly all of them sit in four directories, and the split between those four is the most useful thing to know before opening anything. The counts are from git ls-files at commit d6b12d7.

DirectoryFilesWhat is in itThe rule
app/158Routes: one folder per topic, the tools, the three API routes, and the metadata filesA route's page.tsx is a server component; the interactive part sits next to it as a client view
components/56UI shared by more than one route: the page frames, the reader, the playgroundA component marked "use client" must earn it
lib/91Logic with no markup: content access, storage, runners and workers, and the enginesThe engines are pure functions, so they can be tested without a browser
content/212Every word on the site, and every exercise, as typed TypeScript: 5.4 MB of itRead through lib/, not by pages
public/0The TypeScript lib files and the React sandbox bundleGenerated by scripts before every build and ignored by git
scripts/4The two prebuild steps, the comment checker, and a Vercel clean-up toolPlain Node, run by npm lifecycle hooks, git hooks or by hand
tests/15VitestTests data and pure logic, not components
e2e/1Playwright: one smoke specA real browser against a production build
docs/3The roadmap and an auditNot shipped to readers
.github/2A Vercel clean-up workflow, and DependabotNo CI: the checks run locally

Which way the imports point

content/ 212 files lib/ 91 files app/ 158 files components/ 56 files tests/ scripts/ 2 prebuild steps public/wasm/ generated renders writes An arrow points from what is read to what reads it.
Content flows one way. Nothing in content/ imports code, and pages ask lib/ for chapters rather than reaching into the files themselves.

The rule has three exceptions, and they are worth knowing because each one is a small cost someone accepted. app/git/ imports content/git-body.ts directly, because the Git guide is one pre-written HTML string rather than chapters, so there is nothing for lib/ to do. app/mock/Room.tsx dynamically imports content/practice in the browser the first time a coding question appears, so it can hand the full exercise to the editor; that is the whole exercise set, downloaded once, on the one screen that needs it. And lib/practiceLinks.ts imports a type from app/practice/, the only place lib/ looks upward.

Inside app/

Thirty-one of the route folders are topics. Twenty of them have the same four things: page.tsx for the cover, [chapter]/page.tsx, [chapter]/loading.tsx for the skeleton, and search-index.json/route.ts. The other eleven, Python, Java, C++, Rust, Ruby, Go, MongoDB, DBMS, Networks, Operating Systems and Claude and AI tools, have no chapters yet and hold only page.tsx. Each of those files is a few lines that call into components/reader/topicPages.tsx, so the folders are copies of one shape rather than thirty-one implementations. The architecture folder is the one without a loading.tsx.

The tools are different. app/mock/ has 14 files besides its page: the lobby, the loop wizard, the room, the scorecard, the readiness dashboard, a timer ring, a small design whiteboard, and the hook that fetches question banks. app/whiteboard/ has 11, led by Board.tsx at 1,851 lines, the largest component in the repository. They live with their routes because nothing else uses them. The routes chapter walks the whole tree.

Inside components/ and lib/

WhereWhat
components/frame/PageFrame.tsx, components/frame/FrameHeader.tsx, components/topic/TopicFrame.tsxThe frame every page but the whiteboard uses, and its header (a back pill, a menu button, the brand), which the playground and the problem pages reuse around their full-height shell
components/SiteDrawer.tsx, AppHeader.tsxThe slide-in menu every frame opens, and the header of the full-screen tools
components/Modal.tsx, AppearancePicker.tsxDialogs on the native <dialog> element, and the theme and handwriting picker shared by the playground and the whiteboard
components/reader/11 files: the helpers a chapter page leans on, such as search indexing, narration, the reader's DOM enhancements and the practice strip
components/practice/11 files, led by PracticeWorkspace.tsx at about 1,800 lines and CodeEditor.tsx at about 1,100, with workbench.module.css holding the page shell
lib/polyglot/Records test cases from JavaScript solutions and grades other languages against them
lib/mock/12 files: loops, pacing, scoring, readiness and the session reducer of the mock interview
lib/whiteboard/The board's element model, geometry, export and storage
lib/debug/, lib/editor/The step-through debugger's instrumentation, and the editor's lint and format worker
lib/*Worker.ts, lib/*Runner.tsOne runner per language family on the page side, and the three execution workers behind them

There are two whiteboards. lib/whiteboard/ is the full board at /whiteboard, with freehand, fifteen element kinds and arrows that stay attached. lib/mock/board.ts is a much smaller one inside the mock interview's design round: boxes, databases, queues and arrows, serialised into the interview session so the scorecard can draw it again. The cost of keeping them apart is two drawings of boxes and arrows. The benefit is that the interview's board model is 84 lines and cannot break when the 1,851-line board changes.

The content directory in detail

content/
  types.ts              ← every content type
  topics.ts             ← the shelf: topics, levels, syllabus
  notes.ts              ← barrel for JavaScript (served at /notes)
  js/                   ← 41 chapter files
  react-notes.ts, react/          ← 57
  dsa-notes.ts, dsa/              ← 34 written, 8 outlined
  system-design-notes.ts, system-design/  ← 24
  architecture-notes.ts, architecture/    ← this topic
  html-notes.ts ... kubernetes-notes.ts   ← outlines only
  interview-data.ts     ← the interview book, its own record shape
  git-body.ts           ← the Git guide, one HTML string
  practice.ts           ← barrel over the exercise groups
  practice/
    js-fundamentals.ts, js-applied.ts
    dsa.ts + dsa-1.ts ... dsa-8.ts
    react.ts, react-components.ts

It did not start that way. Four files had grown past the point where an editor is comfortable: the exercises were 840 KB in one file, the DSA notes 626 KB, system design 577 KB and the JavaScript notes 306 KB. They were split by walking the TypeScript AST and moving each chapter's source text into its own file, so the formatting survived and the assembled data came out byte-for-byte identical to what it had been.

Why the split had to be textual

The obvious way to split a data file is to import it, then write each piece back out as JSON. That would have turned hundreds of kilobytes of readable HTML bodies, written as template literals with real newlines, into one escaped line each: making the files worse rather than better, while fixing the size. Moving the source text instead keeps every line where the author put it.

The files that are still large

content/interview-data.ts is 430,468 bytes, the largest tracked file after package-lock.json. It has its own record shape, one object per round with questions, answers, traps and follow-ups, and it is written one field per line on purpose, which is why .prettierignore lists it: formatting it would explode it into thousands of lines without changing what a reader sees. The exercise files are next. content/practice/react-components.ts is 387,720 bytes, and the whole content/practice/ folder is also in .prettierignore. content/topics.ts, at 181,034 bytes, used to be ignored too; it is now formatted like everything else.