Applied Module 12 · AI-Accelerated Government Development

Azure DevOps Git Workflow

What you'll learn

~25 min
  • Configure PAT authentication for Azure DevOps git operations
  • Implement the feature-branch-off-dev branching strategy used on the DS platform
  • Create pull requests programmatically via the Azure DevOps REST API

Git operations on the DS platform

The DS platform uses Azure DevOps for source control, not GitHub. The branching model, the PR workflow, and the API surface are all different. AI tools default to GitHub patterns — gh pr create, .github/workflows/, origin/main. None of that applies here.

This lesson covers the three things you need: PAT authentication so git commands work through the proxy, the branching strategy your team actually follows, and creating PRs through the ADO REST API so you can automate the parts that are currently manual.

This is Azure DevOps Server / Services

Azure DevOps comes in two forms: Azure DevOps Services (cloud-hosted) and Azure DevOps Server (on-prem). The REST API is identical for both. The only difference is the base URL: dev.azure.com/{org} for Services, {server}/{collection} for Server. Everything in this lesson works with either.


PAT authentication

Personal Access Tokens are the standard authentication method for Azure DevOps git operations over HTTPS. SSH keys work too, but many state networks block outbound SSH (port 22), making PATs the reliable option.

Create your PAT

  1. Open Azure DevOps in your browser.
  2. Click your profile icon (top right) > Personal access tokens.
  3. Click New Token.
  4. Set scope: Code (Read & Write), Work Items (Read & Write), Build (Read & Execute).
  5. Set expiration to 90 days (state policy maximum varies — check yours).
  6. Copy the token immediately. You will not see it again.

Configure git

Terminal window
# Store credentials so you don't re-enter the PAT on every push
git config --global credential.helper store
# First push/pull will prompt for username and password.
# Username: your email or ADO username
# Password: paste the PAT (not your AD password)

The NTLM fix

State networks often run NTLM proxy authentication. Git’s built-in HTTP handler tries NTLM negotiation, fails silently, and then retries in a loop until it times out. The fix is a single environment variable.

Terminal window
export GIT_HTTP_NO_NTLM=1

Add this to your ~/.bashrc or ~/.zshrc. Without it, you will see 60-second hangs on git push and git fetch that eventually fail with fatal: unable to access.

GIT_HTTP_NO_NTLM=1 is not optional

If your git operations work fine today, they will still break intermittently when NTLM negotiation races with PAT auth. Set this variable even if things appear to work. It eliminates an entire class of timeout that is extremely difficult to debug because it succeeds sometimes.


Branching strategy

The DS platform uses a three-tier branching model:

main (production — deployed, protected)
└── dev (integration — PR target, protected)
├── feature/add-training-module
├── feature/rbac-superadmin-role
└── bugfix/swr-cache-stale

Rules:

  • main is production. Direct commits are blocked by branch policy. Changes reach main only through PRs from dev after staging verification.
  • dev is the integration branch. All feature work targets dev. Direct commits are blocked — PRs only.
  • Feature branches are created from dev, not main. Naming convention: feature/{ticket-id}-{short-description} or bugfix/{ticket-id}-{short-description}.
  • Work item linking is required. Every commit message and PR must reference an ADO work item ID.

Creating a feature branch

Terminal window
# Always start from the latest dev
git checkout dev
git pull origin dev
# Create the feature branch
git checkout -b feature/4521-training-telemetry
# Work, commit, push
git add .
git commit -m "Add App Insights custom events for training module #4521"
git push -u origin feature/4521-training-telemetry
💡Work item linking in commits

Including #4521 in the commit message automatically links the commit to work item 4521 in Azure DevOps. This is not decoration — the PM and project leads track progress through linked work items. Unlinked commits create gaps in the audit trail.


Creating PRs via the ADO REST API

The Azure DevOps web UI works, but creating PRs through the REST API lets you automate the process from a script or an AI tool. Here is the prompt to generate a PR creation utility.

The prompt

