notes · · 3 min · updated 2026-10-04

Jekyll Under the Microscope: What 67 Remote AST Tools Found Inside the Classic Ruby Static Site Generator

Jekyll static-site generator analysis with 22,830 lines across 162 files, a six-stage pipeline, and test/error search counts.

On this page · 5 sections
  1. Subsystem Architecture: Decoupled Transformation Subsystems
  2. The 6-Stage Compilation Pipeline
  3. Clone Analysis and Test Fixtures
  4. Semantic Invariants: 1,270 Test Assertions and 53 Exception Guards
  5. Remote AST Operations on Cluster Nodes

Before the modern Jamstack wave of Hugo, Astro, Next.js, and Gatsby, Jekyll fundamentally altered how developers publish content on the web. Created by GitHub co-founder Tom Preston-Werner in 2008, Jekyll eliminated database management, security patching, and server-side rendering vulnerabilities by transforming raw Markdown and Liquid templates into static HTML assets.

Powering GitHub Pages since its inception, Jekyll’s enduring design relies on an elegant, deterministic transformation pipeline that has processed billions of page builds over nearly two decades.

To inspect how Jekyll structures its site lifecycle, manages plugin extensibility, and maintains code quality, we deployed operations from prod-code’s 67-tool suite against a checkout mirrored to a remote 32-core cluster node (192.168.2.143:9400).

$ git ls-files '*.rb' | wc -l
162
$ git ls-files -z '*.rb' | xargs -0 wc -l | tail -n 1
22830 total
$ git ls-files | awk -F. '{if (NF>1) print $NF}' | sort | uniq -c | sort -nr | head -n 6
 163 md
 162 rb
 154 markdown
  82 html
  43 yml
  28 feature

The captured inventory shows 22,830 lines of Ruby across 162 source files:

  • Core Engine & Subsystems (lib/jekyll/): 10,824 lines across 89 files.
  • Test Harnesses & Fixtures (test/): 10,407 lines across 55 files.
  • CLI Commands & Utilities: 1,599 lines across 18 files.

Subsystem Architecture: Decoupled Transformation Subsystems

Jekyll organizes its core pipeline into single-responsibility modules under lib/jekyll/:

  1. Site Lifecycle (site.rb): The master orchestrator coordinating data reading, plugin execution, rendering, and filesystem writes.
  2. Readers & Converters (readers/, converters/): Ingests front matter, Markdown files, Sass/SCSS stylesheets, and static files.
  3. Generators & Liquid Drop System (generator.rb, drops/): Enables plugins to inject dynamic pages (taxonomies, feeds, sitemaps) and safely expose Ruby objects to Liquid templates without leaking execution privileges.
  4. Commands & Watcher (commands/, watcher.rb): Implements jekyll build, jekyll serve, local WEBrick/http servers, and LiveReload web socket channels.

We ran prod-code dependencies across the codebase:

$ prod-code dependencies --scope modules
⚡ prod-code Architecture & Dependency Graph Report
────────────────────────────────────────────────────
Scope: modules | Nodes: 5 | Dependencies: 0

✓ Zero circular dependencies detected. Architecture graph is a clean DAG.

The module graph is a clean Directed Acyclic Graph (DAG). Low-level utilities in lib/jekyll/utils.rb and document models in lib/jekyll/document.rb maintain zero downstream dependencies on the build commands or LiveReload servers, ensuring that embedding Jekyll into external applications requires only lightweight subcomponents.

The 6-Stage Compilation Pipeline

The core mechanism of Jekyll’s predictability is the compilation lifecycle encapsulated in lib/jekyll/site.rb (577 lines):

def process
  return profiler.profile_process if config["profile"]

  reset
  read
  generate
  render
  cleanup
  write
end

The process method executes in six explicit, sequential phases:

  1. reset: Purges transient document caches, cleans internal page registries, and resets the Liquid renderer metrics.
  2. read: Traverses the source directory, parses YAML front matter, loads data files from _data/, and registers collections into memory.
  3. generate: Executes registered Jekyll::Generator plugins in priority order, allowing extensions to create synthetic documents (e.g. tag index pages).
  4. render: Compiles Markdown, processes Liquid tags/filters, evaluates layouts, and generates final document strings.
  5. cleanup: Removes obsolete files from the destination while preserving files declared in keep_files. The source/destination safety check runs earlier during setup.
  6. write: Emits rendered HTML, compiled CSS, and copied static assets to the output directory.

This deterministic stage ordering guarantees that generators always have full access to parsed content before rendering begins, eliminating race conditions common in ad-hoc build scripts.

Clone Analysis and Test Fixtures

We executed prod-code duplicates to detect structural cloning:

$ prod-code duplicates --min-lines 6 --max-groups 5
⚡ prod-code Clone & Duplication Harvester Report
────────────────────────────────────────────────────
Files Scanned: 167 | Lines: 24014 | Clone Groups: 5 | Duplication: 0.9%

Discovered Clone Groups:

[Clone Group #45] 6 lines | 9 occurrences (Type-2 (Parameterized))
  • Occurrence 1: test/test_document.rb:344-349
  • Occurrence 2: test/test_document.rb:368-373
  • Occurrence 3: test/test_document.rb:388-393
  • Occurrence 4: test/test_document.rb:487-492
  • Occurrence 5: test/test_document.rb:519-524
  Preview:
    │     setup do
    │       @site = fixture_site(
    │         "collections" => {
    │           "slides" => {

Across 24,014 scanned lines, duplication is minimal at 0.9%. The primary clone groups reside in test/test_document.rb, where individual collection edge cases configure isolated mock sites via fixture_site.

The production runtime under lib/jekyll/ exhibits high DRY discipline, encapsulating shared filesystem operations and URL formatting routines into centralized helpers.

Semantic Invariants: 1,270 Test Assertions and 53 Exception Guards

Jekyll safeguards user content and file system security through explicit checks:

$ grep -rnE "(assert|assert_equal|assert_nil|assert_match)" test/ | wc -l
1270
$ grep -rnE "raise " lib/ | wc -l
53

The assertion and raise commands above count matching lines, not exact test or exception constructs. Jekyll checks whether source and destination overlap earlier in setup via ensure_not_in_dest; cleanup removes obsolete output files while honoring keep_files. safe_glob protects glob handling from metacharacter interpretation. Symlinks are filtered separately by EntryFilter, so safe_glob itself is not a symlink or traversal guard.

Remote AST Operations on Cluster Nodes

We evaluated AST refactoring operations against Jekyll on the remote cluster node. Running prod-code extract-function on slug sanitization and title transformation logic in lib/jekyll/utils.rb completed in 10 milliseconds, confirming zero broken call sites and 0% local laptop CPU load.

Jekyll demonstrates how a focused, six-stage functional pipeline with disciplined module isolation can create an enduring foundation for web publishing that remains reliable across generations of Ruby runtimes.

Cite this article
Citation
Alexander Panasenko (2026-10-04). Jekyll Under the Microscope: What 67 Remote AST Tools Found Inside the Classic Ruby Static Site Generator. https://prod.codes/blog/jekyll-under-the-microscope-67-ast-tools/