Open source — Field Note

Configuration Should Have a Memory: Presetter v9.

Presetter began as a way to stop copying config files. Version 9 turns that small irritation into a clearer system for preserving engineering decisions across projects, packages, and time.

JUN 27, 20268 MINOPEN SOURCE

Every repository begins with a brief period of innocence. The TypeScript config is legible. ESLint has not yet acquired six local exceptions. The test runner does one obvious thing. Then the project lives. A framework changes, a package splits, somebody fixes CI at midnight, and the reasons behind the configuration begin to disappear while the files remain.

That was the irritation behind Presetter. I did not want another generator that made a project look tidy on day one. I wanted a way to preserve the engineering decisions that keep many projects coherent on day one hundred: what strict means here, which files count as source, how tests are named, what a monorepo may override, and where a deliberate exception belongs.

Config drift is organizational memory loss

Teams usually experience configuration drift as friction. One package lints differently. Another compiles to an older target. A third has a hand-edited script nobody wants to touch. But the deeper failure is not duplication. It is memory loss. Each copied file separates a decision from the place where that decision can be explained, reviewed, and improved.

Presetter treats configuration as a composed source of truth. A project names the presets it needs; those presets contribute assets, variables, and scripts; explicit project choices can still override the result. The generated files remain readable because tools need ordinary files, but Git does not have to pretend those artifacts are the source of the decision.

“The point is not fewer config files. It is fewer unexplained decisions.”

Version 9 makes the system visible

Presetter v9 moves every preset package into the @presetter scope. The CLI remains presetter, while packages such as @presetter/preset-essentials, @presetter/preset-react, and @presetter/preset-monorepo now read as parts of one ecosystem. A namespace rename sounds administrative until you maintain the project: it gives the public surface a boundary, makes ownership explicit, and creates room for the system to grow without inventing another naming convention.

The baseline moves forward too. TypeScript 6 is now the required peer, ES2024 is the default target, and the essentials preset enables noUncheckedIndexedAccess. The templates lean more heavily on TypeScript’s strict defaults instead of restating them. These are intentionally opinionated changes. A shared preset is valuable only when it is willing to turn accumulated experience into a better default.

Three new presets make the composition model more literal. Node and Bun now have their own runtime-aware foundations. Storybook becomes a first-class preset that can generate its configuration, scripts, ESLint rules, and Vitest browser project. Monorepos gain sub-level Vitest support, and bootstrap can target packages by package-name glob rather than only by project path.

import essentials from '@presetter/preset-essentials';
import node from '@presetter/preset-node';
import strict from '@presetter/preset-strict';
import { preset } from 'presetter';

export default preset('service', {
  extends: [essentials, node, strict],
});
presetter.config.ts — the project states its stack once

The release pipeline is part of the product

There is another part of v9 I care about because users rarely see it directly. Changelogs now come from git-cliff, packages are published with provenance, and the release workflow is designed to resume when a partially completed publish meets a package version that already exists. Shipping open source is not only the code inside the tarball. It is the evidence around the tarball and the ability to recover when the happy path breaks halfway through.

That work is deliberately boring. Good infrastructure often is. The release should be verifiable; the package graph should move in lockstep; a rerun should continue instead of turning partial success into a manual incident. Reliability is a feature even when its interface is a quiet green check.

Upgrading without pretending nothing changed

Version 9 is a major release because the package namespace, TypeScript baseline, supported Node versions, and several script names change. The honest upgrade path is to follow the migration guide, replace the old presetter-preset-* packages with their @presetter/* equivalents, and regenerate the project files. The generated diff is useful: it shows exactly how the new shared decisions meet the local project.

pnpm add -D presetter@^9.0.0 \
  @presetter/preset-essentials@^9.0.0 \
  @presetter/preset-esm@^9.0.0

pnpm exec presetter bootstrap
Upgrade the core and two common presets with pnpm

Configuration is context

I have spent a lot of time thinking about context for AI, but software projects have always had the same problem. They inherit decisions without inheriting the situation that produced them. Presetter is a small attempt to make that context durable: compose it, name it, keep the escape hatches visible, and let each repository receive the latest version without erasing what makes it local.

Version 9 does not make configuration disappear. It makes configuration quieter because its decisions have somewhere to live. That is the kind of tool I want to keep building: present when judgment is needed, almost invisible when the real work begins.

Watercolor study of a quiet engineering workbench, where configuration becomes background infrastructure instead of the work itself.
Watercolor study of a quiet engineering workbench, where configuration becomes background infrastructure instead of the work itself.
ThemesPresetterTypeScriptOpen sourceDeveloper tools
Back to thinking