RoadmapDay 55 / 80
System DesignMonth 3 · Week 11

Day 55: CI/CD Pipelines for Web & Mobile (Fastlane, GitHub Actions)

Design a CI/CD pipeline that catches problems early (lint/type/test) and safely automates the genuinely painful parts of mobile release (signing, store submission).

Mark this day complete

Study

Concepts

A pipeline is a series of increasingly expensive, increasingly trust-worthy gates

The standard shape: lint + type-check (seconds, catches typos/obvious mistakes) → unit tests (fast, catches logic regressions) → build (confirms it actually compiles/bundles) → integration/E2E tests (slower, catches cross-component regressions) → deploy to a preview/staging environment → (manual or automatic) promote to production. Ordering matters for feedback speed: fail fast on the cheap checks before spending minutes on a full E2E suite for a PR that has an obvious lint error.

For a monorepo (Day 42), this pipeline should run scoped to affected packages only — re-running the full pipeline for every package on every PR does not scale, and Turborepo/Nx's affected-graph tooling is exactly what makes "only test what changed, plus its dependents" practical in CI.

Mobile release automation: the parts unique to iOS/Android

Web deploys are comparatively simple (push a static build/bundle to a host); mobile release involves CODE SIGNING (certificates and provisioning profiles on iOS, keystores on Android — both must be securely stored as CI secrets, never committed) and STORE SUBMISSION (App Store Connect / Google Play Console APIs, review times measured in hours to days, not seconds). Fastlane is the standard tool that automates this: `fastlane match` synchronizes signing certificates/profiles securely across a team's machines and CI, and lanes like `fastlane ios release` chain together build → sign → upload → submit-for-review as one reproducible, scriptable step instead of a manual, error-prone Xcode/Play Console ritual.

A staged rollout (releasing a new version to 5% of users first, then 25%, then 100%, monitored via the crash/error rates from Day 54 at each stage) is standard practice for mobile specifically, because — unlike a web deploy, which can be instantly rolled back — a bad native build already installed on a user's device cannot be un-installed remotely; the blast radius of a bad release must be limited BEFORE it happens, not fixed quickly after.

See It

Visualizations

Visualization

A staged CI/CD pipeline, cheapest gates first

Lint + type-check

seconds

Unit tests

fast, scoped to affected packages

Build
E2E tests

slower, cross-component

Deploy to staging
Promote to production

staged rollout on mobile

Visualization

Mobile staged rollout with monitoring gates

Day 0

Release to 5% of users

watch crash-free rate closely

Day 1-2

If healthy, expand to 25%

Day 3-5

If healthy, expand to 100%

Any stage

If crash rate spikes: halt rollout

no remote uninstall — limiting blast radius is the only lever

Build It

Code Examples

GitHub Actions: scoped, staged pipeline for a monorepo

yaml
name: CI
on: [pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx turbo run lint typecheck --filter=...[origin/main]

  test:
    needs: quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx turbo run test --filter=...[origin/main]

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx turbo run build --filter=...[origin/main]

A Fastlane lane automating signed build → TestFlight upload

ruby
# fastlane/Fastfile
platform :ios do
  lane :beta do
    match(type: 'appstore', readonly: true) # sync signing certs/profiles securely
    increment_build_number(xcodeproj: 'App.xcodeproj')
    build_app(scheme: 'App', export_method: 'app-store')
    upload_to_testflight(skip_waiting_for_build_processing: true)
    slack(message: "New beta build uploaded to TestFlight ✅")
  end
end

# Invoked from CI as: bundle exec fastlane ios beta
# Signing secrets (match's encryption passphrase, App Store Connect API key)
# come from CI secrets, NEVER committed to the repo.

Remember

Key Takeaways

  • Order pipeline stages cheapest-and-fastest first (lint/type-check) so obvious mistakes fail in seconds, not minutes.
  • Scope monorepo CI to affected packages (Day 42's tooling) — re-running everything on every PR does not scale.
  • Mobile release uniquely requires code signing (certs/keystores as CI secrets) and store submission — Fastlane automates both reproducibly.
  • Staged rollouts limit blast radius BEFORE a bad release reaches everyone — mobile builds cannot be remotely un-installed like a web deploy can be rolled back.
  • Signing credentials and API keys belong in CI secrets management, never committed to the repository, even in a private one.

Do It

Practice

  1. 1Set up a GitHub Actions workflow with staged lint → test → build jobs (using needs: to enforce order) on a real or sample repo.
  2. 2Read the Fastlane match documentation and explain, in your own words, how it keeps signing certificates synchronized across a team without committing them to git.
  3. 3Design a staged-rollout policy (percentages + time-between-stages + the specific metric that would halt it) for a hypothetical banking app release.