Build the engine, not the deliverable
slow · inconsistent · each from scratch
many sites, one spine
You've been asked to ship the same kind of artifact again and again: a marketing site per builder, a report per client, a dashboard per team. Each one has to feel bespoke, and the obvious move is to build each one. That's a treadmill. The leverage is to build the engine that builds them, and turn each instance into data.
I learned this building marketing sites for property developers. Every builder needs the same things, pricing, RERA proof, live availability, an EMI story, said completely differently across an apartment tower, a plotted layout, a township, and an off-plan launch. Hand-building each site doesn't scale; a generic template farm can't deliver the craft that converts. So I stopped building sites and built the engine: a site became a template (an ordered list of modules) plus a skin (design tokens) plus a project record. Adding a builder is data entry. Adding a template is one file.
The pattern: three axes that vary independently
The trick is to separate the things that change for different reasons, so each can change without touching the others.
Content is data. What goes on this instance, the project, the numbers, the copy, is a record, not code. The engine reads it.
Composition is a template. Which blocks appear and in what order is an ordered list, pure data. A new layout is a new list, not a new build.
Style is tokens. Color, type, and spacing live as named tokens, a "skin." Components read var(--ink), never a hardcoded hex, so the same component looks native in every skin and re-skinning is swapping a token set.
A module sits at the intersection: a component plus a config schema plus a mapper, registered once. Because it reads tokens and declares its own config, it composes anywhere and looks right everywhere. Optional modules declare themselves optional and drop out cleanly when their data is absent.
Bake the non-negotiables into the engine
This is where an engine beats a pile of templates. The things that must be true on every instance, the conversion rules, accessibility, SEO, the brand floor, live in the engine, not in each output. On the builder sites that meant EMI-first pricing, one CTA per screen, a sticky mobile bar, and schema.org markup: every site inherits them for free, and a new template can't accidentally ship without them. The engine doesn't just save effort; it raises the floor.
What changes when the engine is the product
- The marginal cost of the next instance drops to data entry, so variety stops being expensive.
- Quality compounds: improve a module once and every instance that uses it gets better.
- You can ship breadth a hand-built shop can't match, because breadth is now data, not labor.
When NOT to build an engine
The abstraction has a cost, and it isn't repaid until you've shipped a few instances. If you'll build this twice, build it twice; an engine for two outputs is the same over-engineering trap as a five-agent orchestrator for a one-shot task. Build an engine when three things are true: you'll ship many instances, they share a real spine, and the variation between them is structured (a few axes), not chaotic. If every instance is genuinely unique, there's no engine to find.
The one-line version
If you'll ship it a hundred times, the engine is the product and each one is data. If you'll ship it twice, just ship it twice.