All case studies

Case Study — Tech / Software

FocusGoods V2:
97 tests, a canary controller,
and a rights screener that earns its keep.

A print-on-demand e-commerce platform (Stripe + Printify) rebuilt from the ground up with production-grade architecture: PostgreSQL row-level security, BullMQ job queue, AI-assisted rights screening, multi-stage quality gate, and a canary traffic controller that can increment traffic by 10% or roll back atomically — all before a human approves full exposure.

97Tests passing across full suite
18/18Rights screening passing
10%Canary step increment

Context

FocusGoods (focusgoods.co.uk) is a print-on-demand store built around a "daily drop" model: new products land every day at 08:30, subscribers get an email, and purchases flow through Stripe → Printify → fulfilment. V1 worked. V2 was about making it maintainable, testable, and safe to extend with AI-generated product content without that content creating legal or brand risk.

V2 is not a rewrite for its own sake. Every architectural decision in V2 was made because V1 had a specific brittleness that needed fixing — not because the patterns are interesting in the abstract.

Database: PostgreSQL with row-level security

FocusGoods V2 PostgreSQL row-level security schema with BullMQ job queue and Redis

V1 used SQLite. V2 moved to PostgreSQL with row-level security (RLS) policies on all customer-facing tables. This means the application layer can't accidentally return one customer's order data to another customer — the database enforces isolation at the row level, regardless of what the application query does.

The job queue runs on BullMQ with Redis as the broker. Every asynchronous operation — Printify order submission, email dispatch, product generation, rights check — is a queued job with retry logic, dead-letter handling, and a processing log. Nothing happens in a request handler that can't be audited after the fact.

Rights screening: 18/18

AI-generated product content — names, descriptions, design briefs — can inadvertently reference trademarked terms, protected IP, or content that creates brand liability. The rights screener sits between generation and publication.

Every piece of generated content passes through a rule-based + LLM screening pipeline. Each item gets a decision: Pass, Block, or Escalate. Escalations go to a human review queue. Blocks are logged with the matching rule. Passes proceed to the quality gate. Every decision — including the rule that triggered it — is written to the audit table.

Test results — rights_screener.test.js

✓ blocks trademarked sports brand names (3ms)
✓ blocks protected character names (2ms)
✓ escalates ambiguous IP references (4ms)
✓ passes clean generic content (1ms)
✓ audit trail written for all decisions (6ms)
✓ escalation queue receives correct payload (3ms)
... 18 tests passing

Quality gate: 21/21

Even content that passes rights screening needs quality validation before it can be published as a product. The quality gate checks: minimum description length, image resolution requirements, price range validation, category assignment, and completeness of required fields.

API errors from Printify or Stripe during product creation are caught here and escalated to human review — they don't silently fail and leave an orphaned product record. The gate is the last automated checkpoint before a product becomes purchasable.

Test results — quality_gate.test.js

✓ rejects short descriptions (2ms)
✓ rejects missing images (1ms)
✓ rejects out-of-range prices (2ms)
✓ escalates Printify API errors (8ms)
✓ escalates Stripe product creation failures (7ms)
✓ passes complete valid product (3ms)
... 21 tests passing

Canary traffic controller

FocusGoods V2 canary traffic controller: v1 weight 100 incrementing to v2 by 10% with stop-loss rollback and human hard gate

The canary controller manages the traffic split between the V1 and V2 API paths. It starts with v1_weight=100, v2_weight=0. A probe runs against the V2 path on a schedule. Each probe pass increments v2_weight by 10. A configurable number of consecutive failures triggers an atomic rollback to v2_weight=0.

The hard gate: v2_api_ready=true must be explicitly set by a human operator before the controller will allow v2_weight to reach 100. There is no automated path to full production exposure. This is deliberate — the same principle as a clinical change-control gate.

Every weight change, probe result, and rollback event is written to traffic_split_history with timestamp, trigger source, and the weight values before and after. The controller's history is a complete audit log of the migration.

10%

increment per passing probe

0

v2_weight on atomic rollback

Human

required for full exposure

Email stack

Six transactional email templates handle the full customer lifecycle: email verification, welcome, password reset, order confirmation, drop subscription confirmation, and daily drop. All authenticated with DKIM (Ed25519 + RSA), SPF, and DMARC across the sending domain — deliverability-first from day one, not retrofitted after hitting spam folders.

The daily drop email is dispatched at 08:30 via cron. It's not a newsletter — it's a product discovery habit loop built into the infrastructure. The from header is branded ("Focus Goods" <donotreply@focusgoods.co.uk>) with a separate authenticated envelope sender to separate bounce handling from brand presentation.

The test suite: 97 total

18/18

Rights screening

21/21

Quality gate

18/18

Asset server

20/20

Canary probe

20/20

Other modules

Want to discuss the architecture?

Happy to walk through the canary controller design, the rights screening implementation, or the RLS schema in detail.