Every engineering team eventually asks the same question:
“How do we stop risky code from reaching the main branch?”
In Azure DevOps, the answer is branch policies.
Branch policies are the enforcement layer of your pull request workflow. They define exactly what must happen before code can merge into a protected branch like main, develop, or release/*.
Instead of relying on developers to follow best practices manually, Azure DevOps can enforce rules such as:
minimum number of reviewers
successful CI builds
resolved review comments
linked work items
restricted merge strategies
required status checks from security tools
Once these policies are enabled, Azure DevOps blocks merge automatically until every condition passes.
This guide explains everything you need to configure branch protection correctly, including:
every branch policy setting and what it does
recommended configurations for different team sizes
CLI commands to automate policy setup
best practices used by enterprise teams
common policy conflicts and how to fix them
By the end, you’ll know exactly how to enforce safe, auditable pull request workflows in Azure DevOps.
What Are Branch Policies in Azure DevOps?
Branch policies are server-side rules that Azure DevOps enforces on pull requests targeting a specific branch. When a policy is enabled on a branch, Azure DevOps blocks direct pushes to that branch and requires all changes to go through a pull request that satisfies every configured policy before it can be completed.
Branch policies answer the question: “What conditions must be met before code can merge into this branch?” They are the enforcement layer, they don’t suggest or recommend; they block. A PR cannot be completed until every required policy shows a passing status, regardless of how many reviewers have approved.
Policies are configured per branch (or per branch pattern like release/*) and per repository. You can have strict policies on main, lighter policies on develop, and different policies on release branches, all within the same repository.
For a full overview of how policies fit into the code review workflow, see the complete Azure DevOps code review guide. For who can configure and bypass policies, see the permissions guide.
Complete Policy Settings Reference
Navigate to: Project Settings → Repos → Repositories → [Select repo] → Policies → [Select branch]
1. Require Minimum Number of Reviewers
This policy blocks merge until a minimum number of reviewers have approved the PR.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Minimum number of reviewers | 1–10 | 1 (small teams), 2 (6+ devs) | The number of unique approvers required before merge |
Allow requestors to approve their own changes | Checked / Unchecked | Unchecked | If checked, the PR author’s approval counts toward the minimum. Uncheck to enforce independent review. |
Prohibit the most recent pusher from approving their own changes | Checked / Unchecked | Checked | Prevents the person who pushed the latest commit from being one of the approvers. Closes the loophole where a reviewer pushes a “minor fix” and then approves their own push. |
Allow completion even if some reviewers vote “Wait for author” or “Reject” | Checked / Unchecked | Unchecked | If checked, the minimum count of approvals is sufficient even if other reviewers voted negatively. Unchecked means any Reject or Wait blocks completion. |
When new changes are pushed | Reset all approval votes / Reset approval votes of the person pushing / Do not reset | Reset all approval votes | Controls whether existing approvals remain valid after the author pushes new commits. “Reset all” forces re-review after every push — the strictest option. “Reset approval votes of the person pushing” is a middle ground. |
CLI command:
az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
Common mistake: Setting minimum reviewers to 2 on a team of 3 developers means PRs block until 2/3 of the team reviews. On small teams, start with 1 required reviewer to avoid bottlenecks.
2. Check for Linked Work Items
Requires the PR to be linked to at least one Azure Boards work item (task, bug, user story, etc.).
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Requirement | Required / Optional | Required | “Required” blocks merge if no work item is linked. “Optional” shows a warning but doesn’t block. |
CLI command:
az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
Why it matters: Work item linking creates traceability — every code change maps to a tracked piece of work. This is essential for audits, compliance, and understanding why a change was made six months later.
3. Check for Comment Resolution
Blocks merge until all PR comments are resolved.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Requirement | Required / Optional | Required | “Required” blocks merge if any active (unresolved) comment exists on the PR. “Optional” shows a warning but allows merge. |
CLI command:
az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
How resolution works: A comment can be resolved by anyone with “Contribute to pull requests” permission — the author, the reviewer who left it, or any other reviewer. Reviewers can also mark comments as “Won’t Fix” which counts as resolved. This prevents PRs from being stuck on a single unresponsive reviewer’s comment.
4. Limit Merge Types
Restricts which merge strategies are available when completing a PR targeting this branch.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Basic merge (no fast-forward) | Allowed / Not allowed | Branch-dependent | Standard merge commit. Best for develop→main where you want merge points visible. |
Squash merge | Allowed / Not allowed | Branch-dependent | Collapses all PR commits into one. Best for feature→develop to keep history clean. |
Rebase and fast-forward | Allowed / Not allowed | Branch-dependent | Replays commits on top of target. Produces linear history but rewrites commit hashes. |
Semi-linear merge | Allowed / Not allowed | Branch-dependent | Rebases first, then creates merge commit. Linear + merge points. Strictest, requires no conflicts before merge. |
CLI command:
az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false
az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false
Recommended per branch:
Branch | Allowed Strategies | Rationale |
|---|---|---|
| Merge commit only | Preserve merge points for release tracking |
| Squash only | Clean history, one commit per feature |
| Merge commit only | Preserve full history for hotfix traceability |
| No policy needed | Feature branches are typically short-lived |
For a deep dive on each strategy with visual history diagrams, see the merge strategies guide.
5. Build Validation
Runs an Azure Pipeline and requires it to pass before merge. This is how you enforce automated tests, linting, security scans, and build verification on every PR.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Build pipeline | Select from available pipelines | Your CI pipeline | Which pipeline to trigger when the PR is created or updated |
Trigger | Automatic / Manual | Automatic | “Automatic” triggers the build on every PR create/update. “Manual” requires someone to queue it. |
Policy requirement | Required / Optional | Required | “Required” blocks merge if the build fails. “Optional” shows status but doesn’t block. |
Build expiration | Immediately / After N hours / Never | After 12 hours or Immediately when target branch updates | Controls when a passing build becomes stale and needs re-running. “Immediately” re-triggers when the target branch changes. “After N hours” expires after the set period. |
Display name | Free text | Descriptive (e.g., “CI Build + Tests”) | Shown on the PR status — use clear names so developers know what failed |
Path filter (optional) | Include/exclude paths | Use when pipelines are path-specific | Trigger build only when files in specific paths change. Useful for monorepos with separate pipelines per service. |
CLI command:
az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720
az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720
The --valid-duration is in minutes. 720 = 12 hours. Set to 0 for “Immediately when target branch updates.”
Multiple build validations: You can add more than one build validation policy to the same branch. For example, add your CI pipeline and a separate security scan pipeline. Both must pass before merge. Each one is configured independently.
6. Status Checks (External Services)
Blocks merge until an external service posts a “succeeded” status on the PR. This is how external tools like AI code reviewers, third-party SAST scanners, and coverage analyzers integrate with Azure DevOps branch policies.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Status to check | Select from posted statuses | Tool-specific (e.g., | The status key that the external tool posts. You’ll see available statuses after the tool has posted at least once. |
Policy requirement | Required / Optional | Required for security tools | “Required” blocks merge until the status is “succeeded.” |
Authorized account | Any / Specific account | Specific account | Restricts who can post this status. Set to the service account or PAT identity used by the tool — prevents anyone from spoofing a passing status. |
Reset status | When source branch changes / Never | When source branch changes | Controls whether the status resets (requires re-check) when the author pushes new commits. |
CLI command:
az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
How external tools post status: External tools use the Azure DevOps REST API (POST /_apis/git/repositories/{id}/pullRequests/{id}/statuses) to post a status with a state (succeeded, failed, pending, error) and a target URL. Once a matching status check policy is configured, Azure DevOps blocks merge until the tool posts succeeded.
7. Automatically Included Reviewers
Auto-assigns specific reviewers when a PR modifies files matching a path filter. This is Azure DevOps’s equivalent of GitHub CODEOWNERS.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Reviewers | Users or groups | Use groups | Who to auto-add. Use groups so the team isn’t blocked when one person is unavailable. |
Path filter (optional) | File path patterns | Scope to sensitive paths | Which file changes trigger this reviewer. Example: |
Policy requirement | Required / Optional | Required for sensitive paths | “Required” means this reviewer must approve before merge. “Optional” means they’re added but don’t block. |
Allow requestors to approve their own changes | Checked / Unchecked | Unchecked | If the PR author is the auto-included reviewer (e.g., they’re in the security group), can they approve their own PR? Usually no. |
If the source branch involves a merge with the target | Checked / Unchecked | Unchecked | Technical edge case — whether merge commits from the target branch count as file changes for path filtering. |
CLI command:
az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required for changes to security modules"
az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required for changes to security modules"
Example configuration for a typical enterprise:
Path Filter | Reviewer Group | Required? | Use Case |
|---|---|---|---|
| Backend Team | Required | API changes need backend review |
| Frontend Team | Required | UI changes need frontend review |
| DevOps Team | Required | Infra changes need ops review |
| DBA Team | Required | Schema changes need DBA review |
| Security Team | Required | Auth/security changes need security review |
| Tech Writers | Optional | Doc changes benefit from but don’t require writer review |
For a detailed comparison between GitHub CODEOWNERS and Azure DevOps auto-reviewers, including a migration guide, see the permissions guide.
Step-by-Step: Configure Branch Policies via the Web UI
Protecting the main Branch (From Zero to Production-Ready)
Step 1: Navigate to branch policies
Go to Project Settings → Repos → Repositories → [Select your repo] → Policies. Select the branch you want to protect (e.g., main).
If your branch doesn’t appear in the list, click Add branch protection and type the branch name or pattern.
Step 2: Enable minimum reviewers
Toggle Require a minimum number of reviewers to ON. Set the count to 1 (small teams) or 2 (teams of 6+). Uncheck “Allow requestors to approve their own changes.” Set “When new changes are pushed” to “Reset all approval votes.”
From this moment, no one can push directly to main. All changes must go through a PR with at least one independent approval.
Step 3: Require linked work items
Toggle Check for linked work items to ON. Set to Required. Every PR must now link to at least one Azure Boards work item.
Step 4: Require comment resolution
Toggle Check for comment resolution to ON. Set to Required. PRs cannot be merged until all review comments are resolved.
Step 5: Add build validation
Under Build Validation, click + Add build policy. Select your CI pipeline. Set trigger to Automatic, requirement to Required, and build expiration to 12 hours or Immediately when target branch is updated. Give it a clear display name like “CI Build + Unit Tests.”
Step 6: Restrict merge types
Under Limit merge types, check only the strategies your team uses. For main, a common choice is merge commit only (preserves merge points) or squash only (clean one-commit-per-PR history).
Step 7: Add external status checks (optional)
Under Status checks, click + Add status policy. Select the status posted by your external tools (e.g., CodeAnt AI). Set to Required and configure the authorized account.
Step 8: Add auto-included reviewers (optional)
Under Automatically included reviewers, click + Add. Add groups for sensitive file paths — security team for /src/auth/*, DBA team for *.sql, etc.
Result: Your main branch now requires:
At least N independent approvals (no self-approval)
Linked work item
All comments resolved
Passing CI build
Passing external status checks (if configured)
Specific reviewers for sensitive file paths (if configured)
Only allowed merge strategies
Step-by-Step: Configure All Policies via CLI
The CLI is essential when managing policies across multiple repositories. Every policy type has a create, update, list, and delete command.
List All Existing Policies
# List all policies on a branch az repos policy list \\ --repository-id <REPO_ID> \\ --branch main \\ --output table # Get a specific policy's full configuration az repos policy show --id
# List all policies on a branch az repos policy list \\ --repository-id <REPO_ID> \\ --branch main \\ --output table # Get a specific policy's full configuration az repos policy show --id
Complete CLI Command Reference
# ────────────────────────────────────────────── # 1. Minimum reviewers # ────────────────────────────────────────────── az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true # ────────────────────────────────────────────── # 2. Linked work items # ────────────────────────────────────────────── az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 3. Comment resolution # ────────────────────────────────────────────── az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 4. Merge strategy # ────────────────────────────────────────────── az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false # ────────────────────────────────────────────── # 5. Build validation # ────────────────────────────────────────────── az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720 # ────────────────────────────────────────────── # 6. Required reviewers (auto-included) # ────────────────────────────────────────────── az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required" # ────────────────────────────────────────────── # 7. Status check (external tools) # ────────────────────────────────────────────── az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
# ────────────────────────────────────────────── # 1. Minimum reviewers # ────────────────────────────────────────────── az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true # ────────────────────────────────────────────── # 2. Linked work items # ────────────────────────────────────────────── az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 3. Comment resolution # ────────────────────────────────────────────── az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 4. Merge strategy # ────────────────────────────────────────────── az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false # ────────────────────────────────────────────── # 5. Build validation # ────────────────────────────────────────────── az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720 # ────────────────────────────────────────────── # 6. Required reviewers (auto-included) # ────────────────────────────────────────────── az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required" # ────────────────────────────────────────────── # 7. Status check (external tools) # ────────────────────────────────────────────── az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
Update and Delete Policies
# Update an existing policy (get policy ID from 'az repos policy list') az repos policy approver-count update \\ --id <POLICY_ID> \\ --minimum-approver-count 3 # Delete a policy az repos policy delete --id <POLICY_ID> --yes
# Update an existing policy (get policy ID from 'az repos policy list') az repos policy approver-count update \\ --id <POLICY_ID> \\ --minimum-approver-count 3 # Delete a policy az repos policy delete --id <POLICY_ID> --yes
Recommended Policies by Team Maturity
Tier 1: Startup / Small Team (2–5 Developers)
Priority: Don’t block velocity. Catch the worst mistakes.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 1 | Every change gets a second pair of eyes, but don’t bottleneck on multiple reviewers |
Linked work items | Optional | Encourage traceability without blocking quick fixes during early development |
Comment resolution | Required | Feedback shouldn’t be ignored, this is low-friction |
Build validation | Required (CI pipeline with build + unit tests) | Prevents broken builds from reaching main |
Merge type | Squash only | Keeps history clean while the team is moving fast |
Auto-reviewers | Not needed yet | Team is small enough that everyone sees every PR |
Status checks | Not configured yet | Add when you introduce security scanning tools |
Tier 2: Growth Team (6–20 Developers)
Priority: Enforce process without micromanaging. Teams are specializing.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 2 | Two independent reviews catch more issues and reduce bus factor |
Linked work items | Required | Non-negotiable — every change maps to tracked work |
Comment resolution | Required | Same as Tier 1 |
Build validation | Required (CI + integration tests + linting) | Expand the CI pipeline as the codebase grows |
Merge type | Squash for develop, merge commit for main | Different strategies per branch — clean feature history, visible release points |
Auto-reviewers | Required for sensitive paths | Backend team reviews API, DBA reviews SQL, security reviews auth |
Vote reset | Reset all votes on new pushes | Prevents stale approvals — critical when PRs are larger and reviews take longer |
Status checks | Required (SAST / AI code review) | Add external quality and security checks |
Tier 3: Enterprise (20+ Developers, Compliance Requirements)
Priority: Auditability, compliance, zero unreviewed code reaching production.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 2–3 (branch-dependent) | 2 for develop, 3 for main and release branches |
Linked work items | Required | Audit requirement — every change traceable to a work item |
Comment resolution | Required | Same as Tier 1–2 |
Build validation | Required (CI + integration + security + compliance checks) | Multiple build validations: unit tests, integration tests, security scans, license compliance |
Merge type | Locked per branch | Squash for feature→develop, merge commit for develop→main, merge commit for hotfix→release |
Auto-reviewers | Required for all sensitive paths + optional for all others | Security, infrastructure, database, API, and compliance paths all have mandatory reviewers |
Vote reset | Reset all votes on new pushes | Non-negotiable for compliance — ensures every commit is reviewed |
Status checks | Required (SAST + SCA + AI code review + coverage) | Multiple external checks: CodeAnt AI for code review, security scanning, coverage thresholds |
Self-approval | Disabled everywhere | No developer can approve their own changes |
Bypass permissions | Restricted to a dedicated “Hotfix Approvers” group | Bypass is audited and available only to senior engineers for emergencies |
Policies at Scale: Cross-Repo Scripts
Enterprise teams with dozens or hundreds of repositories need to apply consistent policies without clicking through the UI for each repo. The Azure CLI combined with shell scripting handles this.
Apply Standard Policies to All Repositories
#!/bin/bash # apply-standard-policies.sh # Applies minimum viable policies to all repos in a project ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" PIPELINE_ID="<YOUR_CI_PIPELINE_ID>" # Get all repository IDs REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].id" \\ --output tsv) for REPO_ID in $REPO_IDS; do REPO_NAME=$(az repos show --id "$REPO_ID" --org "$ORG" --project "$PROJECT" --query "name" --output tsv) echo "Configuring policies for:$REPO_NAME ($REPO_ID)" # 1. Minimum 2 reviewers az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "$BRANCH" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id "$REPO_ID" \\ --reset-on-source-push true \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Reviewer policy created" || echo " ⚠ Reviewer policy already exists or failed" # 2. Require linked work items az repos policy work-item-linking create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Work item policy created" || echo " ⚠ Work item policy already exists or failed" # 3. Require comment resolution az repos policy comment-required create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Comment policy created" || echo " ⚠ Comment policy already exists or failed" # 4. Build validation (if pipeline exists for this repo) az repos policy build create \\ --blocking true \\ --branch "$BRANCH" \\ --build-definition-id "$PIPELINE_ID" \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id "$REPO_ID" \\ --valid-duration 720 \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Build policy created" || echo " ⚠ Build policy already exists or failed" echo "---" done echo "Done. Policies applied to all repositories."
#!/bin/bash # apply-standard-policies.sh # Applies minimum viable policies to all repos in a project ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" PIPELINE_ID="<YOUR_CI_PIPELINE_ID>" # Get all repository IDs REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].id" \\ --output tsv) for REPO_ID in $REPO_IDS; do REPO_NAME=$(az repos show --id "$REPO_ID" --org "$ORG" --project "$PROJECT" --query "name" --output tsv) echo "Configuring policies for:$REPO_NAME ($REPO_ID)" # 1. Minimum 2 reviewers az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "$BRANCH" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id "$REPO_ID" \\ --reset-on-source-push true \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Reviewer policy created" || echo " ⚠ Reviewer policy already exists or failed" # 2. Require linked work items az repos policy work-item-linking create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Work item policy created" || echo " ⚠ Work item policy already exists or failed" # 3. Require comment resolution az repos policy comment-required create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Comment policy created" || echo " ⚠ Comment policy already exists or failed" # 4. Build validation (if pipeline exists for this repo) az repos policy build create \\ --blocking true \\ --branch "$BRANCH" \\ --build-definition-id "$PIPELINE_ID" \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id "$REPO_ID" \\ --valid-duration 720 \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Build policy created" || echo " ⚠ Build policy already exists or failed" echo "---" done echo "Done. Policies applied to all repositories."
Audit Policies Across All Repositories
#!/bin/bash # audit-policies.sh # Reports which repos have policies and which don't ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" echo "=== Policy Audit:$PROJECT ===" echo "" REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].{id:id, name:name}" \\ --output json) echo "$REPO_IDS" | jq -r '.[] | "\\(.id) \\(.name)"' | while read REPO_ID REPO_NAME; do POLICY_COUNT=$(az repos policy list \\ --repository-id "$REPO_ID" \\ --branch "$BRANCH" \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "length(@)" \\ --output tsv 2>/dev/null) if [ "$POLICY_COUNT" -gt 0 ] 2>/dev/null; then echo "✅$REPO_NAME —$POLICY_COUNT policies on$BRANCH" else echo "❌$REPO_NAME — NO policies on$BRANCH" fi done
#!/bin/bash # audit-policies.sh # Reports which repos have policies and which don't ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" echo "=== Policy Audit:$PROJECT ===" echo "" REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].{id:id, name:name}" \\ --output json) echo "$REPO_IDS" | jq -r '.[] | "\\(.id) \\(.name)"' | while read REPO_ID REPO_NAME; do POLICY_COUNT=$(az repos policy list \\ --repository-id "$REPO_ID" \\ --branch "$BRANCH" \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "length(@)" \\ --output tsv 2>/dev/null) if [ "$POLICY_COUNT" -gt 0 ] 2>/dev/null; then echo "✅$REPO_NAME —$POLICY_COUNT policies on$BRANCH" else echo "❌$REPO_NAME — NO policies on$BRANCH" fi done
Branch Pattern Policies
Instead of protecting individual branches, protect all branches matching a pattern. This is useful for release branches:
# Protect all release/* branches with 2 reviewers + build validation az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "release/*" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
# Protect all release/* branches with 2 reviewers + build validation az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "release/*" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
Branch patterns use glob matching: release/* matches release/1.0, release/2.0, etc. This means newly created release branches automatically inherit the policy — no manual configuration needed.
Troubleshooting: Common Policy Conflicts and Issues
“All policies pass, but the Complete button is grayed out”
Cause 1: A required reviewer hasn’t voted yet. Even if minimum reviewer count is met (e.g., 2 of 3 required reviewers approved), an auto-included required reviewer who hasn’t voted blocks completion.
Fix: Check the “Reviewers” section on the PR. Look for reviewers marked as “Required” (has a lock icon) who haven’t voted. They must vote “Approve” before the PR can be completed.
Cause 2: Build validation expired. If build expiration is set and the build passed more than N hours ago, it’s considered stale and needs re-running.
Fix: Click “Queue build” to re-trigger the validation pipeline, or ask the PR author to push a new commit (which triggers automatic re-build if configured).
“Build validation keeps re-triggering on every push”
This is expected behavior when “Trigger” is set to “Automatic” and “Queue on source update only” is enabled. Every push to the source branch triggers a new build.
If builds are queueing too frequently: Set “Queue on source update only” to true (ignores target branch updates) and increase the build expiration window. This means the build only re-runs when the PR author pushes new commits, not when someone else merges into main.
“Required reviewer can’t be removed from the PR”
Auto-included reviewers (from “Automatically included reviewers” policy) cannot be removed from the PR manually. They’re added by the policy and will remain for the lifetime of the PR.
Workaround: If the required reviewer is unavailable (on leave, left the company), a Project Administrator can temporarily disable the auto-included reviewer policy, complete the PR, and re-enable it. Alternatively, use groups instead of individuals so that any group member can satisfy the requirement.
“PR shows merge conflicts but I can’t resolve them”
Branch policies block the merge button when there are conflicts, but Azure DevOps doesn’t provide an in-browser conflict resolution tool for all merge types.
Fix: Resolve conflicts locally: pull the target branch into your feature branch (git merge main), resolve conflicts, commit, and push. The PR will update and the conflict status will clear.
“Status check is stuck on ‘Pending’”
An external tool (e.g., AI code reviewer, SAST scanner) hasn’t posted its status yet, or it posted but with a different status key than what the policy expects.
Fix: Check if the tool ran successfully (check the tool’s dashboard or logs). Verify the status key matches exactly — the policy checks for an exact string match on the status name. If the tool uses codeantai/pr-review but the policy expects codeantai/review, it won’t match.
“Policy was configured but it’s not being enforced”
Possible causes:
Policy is on the wrong branch. Policies are per-branch. A policy on
maindoesn’t apply to PRs targetingdevelop.Policy is disabled. Check if the policy is toggled to “Enabled” in the settings.
User has Bypass permission. Users with “Bypass policies when completing pull requests” can merge without satisfying policies. Check the permissions guide for who has this permission.
The policy applies to a branch pattern that doesn’t match. Verify the branch pattern (e.g.,
release/*) matches the actual branch name.
“Different team members see different merge type options”
This is correct behavior. The merge type dropdown on the Complete dialog only shows strategies allowed by the “Limit merge types” policy. If only squash is allowed, developers only see the squash option.
If a developer sees all merge types, the “Limit merge types” policy may not be enabled on the target branch, or the developer has bypass permissions.
“I want different policies for different file paths, not just different branches”
Azure DevOps branch policies apply per-branch, not per-file-path — with one exception: “Automatically included reviewers” supports path filters. For everything else (build validation, minimum reviewers, merge types), the policy applies to the entire PR regardless of which files changed.
Workaround for build validation: Use path filters in the build validation policy. When you add a build policy, you can set path filters so the build only triggers when specific paths change. This lets you run different pipelines for /src/api/* vs /src/frontend/*.
Add AI Quality Gates with CodeAnt AI
Branch policies enforce process, minimum reviewers, linked work items, passing builds. But they don’t analyze the actual code. A PR can satisfy every policy and still contain security vulnerabilities, logic errors, or performance problems that human reviewers missed.
AI code review fills this gap by adding an intelligent code quality gate that evaluates every line of code, not just whether process was followed.

CodeAnt AI, used by Fortune 500 development teams on Azure DevOps and reviewed on Gartner Peer Insights, integrates as a status check in branch policies. When configured:
A PR is created or updated → Azure DevOps fires a webhook → CodeAnt AI analyzes the full diff
CodeAnt AI posts inline comments for quality and security findings, with one-click fixes developers can apply directly from the PR
CodeAnt AI posts a status check (
succeededorfailed) based on the quality gate thresholdThe branch policy blocks merge until the status check passes, just like build validation
This means critical security vulnerabilities and code quality issues block the merge, regardless of whether human reviewers caught them. Developers learn from every fix, and leadership dashboards give engineering leaders visibility into code health, review velocity, and security posture across the organization.
To add CodeAnt AI as a required status check:
Install and configure CodeAnt AI (cloud setup guide | self-hosted setup guide)
Create a test PR so CodeAnt AI posts its first status
Go to Project Settings → Repos → Policies → [branch] → Status checks
Add a new status check, select the CodeAnt AI status, and set it to Required
The quality gate now enforces automatically on every PR targeting this branch, no manual intervention needed. If you face any doubt or need help, book your 1:1 with our experts here.
Every engineering team eventually asks the same question:
“How do we stop risky code from reaching the main branch?”
In Azure DevOps, the answer is branch policies.
Branch policies are the enforcement layer of your pull request workflow. They define exactly what must happen before code can merge into a protected branch like main, develop, or release/*.
Instead of relying on developers to follow best practices manually, Azure DevOps can enforce rules such as:
minimum number of reviewers
successful CI builds
resolved review comments
linked work items
restricted merge strategies
required status checks from security tools
Once these policies are enabled, Azure DevOps blocks merge automatically until every condition passes.
This guide explains everything you need to configure branch protection correctly, including:
every branch policy setting and what it does
recommended configurations for different team sizes
CLI commands to automate policy setup
best practices used by enterprise teams
common policy conflicts and how to fix them
By the end, you’ll know exactly how to enforce safe, auditable pull request workflows in Azure DevOps.
What Are Branch Policies in Azure DevOps?
Branch policies are server-side rules that Azure DevOps enforces on pull requests targeting a specific branch. When a policy is enabled on a branch, Azure DevOps blocks direct pushes to that branch and requires all changes to go through a pull request that satisfies every configured policy before it can be completed.
Branch policies answer the question: “What conditions must be met before code can merge into this branch?” They are the enforcement layer, they don’t suggest or recommend; they block. A PR cannot be completed until every required policy shows a passing status, regardless of how many reviewers have approved.
Policies are configured per branch (or per branch pattern like release/*) and per repository. You can have strict policies on main, lighter policies on develop, and different policies on release branches, all within the same repository.
For a full overview of how policies fit into the code review workflow, see the complete Azure DevOps code review guide. For who can configure and bypass policies, see the permissions guide.
Complete Policy Settings Reference
Navigate to: Project Settings → Repos → Repositories → [Select repo] → Policies → [Select branch]
1. Require Minimum Number of Reviewers
This policy blocks merge until a minimum number of reviewers have approved the PR.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Minimum number of reviewers | 1–10 | 1 (small teams), 2 (6+ devs) | The number of unique approvers required before merge |
Allow requestors to approve their own changes | Checked / Unchecked | Unchecked | If checked, the PR author’s approval counts toward the minimum. Uncheck to enforce independent review. |
Prohibit the most recent pusher from approving their own changes | Checked / Unchecked | Checked | Prevents the person who pushed the latest commit from being one of the approvers. Closes the loophole where a reviewer pushes a “minor fix” and then approves their own push. |
Allow completion even if some reviewers vote “Wait for author” or “Reject” | Checked / Unchecked | Unchecked | If checked, the minimum count of approvals is sufficient even if other reviewers voted negatively. Unchecked means any Reject or Wait blocks completion. |
When new changes are pushed | Reset all approval votes / Reset approval votes of the person pushing / Do not reset | Reset all approval votes | Controls whether existing approvals remain valid after the author pushes new commits. “Reset all” forces re-review after every push — the strictest option. “Reset approval votes of the person pushing” is a middle ground. |
CLI command:
az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
Common mistake: Setting minimum reviewers to 2 on a team of 3 developers means PRs block until 2/3 of the team reviews. On small teams, start with 1 required reviewer to avoid bottlenecks.
2. Check for Linked Work Items
Requires the PR to be linked to at least one Azure Boards work item (task, bug, user story, etc.).
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Requirement | Required / Optional | Required | “Required” blocks merge if no work item is linked. “Optional” shows a warning but doesn’t block. |
CLI command:
az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
Why it matters: Work item linking creates traceability — every code change maps to a tracked piece of work. This is essential for audits, compliance, and understanding why a change was made six months later.
3. Check for Comment Resolution
Blocks merge until all PR comments are resolved.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Requirement | Required / Optional | Required | “Required” blocks merge if any active (unresolved) comment exists on the PR. “Optional” shows a warning but allows merge. |
CLI command:
az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id
How resolution works: A comment can be resolved by anyone with “Contribute to pull requests” permission — the author, the reviewer who left it, or any other reviewer. Reviewers can also mark comments as “Won’t Fix” which counts as resolved. This prevents PRs from being stuck on a single unresponsive reviewer’s comment.
4. Limit Merge Types
Restricts which merge strategies are available when completing a PR targeting this branch.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Basic merge (no fast-forward) | Allowed / Not allowed | Branch-dependent | Standard merge commit. Best for develop→main where you want merge points visible. |
Squash merge | Allowed / Not allowed | Branch-dependent | Collapses all PR commits into one. Best for feature→develop to keep history clean. |
Rebase and fast-forward | Allowed / Not allowed | Branch-dependent | Replays commits on top of target. Produces linear history but rewrites commit hashes. |
Semi-linear merge | Allowed / Not allowed | Branch-dependent | Rebases first, then creates merge commit. Linear + merge points. Strictest, requires no conflicts before merge. |
CLI command:
az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false
Recommended per branch:
Branch | Allowed Strategies | Rationale |
|---|---|---|
| Merge commit only | Preserve merge points for release tracking |
| Squash only | Clean history, one commit per feature |
| Merge commit only | Preserve full history for hotfix traceability |
| No policy needed | Feature branches are typically short-lived |
For a deep dive on each strategy with visual history diagrams, see the merge strategies guide.
5. Build Validation
Runs an Azure Pipeline and requires it to pass before merge. This is how you enforce automated tests, linting, security scans, and build verification on every PR.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Build pipeline | Select from available pipelines | Your CI pipeline | Which pipeline to trigger when the PR is created or updated |
Trigger | Automatic / Manual | Automatic | “Automatic” triggers the build on every PR create/update. “Manual” requires someone to queue it. |
Policy requirement | Required / Optional | Required | “Required” blocks merge if the build fails. “Optional” shows status but doesn’t block. |
Build expiration | Immediately / After N hours / Never | After 12 hours or Immediately when target branch updates | Controls when a passing build becomes stale and needs re-running. “Immediately” re-triggers when the target branch changes. “After N hours” expires after the set period. |
Display name | Free text | Descriptive (e.g., “CI Build + Tests”) | Shown on the PR status — use clear names so developers know what failed |
Path filter (optional) | Include/exclude paths | Use when pipelines are path-specific | Trigger build only when files in specific paths change. Useful for monorepos with separate pipelines per service. |
CLI command:
az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720
The --valid-duration is in minutes. 720 = 12 hours. Set to 0 for “Immediately when target branch updates.”
Multiple build validations: You can add more than one build validation policy to the same branch. For example, add your CI pipeline and a separate security scan pipeline. Both must pass before merge. Each one is configured independently.
6. Status Checks (External Services)
Blocks merge until an external service posts a “succeeded” status on the PR. This is how external tools like AI code reviewers, third-party SAST scanners, and coverage analyzers integrate with Azure DevOps branch policies.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Status to check | Select from posted statuses | Tool-specific (e.g., | The status key that the external tool posts. You’ll see available statuses after the tool has posted at least once. |
Policy requirement | Required / Optional | Required for security tools | “Required” blocks merge until the status is “succeeded.” |
Authorized account | Any / Specific account | Specific account | Restricts who can post this status. Set to the service account or PAT identity used by the tool — prevents anyone from spoofing a passing status. |
Reset status | When source branch changes / Never | When source branch changes | Controls whether the status resets (requires re-check) when the author pushes new commits. |
CLI command:
az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
How external tools post status: External tools use the Azure DevOps REST API (POST /_apis/git/repositories/{id}/pullRequests/{id}/statuses) to post a status with a state (succeeded, failed, pending, error) and a target URL. Once a matching status check policy is configured, Azure DevOps blocks merge until the tool posts succeeded.
7. Automatically Included Reviewers
Auto-assigns specific reviewers when a PR modifies files matching a path filter. This is Azure DevOps’s equivalent of GitHub CODEOWNERS.
Setting | Options | Recommended | What It Does |
|---|---|---|---|
Reviewers | Users or groups | Use groups | Who to auto-add. Use groups so the team isn’t blocked when one person is unavailable. |
Path filter (optional) | File path patterns | Scope to sensitive paths | Which file changes trigger this reviewer. Example: |
Policy requirement | Required / Optional | Required for sensitive paths | “Required” means this reviewer must approve before merge. “Optional” means they’re added but don’t block. |
Allow requestors to approve their own changes | Checked / Unchecked | Unchecked | If the PR author is the auto-included reviewer (e.g., they’re in the security group), can they approve their own PR? Usually no. |
If the source branch involves a merge with the target | Checked / Unchecked | Unchecked | Technical edge case — whether merge commits from the target branch count as file changes for path filtering. |
CLI command:
az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required for changes to security modules"
Example configuration for a typical enterprise:
Path Filter | Reviewer Group | Required? | Use Case |
|---|---|---|---|
| Backend Team | Required | API changes need backend review |
| Frontend Team | Required | UI changes need frontend review |
| DevOps Team | Required | Infra changes need ops review |
| DBA Team | Required | Schema changes need DBA review |
| Security Team | Required | Auth/security changes need security review |
| Tech Writers | Optional | Doc changes benefit from but don’t require writer review |
For a detailed comparison between GitHub CODEOWNERS and Azure DevOps auto-reviewers, including a migration guide, see the permissions guide.
Step-by-Step: Configure Branch Policies via the Web UI
Protecting the main Branch (From Zero to Production-Ready)
Step 1: Navigate to branch policies
Go to Project Settings → Repos → Repositories → [Select your repo] → Policies. Select the branch you want to protect (e.g., main).
If your branch doesn’t appear in the list, click Add branch protection and type the branch name or pattern.
Step 2: Enable minimum reviewers
Toggle Require a minimum number of reviewers to ON. Set the count to 1 (small teams) or 2 (teams of 6+). Uncheck “Allow requestors to approve their own changes.” Set “When new changes are pushed” to “Reset all approval votes.”
From this moment, no one can push directly to main. All changes must go through a PR with at least one independent approval.
Step 3: Require linked work items
Toggle Check for linked work items to ON. Set to Required. Every PR must now link to at least one Azure Boards work item.
Step 4: Require comment resolution
Toggle Check for comment resolution to ON. Set to Required. PRs cannot be merged until all review comments are resolved.
Step 5: Add build validation
Under Build Validation, click + Add build policy. Select your CI pipeline. Set trigger to Automatic, requirement to Required, and build expiration to 12 hours or Immediately when target branch is updated. Give it a clear display name like “CI Build + Unit Tests.”
Step 6: Restrict merge types
Under Limit merge types, check only the strategies your team uses. For main, a common choice is merge commit only (preserves merge points) or squash only (clean one-commit-per-PR history).
Step 7: Add external status checks (optional)
Under Status checks, click + Add status policy. Select the status posted by your external tools (e.g., CodeAnt AI). Set to Required and configure the authorized account.
Step 8: Add auto-included reviewers (optional)
Under Automatically included reviewers, click + Add. Add groups for sensitive file paths — security team for /src/auth/*, DBA team for *.sql, etc.
Result: Your main branch now requires:
At least N independent approvals (no self-approval)
Linked work item
All comments resolved
Passing CI build
Passing external status checks (if configured)
Specific reviewers for sensitive file paths (if configured)
Only allowed merge strategies
Step-by-Step: Configure All Policies via CLI
The CLI is essential when managing policies across multiple repositories. Every policy type has a create, update, list, and delete command.
List All Existing Policies
# List all policies on a branch az repos policy list \\ --repository-id <REPO_ID> \\ --branch main \\ --output table # Get a specific policy's full configuration az repos policy show --id
Complete CLI Command Reference
# ────────────────────────────────────────────── # 1. Minimum reviewers # ────────────────────────────────────────────── az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch main \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true # ────────────────────────────────────────────── # 2. Linked work items # ────────────────────────────────────────────── az repos policy work-item-linking create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 3. Comment resolution # ────────────────────────────────────────────── az repos policy comment-required create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> # ────────────────────────────────────────────── # 4. Merge strategy # ────────────────────────────────────────────── az repos policy merge-strategy create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --allow-no-fast-forward true \\ --allow-squash true \\ --allow-rebase false \\ --allow-rebase-merge false # ────────────────────────────────────────────── # 5. Build validation # ────────────────────────────────────────────── az repos policy build create \\ --blocking true \\ --branch main \\ --build-definition-id <PIPELINE_ID> \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id <REPO_ID> \\ --valid-duration 720 # ────────────────────────────────────────────── # 6. Required reviewers (auto-included) # ────────────────────────────────────────────── az repos policy required-reviewer create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --required-reviewer-ids "<USER_OR_GROUP_ID>" \\ --path-filter "/src/security/*" \\ --message "Security team review required" # ────────────────────────────────────────────── # 7. Status check (external tools) # ────────────────────────────────────────────── az repos policy status create \\ --blocking true \\ --branch main \\ --enabled true \\ --repository-id <REPO_ID> \\ --status "codeantai/review" \\ --status-genre "codeantai"
Update and Delete Policies
# Update an existing policy (get policy ID from 'az repos policy list') az repos policy approver-count update \\ --id <POLICY_ID> \\ --minimum-approver-count 3 # Delete a policy az repos policy delete --id <POLICY_ID> --yes
Recommended Policies by Team Maturity
Tier 1: Startup / Small Team (2–5 Developers)
Priority: Don’t block velocity. Catch the worst mistakes.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 1 | Every change gets a second pair of eyes, but don’t bottleneck on multiple reviewers |
Linked work items | Optional | Encourage traceability without blocking quick fixes during early development |
Comment resolution | Required | Feedback shouldn’t be ignored, this is low-friction |
Build validation | Required (CI pipeline with build + unit tests) | Prevents broken builds from reaching main |
Merge type | Squash only | Keeps history clean while the team is moving fast |
Auto-reviewers | Not needed yet | Team is small enough that everyone sees every PR |
Status checks | Not configured yet | Add when you introduce security scanning tools |
Tier 2: Growth Team (6–20 Developers)
Priority: Enforce process without micromanaging. Teams are specializing.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 2 | Two independent reviews catch more issues and reduce bus factor |
Linked work items | Required | Non-negotiable — every change maps to tracked work |
Comment resolution | Required | Same as Tier 1 |
Build validation | Required (CI + integration tests + linting) | Expand the CI pipeline as the codebase grows |
Merge type | Squash for develop, merge commit for main | Different strategies per branch — clean feature history, visible release points |
Auto-reviewers | Required for sensitive paths | Backend team reviews API, DBA reviews SQL, security reviews auth |
Vote reset | Reset all votes on new pushes | Prevents stale approvals — critical when PRs are larger and reviews take longer |
Status checks | Required (SAST / AI code review) | Add external quality and security checks |
Tier 3: Enterprise (20+ Developers, Compliance Requirements)
Priority: Auditability, compliance, zero unreviewed code reaching production.
Policy | Setting | Why |
|---|---|---|
Minimum reviewers | 2–3 (branch-dependent) | 2 for develop, 3 for main and release branches |
Linked work items | Required | Audit requirement — every change traceable to a work item |
Comment resolution | Required | Same as Tier 1–2 |
Build validation | Required (CI + integration + security + compliance checks) | Multiple build validations: unit tests, integration tests, security scans, license compliance |
Merge type | Locked per branch | Squash for feature→develop, merge commit for develop→main, merge commit for hotfix→release |
Auto-reviewers | Required for all sensitive paths + optional for all others | Security, infrastructure, database, API, and compliance paths all have mandatory reviewers |
Vote reset | Reset all votes on new pushes | Non-negotiable for compliance — ensures every commit is reviewed |
Status checks | Required (SAST + SCA + AI code review + coverage) | Multiple external checks: CodeAnt AI for code review, security scanning, coverage thresholds |
Self-approval | Disabled everywhere | No developer can approve their own changes |
Bypass permissions | Restricted to a dedicated “Hotfix Approvers” group | Bypass is audited and available only to senior engineers for emergencies |
Policies at Scale: Cross-Repo Scripts
Enterprise teams with dozens or hundreds of repositories need to apply consistent policies without clicking through the UI for each repo. The Azure CLI combined with shell scripting handles this.
Apply Standard Policies to All Repositories
#!/bin/bash # apply-standard-policies.sh # Applies minimum viable policies to all repos in a project ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" PIPELINE_ID="<YOUR_CI_PIPELINE_ID>" # Get all repository IDs REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].id" \\ --output tsv) for REPO_ID in $REPO_IDS; do REPO_NAME=$(az repos show --id "$REPO_ID" --org "$ORG" --project "$PROJECT" --query "name" --output tsv) echo "Configuring policies for:$REPO_NAME ($REPO_ID)" # 1. Minimum 2 reviewers az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "$BRANCH" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id "$REPO_ID" \\ --reset-on-source-push true \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Reviewer policy created" || echo " ⚠ Reviewer policy already exists or failed" # 2. Require linked work items az repos policy work-item-linking create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Work item policy created" || echo " ⚠ Work item policy already exists or failed" # 3. Require comment resolution az repos policy comment-required create \\ --blocking true \\ --branch "$BRANCH" \\ --enabled true \\ --repository-id "$REPO_ID" \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Comment policy created" || echo " ⚠ Comment policy already exists or failed" # 4. Build validation (if pipeline exists for this repo) az repos policy build create \\ --blocking true \\ --branch "$BRANCH" \\ --build-definition-id "$PIPELINE_ID" \\ --display-name "CI Build + Tests" \\ --enabled true \\ --manual-queue-only false \\ --queue-on-source-update-only true \\ --repository-id "$REPO_ID" \\ --valid-duration 720 \\ --org "$ORG" \\ --project "$PROJECT" \\ 2>/dev/null && echo " ✓ Build policy created" || echo " ⚠ Build policy already exists or failed" echo "---" done echo "Done. Policies applied to all repositories."
Audit Policies Across All Repositories
#!/bin/bash # audit-policies.sh # Reports which repos have policies and which don't ORG="<https://dev.azure.com/your-org>" PROJECT="your-project" BRANCH="main" echo "=== Policy Audit:$PROJECT ===" echo "" REPO_IDS=$(az repos list \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "[].{id:id, name:name}" \\ --output json) echo "$REPO_IDS" | jq -r '.[] | "\\(.id) \\(.name)"' | while read REPO_ID REPO_NAME; do POLICY_COUNT=$(az repos policy list \\ --repository-id "$REPO_ID" \\ --branch "$BRANCH" \\ --org "$ORG" \\ --project "$PROJECT" \\ --query "length(@)" \\ --output tsv 2>/dev/null) if [ "$POLICY_COUNT" -gt 0 ] 2>/dev/null; then echo "✅$REPO_NAME —$POLICY_COUNT policies on$BRANCH" else echo "❌$REPO_NAME — NO policies on$BRANCH" fi done
Branch Pattern Policies
Instead of protecting individual branches, protect all branches matching a pattern. This is useful for release branches:
# Protect all release/* branches with 2 reviewers + build validation az repos policy approver-count create \\ --allow-downvotes false \\ --blocking true \\ --branch "release/*" \\ --creator-vote-counts false \\ --enabled true \\ --minimum-approver-count 2 \\ --repository-id <REPO_ID> \\ --reset-on-source-push true
Branch patterns use glob matching: release/* matches release/1.0, release/2.0, etc. This means newly created release branches automatically inherit the policy — no manual configuration needed.
Troubleshooting: Common Policy Conflicts and Issues
“All policies pass, but the Complete button is grayed out”
Cause 1: A required reviewer hasn’t voted yet. Even if minimum reviewer count is met (e.g., 2 of 3 required reviewers approved), an auto-included required reviewer who hasn’t voted blocks completion.
Fix: Check the “Reviewers” section on the PR. Look for reviewers marked as “Required” (has a lock icon) who haven’t voted. They must vote “Approve” before the PR can be completed.
Cause 2: Build validation expired. If build expiration is set and the build passed more than N hours ago, it’s considered stale and needs re-running.
Fix: Click “Queue build” to re-trigger the validation pipeline, or ask the PR author to push a new commit (which triggers automatic re-build if configured).
“Build validation keeps re-triggering on every push”
This is expected behavior when “Trigger” is set to “Automatic” and “Queue on source update only” is enabled. Every push to the source branch triggers a new build.
If builds are queueing too frequently: Set “Queue on source update only” to true (ignores target branch updates) and increase the build expiration window. This means the build only re-runs when the PR author pushes new commits, not when someone else merges into main.
“Required reviewer can’t be removed from the PR”
Auto-included reviewers (from “Automatically included reviewers” policy) cannot be removed from the PR manually. They’re added by the policy and will remain for the lifetime of the PR.
Workaround: If the required reviewer is unavailable (on leave, left the company), a Project Administrator can temporarily disable the auto-included reviewer policy, complete the PR, and re-enable it. Alternatively, use groups instead of individuals so that any group member can satisfy the requirement.
“PR shows merge conflicts but I can’t resolve them”
Branch policies block the merge button when there are conflicts, but Azure DevOps doesn’t provide an in-browser conflict resolution tool for all merge types.
Fix: Resolve conflicts locally: pull the target branch into your feature branch (git merge main), resolve conflicts, commit, and push. The PR will update and the conflict status will clear.
“Status check is stuck on ‘Pending’”
An external tool (e.g., AI code reviewer, SAST scanner) hasn’t posted its status yet, or it posted but with a different status key than what the policy expects.
Fix: Check if the tool ran successfully (check the tool’s dashboard or logs). Verify the status key matches exactly — the policy checks for an exact string match on the status name. If the tool uses codeantai/pr-review but the policy expects codeantai/review, it won’t match.
“Policy was configured but it’s not being enforced”
Possible causes:
Policy is on the wrong branch. Policies are per-branch. A policy on
maindoesn’t apply to PRs targetingdevelop.Policy is disabled. Check if the policy is toggled to “Enabled” in the settings.
User has Bypass permission. Users with “Bypass policies when completing pull requests” can merge without satisfying policies. Check the permissions guide for who has this permission.
The policy applies to a branch pattern that doesn’t match. Verify the branch pattern (e.g.,
release/*) matches the actual branch name.
“Different team members see different merge type options”
This is correct behavior. The merge type dropdown on the Complete dialog only shows strategies allowed by the “Limit merge types” policy. If only squash is allowed, developers only see the squash option.
If a developer sees all merge types, the “Limit merge types” policy may not be enabled on the target branch, or the developer has bypass permissions.
“I want different policies for different file paths, not just different branches”
Azure DevOps branch policies apply per-branch, not per-file-path — with one exception: “Automatically included reviewers” supports path filters. For everything else (build validation, minimum reviewers, merge types), the policy applies to the entire PR regardless of which files changed.
Workaround for build validation: Use path filters in the build validation policy. When you add a build policy, you can set path filters so the build only triggers when specific paths change. This lets you run different pipelines for /src/api/* vs /src/frontend/*.
Add AI Quality Gates with CodeAnt AI
Branch policies enforce process, minimum reviewers, linked work items, passing builds. But they don’t analyze the actual code. A PR can satisfy every policy and still contain security vulnerabilities, logic errors, or performance problems that human reviewers missed.
AI code review fills this gap by adding an intelligent code quality gate that evaluates every line of code, not just whether process was followed.

CodeAnt AI, used by Fortune 500 development teams on Azure DevOps and reviewed on Gartner Peer Insights, integrates as a status check in branch policies. When configured:
A PR is created or updated → Azure DevOps fires a webhook → CodeAnt AI analyzes the full diff
CodeAnt AI posts inline comments for quality and security findings, with one-click fixes developers can apply directly from the PR
CodeAnt AI posts a status check (
succeededorfailed) based on the quality gate thresholdThe branch policy blocks merge until the status check passes, just like build validation
This means critical security vulnerabilities and code quality issues block the merge, regardless of whether human reviewers caught them. Developers learn from every fix, and leadership dashboards give engineering leaders visibility into code health, review velocity, and security posture across the organization.
To add CodeAnt AI as a required status check:
Install and configure CodeAnt AI (cloud setup guide | self-hosted setup guide)
Create a test PR so CodeAnt AI posts its first status
Go to Project Settings → Repos → Policies → [branch] → Status checks
Add a new status check, select the CodeAnt AI status, and set it to Required
The quality gate now enforces automatically on every PR targeting this branch, no manual intervention needed. If you face any doubt or need help, book your 1:1 with our experts here.
FAQs
What is a branch policy in Azure DevOps?
How do I protect the main branch in Azure DevOps?
Can I have different policies for different branches?
What’s the difference between build validation and status checks?
Can policies be bypassed?
Table of Contents
Start Your 14-Day Free Trial
AI code reviews, security, and quality trusted by modern engineering teams. No credit card required!
Share blog:











