Environment

devops

Definition and Overview

In software engineering, an environment refers to a distinct deployment target where an application runs with a specific configuration profile. Each environment serves a different purpose in the software development lifecycle: development environments support rapid iteration with debug tooling, staging environments mirror production for pre-release validation, and production environments serve real users with optimized performance and security configurations.

Environments are distinguished by their configuration values, connected services, resource allocation, security posture, and operational characteristics. The principle of environment parity (from the Twelve-Factor App methodology) states that development, staging, and production should be as similar as possible to minimize deployment surprises. However, practical differences are unavoidable -- development uses local databases while production uses managed clusters, staging may have synthetic data while production has real data, and debug tooling is enabled in development but disabled in production.

In the Elixir ecosystem, the Mix build tool provides first-class environment support through MIX_ENV, which controls compile-time configuration, dependency selection, and code compilation. The standard environments are :dev (development), :test (testing), and :prod (production), though custom environments can be defined. Importantly, MIX_ENV is a compile-time concept in Elixir releases -- the environment is baked into the compiled release artifact, and runtime configuration uses a separate mechanism (config/runtime.exs).

Technical Deep Dive

Standard Environment Hierarchy

EnvironmentPurposeConfiguration Profile
Development (:dev)Local coding, debugging, rapid iterationDebug logging, code reload, local services, seed data
Test (:test)Automated testing, CI/CD pipelinesIn-memory/sandboxed DB, mocked externals, deterministic seeds
StagingPre-production validation, integration testingProduction-like config, synthetic data, restricted access
Production (:prod)Live user-facing deploymentOptimized compilation, structured logging, real services, secrets

Elixir Configuration Layers

Elixir uses a layered configuration system where each layer can override values from the previous:

config/config.exs          # Base configuration (all environments)
  |
  +-- config/dev.exs       # Development overrides (MIX_ENV=dev)
  +-- config/test.exs      # Test overrides (MIX_ENV=test)
  +-- config/prod.exs      # Production overrides (MIX_ENV=prod)
  |
  +-- config/runtime.exs   # Runtime configuration (reads env vars at boot)

Environment Variable Categories

CategoryExamplesInjection Method
Application ConfigPORT, HOST, LOG_LEVELconfig/runtime.exs
SecretsSECRET_KEY_BASE, DATABASE_URLFly.io secrets, Vault, env vars
Feature FlagsENABLE_OSINT, ENABLE_DDRuntime config + ETS cache
Service URLsDATABASE_URL, REDIS_URL, MEILISEARCH_URLRuntime config
Build InfoGIT_SHA, BUILD_TIME, MIX_ENVCompile-time config

Architecture and Implementation

Environment management architecture in modern deployments separates compile-time from runtime configuration. Compile-time configuration (determined by MIX_ENV) controls which code paths are compiled, which dependencies are included, and how the application binary is structured. Runtime configuration (read at application boot) controls operational parameters like database URLs, API keys, port numbers, and feature flags.

This separation is critical for Elixir releases, where the compiled artifact is immutable once built. A single release binary compiled with MIX_ENV=prod can be deployed to multiple environments (staging, production, canary) by varying runtime environment variables. This ensures that the exact same binary tested in staging is deployed to production, eliminating "works on my machine" discrepancies.

Secret management varies by environment. Development uses .env files (excluded from version control via .gitignore), CI/CD uses pipeline-scoped variables, and production uses platform-native secret stores (Fly.io secrets, AWS Secrets Manager, HashiCorp Vault). The principle is that secrets never appear in source code, configuration files, or build artifacts.

Usage in Prismatic Platform

The Prismatic Platform runs across three deployment targets with environment-specific configuration managed through Mix config files and Fly.io secrets.

# config/runtime.exs
import Config

if config_env() == :prod do
  database_url =
    System.get_env("DATABASE_URL") ||
      raise "DATABASE_URL environment variable is not set"

  config :prismatic, Prismatic.Repo,
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
    ssl: true,
    ssl_opts: [verify: :verify_peer]

  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise "SECRET_KEY_BASE environment variable is not set"

  config :prismatic_web, PrismaticWeb.Endpoint,
    url: [host: System.get_env("PHX_HOST") || "prismatic-prod.fly.dev", port: 443, scheme: "https"],
    http: [port: String.to_integer(System.get_env("PORT") || "4000")],
    secret_key_base: secret_key_base

  config :prismatic_api, PrismaticApi.Endpoint,
    url: [host: System.get_env("PHX_HOST") || "prismatic-prod.fly.dev", port: 443, scheme: "https"],
    http: [port: String.to_integer(System.get_env("API_PORT") || "4004")],
    secret_key_base: secret_key_base
end

Platform Environment Matrix

SettingDevelopmentStagingProduction
MIX_ENVdevprodprod
DatabaseLocal PostgreSQLFly PostgresFly Postgres (HA)
Meilisearchlocalhost:7700Fly serviceFly service (HA)
KuzuDBLocal file storeFly volumeFly volume (persistent)
ETS BackendLocal ETSLocal ETSHorde (distributed)
LoggingConsole, debug levelJSON, info levelJSON, info level
Code ReloadEnabledDisabledDisabled
WarningsDisplayed--warnings-as-errors--warnings-as-errors
OSINT API KeysTest/sandbox keysStaging keysProduction keys
Fly Hostlocalhost:4000prismatic-staging.fly.devprismatic-prod.fly.dev
defmodule Prismatic.Environment do
  @moduledoc """
  Environment detection and configuration helpers.
  Provides runtime environment information for feature
  flags and operational decisions.
  """

  @spec current_env() :: :dev | :test | :prod
  def current_env, do: Application.get_env(:prismatic, :env, :dev)

  @spec production?() :: boolean()
  def production?, do: current_env() == :prod

  @spec staging?() :: boolean()
  def staging? do
    production?() and
      System.get_env("FLY_APP_NAME") == "prismatic-staging"
  end

  @spec feature_enabled?(atom()) :: boolean()
  def feature_enabled?(feature) do
    Application.get_env(:prismatic, :features, [])
    |> Keyword.get(feature, false)
  end
end

Cross-References

  • Deployment -- Process of releasing to environments
  • Configuration -- Environment-specific settings
  • Health Check -- Environment health verification
  • Docker -- Container-based environment isolation
  • Livebooks: platform_administration/ notebooks cover environment management
  • Academy: DevOpsSecurityPipeline topic covers environment security

See Also


Connect & Contribute

Created by Tomas Korcak (korczis) | Open Source under GHL