How do you create multi-stage YAML pipelines with build, test, and deploy stages with approval gates?
Quick Answer
Multi-stage YAML pipelines define sequential stages in azure-pipelines.yml with dependsOn relationships, conditions for execution, and Environment-based approval gates. Each stage contains jobs that run on agents, and Environments configured in Azure DevOps enforce manual approvals, business hours checks, and other governance controls before production deployments proceed.
Detailed Answer
Think of multi-stage pipelines like a pharmaceutical manufacturing process where a drug must pass through distinct phases: synthesis, quality testing, regulatory review, and distribution. Each phase has its own specialized equipment and personnel, and a drug cannot advance to the next phase until the current one completes successfully. Between critical phases like regulatory review, a human authority must sign off before proceeding. The process is linear and auditable, with clear documentation of who approved what and when. Multi-stage YAML pipelines implement this same controlled progression for software delivery.
A multi-stage pipeline divides the CI/CD workflow into logical stages that execute sequentially or in parallel depending on their dependencies. The most common pattern is Build, Test, Deploy-Staging, Deploy-Production. Each stage contains one or more jobs, and each job contains a sequence of steps that execute on an agent. Stages can depend on other stages using the dependsOn property, creating a directed acyclic graph of execution. A stage only executes if all its dependencies completed successfully, unless overridden with a condition expression. This structure provides clear separation of concerns: build artifacts are created once and promoted through environments rather than rebuilt at each stage.
Approval gates are implemented through Azure DevOps Environments. An Environment is a resource in Azure DevOps that represents a deployment target like payments-staging or payments-production. You configure checks on environments including manual approvals, business hours gates, Azure Monitor alerts, REST API checks, and required template usage. When a pipeline references an environment in a deployment job, Azure DevOps automatically pauses the pipeline and triggers the configured checks. For manual approvals, designated approvers receive email or Teams notifications and must explicitly approve or reject the deployment through the Azure DevOps portal. The approval history is recorded for audit compliance.
Deployment jobs differ from regular jobs in several important ways. They track deployment history per environment, support deployment strategies like runOnce, rolling, and canary, and automatically download artifacts from previous stages. The deployment strategy defines how the new version is rolled out: runOnce deploys everything at once, rolling deploys to servers in batches with health checks between batches, and canary routes a percentage of traffic to the new version before full rollout. Each strategy supports lifecycle hooks including preDeploy, deploy, routeTraffic, postRouteTraffic, and on.failure, giving teams fine-grained control over the deployment process.
Stage conditions enable sophisticated pipeline logic without manual intervention. You can restrict production deployments to only the main branch using condition expressions, skip stages when specific files changed, or conditionally include stages based on pipeline variables. Combined with variable groups that hold environment-specific configuration and template parameters that customize behavior per stage, multi-stage pipelines support complex real-world scenarios like deploying to multiple regions sequentially with health checks between each region, or deploying microservices in dependency order.
The production gotcha is managing pipeline artifacts between stages. Each stage can run on a different agent, so files from the build stage are not automatically available in deploy stages. Teams must explicitly publish artifacts using the PublishPipelineArtifact task and download them in subsequent stages using DownloadPipelineArtifact. A common mistake is publishing large artifacts including test binaries or debug symbols that slow down stage transitions. Another issue is approval fatigue: if every deployment requires manual approval, approvers become rubber-stampers. Best practice is to require approvals only for production environments while staging deployments proceed automatically, giving teams a chance to validate in staging before the production approval request arrives.