Skip to main content

Production Deployment

This is the master checklist for taking a release to production. It assumes the code has already flowed through development → main → uat and is sitting on the uat branch (or merged into production for web). Use it top-to-bottom for each release.

Companion page: Environments & Branches — branch flow, Firebase projects, URLs.


1. Pre-deployment evaluation

Goal: confirm which tickets are going out, validate them with the PM/PO, prepare release notes, and apply any infra changes that must land before code.

1.1 Build the ticket list from the diff

  • Identify the diff that will ship (e.g. git log uat...production --oneline if deploying web, or the equivalent range for mobile)

  • Extract all PIVOT-XXXX keys from commit messages and PR titles in that range

  • Build a Jira search link using the exact ticket keys (this is the format Ynna/Steph expect):

    https://pivot-app.atlassian.net/issues/?jql=key%20in%20(PIVOT-1234%2C%20PIVOT-1235%2C%20PIVOT-1236)

    (Comma-separated, URL-encoded. Use acli if helpful — see the acli reference.)

1.2 Verify ticket readiness

  • Each ticket on the list is in a state that's ready for production or higher (i.e. not still in QA/UAT)
  • Tickets already hotfixed straight to prod don't accidentally re-appear on the list (cross-check fix versions / status)
  • Send the Jira search link to Ynna (PM) and Steph (PO) for confirmation that the list is correct

1.3 Apply infrastructure changes first

  • Review any pending Terraform changes in infra/ (firebase, github, infrastructure, airbyte, slack)
  • terraform plan from each affected root module and read the output carefully — IAM, Firebase rules, and Cloud Run changes can break prod if misapplied
  • terraform apply only after the plan is fully understood
  • Re-run plan afterwards to confirm a clean state

1.4 Prepare release notes

  • Generate release notes from the commit list + Jira tickets (Claude can draft these — double-check accuracy)
  • Save the final release notes to both locations:
    • ~/.pivot/notes/releases/<release-date-or-tag>.md
    • The GitHub draft release for this RC (auto-created on merge to main)
  • Keep two variants ready:
    • Long form: for the GitHub release body
    • Short form (~3-5 bullets, EN + FR): for App Store / Play Store "What's new" sections

2. Web deployment

Once section 1 is green, deploy the web app.

  • Open GitHub Actions for pivotteam/pivot → workflow "Deploy to Dev, Staging & Production" (deploy.yml)
  • Click Run workflow (workflow_dispatch)
    • Branch: production
    • Environment: production
  • Notify Ynna that a production deployment is in progress so she can broadcast to the team
  • (WIP) Put up the in-app deployment banner — confirm with the team where this lives once implemented
  • Monitor the GitHub Action run end-to-end:
    • Build frontend job succeeds
    • Build backend job succeeds (skipped if no functions changed)
    • Deploy Frontend to Firebase Hosting (Production) succeeds
    • Deploy Cloud Functions (Production) succeeds (if applicable)
  • Smoke-test https://the.pivotapp.ca/signin and a couple of critical flows
  • Check Rollbar for new errors in the production project

3. Mobile deployment

Lead time: prepare mobile releases 2-3 days in advance of the intended public release date to absorb Apple/Google review time.

3.1 Bump version and build

  • Bump the mobile version directly on the production branch (single commit pushed to prod):
    • Android: mobile/android/app/build.gradleversionCode (+1) and versionName
    • iOS: mobile/ios/pivot3.xcodeproj/project.pbxprojCURRENT_PROJECT_VERSION (+1) and MARKETING_VERSION
  • Push the commit to origin/production — this triggers the iOS Production Build and Android Production Build workflows in pivotteam/Pivotmobile
  • Verify both CI builds finish green and upload their artifacts (TestFlight build for iOS, AAB for Play Console)

3.2 Prepare the App Store / Play Console releases

  • Google Play Console: create a new Production release using the latest AAB built by CI
  • Apple App Store Connect: create a new App Store version using the latest TestFlight build
  • Paste the short release notes into "What's new":
    • EN and FR on Play Console
    • EN and FR on App Store Connect (per-locale field — both must be filled)
  • Set both releases to manual release (do not auto-release on approval)
  • Submit both for review

3.3 Release

  • Wait for Apple and Google approvals
  • On the user's go-ahead, press the Release button on each store

4. Post-deployment cleanup

Goal: keep Jira, GitHub, and the team in sync so the next deploy starts from a clean slate.

4.1 Jira

  • Move every ticket in the release list to "Deployed to Production" (Pivot uses this non-standard status name — not "Done"/"Released")
  • Set the Deployment Environment field to prod on each ticket

4.2 GitHub

  • For each deployed repo (pivot, Pivotmobile), find the draft release at the head of production
  • Update the release body to include the Jira tickets (use the same search link from step 1.1 for easy referral)
  • Publish the release (turns the draft into a tagged, public release)

4.3 Comms

  • Notify Ynna that deployment is complete so she can announce to the team
  • (WIP) Remove the in-app deployment banner

Rollback

There's no single "rollback" button — what's reversible depends on which layer broke. None of these are cheap; the goal is to know up front which one you'd actually be able to use.

Web — Firebase Hosting

  • Easy. Firebase Console → Hosting → previous version → Rollback. One click, takes effect immediately.
  • This only rolls back the static frontend bundle. Functions and DB state are untouched.

Web — Cloud Functions

  • Hard. No bulk "previous deploy" button. Two paths:
    • Targeted: identify the offending function and roll its Cloud Run revision back to the previous one (Cloud Run Console → service → Revisions → "Manage Traffic" → 100% to prior revision).
    • Full: re-deploy every function from a previous git SHA. Slow (npm run deploy redeploys all functions) and disruptive if many functions are healthy.
  • Prefer the targeted path. The full path is a last resort.

Web — Database migrations

  • Very hard. No automatic reverse. Two paths, both costly:
    • Write and run the inverse migration (new code, new review, new deploy).
    • Restore the database from a previous checkpoint (long restore window; loses any writes since the checkpoint).
  • Implication: treat migrations as one-way. Sequence the deploy so a bad migration is caught before traffic depends on it.

Mobile (iOS + Android)

  • No clean rollback. Once a build is released through App Store / Play Console, the only way back is to submit a new version with the fix (or the previous code re-built) and go through review again.
  • On the git side, each prod release is tagged (format: production-YYYY-MM-DD). That's useful for rebuilding the previous version quickly — but it doesn't shorten store review time.

Open items / WIP

These are flagged as work-in-progress in the current process and should be filled in as the tooling lands:

  • Deployment banner: mechanism, where it's configured, and how to toggle it on/off