Introduction to Azure Repos
Azure Repos is the source control service within Azure DevOps, providing unlimited free private Git repositories (and legacy TFVC support) for teams of any size. Every Azure DevOps project gets a default repository automatically, and you can create as many additional repos as your architecture requires — monorepo, multi-repo, or a hybrid approach.
Think of Azure Repos as a bank vault for your code — branch policies are the security protocols that determine who can access what and under which conditions; required reviewers are the dual-key requirement ensuring no single person can push changes to production alone; and build validation is the alarm system that catches problems before they reach the vault's inner sanctum (your main branch). Together, these layers ensure your code is always in a trustworthy state.
For Git fundamentals (commits, branches, merges, rebases), see Part 9: Git & Version Control and Part 10: Git Internals. This module focuses on Azure Repos-specific features that layer on top of Git.
Git vs TFVC — Which One?
Use Git for all new projects. TFVC (Team Foundation Version Control) is a centralized version control system that Azure DevOps still supports for legacy compatibility, but Microsoft recommends Git for all new development. Here's a quick comparison:
| Aspect | Git (Recommended) | TFVC (Legacy) |
|---|---|---|
| Model | Distributed — full history on every clone | Centralized — server holds canonical history |
| Offline work | Full commit/branch/merge offline | Requires server connection for most operations |
| Branching | Lightweight (pointers) — branch per feature | Path-based — expensive, rarely used |
| Pull requests | Native with rich review experience | Code review via shelvesets (limited) |
| Industry adoption | ~95% of developers use Git | Declining, mostly legacy enterprise |
| Azure DevOps support | Full feature investment continues | Maintenance mode — no new features |
The only scenario where TFVC might still be appropriate is large binary assets (game art, CAD files) with exclusive checkout locks — and even then, Git LFS is usually a better choice.
Repository Setup & Configuration
Setting up a repository properly from the start saves hours of pain later. Let's walk through creating, cloning, and configuring a repo for team development.
Creating Repositories
You can create repositories through the web UI (Project Settings → Repos → Create), through the Azure CLI, or via the REST API. The CLI approach is especially useful for automation — creating repos as part of project bootstrapping scripts.
# Create a new Git repository in your Azure DevOps project
# --name: The repository name (use lowercase-kebab-case for consistency)
# --project: The Azure DevOps project that will contain this repo
az repos create \
--name "payment-service" \
--project "ecommerce-platform" \
--org "https://dev.azure.com/contoso"
# The output shows the clone URL, default branch, and repo ID
# Clone the newly created repository using HTTPS
git clone https://dev.azure.com/contoso/ecommerce-platform/_git/payment-service
# Navigate into the repo and set up initial structure
cd payment-service
# Create a comprehensive .gitignore from a template
# Azure DevOps provides templates for common languages/frameworks
curl -o .gitignore https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Create initial project structure
mkdir -p src tests docs .azuredevops
# Create a README with project context
cat > README.md << 'EOF'
# Payment Service
Handles payment processing, refunds, and transaction history.
## Getting Started
1. Clone: `git clone `
2. Install: `npm install`
3. Test: `npm test`
4. Run: `npm start`
## Branch Strategy
- `main` — production-ready code (protected)
- `develop` — integration branch for next release
- `feature/*` — individual feature work
- `hotfix/*` — emergency production fixes
EOF
# Initial commit and push
git add .
git commit -m "Initial project scaffold with README, .gitignore, and folder structure"
git push origin main
Cloning — HTTPS vs SSH vs Credential Manager
Azure Repos supports three authentication methods for Git operations:
- HTTPS + Git Credential Manager (recommended) — Works everywhere, handles token refresh automatically, supports MFA. Install GCM and it "just works" on Windows, macOS, and Linux.
- SSH keys — Add your public key to Azure DevOps → User Settings → SSH Public Keys. Best for CI/CD agents and headless environments where interactive auth isn't possible.
- Personal Access Tokens (PATs) — Time-limited tokens with scoped permissions. Use for scripts, service accounts, or legacy tools that can't use GCM.
Repository Settings
Configure these settings early to prevent problems at scale:
- Default branch name — Set to
main(or your team's convention) for all new repos - Disable forks — For internal projects, disable forking to keep code in controlled repos
- Maximum file size — Set limits (e.g., 100 MB) to prevent accidental binary commits
- Case enforcement — Enable on Windows teams to prevent path casing issues between Windows/Linux developers
- Reserved names — Block commits to files with reserved Windows names (CON, PRN, AUX)
Branch Policies
Branch policies are the single most important feature of Azure Repos for enterprise teams. They transform your main branch from "the branch everyone pushes to" into a protected, quality-gated trunk that only accepts code meeting your team's standards. Once a branch policy is set, no one — not even project administrators — can bypass it with a direct push.
Available Policy Types
| Policy | What It Enforces | When to Use |
|---|---|---|
| Minimum reviewers | N people must approve before merge | Always — at least 1 reviewer for main |
| Required reviewers | Specific people/groups must approve for certain paths | Security team for auth code, DBA for schema changes |
| Build validation | A CI pipeline must pass before merge | Always — ensures code compiles, tests pass |
| Linked work items | PR must reference at least one work item | Teams needing traceability (regulated industries) |
| Comment resolution | All review comments must be resolved | Prevents "ship it" culture — forces addressing feedback |
| Merge strategy | Restricts to specific merge types (squash only, etc.) | Teams wanting clean linear history |
| Status checks | External services must report success (SonarQube, etc.) | Third-party quality gates beyond CI |
| Automatically included reviewers | Auto-adds reviewers based on file paths changed | CODEOWNERS-style ownership enforcement |
Configuring Policies via CLI
While you can set policies through the web UI, the CLI approach is reproducible and version-controllable — critical for teams managing policies across dozens of repos.
# Set minimum 2 reviewers on the 'main' branch
# --blocking true = PR cannot complete without meeting this policy
# --allow-downvotes false = a single "Reject" blocks the PR
# --creator-vote-counts false = PR author's approval doesn't count
az repos policy approver-count create \
--project "ecommerce-platform" \
--repository-id "$(az repos show --repository payment-service --query id -o tsv)" \
--branch main \
--minimum-approver-count 2 \
--creator-vote-counts false \
--allow-downvotes false \
--reset-on-source-push true \
--blocking true
# Require linked work items — enforces traceability
az repos policy work-item-linking create \
--project "ecommerce-platform" \
--repository-id "$(az repos show --repository payment-service --query id -o tsv)" \
--branch main \
--blocking true
# Build validation — run CI pipeline on every PR
# The pipeline must pass before the PR can be completed
az repos policy build create \
--project "ecommerce-platform" \
--repository-id "$(az repos show --repository payment-service --query id -o tsv)" \
--branch main \
--build-definition-id 42 \
--queue-on-source-update-only false \
--valid-duration 720 \
--display-name "CI Build Validation" \
--blocking true
# Require all comments to be resolved before merge
az repos policy comment-required create \
--project "ecommerce-platform" \
--repository-id "$(az repos show --repository payment-service --query id -o tsv)" \
--branch main \
--blocking true
Path-Based Required Reviewers
One of Azure Repos' most powerful features is automatically including reviewers based on the files changed. This acts like a CODEOWNERS file but is enforced at the policy level — you can't merge without the designated owner's approval.
Example scenarios:
- Changes to
/infrastructure/→ require Platform Engineering team approval - Changes to
*.sqlor/migrations/→ require DBA team approval - Changes to
/src/auth/→ require Security team approval - Changes to
/docs/api/→ require Technical Writer review
PR Lifecycle with Policy Gates
flowchart TD
A[Developer pushes feature branch] --> B[Create Pull Request]
B --> C{Policies Evaluate}
C --> D[Build Validation triggers CI pipeline]
C --> E[Required reviewers auto-assigned]
C --> F[Work item link checked]
D --> G{CI passes?}
G -->|No| H[Developer fixes issues]
H --> D
G -->|Yes| I[Build policy ✓]
E --> J{Reviewers approve?}
J -->|Request changes| K[Developer addresses feedback]
K --> J
J -->|Approved| L[Reviewer policy ✓]
F -->|Linked| M[Work item policy ✓]
F -->|Missing| N[Developer links work item]
N --> M
I --> O{All policies pass?}
L --> O
M --> O
O -->|Yes| P[PR completes — merge to main]
O -->|No| Q[Waiting — cannot merge]
Pull Request Workflows
Pull requests (PRs) in Azure Repos are far more than a merge mechanism — they're a collaborative workspace where code review, automated validation, and documentation converge. A well-crafted PR tells a story: what problem was solved, how it was approached, and what trade-offs were made.
Creating Pull Requests
You can create PRs from multiple entry points:
- Web UI — Navigate to Repos → Pull Requests → New Pull Request. Select source and target branches.
- Azure CLI — Best for automation and scripting (shown below)
- VS Code — Azure Repos extension enables PR creation directly from the editor
- After push — Azure DevOps shows a "Create a pull request" banner after you push a new branch
# Create a pull request with auto-complete enabled
# Auto-complete means: merge automatically once all policies pass
# This is perfect for PRs where you're confident and just waiting on CI
az repos pr create \
--project "ecommerce-platform" \
--repository "payment-service" \
--source-branch "feature/add-stripe-webhook" \
--target-branch "main" \
--title "Add Stripe webhook handler for payment confirmations" \
--description "## Summary
Implements webhook endpoint for Stripe payment_intent.succeeded events.
## Changes
- Added POST /webhooks/stripe endpoint
- Signature verification using Stripe SDK
- Idempotency check against processed event IDs
- Updates order status in database on confirmation
## Testing
- Unit tests for signature verification
- Integration tests with Stripe test webhooks
- Load tested at 500 req/s (P99 < 200ms)
## Related
Closes AB#1234" \
--work-items 1234 \
--auto-complete true \
--merge-commit-message "feat: add Stripe webhook handler (#1234)" \
--delete-source-branch true \
--squash true
# Set auto-complete on an existing PR (by PR ID)
az repos pr update \
--id 567 \
--auto-complete true \
--squash true \
--delete-source-branch true
PR Description Templates
Azure Repos supports PR templates stored in your repository. Place a file at .azuredevops/pull_request_template.md and it will auto-populate the description field for every new PR in that repo:
## Summary
## Changes
## Testing
## Screenshots
## Checklist
- [ ] Unit tests added/updated
- [ ] Documentation updated
- [ ] No secrets in code
- [ ] Backward compatible (or migration plan documented)
Draft Pull Requests
Draft PRs signal "work in progress — not ready for formal review." They don't trigger required reviewer notifications or build validation policies (unless configured otherwise). Use drafts to:
- Get early design feedback before investing in full implementation
- Show progress to teammates without implying "please review now"
- Run CI to validate your approach without notifying the whole team
When ready, click "Publish" to convert the draft into a full PR — policies engage and reviewers are notified.
Merge Strategies Compared
Azure Repos offers four merge strategies. Your choice affects how history looks, how easy it is to revert changes, and how bisectable your history is.
| Strategy | History Shape | Advantages | Disadvantages | Best For |
|---|---|---|---|---|
| Merge commit | Non-linear (merge bubbles) | Preserves full branch history; easy to see what was developed together | Noisy history with many merge commits; harder to read | Teams wanting full audit trail |
| Squash merge | Linear (one commit per PR) | Clean history; each PR = one commit; easy to revert entire features | Loses individual commit history from the branch | Most teams (recommended default) |
| Rebase | Linear (preserves individual commits) | Clean linear history while keeping granular commits | Rewrites commit SHAs; can confuse if branch is shared | Solo developers or small PRs |
| Semi-linear merge | Linear with merge commit | Rebases first (linear), then adds merge commit (grouping) | More complex; unfamiliar to some teams | Teams wanting both linearity and PR grouping |
Code Review Best Practices
Code review in Azure Repos is more than a gatekeeping exercise — it's a knowledge-sharing mechanism, a teaching opportunity, and a quality assurance process all in one. The best teams treat reviews as collaborative improvement, not adversarial inspection.
Reviewer Assignment
Azure Repos provides multiple mechanisms for assigning reviewers:
- Manual assignment — PR author adds specific reviewers who have relevant expertise
- Required reviewers (policy) — Automatically assigned based on file paths changed (like CODEOWNERS)
- Auto-complete groups — Groups where any member can fulfill the review requirement
- Suggested reviewers — Azure DevOps suggests people who recently modified the same files
The Voting System
Azure Repos uses a nuanced 5-level voting system that communicates intent clearly:
| Vote | Icon | Meaning | Effect on Merge |
|---|---|---|---|
| Approve | ✓ (green) | "This looks good — ship it" | Counts toward minimum reviewer requirement |
| Approve with suggestions | ✓ (green, lighter) | "Good to go, but consider these improvements" | Counts as approval; suggestions are optional |
| No vote | — (gray) | "I haven't reviewed this yet" | Default state; doesn't count for or against |
| Wait for author | ⏳ (orange) | "I have concerns — let's discuss before merging" | Doesn't block (unless policy requires it); signals caution |
| Reject | ✗ (red) | "This has significant issues that must be fixed" | Blocks merge (if "allow downvotes" is disabled in policy) |
Review Iterations
When an author pushes updates after receiving feedback, Azure Repos tracks these as iterations. Reviewers can see exactly what changed between review rounds — viewing only the diff from "iteration 2 to iteration 3" rather than re-reading the entire PR. This dramatically speeds up follow-up reviews.
Code Review Anti-Patterns to Avoid
- "LGTM" reviews — Rubber-stamping without reading. If you can't spend 10 minutes reviewing, say so and let someone else do it.
- Bike-shedding — Spending 30 minutes debating variable names while ignoring architectural concerns. Focus on what matters most first.
- Mega-PRs — 2,000-line PRs are impossible to review well. If your PR exceeds ~400 lines of meaningful changes, break it up.
- Review as gatekeeping — Using reviews to block work you disagree with politically. Reviews evaluate code quality, not feature desirability (that's the product owner's job).
- Delayed reviews — Letting PRs sit for days. Set a team norm: reviews within 4 hours during business hours.
Branching Strategies in Azure Repos
Your branching strategy defines how code flows from developer laptops to production. Azure Repos supports any Git branching model, but some work better than others at scale. For comprehensive coverage of branching strategies, see Part 11: Advanced Git Workflows. Here we focus on what works best with Azure DevOps specifically.
Feature Branch Workflow (Recommended for Most Teams)
The simplest effective strategy: developers create short-lived branches from main, implement a feature, open a PR, and merge back to main after review and CI passes.
- Branch naming:
feature/AB#1234-add-payment-webhook(include work item number for auto-linking) - Branch lifetime: 1–3 days maximum (longer = more merge conflicts)
- One feature per branch (single responsibility)
- Delete branch after merge (branch policy can enforce this)
Release Branch Workflow
For teams that need to maintain multiple versions simultaneously (e.g., SaaS with enterprise customers on different versions):
main— always deployable (latest development)release/2.1,release/2.2— stabilization branches for specific releaseshotfix/CVE-2026-XXXX— emergency fixes cherry-picked to affected release branches
Trunk-Based Development with Branch Policies
The most streamlined approach for high-performing teams: everyone works from main, using very short-lived branches (hours, not days) that merge via PR. Azure Repos branch policies enforce quality without slowing velocity.
gitGraph
commit id: "Initial"
branch "feature/AB-101-auth"
commit id: "Add OAuth"
commit id: "Add tests"
checkout main
merge "feature/AB-101-auth" id: "PR-1 merged"
branch "feature/AB-102-cart"
commit id: "Cart logic"
checkout main
branch "hotfix/fix-login"
commit id: "Fix null check"
checkout main
merge "hotfix/fix-login" id: "Hotfix merged"
checkout "feature/AB-102-cart"
commit id: "Cart tests"
checkout main
merge "feature/AB-102-cart" id: "PR-2 merged"
commit id: "Deploy v1.3"
Branch Naming Conventions
Establish conventions early and enforce them via naming restrictions in branch policies:
| Prefix | Purpose | Example |
|---|---|---|
feature/ | New functionality | feature/AB#1234-stripe-webhooks |
bugfix/ | Non-urgent bug fixes | bugfix/AB#1235-cart-total-rounding |
hotfix/ | Urgent production fixes | hotfix/fix-payment-timeout |
release/ | Release stabilization | release/2.4.0 |
users/ | Personal experiments | users/wasil/spike-graphql |
Forks vs Branches — Inner Source Model
For organizations with hundreds of developers where not everyone should have write access to every repo, Azure Repos supports forks — personal copies of a repository within the same Azure DevOps project. Developers fork, work in their fork, and open PRs back to the upstream repo. This is the "inner source" model (applying open-source practices within a company).
Advanced Repository Features
Cross-Repository Policies
Managing policies across 50+ repositories individually is tedious. Azure DevOps supports cross-repo policies at the project level — set a policy once and it applies to all repos (or repos matching a pattern). Navigate to Project Settings → Repos → Policies to configure these.
Common cross-repo policies:
- Minimum 1 reviewer on all
mainbranches across the project - Block pushing secrets (detected via credential scanner build task)
- Require work item linking for all PRs
- Maximum file size (prevent accidental binary commits)
Repository Permissions
Azure Repos provides granular permission control at the repository, branch, and even path level:
| Permission | What It Controls | Typical Assignment |
|---|---|---|
| Read | Clone and fetch | All project members |
| Contribute | Push to non-protected branches | Developers |
| Create branch | Create new branches | Developers |
| Force push | Rewrite history on branches | Deny for everyone (emergency only) |
| Manage permissions | Change repo security settings | Repo administrators only |
| Bypass policies | Push directly to protected branches | Service accounts (build agents) |
Git Large File Storage (LFS)
Git wasn't designed for large binary files (images, videos, ML models, game assets). Every clone downloads the full history, so a 500 MB model file with 10 versions means 5 GB of downloads. Git LFS solves this by storing large files on a separate server and replacing them with lightweight pointers in your Git history.
# Install Git LFS (one-time setup)
git lfs install
# Track large file patterns — stored in .gitattributes
git lfs track "*.psd" # Photoshop files
git lfs track "*.zip" # Archives
git lfs track "*.onnx" # ML models
git lfs track "datasets/**" # Entire folder
# Verify tracking rules
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text
# *.zip filter=lfs diff=lfs merge=lfs -text
# Commit the .gitattributes file first
git add .gitattributes
git commit -m "Configure Git LFS for binary assets"
# Now add and commit large files normally — LFS handles the rest
git add models/classifier.onnx
git commit -m "Add trained classifier model (450 MB via LFS)"
git push origin main
Tags and Releases
Use Git tags to mark specific commits as release points. Azure Repos displays tags in the web UI and supports both lightweight and annotated tags:
# Create an annotated tag for a release
# Annotated tags store tagger info, date, and a message
git tag -a v2.4.0 -m "Release 2.4.0: Stripe webhook support, cart improvements"
# Push tags to Azure Repos
git push origin v2.4.0
# List tags with messages
git tag -n1
# Create a tag from a specific commit (for retroactive tagging)
git tag -a v2.3.1 abc1234 -m "Hotfix: payment timeout fix"
Code Search & Navigation
Azure DevOps includes a powerful semantic code search that indexes all repositories in your project. Unlike a simple grep, it understands code structure — searching for a class name finds its definition, usages, and derived types.
Search Syntax & Filters
The search bar (top-right of any Azure DevOps page) supports powerful query syntax:
| Filter | Syntax | Example |
|---|---|---|
| Repository | repo: | repo:payment-service CreatePayment |
| File path | path: | path:src/controllers AuthController |
| File extension | ext: | ext:cs IPaymentGateway |
| Language | lang: | lang:TypeScript webhook handler |
| Class definition | class: | class:PaymentService |
| Method/function | method: / func: | func:processWebhook |
| Comment | comment: | comment:TODO security review |
Go-to-Definition & Find-All-References
For supported languages (C#, Java, TypeScript, Python), Azure Repos provides IDE-like navigation directly in the web UI:
- Go to definition — Ctrl+click on a symbol to jump to where it's declared
- Find all references — Right-click a symbol to see everywhere it's used across the repo
- Peek definition — View the definition inline without leaving the current file
This makes code review significantly more efficient — reviewers can understand the full context of changes without cloning the repo locally.
File History & Comparison
Every file in Azure Repos has a complete history view showing all commits that modified it, who made each change, and when. The compare feature lets you diff any two commits, branches, or tags side-by-side — useful for understanding what changed between releases.
Case Study: Migrating from TFVC to Git
Context: A healthcare software company with 120 developers had used TFVC for 12 years, accumulating 180,000 changesets across a single monolithic repository. Their CI/CD modernization initiative required Git for compatibility with modern tooling (container builds, infrastructure-as-code, microservices).
Migration Approach
- Tool choice: Used
git-tfs(open-source bridge) for history-preserving migration, validated against Azure DevOps's built-in import tool (which only imports tip, not history) - Phased migration: Migrated one team's codebase per sprint (not big-bang), running TFVC and Git in parallel during transition
- History decisions: Imported full history for actively-maintained code (5 services), but only imported the latest version for archived/legacy code (saved 3 weeks of migration time)
- Branch mapping: TFVC's $/Project/Main → Git
main; $/Project/Dev → Gitdevelop; $/Project/Releases/R2.x → Gitrelease/2.x
Challenges & Solutions
- Large binaries: TFVC repo contained 8 GB of NuGet packages checked in. Solution: removed from Git history, migrated to Azure Artifacts feeds
- Exclusive locks: TFVC supported file locking (critical for their Word-based specs). Solution: moved specs to SharePoint/Wiki; only code lives in Git
- Training gap: 40% of developers had never used Git. Solution: mandatory 2-day workshop with hands-on exercises before each team's migration date
- Build system: TFVC builds used workspace mappings. Solution: rewrote 35 build definitions as YAML pipelines (took 4 sprints)
Results (6 Months Post-Migration)
- PR cycle time: from 3 days (TFVC code reviews via shelvesets) to 6 hours (Git PRs with branch policies)
- Deploy frequency: from monthly to twice-weekly (enabled by branch policies + CI/CD)
- Developer satisfaction: NPS improved from +12 to +67 for source control tooling
- Merge conflicts: initially increased (learning curve) then dropped 60% below TFVC baseline after 3 months
Exercises
Create a fully-configured repository with enterprise-grade protections:
- Create a new repository called
inventory-servicein your Azure DevOps project - Clone it locally, add a
.gitignore(Node.js template),README.md, and a.azuredevops/pull_request_template.md - Push the initial commit to
main - Configure branch policies on
main:- Minimum 2 reviewers (author's vote doesn't count, reset on new pushes)
- Build validation (use any existing CI pipeline, or create a simple one that runs
echo "Build OK") - Require linked work items
- Require comment resolution
- Try pushing directly to
main— verify it's blocked
Verify: Create a feature branch, push a change, and open a PR. Confirm that all policy requirements appear as checkboxes on the PR.
Simulate a complete PR workflow from creation to merge:
- Create a work item (User Story or Task) in Azure Boards: "Add health check endpoint"
- Create a branch:
feature/AB#[ID]-health-check - Implement a simple change (add a file, modify the README, etc.) and push
- Create a PR via the Azure CLI with
--auto-complete true --squash true - Link the work item to the PR
- Have a teammate (or a second account) review: add an inline comment, then vote "Wait for author"
- Address the comment, push an update, and resolve the thread
- Have the reviewer change their vote to "Approve"
- Watch auto-complete trigger the squash merge once all policies pass
Verify: After merge, check that (1) the work item state updated automatically, (2) the feature branch was deleted, (3) main has exactly one new squash commit.
Set up path-based policies that auto-assign specialized reviewers:
- Create the following folder structure in your repo:
/src/app/,/src/auth/,/infrastructure/,/database/migrations/ - Configure automatically included reviewers:
- Changes to
/infrastructure/*→ require "Platform Team" group approval - Changes to
/src/auth/*→ require "Security Team" group (or a specific user) - Changes to
/database/migrations/*→ require "DBA Team" approval
- Changes to
- Create a PR that modifies a file in
/infrastructure/— verify the Platform Team is auto-added - Create a PR that modifies files in both
/src/app/and/src/auth/— verify Security Team is added but only because of the auth change
Verify: The PR shows "Required" next to the auto-assigned reviewers, and the PR cannot complete without their approval.
Implement organization-wide policies that apply across all repositories:
- Navigate to Project Settings → Repos → Policies (cross-repo section)
- Enable "Block pushes that contain credentials or secrets" for all repositories
- Set a maximum file size of 50 MB across all repos
- Enable "Require at least 1 reviewer" on all
mainandrelease/*branches - Test by trying to commit a file containing a fake AWS key pattern:
AKIAIOSFODNN7EXAMPLE - Verify the push is blocked with a clear error message
Verify: Create a new repository in the project — confirm that the cross-repo policies are automatically applied without any additional configuration.
Next in the Bootcamp
In Module 4: Azure Pipelines — YAML CI/CD, we'll build production-grade CI/CD pipelines from scratch — YAML syntax, triggers, stages, jobs, templates, environments, approvals, and deployment strategies.