Enterprise Change Management for Technology

Manage technology change in enterprises. Covers ITIL change management, CAB processes, change risk assessment, progressive rollouts, and balancing governance with engineering velocity.

Enterprise Change Management for Technology is the disciplined practice of planning, executing, and governing software and infrastructure changes across a large, distributed organisation. It matters when a new feature, system integration, or platform upgrade must roll out reliably across multiple teams, systems, and environments—especially when failure in production has cascading consequences. It is the backbone of technology delivery at scale, ensuring that changes from development to production are predictable, traceable, and reversible.

Planning Change Introductions

Change introductions begin with a clear, shared understanding of what is changing, why, and how it affects existing systems. A change introduction must be documented in a change request (CR), which includes the following core elements:

{
  "dependencies": [
    {
      "system": "hr/payroll",
      "version": "2024.03",
      "required-by": "2024-07-15",
      "status": "pending",
      "notes": "Payroll must support new tax code format before payroll run."
    },
    {
      "system": "logistics/warehouse",
      "version": "v4.1",
      "required-by": "2024-07-10",
      "status": "verified",
      "notes": "Warehouse must accept batch IDs from ERP before shipping."
    }
  ]
}

A change introduction is not complete until it includes a risk assessment and rollback plan. Risks are recorded as a list of tuples: (risk, probability, impact, mitigation). For example:

- Risk: ERP integration fails during peak load
  - Probability: high
  - Impact: high
  - Mitigation: pre-load test data, enable throttling on API gateway, monitor queue depth
- Risk: Database migration fails on first run
  - Probability: medium
  - Impact: medium
  - Mitigation: validate schema migration script, use point-in-time backup, enable incremental migration

Executing Change Deployments

Deployment is a staged process governed by a deployment pipeline defined in a pipeline.yaml file, using standard CI/CD tooling. The pipeline defines stages, each with specific commands, configuration, and validation steps.

pipeline:
  stages:
    - name: build
      steps:
        - run: npm run build
        - run: docker build -t app:${VERSION} .
        - run: docker push app:${VERSION}
    - name: test
      steps:
        - run: npm run test:unit
        - run: npm run test:integration
        - run: psql -d testdb -c "SELECT validate_data_integrity();"
        - run: curl -s -H "Authorization: Bearer $API_KEY" http://test-api:8080/health
    - name: deploy-staging
      steps:
        - run: kubectl apply -f k8s/staging/
        - run: kubectl rollout status deployment/app-staging
        - run: kubectl patch deployment/app-staging -p '{"spec":{"replicas":5}}'
    - name: promote-to-prod
      steps:
        - run: kubectl set image deployment/app-prod app=app:v1.2.3
        - run: kubectl rollout status deployment/app-prod --timeout=300s
        - run: kubectl rollout undo deployment/app-prod --to-revision=2

Each stage has a runbook that defines pre- and post-checks. A pre-check is a command or set of commands that must pass before a stage begins. For instance, the deploy-staging stage requires the following pre-checks:


psql -d staging-erp -c "SELECT 1 FROM pg_catalog.pg_database WHERE datname = 'staging-erp';"

# Pre-check: Verify API gateway is up and routing
curl -f -H "Host: staging.erp.company.com" http://api-gateway:8080/health

# Pre-check: Verify configuration is deployed
diff <(kubectl get configmap app-config -o jsonpath='{.data}') <(cat config/staging-config.json)

Post-checks run after a stage completes and are validated via a verify.yaml file in the deployment directory:

verify:
  - name: "API health check"
    command: "curl -s http://app-staging:8080/health | jq -e '.status == \"healthy\"'"
    timeout: "60s"
  - name: "Queue depth monitoring"
    command: "kafka-topics.sh --bootstrap-server kafka:9092 --describe --topic orders"
    expected: "High watermark: 10000"
    retries: 3

Managing Change Dependencies

Dependencies between changes are managed through a change dependency graph, where each change is a node and each dependency is a directed edge. The graph is maintained in a dependency-graph.json file, which is updated by a change manager after every deployment.

{
  "edges": [
    {
      "from": "CHG-2024-0127",
      "to": "CHG-2024-0151",
      "type": "upstream",
      "required-by": "2024-08-01",
      "status": "in-progress"
    },
    {
      "from": "CHG-2024-0151",
      "to": "CHG-2024-0173",
      "type": "downstream",
      "required-by": "2024-08-10",
      "status": "blocked"
    }
  ]
}

Changes are considered ready only when all upstream dependencies are completed and verified. A change is blocked if any required dependency is not yet verified. A change manager runs a daily check-dependencies.sh script to surface issues:

#!/bin/bash
# check-dependencies.sh
set -euo pipefail

