Engineering

From Health Score to Production: The Deploy Pipeline

Three-phase production deployment with doctrine-enforced quality gates

Mar 25, 2026 Β· 9 min read Β· Prismatic Engineering

#The Three-Phase Doctrine

Deploying to production is the highest-stakes operation in software engineering. A bad deploy can take down a running system, corrupt data, or expose security vulnerabilities. The Prismatic Platform mitigates this risk through a three-phase deployment pipeline where each phase must pass before the next begins.

#Phase 1: Pre-Deploy Validation

Before any code reaches production, it must pass three categories of checks:

#Quality Gates

mix format --check-formatted    # Code formatting
mix compile --warnings-as-errors --force  # Zero-warning compilation
mix credo --strict              # Static analysis
mix test --cover                # Test suite with coverage
mix dialyzer                    # Type checking

All quality gates must pass. A single warning, a single failing test, or a type error blocks the deploy.

#Doctrine Compliance

The platform’s 18-pillar doctrine is validated by mix check.doctrines:

PillarCheckEnforcement
ZERONo String.to_atom, no bare rescueBlocking
SEALNo hardcoded secrets, no SQL injectionBlocking
PERFNo N+1 queries, no unbounded Repo.allBlocking
TACHTest files exist for changed modulesBlocking
DOCS@moduledoc, @doc, @spec presentBlocking
DEPSVersion constraints, no unstable git depsBlocking
RDMEREADME.md in every appBlocking
HYGIENENo stale files, clean git stateBlocking
NMNDNo placeholders, stubs, or shortcutsBlocking
OTELTelemetry in GenServers and controllersAdvisory
GITLConventional commit formatAdvisory
KNOWGlossary coverage for new modulesAdvisory

#Security Scan

A security-specific scan checks for:

  • Dependencies with known vulnerabilities (mix deps.audit)
  • Secrets accidentally committed to the repository
  • Unsafe code patterns that could enable injection attacks

#Health Score Gate

The platform’s health score (computed by mix health.score) must meet the minimum threshold. The health score is a composite metric combining compilation health, test coverage, doctrine compliance, and documentation completeness.

#Phase 2: Deployment via Fly.io

The platform runs on Fly.io, which provides zero-downtime deployments through rolling updates:

#Build Process

# Build the Docker image
fly deploy --build-only

# The Dockerfile:
# 1. Installs Erlang/OTP and Elixir
# 2. Fetches and compiles dependencies
# 3. Compiles the umbrella application
# 4. Builds production assets (esbuild + tailwind)
# 5. Creates the Mix release
# 6. Copies the release to a minimal runtime image

#Rolling Update Strategy

Fly.io deploys new instances before shutting down old ones:

  1. New VM is started with the updated release
  2. Health check endpoint (/api/v1/health) is polled
  3. Once the new VM responds with HTTP 200, traffic is routed to it
  4. Old VM receives a SIGTERM and has 30 seconds to drain connections
  5. Old VM is shut down

This ensures zero downtime – at no point are zero instances serving traffic.

#Release Configuration

# rel/env.sh.eex
export RELEASE_DISTRIBUTION=name
export RELEASE_NODE=prismatic@${FLY_APP_NAME}.internal
export ERL_AFLAGS="-proto_dist inet6_tcp"

The release is configured for IPv6 (required by Fly.io’s internal network) and uses named distribution for potential clustering.

#Phase 3: Post-Deploy Validation

After deployment, automated checks verify the release is healthy:

#Smoke Tests

Four critical endpoints are tested within 60 seconds of deployment:

# Health endpoint (must respond with 200)
curl -f https://prismatic-prod.fly.dev/api/v1/health

# Landing page (must respond with 200)
curl -f https://prismatic-prod.fly.dev/

# Dashboard (must respond with 200, authenticated)
curl -f https://prismatic-prod.fly.dev/dashboard

# API status (must return valid JSON)
curl -f https://prismatic-prod.fly.dev/api/v1/status

#Performance Verification

Response times are measured and compared against baselines:

  • Health endpoint: must respond in less than 10ms
  • Page load: must complete in less than 250ms
  • Server render: must complete in less than 100ms
  • LiveView mount: must complete in less than 150ms

If any metric exceeds 2x the baseline, an alert is triggered. If any metric exceeds 5x the baseline, automatic rollback is initiated.

#Functional Verification

Key user journeys are tested end-to-end:

  • Can a user log in and reach the dashboard?
  • Does the OSINT toolbox load and display adapters?
  • Can a DD case be created and viewed?
  • Do real-time updates flow through PubSub?

#Rollback Strategy

If post-deploy validation fails, the pipeline initiates automatic rollback:

# Immediate rollback to previous release
fly releases rollback --app prismatic-prod

# Verify rollback succeeded
curl -f https://prismatic-prod.fly.dev/api/v1/health

Rollback is fast (typically under 30 seconds) because Fly.io retains the previous release image. The old VMs are restarted from the cached image without requiring a rebuild.

#Rollback Triggers

ConditionActionTiming
Health check fails 3xAutomatic rollbackWithin 90 seconds
Response time 5x baselineAutomatic rollbackWithin 2 minutes
Error rate exceeds 5%Alert + manual reviewWithin 5 minutes
Smoke test failureAutomatic rollbackWithin 60 seconds

#The Deploy Command

The entire pipeline is orchestrated by the /deploy command:

# Full pipeline: validate + deploy + verify
just deploy-validate staging

# Production (requires confirmation)
just deploy-production

# Dry run (preview without executing)
just deploy-dry-run production

The command provides real-time progress output for each phase, with clear pass/fail indicators and timing information. A typical production deploy takes 4-6 minutes end-to-end: 1-2 minutes for validation, 2-3 minutes for deployment, and 30-60 seconds for post-deploy verification.

#Deployment History

Every deployment is recorded with its validation results, timing, and outcome:

v546: 2026-03-25 14:32 UTC | staging  | PASS | 4m 12s
v547: 2026-03-25 15:01 UTC | staging  | PASS | 3m 58s
v548: 2026-03-25 15:45 UTC | prod     | PASS | 5m 03s

This history enables trend analysis: are deploys getting slower? Are failures increasing? Which phase fails most often?


Ship with confidence. The pipeline catches what humans miss.

Browse all β†’