Back to Blog
Engineering March 25, 2026 | 9 min read

From Health Score to Production: The Deploy Pipeline

Three-phase production deployment with doctrine-enforced quality gates

Prismatic Engineering

Prismatic Platform

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:


  • 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.


    Tags

    deployment fly-io health-score quality-gates ci-cd