01. What this site is
The site is a self-paced AI-engineering curriculum delivered as a single Next.js App Router application deployed to Vercel. Every lesson, from "Phase 1" fundamentals to the production‑ship phase, is a plain Markdown file under content/ — for example, content/phase-2-prompting.md. There is no database of lesson text at rest; the filesystem is the authoritive store.
The architecture’s backbone is one ordered list: LESSON_SLUGS in lib/articles.ts. Each slug’s position in that array (1‑indexed) defines its lesson number. The same array also drives the audiobook auto‑advance sequence — the “flow order” that the persistent player follows. Because the array is used for both numbering and sequencing, the two are always in sync: lesson 4 follows lesson 3 in the player because the slug at index 4 follows the slug at index 3. This single source of truth eliminates drift between a separate numbering list and a separate play‑order list. LESSON_NUMBER is a computed lookup object derived from the same array, so every getLessonBySlug call can retrieve the correct number without a second config.
The lesson read path lives in lib/data.ts and implements a three‑tier fallback designed for a Vercel‑deployed app that may or may not have a D1 database attached. On each read (getLessonBySlug), the first tier attempts to fetch from a D1 content‑cache — a writable database that lets authors push updated lesson content without redeploying the entire site. If D1 is unavailable or empty, the second tier loads from a JSON bundle (export-content) that was generated at build time from the same Markdown files. This bundle is fast and cacheable but static. The final fallback is the filesystem parser in lib/articles.ts itself, which reads the .md files directly at runtime — useful during local development or when no build output exists.
Why not just rely on one tier? Each tier buys a different operational property. D1 gives hot‑content updates without a build — ideal for corrections or additions between deployments. The JSON bundle makes cold starts fast and survives D1 outages. The filesystem parser keeps the app runnable out of the box on a fresh clone without any infrastructure setup. The fallback chain is expressed with a simple pattern: try the fast path, catch silently and move to the next. The same three‑tier pattern is used for getAllLessons, getCategoryMeta, and getGroupedLessons. This layered approach means the site can operate in environments ranging from a fully‑provisioned production Vercel deployment, through preview branches with D1 replicas, down to a local npm run dev with nothing but a content/ directory.
The Lesson interface itself is lean: slug, number, title, category, excerpt, difficulty, wordCount, readingTimeMin, url. The difficulty field is never stored — it is derived positionally by getDifficulty in lib/articles.ts, which slices each category’s lesson range into beginner, intermediate, and advanced bands. This keeps the data model immutable and avoids stale difficulty metadata. The entire curriculum — numbering, ordering, difficulty classification, and audiobook spine — flows from that single LESSON_SLUGS array, a design that trades flexibility for coherence and makes the player’s auto‑advance both predictable and trivially debuggable.
It's a website that teaches you how to become an AI engineer. It's one app, and every lesson is just a text file — there's no lesson database sitting underneath it.
2Morego a level deeper
The lessons are markdown files in a folder, and one ordered list decides both what number each lesson gets and what plays next in the audiobook, so the two can never drift apart. When a page needs a lesson it tries the live database first, then a copy baked in at build time, then the raw file on disk — whichever answers first wins.
3Deepthe full mechanics
It's a Next.js App Router app deployed to Vercel. LESSON_SLUGS in lib/articles.ts is the single spine: array position (1-indexed) is the lesson number, LESSON_NUMBER is derived from it, and the same order drives the player's auto-advance. getLessonBySlug in lib/data.ts is a three-tier try/catch fallback — D1 content cache, then the build-time export-content JSON bundle, then the filesystem parser — and difficulty is never stored, just sliced positionally by getDifficulty. One array for everything trades flexibility for coherence.
Where this chapter's machinery lives in the repo:
lib/articles.ts:8-19
The typed Lesson metadata record the taxonomy grid and lesson pages are built from.
export interface Lesson {
slug: string;
fileSlug: string;
number: number;
title: string;
category: string;
excerpt: string;
difficulty: DifficultyLevel;
wordCount: number;
readingTimeMin: number;
url: string;
}
lib/articles.ts:43-53
LESSON_SLUGS — the ordered spine: a slug's position sets its lesson number and its audiobook play order.
// `*` slugs are new Cloudflare-specific pilot chapters.
const LESSON_SLUGS = [
// Phase 1 · Foundations & Model Inference (1-9)
"ai-on-cloudflare-workers", // *
"roadmap",
"workers-ai-models", // *
"transformer-architecture",
"tokenization",
"model-architectures",
"scaling-laws",
"inference-optimization",
lib/data.ts:25-29
The lesson read path the [slug] route actually uses: D1 content_cache first, bundled JSON export second, markdown parser only as the dev-time fallback (see getLessonBySlug below this comment).
// Lesson content resolves D1 first (dynamic, written by the `kg.sync_d1`
// publish step → new/edited content shows on refresh, no redeploy), then the
// build-bundled JSON exported by `export-content`, then the markdown parser in
// ./articles as the last-resort fallback. Each layer is independent, so an
// unconfigured/empty D1 silently falls through.
What single array in lib/articles.ts drives lesson numbering, ordering, difficulty classification, and audiobook auto-advance?
Show answer
LESSON_SLUGS
From the research: Retrieval practice / testing effect — Testing (quizzing) boosts classroom learning: A systematic and meta-analytic review (2021)