Create a bash script called create-pr.sh that creates a pull request in
Azure DevOps using the REST API.
Requirements:
1. Accept these arguments:
--title "PR title"
--description "PR description"
--source (default: current branch)
--target (default: dev)
--work-items (comma-separated work item IDs to link)
--reviewers (comma-separated email addresses)
2. Use the Azure DevOps REST API:
POST https://dev.azure.com/{org}/{project}/_apis/git/repositories/{repo}/pullrequests?api-version=7.1
3. Authentication: PAT from environment variable AZURE_DEVOPS_PAT
(base64-encode as :PAT for the Authorization header)
4. The request body should include:
- sourceRefName: refs/heads/{source-branch}
- targetRefName: refs/heads/{target-branch}
- title, description
- Work item links as resourceRef objects
- Auto-complete enabled (completionOptions.mergeStrategy: squash)
5. After creating the PR, output:
- PR number
- PR URL (clickable link to the ADO web UI)
- Linked work items
6. Error handling:
- Verify the source branch exists on the remote before creating
- Check that the PAT is set
- Parse API error responses and show the actual message

What the script produces

The AI generates a bash script that wraps the ADO REST API. The key API call looks like this:

Terminal window
# Base64-encode the PAT for the Authorization header
AUTH=$(echo -n ":${AZURE_DEVOPS_PAT}" | base64)
curl -s -X POST \
"https://dev.azure.com/${ORG}/${PROJECT}/_apis/git/repositories/${REPO}/pullrequests?api-version=7.1" \
-H "Authorization: Basic ${AUTH}" \
-H "Content-Type: application/json" \
-d '{
"sourceRefName": "refs/heads/feature/4521-training-telemetry",
"targetRefName": "refs/heads/dev",
"title": "Add App Insights telemetry for training module",
"description": "Integrates custom events for module page loads and density view switches. Closes #4521.",
"completionOptions": {
"mergeStrategy": "squash",
"deleteSourceBranch": true
},
"workItemRefs": [
{ "id": "4521" }
]
}'
api-version=7.1

The Azure DevOps REST API is versioned. Using 7.1 targets the current stable API. Older examples online use 6.0 or 5.1 — those still work but lack features like auto-complete merge strategies. Always specify the version explicitly.


See it in action

Claude Code — AI-assisted
/home/user $ Create a feature branch for work item 4521 — adding telemetry to the training module. Push it and create a PR targeting dev.
Creating the branch and PR: ``` $ git checkout dev Switched to branch 'dev' $ git pull origin dev Already up to date. $ git checkout -b feature/4521-training-telemetry Switched to a new branch 'feature/4521-training-telemetry' ``` I see you already have uncommitted changes in src/lib/telemetry/. Let me commit those: ``` $ git add src/lib/telemetry/ $ git commit -m "Add App Insights custom events for training module #4521" [feature/4521-training-telemetry a3b2c1d] Add App Insights custom events for training module #4521 4 files changed, 142 insertions(+) $ git push -u origin feature/4521-training-telemetry Enumerating objects: 12, done. remote: Analyzing objects... (12/12) remote: Storing packfiles... To https://dev.azure.com/StateOrg/DSPlatform/_git/ds-platform * [new branch] feature/4521-training-telemetry -> feature/4521-training-telemetry ``` Now creating the PR via the ADO REST API: ``` PR #287 created successfully URL: https://dev.azure.com/StateOrg/DSPlatform/_git/ds-platform/pullrequest/287 Source: feature/4521-training-telemetry → dev Linked work items: #4521 Merge strategy: squash ```
/home/user $ The git push hung for 45 seconds and then failed. I'm on the state VPN.
That's the NTLM negotiation timeout. The state VPN proxy is trying to negotiate NTLM authentication before your PAT can authenticate. Fix: ``` $ export GIT_HTTP_NO_NTLM=1 $ git push -u origin feature/4521-training-telemetry ``` Add it to your shell profile so it persists: ``` $ echo 'export GIT_HTTP_NO_NTLM=1' >> ~/.bashrc $ source ~/.bashrc ``` This tells git to skip NTLM negotiation entirely and use Basic auth (your PAT) directly. Without this, git tries NTLM first, waits for a response from the proxy, times out after 60 seconds, and only then falls back to Basic auth — or gives up.
/home/user $ Can I add reviewers to the PR after it's already created?
Yes. Use the update PR endpoint with a PATCH request: ```bash curl -s -X PATCH \ "https://dev.azure.com/${ORG}/${PROJECT}/_apis/git/repositories/${REPO}/pullrequests/287?api-version=7.1" \ -H "Authorization: Basic ${AUTH}" \ -H "Content-Type: application/json" \ -d '{ "reviewers": [ { "id": "reviewer-guid-here", "isRequired": true } ] }' ``` To get the reviewer's GUID from their email, query the identities API first: ```bash curl -s "https://dev.azure.com/${ORG}/_apis/identities?searchFilter=General&filterValue=jane.doe@state.gov&api-version=7.1" \ -H "Authorization: Basic ${AUTH}" ``` Or use the ADO web UI — clicking "Add reviewer" is faster for one-off additions. The API is useful when you want to auto-assign reviewers based on which files changed.
/home/user $