DEPGRAPH=$(cat dependency-graph.json)
for edge in $(echo "$DEPGRAPH" | jq -r '.edges[] | @json'); do
  FROM=$(echo "$edge" | jq -r '.from')
  TO=$(echo "$edge" | jq -r '.to')
  REQUIRED_BY=$(echo "$edge" | jq -r '.required-by')

  STATUS=$(curl -s http://change-manager/api/changes/$FROM | jq -r '.status')
  if [[ "$STATUS" != "completed" ]]; then
    echo "Change $FROM not completed; blocking $TO"
    continue
  fi

  VERIFIED=$(curl -s http://change-manager/api/changes/$FROM | jq -r '.verification.status')
  if [[ "$VERIFIED" != "passed" ]]; then
    echo "Change $FROM not verified; blocking $TO"
    continue
  fi

  NOW=$(date -I)
  if [[ "$NOW" > "$REQUIRED_BY" ]]; then
    echo "Change $FROM overdue for $TO; deadline was $REQUIRED_BY"
  fi
done

Tracking Change Impact

After deployment, the change’s impact is tracked through impact indicators defined in a impact-indicators.yaml file. Each indicator is a query or metric that measures the success or health of a change.

impact-indicators:
  - name: "Order processing latency"
    query: |
      SELECT avg(duration_ms) 
      FROM orders 
      WHERE created_at >= '2024-07-15' 
        AND created_at < '2024-07-16'
    threshold: "400ms"
    target: "300ms"
    alert: true
    severity: "critical"
  - name: "User login success rate"
    query: |
      SELECT 
        count(*) FILTER (WHERE success = true) * 100.0 / count(*) AS success_rate
      FROM user_sessions 
      WHERE session_start >= '2024-07-15'
    threshold: "95%"
    target: "98%"
    alert: true
    severity: "warning"

A change is considered successful if all critical indicators meet their thresholds within 48 hours of deployment. A change impact report is generated automatically using a generate-impact-report.sh script:

#!/bin/bash
# generate-impact-report.sh
set -euo pipefail

CHG_ID=$1
OUTPUT_DIR="reports/$CHG_ID"

mkdir -p "$OUTPUT_DIR"

for indicator in $(ls impact-indicators.yaml | jq -r '.[] | @json'); do
  NAME=$(echo "$indicator" | jq -r '.name')
  QUERY=$(echo "$indicator" | jq -r '.query')
  THRESHOLD=$(echo "$indicator" | jq -r '.threshold')

  RESULT=$(psql -d production -c "$QUERY" -t -A)

  if [[ "$RESULT" > "$THRESHOLD" ]]; then
    STATUS="success"
  else
    STATUS="failed"
  fi

  cat > "$OUTPUT_DIR/$NAME.md" <<EOF
## $NAME

- Threshold: $THRESHOLD
- Actual: $RESULT
- Status: $STATUS
- Query: $QUERY
EOF
done

# Generate summary
cat > "$OUTPUT_DIR/summary.md" <<EOF
# Change Impact Report: $CHG_ID

Generated: $(date)

## Summary

- All indicators: $(ls "$OUTPUT_DIR" | wc -l) indicators evaluated
- Success: $(find "$OUTPUT_DIR" -name "*.md" -exec grep -l "Status: success" {} \; | wc -l) of $(ls "$OUTPUT_DIR" | wc -l)
EOF

Handling Change Failures

When a change fails, the change failure state machine is triggered. A change is in one of the following states: planned, in-progress, verified, completed, failed, rollback-in-progress, rolled-back.

A change fails when any critical impact indicator fails, or when a deployment stage fails to pass its post-checks. The failure is logged in a change-events.log file, with each event in a JSON-lined format:

{"timestamp": "2024-07-15T10:23:45Z", "change": "CHG-2024-0127", "event": "deployment-stage-failed", "stage": "deploy-staging", "details": {"error": "Timeout waiting for rollout", "exit-code": 1, "log": "kubectl rollout status deployment/app-staging timed out"}}
{"timestamp": "2024-07-15T10:25:10Z", "change": "CHG-2024-0127", "event": "impact-indicator-failed", "indicator": "Order processing latency", "actual": "520ms", "threshold": "400ms", "details": {"query": "SELECT avg(duration_ms) FROM orders WHERE created_at >= '2024-07-15'"}}

Upon failure, the change manager runs trigger-rollback.sh, which:

  1. Patches the pipeline.yaml to include a rollback stage.
  2. Updates the change status in the change-status.json file.
{
  "change": "CHG-2024-0127",
  "status": "failed",
  "failure-reason": "Order processing latency exceeded threshold",
  "last-updated": "2024-07-15T10:25:10Z",
  "rollback": {
    "started": true,
    "stage": "rollback-in-progress",
    "target-version": "v1.2.2",
    "steps": [
      "kubectl rollout undo deployment/app-prod --to-revision=1",
      "psql -d production -c \"UPDATE settings SET value = 'rollback' WHERE key = 'maintenance-mode';\"",
      "curl -X POST http://alerting:8080/api/alert -d '{\"message\": \"Rollback initiated for CHG-2024-0127\"}'"
    ]
  }
}

Sharp Edges

Common Failure Patterns

Enterprise Change Management for Technology is not a checklist. It is a living practice—where every change leaves behind evidence, every failure teaches, and every success is measurable.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.