The story
Context & problem
Small local businesses need simple websites, and simple websites are where the hard parts get skipped. Cookie consent, a real privacy policy, account deletion, accessible forms, bot protection and structured data rarely fit a small budget, so they are left out or copied in half-working.
Repeating that work per client is slow and drifts: each site ends up a little different and a little wrong. I wanted one base where the right choice is the default and the wrong one fails the build.
What I did
- Designed the folder and dependency rules and encoded them in lint; every rule in the docs says what catches it.
- Built the pipeline: CI with a throwaway Postgres branch per run, deploys from CI, a smoke suite against production.
- Ran compliance as engineering: GDPR, WCAG 2.2 AA with axe in the end-to-end tests, technical and local SEO.
- Turned a security audit into 14 decisions and shipped them in six steps, from a static CSP to split secrets.
Decisions & trade-offs
The architecture is executable, not a wiki page. Only the data access layer may open a database connection; a page that imports the client fails lint, and server code in a browser bundle fails the build. The cost is friction when adding folders. The gain is a rule that does not depend on who reviews.
A real database per CI run, not mocks. Each run creates its own Postgres branch from preview, never from production, runs the integration and end-to-end tests against it and always deletes it. Runs are slower; the tests run the same SQL as production.
Bot protection that does not break the business. The platform's managed bot protection blocked WhatsApp link previews and our uptime monitor. I left it off and put a bot check on the four routes that send mail or check a password, behind a rate limit.
Outcome
- Around 75 pull requests in six weeks, each gated by the full check and a CI run on its own database.
- 40 unit and integration test files, axe on every end-to-end run, and a production smoke suite that passes.
- A new client site starts with privacy, terms, an accessibility statement and SEO already in place.