Work item linking patterns

Azure DevOps tracks work items (user stories, tasks, bugs) and links them to commits and PRs. This linkage is not optional on state projects — auditors review it.

In commit messages:

Terminal window
# Links to work item 4521
git commit -m "Add telemetry events for density view switches #4521"
# Links to multiple work items
git commit -m "Refactor auth helpers for session timeout #4510 #4512"

In PR descriptions:

Closes #4521
Changes:
- Added App Insights custom events for module page loads
- Added density view switch tracking
- Integrated Key Vault for connection string retrieval

The Closes #4521 syntax automatically transitions the work item to “Done” when the PR completes. Other keywords: Fixes, Resolves.

💡PR templates

Azure DevOps supports PR description templates stored in .azuredevops/pull_request_template.md. Ask the AI to generate one that includes sections for changes, work items, testing, and security considerations. Every PR gets the same structure, which makes reviews faster.


Branch policies

The DS platform enforces these branch policies on dev and main:

Policydevmain
Minimum reviewers12
Work item linkingRequiredRequired
Build validationCI must passCI + staging deploy must pass
Comment resolutionAll resolvedAll resolved
Merge strategySquashSquash

These policies are configured in the Azure DevOps project settings, not in code. But knowing they exist changes how you structure your work: one feature per branch, one work item per PR, all comments resolved before merge.

💬Squash merge is deliberate

Squash merging collapses all commits in a feature branch into a single commit on dev. This keeps the dev history clean and linear. Your individual commits during development (the messy ones, the “fix typo” ones, the “actually fix it this time” ones) disappear from the integration branch. This is a feature, not a limitation. The PR preserves the full commit history if anyone needs it.


KNOWLEDGE CHECK

A developer creates a feature branch from main, completes their work, and opens a PR targeting dev. What is wrong with this workflow?


Key takeaways

  • PAT authentication with credential.helper store is the reliable method for Azure DevOps git over HTTPS. SSH is blocked on many state networks.
  • GIT_HTTP_NO_NTLM=1 eliminates the 60-second timeout caused by NTLM proxy negotiation. Set it in your shell profile and forget about it.
  • Feature branches come from dev, never main. Main is production. Dev is integration. Branching from the wrong base creates merge conflicts and audit gaps.
  • Work item linking is mandatory. Use #NNNN in commit messages and Closes #NNNN in PR descriptions. Auditors review this trail.
  • The ADO REST API (api-version=7.1) lets you create, update, and query PRs programmatically. The authentication pattern is Basic auth with a base64-encoded :PAT string.

What’s next

In the next lesson, you will package the Next.js platform for deployment to Azure Government: standalone output mode, the static file copy step that trips up every team at least once, and zip deployment to Azure App Service.