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 the PREREQS dependency map and graph traversal logic including getAncestors() and getDescendants() functions.
  • site/data.js: Auto-generated file containing the PHASES array with lesson metadata, built from markdown source files by the CI pipeline.
  • site/build.js: CI script that parses lesson front-matter and regenerates data.js on 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 PREREQS object within site/prereqs.html, while lesson-level requirements are declared via front-matter in individual docs/en.md files.
  • The build pipeline (site/build.js) automatically generates site/data.js by scanning markdown front-matter, ensuring the UI always reflects current curriculum structure.
  • Runtime traversal uses getAncestors() and getDescendants() 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →