Skip to main content

Deployment Workflow

A simple, robust process for continuous development while maintaining a stable QA environment.

Overview

Terra uses a two-branch deployment strategy that separates active development from stable releases:
┌─────────────────┐         ┌─────────────────┐
│      main       │ ──────► │     stable      │
│   (staging)     │ release │  (production)   │
└─────────────────┘         └─────────────────┘
        │                           │
        ▼                           ▼
   Preview URL               Production URL
  (dev testing)              (QA testing)
BranchPurposeVercel DeploymentWho Uses It
mainActive developmentPreview (staging)Engineers
stableTested, approved codeProductionQA team, stakeholders

Quick Access URLs

EnvironmentURLBranch
Productionterra.withunify.orgstable
Stagingstaging-terra.withunify.orgmain

Why This Approach

Feature flags add complexity:
  • Code pollution with if (flag) conditionals
  • Flag management overhead (LaunchDarkly, etc.)
  • Technical debt from forgotten flags
  • Testing combinatorial explosion
Branch separation is simpler: unfinished work stays on main, only release when ready.
QA needs a stable target:
  • Testing a moving target is frustrating
  • Hard to know if a bug is old or newly introduced
  • Stakeholders need predictable demo environments
The stable branch gives QA a consistent URL that only changes on intentional releases.
Simpler is better for small teams:
  • Two branches to understand, not five
  • No release branch management
  • Fast-forward merges keep history clean
  • Works perfectly with Vercel’s deployment model

One-Time Setup

Step 1: Create the Stable Branch

# From main, create stable
git checkout main
git pull origin main
git checkout -b stable
git push -u origin stable

Step 2: Configure Vercel

1

Open Project Settings

Go to Vercel Dashboard → Your Project → SettingsEnvironments
2

Edit Production Environment

Click on Production environment
3

Change Branch Tracking

In the Branch Tracking section, change the branch from main to stable and click Save
4

Verify Configuration

After saving:
  • Pushes to stable → deploy to production domain
  • Pushes to main → deploy to preview URL
In GitHub → Settings → Branches → Add rule: For stable branch:
Branch name pattern: stable
☑ Require pull request before merging
☑ Require status checks to pass
  - terra-ci (or your CI workflow name)
☑ Require branches to be up to date
☐ Allow force pushes (keep disabled!)
For main branch (optional, less strict):
Branch name pattern: main
☑ Require status checks to pass
  - terra-ci

Daily Development Workflow

For Engineers

1

Start Your Work

git checkout main
git pull origin main
2

Make Changes

Work directly on main or create a feature branch:
# Option A: Work on main (simpler)
# Just start coding

# Option B: Feature branch (for larger changes)
git checkout -b feat/my-feature
3

Commit and Push

git add .
git commit -m "feat(forms): add export button"
git push origin main
# Or: git push origin feat/my-feature
4

Verify on Staging

  • Vercel auto-deploys to your preview URL
  • Test your changes at the staging URL
  • Check the Vercel deployment logs if something fails
5

If Using Feature Branch: Create PR

gh pr create --base main --title "feat: add export button"
  • CI runs automatically
  • Greptile reviews the code (if configured)
  • Merge when ready

Quick Reference Commands

# Start work
git checkout main && git pull

# Check what you've changed
git status
git diff

# Commit
git add . && git commit -m "feat: description"

# Push to staging
git push origin main

# See staging deployment status
vercel ls

Release Process

When you’re ready for QA to test your changes:

Standard Release

# 1. Ensure you're up to date
git checkout main
git pull origin main

# 2. Switch to stable
git checkout stable
git pull origin stable

# 3. Fast-forward merge (clean history)
git merge main --ff-only

# 4. Push to trigger production deploy
git push origin stable
Always use --ff-only for clean history. If this fails, it means stable has diverged from main (usually from a hotfix). See Handling Diverged Branches.

Check What Will Be Released

Before releasing, see what commits are queued:
# Show commits on main that aren't on stable
git log stable..main --oneline
Example output:
a1b2c3d feat(forms): add export button
e4f5g6h fix(auth): handle expired tokens
i7j8k9l refactor: clean up logger

Notify the Team

After releasing, notify QA:
## Release Notes - [Date]

**Deployed to production:**
- feat: Add export button to submissions table
- fix: Handle expired auth tokens gracefully
- refactor: Logger cleanup (internal)

**Testing focus:**
- [ ] Export button on /forms/[id]/submissions
- [ ] Login flow after session timeout

Hotfix Process

For urgent bugs found in production:
1

Branch from Stable

git checkout stable
git pull origin stable
git checkout -b hotfix/critical-bug
2

Fix and Test

Make the minimal fix, test locally.
3

PR to Stable

git push origin hotfix/critical-bug
gh pr create --base stable --title "hotfix: fix critical bug"
4

Merge to Stable

After review, merge the PR. Production auto-deploys.
5

Backport to Main

Important: Bring the fix back to main so it’s not lost:
git checkout main
git pull origin main
git merge stable
git push origin main

Vercel Deployment Details

Understanding Deployment URLs

BranchURL PatternExample
stableProduction domainterra.withunify.org
mainPreview URLterra-git-main-unify.vercel.app
Feature branchesPreview URLterra-git-feat-export-unify.vercel.app

Deployment Status

Check deployment status:
# Via Vercel CLI
vercel ls

# Via GitHub
# Look for the "Vercel" check on your commit/PR

Environment Variables

Both main and stable use the same environment variables by default. If you need different values:
  1. Go to Vercel → Project → Settings → Environment Variables
  2. Set the variable with scope:
    • Production: Only stable
    • Preview: Only main and feature branches
    • Development: Local only

Rollback

If a bad release reaches production:
# Option 1: Revert the commit on stable
git checkout stable
git revert HEAD
git push origin stable

# Option 2: Rollback in Vercel dashboard
# Vercel → Deployments → Find previous good deployment → "..." → Promote to Production

Working with AI Code Review (Greptile)

If you have Greptile configured:
  1. On PR creation: Greptile automatically analyzes changes
  2. Review comments: Address any flagged issues
  3. Re-review: Push fixes, Greptile re-analyzes
  4. Merge: When Greptile approves + CI passes
Greptile is advisory, not blocking. Use judgment on its suggestions.

Working with Merge Queue (Graphite)

If you have Graphite configured:
  1. Approve PR: After review, approve the PR
  2. Add to queue: Graphite queues the merge
  3. Sequential CI: Graphite runs CI on rebased code
  4. Auto-merge: If CI passes, PR merges automatically
This catches “works alone, breaks together” issues.

Handling Diverged Branches

If git merge main --ff-only fails:
# See what's different
git log main..stable --oneline  # Commits on stable not on main

# Usually this is a hotfix. Merge stable into main first:
git checkout main
git merge stable
git push origin main

# Now stable can fast-forward
git checkout stable
git merge main --ff-only
git push origin stable

Troubleshooting

  1. Check Vercel dashboard for build logs
  2. Common issues:
    • Type errors: Run pnpm --filter terra tsc locally
    • Missing env vars: Check Vercel environment settings
    • Build timeout: Optimize build or increase timeout
  1. Ensure you pushed all changes
  2. Check Node version matches CI (20.x)
  3. Run exact CI commands locally:
    pnpm --filter terra tsc
    pnpm --filter terra lint
    pnpm --filter terra test
    
This shouldn’t happen with fast-forward merges. If it does:
  1. Check for hotfixes that weren’t backported
  2. Merge stable into main first
  3. Then fast-forward stable to main
  1. Verify stable branch has expected commits: git log stable --oneline -10
  2. Check Vercel is deploying from stable
  3. Force redeploy: Vercel dashboard → Deployments → Redeploy

Summary

TaskCommand
Start workgit checkout main && git pull
Push to staginggit push origin main
See pending releasegit log stable..main --oneline
Release to productiongit checkout stable && git merge main --ff-only && git push
Hotfix productionBranch from stable, PR to stable, backport to main