AZ
All writing
1 min read

An architecture lint that fails the build

typescriptfastifymonorepoarchitecturepostgres
Part of the projectAisolverA modern rebuild of a task/list manager — lists, nested tasks, groups, drag-drop, calendar, alarms — as a pnpm monorepo.View project

Aisolver (package taskwise-v2) is a rebuilt task/list manager — lists, nested tasks, groups, drag-and-drop, a calendar, alarms, a trash bin, admin, and invite-code registration. For a "just a to-do app," it carries a surprisingly grown-up spine.

Boundaries you can't argue with

The centrepiece is discipline: an arch-check.ts lint that enforces the intended module boundaries and fails the build when one is crossed. That's the whole trick. "Please don't import the database from the UI layer" is a code-review plea that erodes under deadline pressure; a script that turns the same rule into a red CI check does not get tired, does not get talked out of it, and does not forget.

The shape

It's a pnpm monorepo:

  • web — React 19 + Vite + TypeScript + Tailwind v4,
  • api — Fastify 5 + node-pg + Zod,
  • db — PostgreSQL 17,
  • with WebSockets for live updates.

It ships real operational docs too — an ARCHITECTURE.md and a RUNBOOK.md — so the boundaries the lint enforces are also written down for a human.

Why it matters on a small project

Architecture rot doesn't wait for scale; it starts on day two of any codebase with layers. Encoding the layering as an executable check is how a small tool stays refactorable — the structure defends itself instead of relying on everyone remembering the plan.

Back to the projectAisolverSee the full case study and related write-ups.View project