Prerequisite System for Lesson Ordering in AI Engineering from Scratch
The curriculum implements a directed acyclic graph of 20 phases and approximately 503 lessons, enforcing completion order through front-matter declarations in markdown files and a JavaScript dependency map that drives the interactive roadmap UI.
The prerequisite system for lesson ordering in the rohitg00/ai-engineering-from-scratch repository ensures learners progress through complex AI engineering topics in a logical, dependency-aware sequence. This open-source curriculum organizes content into a directed acyclic graph where phases depend on predecessor phases and individual lessons reference specific upstream requirements. Understanding this architecture helps contributors maintain dependency integrity while allowing learners to navigate the learning path efficiently.
Architecture of the Prerequisite System
Phase-Level Dependency Mapping
The high-level curriculum structure is defined in site/prereqs.html (lines 523-545) through a JavaScript object named PREREQS. This hand-maintained map encodes which of the 20 phases depend on others, creating the backbone of the learning roadmap. The system treats phases as nodes in a graph where edges represent completion requirements. For example, declaring 5: [3, 4] establishes that Phase 5 requires completion of Phases 3 and 4.
Lesson-Level Prerequisites
Individual lessons declare their requirements within markdown front-matter. Each lesson's docs/en.md file contains a line formatted as **Prerequisites:** … listing specific phases or lessons that must be completed first. As seen in the tokenizer lesson (line 7), this human-readable declaration specifies exact upstream dependencies like Phase 04 lessons, Phase 07 transformer lessons, providing granular ordering constraints at the topic level.
Build-Time Data Generation
The site/build.js script automates the bridge between static markdown and runtime data structures. Running on every CI push, this script scans all phase folders under the phases/ directory, extracts front-matter including prerequisite fields, and generates site/data.js. The resulting PHASES array (lines 4-19) contains each phase's ID, name, status, and lesson list, enabling the UI to construct the complete dependency graph without manual updates.
Runtime Dependency Resolution
Graph Traversal Functions
The site/prereqs.html file implements the runtime engine for prerequisite validation through two critical functions: getAncestors(id) and getDescendants(id) (lines 648-660). These functions perform breadth-first traversal of the PREREQS and CHILDREN structures to compute the transitive closure of dependencies for any selected node. The algorithm maintains a queue of pending nodes and a result object to track visited prerequisites, ensuring complete ancestor chains are resolved.
UI Locking Mechanism
When a user selects a phase node, the roadmap calls getAncestors() to highlight prerequisite phases and determine if the "Read" button should be enabled. The system checks if all ancestor phases are satisfied before allowing access to lesson content. This runtime evaluation ensures the DAG integrity is respected throughout the learning experience, preventing learners from accessing advanced topics without foundational knowledge.
Core Implementation Files
site/prereqs.html: Contains thePREREQSdependency map and graph traversal logic includinggetAncestors()andgetDescendants()functions.site/data.js: Auto-generated file containing thePHASESarray with lesson metadata, built from markdown source files by the CI pipeline.site/build.js: CI script that parses lesson front-matter and regeneratesdata.json repository updates.phases/**/docs/en.md: Individual lesson files containing the**Prerequisites:**front-matter declarations.
Working with the Prerequisite System
Extract lesson prerequisites from the repository using Python:
import pathlib, re, json
repo_root = pathlib.Path("/cache/repos/github.com/rohitg00/ai-engineering-from-scratch/main")
prereq_pattern = re.compile(r"\*\*Prerequisites:\*\*\s*(.+)")
def extract_prereqs():
data = {}
for md in repo_root.glob("phases/**/*.md"):
with md.open() as f:
for line in f:
m = prereq_pattern.search(line)
if m:
data[str(md)] = m.group(1).strip()
break
return data
if __name__ == "__main__":
prereqs = extract_prereqs()
print(json.dumps(prereqs, indent=2)[:500])
Compute the full ancestor set for a phase in JavaScript:
function getAncestors(id) {
var result = {};
var queue = PREREQS[id] ? PREREQS[id].slice() : [];
while (queue.length) {
var p = queue.shift();
if (result[p]) continue;
result[p] = true;
if (PREREQS[p]) {
for (var i = 0; i < PREREQS[p].length; i++) queue.push(PREREQS[p][i]);
}
}
return result;
}
Add a new phase dependency by editing site/prereqs.html:
// To make Phase 5 depend on Phases 3 and 4:
5: [3, 4],
The UI will automatically draw edges from phases 3 and 4 to phase 5, and the graph traversal will treat them as prerequisites for unlocking content.
Summary
- The prerequisite system for lesson ordering implements a directed acyclic graph across 20 phases and approximately 503 lessons to prevent circular dependencies and ensure logical progression.
- Phase-level dependencies are defined in the
PREREQSobject withinsite/prereqs.html, while lesson-level requirements are declared via front-matter in individualdocs/en.mdfiles. - The build pipeline (
site/build.js) automatically generatessite/data.jsby scanning markdown front-matter, ensuring the UI always reflects current curriculum structure. - Runtime traversal uses
getAncestors()andgetDescendants()functions to compute dependency closures and control access to lesson content. - All components work together to guarantee learners encounter advanced topics only after completing foundational prerequisites.
Frequently Asked Questions
How does the system prevent circular dependencies in the curriculum?
The prerequisite system enforces a directed acyclic graph (DAG) structure at the architectural level. Because the PREREQS map in site/prereqs.html only allows unidirectional phase dependencies and the build process validates these relationships, circular references cannot be introduced without breaking the generation script or creating infinite loops in the getAncestors traversal.
Where are lesson prerequisites defined in the source code?
Individual lesson prerequisites are declared in the markdown front-matter of each lesson's documentation file, specifically within phases/**/docs/en.md files using the format **Prerequisites:** [list of requirements]. These declarations are harvested during the CI build process by site/build.js to populate the generated data structures used by the roadmap interface.
What happens when I add a new phase to the curriculum?
When adding a new phase, you must edit the PREREQS object in site/prereqs.html to specify which existing phases it depends on, and optionally declare which phases depend on it. After updating the phase directory structure, the build.js script automatically includes the new phase in site/data.js during the next CI run, making it available to the roadmap UI and traversal functions.
How does the UI determine if a lesson is unlocked for reading?
The roadmap interface calls getAncestors(phaseId) to retrieve the complete set of prerequisite phases, then checks completion status against these requirements. Only when all ancestor phases are marked complete does the interface render the "Read" button linking to the lesson content, ensuring strict adherence to the dependency chain defined in the curriculum source files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →