The environment — a named target that remembers
Up to this part, a deploy has been a step that copies an artifact somewhere. Now give the somewhere a name. An environment in Azure DevOps is a named deployment target — staging, production, qa — that a special kind of job, the deployment job, points at with one line: environment: production. The name buys you two things a raw deploy step cannot. The first is deployment history: every run that lands on the environment is recorded — which pipeline, which commit, which work items, at what time — so months later you can answer "what is actually running in production, and what change put it there" without archaeology. The second is a place for protection to live, which is the whole subject of this part: the environment is the door, and a door is where you hang a lock.
Naming the target is not bureaucracy; it is the move that turns "we deployed something" into "we can trace and govern what deploys here." Two microservices with their own pipelines can both target the same production environment, and the environment's history stitches their separate runs into one legible sequence of who changed production and when. That single fact — one accountable record of every landing — is worth naming for on its own; the gates you hang on it are the second dividend.
- Environment
- A named collection of deployment resources in Azure DevOps that a deployment job targets with environment:. It records the deployment history for everything that lands there — pipeline, commit, and work items — and it is the resource on which approvals and checks are configured.
Create the environment once — in Pipelines → Environments, or let the first pipeline that references it create it — and thereafter every deployment job that names it writes to the same ledger. Keep the names honest and few: an environment per real target the business can name, not one per branch or per engineer. The value of the record collapses the moment "production" is actually five half-named things nobody can tell apart in the outage.
You met one CI/CD system already — GitHub Actions, across Classes Twenty-Two through Twenty-Four — and built real pipelines on it. This track teaches the same discipline on Azure DevOps Pipelines, and does it from the ground up rather than as a translation, because the concepts underneath — trigger, build, test, artifact, environment, approval — are not GitHub's or Microsoft's; they are the field's, and an engineer who understands them cold can walk into either system and read it on the first morning. Azure DevOps is not legacy: it is actively supported, deeply wired into Azure, and runs an enormous share of the world's production deploys — so the company that hires you may well ship on it, and the engineer who can speak both platforms is worth two who can only speak one.1
A pipeline you can't diff is folklore.
The approval — the human gate before production
An approval is the simplest and most important gate: before a stage is allowed to deploy to the environment, the run pauses and waits for a named human — or any one of a named group — to say yes. Nobody's merge silently reaches customers at four on a Friday; a person looked at what was about to ship and put their name to it. This is the same idea you already met as GitHub Actions environment protection rules back in Class Twenty-Three — the noun is different, the control is identical — which is exactly why the discipline travels: learn "a human gate lives on the environment" once and you can find it in either platform on the first morning.
The load-bearing detail — the one this whole part turns on — is where the approval is configured. It is not in the YAML. The pipeline author writes only environment: production; the approval is attached to the production environment itself, in the web interface, by the environment's owner. That separation is deliberate and it is a security boundary: a developer editing the pipeline file cannot grant themselves a shortcut past production, because the gate does not live in the file they can edit. It lives on the resource, and only a resource administrator can change it.
# azure-pipelines.yml — the deploy stage stages: - stage: DeployProd jobs: - deployment: WarehouseRelease environment: production # names the target strategy: runOnce: deploy: steps: - script: ./release.sh # NOTE: the approval is not written here. It is configured # ON the 'production' environment in Pipelines > Environments # > Approvals and checks — by the environment owner, not the # pipeline author. The file names the door; the lock is hung # on the door, by someone who holds the key.
When you add the approval you choose the approvers — users or groups — set a timeout after which the run is skipped rather than left hanging forever, and decide one small setting that turns out to matter enormously: whether an approver is permitted to approve their own run. Leave that permitted and the gate becomes theatre; restrict it and you have separation of duties, the difference explored in §4. If a group is named, only one member need approve — a deliberate convenience, so a single person's holiday does not stop every release, but also the first thing an attacker studies when they want a rubber stamp.
The checks — the machines that gate beside the human
An approval is a person; a check is a machine. Both are gates on the environment, both pause the stage until satisfied, and both are configured in the same place — the environment's Approvals and checks tab, never in the YAML. The difference is who decides: an approval waits for human judgement, a check evaluates an automated condition and passes or fails on its own. Keep the two words apart, because "we have approvals" and "we have checks" answer different questions — who agreed versus what was verified — and a mature production door usually has both.
Approvals gate who; checks gate what.
Azure DevOps ships a fixed menu of check types, and you will not memorise them by rote so much as recognise the shape of problem each solves. They run in a defined order — static checks first, then approvals, then the dynamic checks, and an exclusive lock last — so a run only reaches the human once the cheap automated gates have already passed. The ones worth knowing by name:
| Check | Kind | What it verifies before the stage runs |
|---|---|---|
| Approval | Human | A named user or group signs off manually — the production gate |
| Invoke REST API | Automated | Calls one of your services; proceeds only on a successful response — a hook for any external condition |
| Invoke Azure Function | Automated | Runs your own serverless logic as a check when a REST call is not enough |
| Query Azure Monitor alerts | Automated | Passes only if no alert rules are firing — health-gates a rollout on live signals |
| Business Hours | Automated | Holds the deploy until an allowed time window — no releases at 2am |
| Required template | Static | Fails unless the pipeline extends a specified YAML template — a governance boundary |
| Branch control | Static | Allows the deploy only from named, protected branches |
| Exclusive lock | Serialising | Lets only one run at a time hold the environment — no two deploys colliding |
The pattern under the menu is simple: a check lets the resource owner, not the pipeline author, impose a condition that every pipeline consuming that environment must satisfy. Business Hours turns "please don't deploy overnight" from a Slack plea into an enforced rule; Query Azure Monitor alerts turns "watch the dashboards after the canary" into a gate the machine holds; Required template quietly makes a security baseline non-optional. You will not use all of them, and you should resist bolting on checks you cannot explain. But knowing the menu means that when a governance requirement lands on your desk, you reach for the check that already exists instead of writing a fragile script to reinvent it.1
The discipline — gates decided while it is calm
The machinery of this part is a morning's work; the judgement is the career. Three habits separate an engineer whose gates protect production from one whose gates are decorations. The first: decide the gates while nothing is on fire. An approval invented at deploy time — added in a panic because a bad release just went out — is a reaction, not a policy, and it will be quietly bypassed the first time it is inconvenient. Sit down when the estate is calm and write down what production requires: who approves, what checks must pass, from which branches. A gate you designed in daylight survives the 3am pressure to skip it; one you bolted on mid-incident does not.
The second: least privilege on who can approve. The approver list is a permission, and it should be as short as the risk demands and no shorter — the people who can actually judge whether this release is safe, not the whole team for convenience. And the setting that quietly decides whether the gate is real: restrict approvers from approving their own runs. An engineer who can deploy and approve their own deploy is a gate with no one on the other side of it — separation of duties collapses into a formality. Permit self-approval only where the risk genuinely does not warrant a second person, and know that you have done so.
A rubber stamp is not a gate.
The third follows from the second: the approver who rubber-stamps is worse than no gate at all, because they manufacture the appearance of control while providing none. A gate everyone knows is real slows people into looking; a gate that always says yes within four seconds trains the team that production sign-off is a formality, and the audit trail lies on your behalf. If an approval is not being read, you do not have an approval — you have a delay with a signature on it. The fix is not more gates; it is fewer approvers who actually look, and automated checks doing the mechanical verification so the human is spending their judgement on the thing only a human can judge.
A gated staging-to-production flow under the warehouse deploy
Basecamp's warehouse deploy — the one an engineer used to do by hand on Friday nights — now has a real pipeline from the earlier parts, and this part gives it a door. The reader creates two environments. Staging deploys automatically on every merge to main: no approval, so the team gets fast feedback and a running copy to test against, and its deployment history quietly records every landing. Production is a separate environment with a manual approval naming two owners — the warehouse lead and one Campux engineer — with self-approval restricted, so the person who wrote the change cannot be the one who waves it through. No warehouse release reaches customers until one of those two humans has looked and confirmed.
The decision that matters is not the tooling — it is that Campux wrote the gate down while the estate was calm, in the first quiet week of the acquisition, rather than after a bad release forced it. The staging environment absorbs the mistakes; the production approval catches the one that staging missed. The Basecamp engineers, braced to have their Friday ritual mocked, instead watch it become the safest deploy they have ever run — automatic where speed is cheap, gated exactly where a customer would feel the error.
Why the gate is the part a hiring manager can read
Of everything in a pipeline, the production gate is the line that reveals how you think when no one is watching. A hiring manager asking "how does a change reach production on your team" is really asking whether a merge silently deploys at 4pm on a Friday or whether a named human looked first; whether the person who wrote the change is the same person who approved it; whether "don't deploy during peak" is a Slack message or an enforced check. Those answers are not preferences — they are the difference between an engineer a team trusts with production and one it does not. Every one of them is a decision you hang on an environment, in daylight, and can defend out loud.
So carry the shape, not the menu. The environment is the door and the record of who came through it; the approval is a human before customers feel the change; the checks are the machines verifying what a human should not have to. Name the target, hang the right gate on it, keep the approver list short and the self-approval switch honest — and you can answer the interview question in three sentences and back it on the job the same afternoon. That is the whole of this part: not the clicks, but the judgement of where a door belongs and who holds its key.
The release nobody had to be brave to ship
A teammate merges the warehouse fix. It deploys to staging on its own, and the deployment history logs it. Production waits: the run pauses on the approval, one of the two named owners reads the diff, confirms, and the release goes out — traceable, reviewed, and impossible to have shipped by accident. No one clicked into a server, no one held their breath, and if a Monitor alert had fired the check would have held the door on its own. You did nothing heroic. You decided, while it was calm, where the door was and who held the key — and the calm is exactly the achievement.
Examination
Four drills, then two situations. Write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored.
B — the gate does not live in the file the developer can edit. The pipeline author writes only the environment name; the approval is configured on the production environment itself, in Pipelines → Environments → Approvals and checks, by a resource administrator. That separation is the point: if approvals lived in the YAML, anyone who could edit the pipeline could grant themselves a shortcut past production, and the gate would protect nothing. A is the intuitive-but-wrong answer that the whole design exists to defeat. C describes real damage — dropping the environment name does forfeit history and protection — but it does not remove the approval, it removes the target the approval guards, and an administrator would notice a job that no longer deploys anywhere accountable. D confuses environment checks with branch policies. The durable fact: the file names the door; the lock is hung on the door.
B — an approval is a person, a check is a machine. The Query Azure Monitor alerts gate is an automated check: it passes on its own when no alert rules are active, no human involved. The sign-off is an approval: it waits for a named human's judgement. Both are configured on the environment, both pause the stage, but they answer different questions — what was verified versus who agreed — and conflating them is how teams end up thinking a green automated check means someone looked, or that a human approval proves the system is healthy. A and C collapse the distinction the whole section is built on. D is the recurring error of this part: these are resource-owner controls on the environment, deliberately kept out of the YAML so a pipeline author cannot weaken them. Keep the pair straight: checks gate what; approvals gate who.
Environments record history; self-approval must be restricted for separation of duties; Business Hours and Monitor are owner-set checks. Those three are the load-bearing facts. The two rejects are the confusions to burn out. Approvals and checks are emphatically not in the YAML — they are configured on the resource by its owner, so a pipeline author cannot edit past them; believing otherwise is exactly the mistake that lets someone assume the gate is in reach of anyone who can touch the file. And when a group is the approver, only one member need approve — a deliberate convenience so a single holiday does not freeze every release, but also the reason a large approver group can quietly become a rubber stamp, since it only takes the one person who never reads. Size approver groups the way you size any permission: as small as the risk allows.
# production gate — warehouse deploy
1. Target a named 'production' environment so every
release is recorded in deployment history.
2. Add a manual approval on the environment naming
the warehouse lead and one Campux engineer.
3. Leave "approvers may approve their own runs" ON,
so whoever deploys can just approve it themselves
and we don't get blocked waiting.
4. Add a Business Hours check so nothing deploys
to the warehouse system overnight.
Line three — the gate now has no one on the other side of it. An approval exists so that someone other than the author looks before a change reaches customers. Leaving self-approval permitted means the person who wrote and deployed the warehouse change is the same person who waves it through — separation of duties collapses into a click, and the audit trail records a sign-off that verified nothing. The whole apparatus of environment, history, and named approvers is intact, and this one setting quietly makes it decorative. The fix is one toggle: restrict approvers from approving their own runs. The distractors are the plan at its best — history is the reason to name the environment (A inverts it), two named approvers with self-approval restricted is exactly right so a holiday does not freeze releases (B), and Business Hours is a genuine check that holds overnight deploys (D). The test for any gate: who is actually on the other side of it? Here, until line three is fixed, no one is.
The trap is that the shortcut is one click and technically within your power. You have admin; you could add yourself and approve your own change in ten seconds. But that is the exact move the gate exists to prevent — the deployer approving their own deploy is separation of duties collapsing into a formality, and doing it once, under pressure, is how it becomes the normal path. The approval is not being annoying by accident; it is being annoying on purpose, and the moment you bypass it because a customer is waiting is the moment it stops protecting the next customer.
Separate the urgent from the permanent. If this release genuinely cannot wait, the right escape hatch is not self-approval — it is finding a legitimate second human. Is there another authorised approver, or an on-call lead who can be added and asked to actually look? A real review by someone other than the author, even hurried, preserves the control; a self-approval destroys it. If no qualified second person can be reached, then the honest answer is that the release waits, and you say so — "we designed production to need a second set of eyes, and I'm not going to be the one who quietly removes that on a Friday."
Fix the system, not just tonight. The real defect is that a two-person approver list with no coverage can strand a release at 4:30 on a Friday. That is a rota problem: widen the approver group enough that someone qualified is always reachable, or define a documented break-glass procedure that is logged and reviewed — not an ad-hoc "the admin approved their own thing." Raise it Monday. The seniority here is refusing a defensible-sounding shortcut and turning the incident into the reason the gate finally gets staffed properly.
Answer as a structure, not a list of tools. "Every deploy lands on a named environment, and the environment is where I hang the controls." Then walk it: staging deploys automatically so the team gets fast feedback, and its deployment history records every landing — which commit, which run, when — so I can always say what is actually running. Production is a separate environment, and it is the door I lock.
Name the two kinds of gate and where they live. "On production I put a manual approval — a named human, not the person who wrote the change, confirms before it ships, so nothing reaches customers by accident at 4pm on a Friday. Beside it I put automated checks: a Monitor-alerts check so a deploy holds if the system is already unhealthy, Business Hours so nothing lands overnight. And the load-bearing detail — none of that is in the YAML. The pipeline author just writes the environment name; the approval and checks are configured on the environment by its owner, so a developer editing the file cannot approve past their own change." That one sentence tells the interviewer you understand it as a security boundary, not a convenience.
Close on the judgement, not the clicks. "The reason I answer it as a door rather than a feature is that it's the same idea on Azure DevOps or GitHub Actions — a human before customers feel the change, machines verifying what a human shouldn't have to, and the gate decided while it's calm rather than bolted on after an outage. What's portable isn't the tool; it's knowing where a door belongs and who holds its key." That reframes you from "person who can click approve" to "engineer a team trusts with production" — which is the whole point of the part.
Five things worth carrying out of this part
- An environment is a named deployment target a deployment job points at with environment:. Naming it buys deployment history — a legible record of what landed and when — and a place to hang protection.
- An approval is the human gate: the run pauses until a named person (or one of a group) signs off — the same idea as GitHub Actions environment protection rules from Class Twenty-Three.
- Approvals and checks are configured on the environment by its owner, never in the YAML. The pipeline author only names the door; the lock is hung on the door — a deliberate security boundary.
- A check is a machine, an approval is a human. Checks gate what (Monitor alerts, Business Hours, Required template, Exclusive lock); approvals gate who. A production door usually has both.
- Decide the gates while it is calm, keep the approver list short, and restrict self-approval — an approver who is also the deployer, or one who rubber-stamps, is worse than no gate at all.
- The exact menu of checks, their categories, and their evaluation order are sprint-versioned and shift — new check types arrive and behaviours change between updates. Treat the shape as settled (an approval is a human gate; a check is an automated one; both are owner-configured on the resource, never in the YAML) but confirm the current list and ordering on Microsoft Learn before you rely on a specific check existing or running when you expect. This page names the ones that have been stable for years; do not assume it is exhaustive. ↩
- "Environment" here means an Azure DevOps Pipelines environment — the deployment target that records history and carries approvals and checks. It is available for YAML pipelines, not Classic release pipelines, which use deployment groups for the comparable role; and the resource types an environment can hold (virtual machines, Kubernetes) are a separate concern from the approvals-and-checks governance this part is about. ↩