Explore Whiskey

The static site generator this portfolio is built with

whiskey-ssg.netlify.app

Most static site generators follow the same workflow: read every page, apply templates, and regenerate the entire site.

For small websites this works well. As projects grow, however, rebuild times increase even when only a single page changes.

Whiskey began with a simple question:

Why rebuild the entire site when only a handful of pages actually changed?

That question became the foundation for everything that followed.


The Problem

A modern documentation site is more than a collection of Markdown files.

Pages depend on:

  • layouts
  • partials
  • images
  • stylesheets
  • local includes
  • remote documentation
  • generated indexes
  • RSS feeds

These relationships form a graph.

Most static site generators either rebuild everything or rely on coarse invalidation strategies that rebuild much more than necessary.


A Dependency-Aware Approach

Whiskey models a site as a dependency graph.

Every page, template, asset and include becomes a node. Relationships between them become edges.

Instead of asking:

"Which files changed?"

Whiskey asks:

"Which pages depend on what changed?"

Only those pages are rebuilt.

The goal isn't simply faster builds—it is to perform the minimum amount of work while producing exactly the same output as a clean rebuild.


Remote Content as a First-Class Citizen

Modern documentation rarely lives in one repository.

A page might include:

  • a README from GitHub
  • API documentation
  • generated Markdown
  • shared documentation maintained elsewhere

Instead of requiring external scripts, Whiskey treats remote content as part of the dependency graph.

Remote sources are downloaded, cached locally and refreshed only when they change, allowing them to participate in incremental builds just like local files.


Building Whiskey

Whiskey is written in Go and built around a small number of composable components:

  • a dependency graph
  • fingerprint-based change detection
  • an incremental build planner
  • a rendering pipeline
  • a remote content workspace

Each component has a single responsibility, making the codebase easier to understand and extend.


Dogfooding the Documentation

This website is built with Whiskey itself.

Several pages are reused through local includes, the documentation demonstrates the features it describes, and every edit exercises the incremental build system during development.

Rather than creating examples solely for documentation, Whiskey uses its own capabilities to build its documentation.


Looking Ahead

Whiskey is still evolving.

Future work will continue to improve performance, expand supported content sources and simplify site authoring while preserving the project's central idea:

Build only what changed.

Everything else follows from that principle. Most static site generators follow the same workflow:

Read everything
        ↓
Render everything
        ↓
Write everything

This approach is simple and reliable, but it also means a small change can trigger work across the entire site.

Whiskey approaches the problem differently.

Instead of asking:

"Which files changed?"

it asks:

"Which pages depend on what changed?"

That single design decision influences almost every part of the project.


Dependency-Aware Builds

A Whiskey site is represented as a dependency graph.

Pages depend on:

  • layouts
  • partials
  • assets
  • local includes
  • remote includes

Rather than treating Markdown files as isolated documents, Whiskey records these relationships so they can be used during future builds.

For example:

index.md
    │
    ├── getting-started.md
    │
    └── index.html
            │
            └── base.html
                    │
                    └── header.html

If getting-started.md changes, only the pages that include it are rebuilt.

If header.html changes, every page using that layout is rebuilt.

The result is a build that performs only the work required to produce a correct site.


Incremental by Default

Incremental builds are not a separate mode in Whiskey—they are the default.

Every build begins by determining what has changed since the previous build.

If only a single page changes, only that page is rebuilt.

If a shared layout changes, Whiskey automatically performs a full rebuild because every page may be affected.

This keeps the behaviour predictable while avoiding unnecessary work.


Remote Content

Modern documentation often spans multiple repositories.

A page may include:

  • a GitHub README
  • shared documentation
  • generated Markdown
  • documentation maintained by another team

Rather than requiring separate download scripts, Whiskey treats remote content exactly like local content.

@ include https://raw.githubusercontent.com/user/project/main/README.md

Remote sources become part of the dependency graph and participate in incremental builds just like local files.


Workspace Caching

Downloaded content is cached locally inside Whiskey's workspace.

On future builds, Whiskey checks whether a remote resource has changed before downloading it again.

If nothing has changed, the cached copy is reused.

This reduces unnecessary network requests while keeping builds reproducible.


Offline Development

Because remote content is cached locally, development doesn't stop when the network disappears.

Running:

whiskey build --offline

uses the cached workspace instead of making HTTP requests.

The same applies to the development server:

whiskey serve --offline

This makes remote content behave much more like local content during day-to-day development.


Correctness Before Speed

The goal of Whiskey is not simply to build faster.

The goal is to perform the minimum amount of work necessary while producing exactly the same output as a clean rebuild.

Sometimes that means rebuilding one page.

Sometimes it means rebuilding the entire site.

The dependency graph allows Whiskey to make that decision automatically, ensuring that incremental builds remain both fast and correct. Whiskey is built around a simple idea:

Everything that influences a page should be represented as a dependency.

Rather than treating pages, templates, assets and remote content as separate systems, Whiskey models them as parts of a single dependency graph. That graph becomes the foundation for incremental builds, remote content, live reload and secondary outputs such as RSS feeds.

This article provides a high-level overview of the major architectural components.


The Big Picture

At a high level, Whiskey consists of five major systems:

Content
          │
          ▼
 Dependency Graph
          │
          ▼
  Change Detection
          │
          ▼
Incremental Planner
          │
          ▼
     Rendering

Each system has a single responsibility and passes its output to the next stage.


Dependency Graph

The dependency graph is the heart of Whiskey.

Every significant object becomes a node:

  • pages
  • layouts
  • partials
  • assets
  • local includes
  • remote includes

Edges represent dependencies.

For example:

index.md
    │
    ├── getting-started.md
    │
    └── index.html
             │
             └── base.html
                      │
                      └── header.html

This graph allows Whiskey to answer one question efficiently:

Which pages depend on what changed?


Change Detection

Once the graph has been constructed, Whiskey determines which nodes have changed since the previous build.

Rather than immediately rebuilding pages, Whiskey first identifies the smallest possible set of changed nodes.

Those changes are then passed to the incremental planner.


Incremental Planning

The planner decides whether the build can remain incremental.

For example:

  • editing a page usually rebuilds only that page
  • changing a local include rebuilds every page using it
  • changing a shared layout rebuilds the entire site

This keeps incremental builds both fast and correct.


Remote Content

Remote Markdown participates in the build exactly like local content.

When Whiskey encounters a remote include:

@ include https://example.com/page.md

it downloads the content, stores it locally and adds it to the dependency graph.

From that point onward, remote content behaves like any other source file.


Rendering

Once the planner has identified the pages that need rebuilding, Whiskey renders only those pages.

Each page passes through the same pipeline:

Markdown
      ↓
Resolve Includes
      ↓
Expand Shortcodes
      ↓
Markdown → HTML
      ↓
Apply Layout
      ↓
Write Output

After rendering completes, Whiskey regenerates collections, RSS feeds, sitemaps and other derived outputs as needed.


Why This Architecture?

Each subsystem has a clearly defined responsibility.

  • The dependency graph records relationships.
  • Change detection identifies modified nodes.
  • The planner determines what must be rebuilt.
  • The renderer produces HTML.

Keeping these responsibilities separate makes the codebase easier to understand while allowing new features to integrate naturally with the existing build pipeline.

For example, remote content, local includes and future source providers all become additional node types in the dependency graph rather than requiring entirely separate build systems.


Learn More

The architecture described here is implemented throughout Whiskey's codebase.

If you'd like to explore the implementation in more detail, continue to the Developer Guide, which explains the repository layout, packages and build pipeline in depth.