Override, don't fork

How a Rustelo site changes anything without editing the framework

Rustelo 4 min read
The moment a site edits a framework file, it owns a fork and loses safe updates. Rustelo gives every customisation a place outside the framework: a local override layer, compile-time plugins and sites generated from maintained templates.

Override, don’t fork

Every framework meets the same moment. A site needs a different theme, an extra command, a component that behaves a little differently — and the quickest path is to open the framework’s file and change it. It works once. From then on the site owns a fork: every framework update has to be merged by hand into a file that has drifted, and the update that would have fixed a bug is the one nobody applies.

Rustelo treats “no framework forks” as an invariant, not as advice, and gives each kind of customisation a place where it can live outside the framework.

A layered override system

Configuration defaults, justfile commands, CSS themes and component styling resolve through four layers, with strict precedence (ADR-003):

  1. Local — what this project decides.
  2. Feature — defaults contributed by an enabled feature.
  3. Template — defaults for the kind of project.
  4. Framework — the base.

The first layer that provides a value wins. Resolution is deterministic, so for any key the winning value is predictable; and because the framework layer is never edited, an update only changes the values nobody overrode. There are no merge conflicts, because nothing was merged.

Plugins compiled in

Some extensions are not values but behaviour: a theme, a menu, a set of translations, a page. Those go through two traits, ResourceContributor and PageContributor (ADR-004). A plugin is a crate compiled into the binary and registered at startup. There is no runtime plugin loader to secure or version; the interfaces were designed so that moving to runtime plugins later changes how plugins are registered, not how they are written.

Sites generated, not copied

A new site does not start as a copy of someone else’s site. just site::new generates it from a maintained template, a sanitized snapshot of a living reference site (ADR-007), with the choices you make — the project name, the domain, the rendering profile — applied on the way.

A thin site goes one step further. It owns no Rust at all: it runs a shared, content-agnostic server binary and brings only its configuration, its templates, its content and its brand. Templates resolve through a chain — framework, then implementation, then the site — so a site overrides a single template by placing its own copy in its local layer, and every template it does not override keeps following upstream.

What the invariant buys

Customisation is visible: everything a site changed is in its own tree, and nothing in the framework’s. Updates are safe: what was not overridden picks up the new default. And the framework stays one framework, serving every site built on it.

This site is a thin site.

· reads