Architecture Rules That Fail the Build: Shipping Multiple Flutter Apps From One Platform

Mobile App Factory exists to answer one question: can two Flutter products share every package without importing each other — and stay that way a year from now? Documentation does not keep that promise. Tooling does.
Rules that fail the build
check-architecture walks 57 files against 9 rules and exits non-zero on violations. The central rule: product code may import shared packages, never another product. No exceptions, no "just this once".
Compile-time product selection replaced runtime conditionals:
// forbidden — silent drift
if (product == 'eiken') { ... }
// required — the analyzer sees both branches
import 'product_eiken.dart' if (dart.library.js) 'product_default.dart';
When a rule can only be enforced by review, it will be broken by the third sprint. Make the check boring and automatic.
Delete abstractions that make products harder
The Sprint 2 review's job was to catch "abstractions that make products harder rather than easier". The best finding: a RandomSource wrapper existed solely because Dart's seeded RNG "can change between releases of the library". That is a dependency you cannot test against — so we deleted the wrapper and implemented an explicit FNV-1a seed with xorshift32. Small, stable, verifiable.
The lesson generalizes: a wrapper that exists to hide a dependency's instability is a liability wearing a pattern's clothes.
Translation fallbacks hide bugs
resolve() fell back to another language when a key was missing — so a missing Japanese string rendered as English. Nobody notices wrong-but-readable text. resolveStrict plus a content validator caught six untranslated option labels immediately.
The architectural version of this principle: fallbacks are for availability, not for correctness. Missing content should be loud.
ADRs or it didn't happen
Thirteen architecture decision records now cover the platform — including decisions like "no CI at the platform level; a single ./scripts/verify.sh gate", which would otherwise get relitigated every month. An ADR records the decision, the alternatives, and the conditions under which it should be revisited. A finding without a ticket will be forgotten; a decision without an ADR will be re-argued.
Where AI fits
Parts of this platform were built with agentic tooling, with one hard rule: AI-generated changes require human approval before merge (ADR-011). The agents draft, the verifier judged, and the human signs. 375 tests green across two products is the current bar — and the verify script is the gate, not the agent's confidence.
The numbers
- 375 tests green across two products on shared packages
- 9 architecture rules enforced across 57 files
- 13 ADRs covering platform-level decisions
- 2 products (EIKEN Kids Quest and the next one) sharing every package
The bet is simple: if the architecture is mechanically enforced, adding the third product costs content, not refactoring.
