All posts

Guides / 2026-09-08

Why every feature in this template is deletable

A starter you delete from is more useful than one you add to — but only if deleting is a recipe rather than an archaeology dig.

Why every feature in this template is deletable

Most SaaS starters are judged by what they include (our comparison of TanStack Start boilerplates does it too). That is the wrong axis. You will use maybe half of any starter's features, and the half you do not use is not free: it is schema you migrate, code you typecheck, dependencies you patch, and surface area you have to understand before you can safely change anything near it.

So the rule here is the opposite one: every optional feature must be removable in minutes, by a recipe, without leaving a trace.

What that forces

It turns out you cannot bolt deletability on afterwards. It constrains the architecture from the first commit:

  • Features are vertical slices. src/features/<name>/ owns its schema, its server functions, its queries and its components. Nothing in one feature imports from another — shared ground lives in src/core/.
  • Registration is one line. A feature does not get wired in from ten places. It exports a handler and adds a single import line to a registry: the schema barrel, the payment handlers, the stats providers, the admin user-detail sections, the cron jobs. Deleting a feature is deleting those lines.
  • Registries are lazy and tolerant of being empty. audit() with no sink registered is a no-op, not a crash. That is what lets you delete the audit feature while a dozen call sites keep calling it.
  • Anything that cannot be one line is fenced. Where a feature genuinely has to touch shared UI — a sidebar row, a dashboard card — the block sits between [feature: name] start and end comments so a recipe can cut it out exactly.

The part that keeps it true

A documented recipe rots the first time someone renames a file. So the recipes are executable: bun run verify:deletion copies the repo, performs each deletion for real, and requires typecheck, build and the unit tests to pass afterwards. It runs on every pull request, one recipe per job, and bun run verify:deletion all applies every recipe to a single copy to prove they also work together. Eleven features have a recipe today: affiliate, credits, files, content, audit, email log, feedback, notifications, waitlist, analytics and the public demo. The /delete-feature skill runs a recipe for you.

That job has caught recipe rot more than once — a moved import, a new registration nobody remembered to document. Which is the point: the claim on the landing page is only worth making because a machine re-checks it every time the code changes. The same idea runs through the rest of the template: a rule in CLAUDE.md counts only once a test enforces it, the permission check on every admin server function for one.

More posts