Skip to main content
Status: Superseded Date: 2026-02-10 Deciders: Core team
This ADR has been superseded. The React Native app was removed in favor of a native Swift iOS app (apps/ios/).

Context

The monorepo runs pnpm + Turborepo with strict TypeScript and workspace packages. The Expo (Expo Router) mobile app in apps/mobile was sensitive to:
  • pnpm’s non-flattened node_modules layout (symlinks + virtual store)
  • Monorepo workspace package boundaries and hoisting
  • Metro resolver settings (watch folders, node module search paths)
  • Babel plugin ordering requirements (notably react-native-reanimated/plugin must be last)
  • Expo Router route availability (e.g. OAuth callback path)
  • EAS build profiles (dev-client vs production)
These are high-churn configuration surfaces that tend to regress silently. Deterministic, reviewable configuration was needed to prevent “works on my machine” failures. Constraints:
  • pnpm remains the monorepo package manager.
  • Avoid speculative infra or abstractions (0-user mode).
  • Fast feedback when config drifts (tests over tribal knowledge).

Decision

Treat Expo mobile configuration as a contract and enforce it in-repo via tests and minimal explicit configuration:
  • Keep the Expo app in apps/mobile using Expo Router.
  • Commit an EAS configuration with a dev-client profile (developmentClient: true) as the default for local device/simulator testing.
  • Add a Metro configuration compatible with pnpm workspaces that preserves hierarchical module lookup while explicitly setting workspace watch folders and node module search paths.
  • Enforce critical invariants via lightweight config contract tests (Vitest), including:
    • app.json required fields and plugin configuration
    • Babel config includes react-native-reanimated/plugin and is last
    • Metro config does not disable hierarchical lookup and includes required node module paths
    • Required runtime dependencies exist
    • OAuth callback route exists and redirect helper produces stable URLs

Alternatives considered

Alternative 1: Switch package manager for Expo compatibility

Switch the monorepo (or just mobile) to a package manager with a flatter node_modules layout. Pros:
  • Fewer Metro/pnpm edge cases.
  • More “default path” Expo documentation applies without translation.
Cons:
  • Breaks repo-wide tooling and workflows built around pnpm.
  • Introduces churn and risk across unrelated apps/packages.
Not chosen because pnpm is a repo-wide standard and changing it is disproportionate to the problem.

Alternative 2: Metro with disableHierarchicalLookup = true

Force Metro to resolve only via explicit nodeModulesPaths. Pros:
  • Deterministic resolution when a single node_modules directory is authoritative.
Cons:
  • pnpm workspaces rely on hierarchical lookup to resolve from the virtual store.
  • Increased risk of resolving the wrong version of a dependency or failing to resolve at all.
Not chosen because it caused version skew/failures with pnpm-style installs.

Alternative 3: No explicit contracts; rely on expo doctor / CI failures

Keep configs “best effort” and let runtime failures surface issues. Pros:
  • Less test code.
Cons:
  • Slow feedback loops; failures often appear late (CI/device-only).
  • Harder code review; config intent is not explicit.
Not chosen because config regressions are common and costly; fast, local failure is worth a small set of contract tests.

Consequences

Benefits:
  • Reduced Metro/EAS drift and “works on my machine” failures.
  • Clear reviewable intent for critical Expo build/runtime requirements.
  • Faster regression detection via cheap unit-style tests.
Costs:
  • Contract tests add maintenance overhead when intentional config changes occur.
  • Some invariants are necessarily coupled to Expo/Metro implementation details.
Risks:
  • Contract tests could become brittle and block legitimate upgrades. Mitigation: keep tests focused on truly load-bearing invariants; update tests alongside deliberate upgrades.
  • Metro config remains a subtle failure surface as pnpm/Expo evolves. Mitigation: keep configuration minimal and documented; validate with expo export in CI-like builds.