Compliance, from zero — down to the mechanism
Privacy compliance here is not a banner; it is a code path, and the code path has teeth:
Consent gating (app, analytics_service.dart):
- Default is off.
_consentGranted = prefs.getBool('analytics_consent_granted') ?? false— absent consent means no collection, thensetAnalyticsCollectionEnabled(_consentGranted)pushes the gate into Firebase itself, stopping even native auto-events (first_open,session_start,screen_view), not just custom ones. grantConsentis called only after the user accepts the terms on the consent screen — and the grant isawaited in the E2E suite (2026-04-18 commit fixed exactly that), so consent is a tested path, not decoration.- A build-flag hard-kill for test/dev builds goes further:
setAnalyticsCollectionEnabled(false)plusresetAnalyticsData()— dropping the cached app-instance id "so test simulators don't masquerade as returning users." Data hygiene enforced at the source; the analytics CLI excludes simulators at the query end — the same discipline at both ends of the pipe (measurement-stack). - Fire-and-forget with try/catch throughout — analytics is never allowed to crash the product it measures; failures log instead of silently vanishing (a bare-
debugPrintswallow was later indicted as "GA4 全瞎" in the camera investigation — camera-bug-looked-like-user-behavior).
Web (same law, same day): GA4 Consent Mode v2 cookie banner, scoped to PIPEDA (Canada's federal privacy law — the Personal Information Protection and Electronic Documents Act), landed 2026-03-27 — the same day as the app's consent gating commit. One law, two surfaces, one changeset boundary.
Session replay behind the same wall (2026-07-24): Clarity screen recording on mobile starts only from one combination — release build, not build-disabled, consent granted — and the gate is a pure shouldStart() whose full decision table is tested: the one allowed combination starts, all fifteen others refuse. Recording a driver's screen without consent is a privacy violation; a dead gate ships a silently useless instrument. Dashboard masking is strict, so recipient names and addresses on waybill screens never appear in a recording.
Store-mandated surfaces: privacy policy, terms, delete-account page (the App Store requirement indie apps most often fail) — shipped on the site.
Policy as lint: ESLint-enforced i18n (2025-12-24, "fix all hardcoded strings") converts "everything must be localizable" from a review nag into a build failure — the owner's constraints mechanized, not socialized applied to compliance.
Claims held to the same bar: hardcoded fake aggregate-rating schema deleted (2026-03-29); unguaranteeable on-device-data claims stripped from the landing About (2026-07-09). Compliance extended to what the marketing says, not just what the code does.
Interview archetype: "how do you handle privacy/compliance?" — consent defaults off and is pushed into the SDK, the grant path is E2E-tested, dev builds can't pollute cohorts, the policy is a lint rule, and marketing claims I can't guarantee get deleted.