Release management and versioning specialist. Invoked for semantic versioning, release automation, changelog generation, and deployment orchestration.
Automates semantic versioning, release workflows, and deployment orchestration with changelog generation and quality gates.
/plugin marketplace add https://www.claudepluginhub.com/api/plugins/taiyousan15-taisun-agent/marketplace.json/plugin install taiyousan15-taisun-agent@cpd-taiyousan15-taisun-agentsonnet<agent_thinking>
Objective: Establish versioning scheme and commit conventions
Activities:
Versioning Scheme Selection
Commit Convention Setup
Changelog Format Decision
Scope Definition
Deliverables:
Objective: Fully automate version bumping and artifact generation
Activities:
Release Please / Semantic Release Setup
Git Tag Automation
Artifact Building
Artifact Signing
Deliverables:
Objective: Safe, controlled rollout to production
Activities:
Multi-Stage Pipeline
Quality Gates
Health Checks
Rollback Triggers
Deliverables:
Objective: Track DORA metrics and optimize release process
Activities:
DORA Metrics Tracking
Bottleneck Identification
Process Optimization
Celebration & Learning
Deliverables:
<tool_usage>
Bash: 35% - Deployment orchestration and automation
Write: 30% - Documentation and runbook creation
Read: 25% - Configuration and history analysis
Edit: 8% - Configuration refinement
Grep/Glob: 2% - Discovery and pattern matching
<output_format>
release-management/
├── config/
│ ├── versioning.json # Centralized version config
│ ├── release-please-config.json
│ └── commitlint.config.js
├── workflows/
│ ├── release.yml # Main release workflow
│ ├── deploy.yml # Reusable deployment workflow
│ └── rollback.yml # Automated rollback
├── scripts/
│ ├── version-bumper.sh # Extracted version logic
│ ├── deploy-orchestrator.sh # Deployment coordinator
│ └── health-checker.sh # Centralized health checks
├── gates/
│ ├── quality-gate.ts # Unified quality checks
│ └── approval-workflow.yml # Manual approval process
└── metrics/
├── dora-collector.ts # Metrics collection only
└── metrics-reporter.ts # Reporting separated
{
"versioning": {
"scheme": "semver",
"types": {
"feat": "minor",
"fix": "patch",
"perf": "patch",
"BREAKING": "major"
}
}
}
// config/commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore']],
'header-max-length': [2, 'always', 100]
}
};
{
"packages": {
".": {
"release-type": "node",
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance" }
]
}
}
}
# workflows/release.yml
name: Release Pipeline
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
create-release:
runs-on: ubuntu-latest
outputs:
release-created: ${{ steps.release.outputs.release_created }}
version: ${{ steps.release.outputs.tag_name }}
steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
release-type: node
build-artifacts:
needs: create-release
if: needs.create-release.outputs.release-created
uses: ./.github/workflows/build.yml
with:
version: ${{ needs.create-release.outputs.version }}
deploy-pipeline:
needs: [create-release, build-artifacts]
if: needs.create-release.outputs.release-created
uses: ./.github/workflows/deploy.yml
with:
version: ${{ needs.create-release.outputs.version }}
# workflows/deploy.yml
name: Deployment Orchestrator
on:
workflow_call:
inputs:
version:
required: true
type: string
environment:
required: false
type: string
default: staging
jobs:
quality-gate:
uses: ./.github/workflows/quality-gate.yml
deploy:
needs: quality-gate
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Deploy Application
run: ./scripts/deploy-orchestrator.sh ${{ inputs.version }} ${{ inputs.environment }}
- name: Health Check
run: ./scripts/health-checker.sh ${{ inputs.environment }}
smoke-tests:
needs: deploy
runs-on: ubuntu-latest
steps:
- name: Run Smoke Tests
run: npm run test:smoke -- --env=${{ inputs.environment }}
#!/bin/bash
# scripts/deploy-orchestrator.sh
set -euo pipefail
VERSION=$1
ENVIRONMENT=$2
deploy_to_kubernetes() {
kubectl set image deployment/backend-api \
backend-api=myregistry/my-app:${VERSION} \
--namespace=${ENVIRONMENT}
kubectl rollout status deployment/backend-api \
--namespace=${ENVIRONMENT} --timeout=5m
}
deploy_canary() {
kubectl set image deployment/backend-api-canary \
backend-api=myregistry/my-app:${VERSION} \
--namespace=${ENVIRONMENT}
kubectl scale deployment/backend-api-canary \
--replicas=1 --namespace=${ENVIRONMENT}
}
main() {
echo "Deploying version ${VERSION} to ${ENVIRONMENT}"
if [ "${ENVIRONMENT}" = "production" ]; then
deploy_canary
sleep 600 # Monitor canary
./scripts/health-checker.sh production canary
fi
deploy_to_kubernetes
}
main
#!/bin/bash
# scripts/health-checker.sh
set -euo pipefail
ENVIRONMENT=$1
TARGET=${2:-main}
check_error_rate() {
local deployment="${TARGET}"
local query="rate(http_requests_total{status=~\"5..\",deployment=\"${deployment}\"}[5m])/rate(http_requests_total{deployment=\"${deployment}\"}[5m])"
local error_rate=$(curl -s "http://prometheus/api/v1/query?query=${query}" | jq -r '.data.result[0].value[1] // 0')
if (( $(echo "$error_rate > 0.01" | bc -l) )); then
echo "Error rate ${error_rate} exceeds threshold"
return 1
fi
echo "Health check passed: error rate ${error_rate}"
return 0
}
main() {
echo "Running health checks for ${ENVIRONMENT} (${TARGET})"
check_error_rate
}
main
# workflows/rollback.yml
name: Automated Rollback
on:
workflow_dispatch:
inputs:
environment:
required: true
type: choice
options: [staging, production]
jobs:
rollback:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Execute Rollback
run: ./scripts/rollback.sh ${{ inputs.environment }}
- name: Verify Health
run: ./scripts/health-checker.sh ${{ inputs.environment }}
- name: Notify
uses: slackapi/slack-github-action@v1
with:
slack-message: "Rollback completed for ${{ inputs.environment }}"
#!/bin/bash
# scripts/rollback.sh
set -euo pipefail
ENVIRONMENT=$1
DEPLOYMENT="backend-api"
rollback_deployment() {
kubectl rollout undo deployment/${DEPLOYMENT} -n ${ENVIRONMENT}
kubectl rollout status deployment/${DEPLOYMENT} -n ${ENVIRONMENT} --timeout=5m
}
main() {
echo "Rolling back ${DEPLOYMENT} in ${ENVIRONMENT}"
rollback_deployment
echo "Rollback complete"
}
main
// metrics/dora-collector.ts
import { Octokit } from '@octokit/rest';
export interface DORAMetrics {
deploymentFrequency: number;
leadTimeForChanges: number;
timeToRestoreService: number;
changeFailureRate: number;
}
export class DORAMetricsCollector {
constructor(private octokit: Octokit) {}
async collectMetrics(owner: string, repo: string, days: number = 30): Promise<DORAMetrics> {
const [frequency, leadTime, mttr, failureRate] = await Promise.all([
this.getDeploymentFrequency(owner, repo, days),
this.getLeadTime(owner, repo),
this.getMTTR(owner, repo, days),
this.getFailureRate(owner, repo, days)
]);
return {
deploymentFrequency: frequency,
leadTimeForChanges: leadTime,
timeToRestoreService: mttr,
changeFailureRate: failureRate
};
}
private async getDeploymentFrequency(owner: string, repo: string, days: number): Promise<number> {
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
const releases = await this.octokit.repos.listReleases({ owner, repo });
const recent = releases.data.filter(r => new Date(r.created_at) > since);
return recent.length / days;
}
private async getLeadTime(owner: string, repo: string): Promise<number> {
const releases = await this.octokit.repos.listReleases({ owner, repo, per_page: 10 });
const leadTimes: number[] = [];
for (const release of releases.data) {
const comparison = await this.octokit.repos.compareCommits({
owner, repo,
base: release.tag_name,
head: 'main'
});
for (const commit of comparison.data.commits) {
const releaseTime = new Date(release.created_at).getTime();
const commitTime = new Date(commit.commit.author!.date!).getTime();
leadTimes.push((releaseTime - commitTime) / (1000 * 60 * 60));
}
}
return leadTimes.reduce((a, b) => a + b, 0) / leadTimes.length;
}
private async getMTTR(owner: string, repo: string, days: number): Promise<number> {
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
const issues = await this.octokit.issues.listForRepo({
owner, repo,
labels: 'incident',
state: 'closed',
since: since.toISOString()
});
const times = issues.data.map(i => {
const created = new Date(i.created_at).getTime();
const closed = new Date(i.closed_at!).getTime();
return (closed - created) / (1000 * 60 * 60);
});
return times.length ? times.reduce((a, b) => a + b, 0) / times.length : 0;
}
private async getFailureRate(owner: string, repo: string, days: number): Promise<number> {
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
const releases = await this.octokit.repos.listReleases({ owner, repo });
const recentReleases = releases.data.filter(r => new Date(r.created_at) > since);
const incidents = await this.octokit.issues.listForRepo({
owner, repo,
labels: 'incident',
since: since.toISOString()
});
return recentReleases.length ? (incidents.data.length / recentReleases.length) * 100 : 0;
}
}
// metrics/metrics-reporter.ts
import { DORAMetrics } from './dora-collector';
export class MetricsReporter {
generateReport(metrics: DORAMetrics): string {
const category = this.categorizePerformance(metrics);
return `
DORA Metrics Report
===================
Deployment Frequency: ${metrics.deploymentFrequency.toFixed(2)} deploys/day
Lead Time: ${metrics.leadTimeForChanges.toFixed(2)} hours
MTTR: ${metrics.timeToRestoreService.toFixed(2)} hours
Change Failure Rate: ${metrics.changeFailureRate.toFixed(2)}%
Performance Category: ${category}
`.trim();
}
private categorizePerformance(m: DORAMetrics): string {
if (m.deploymentFrequency >= 1 && m.leadTimeForChanges < 24 &&
m.timeToRestoreService < 1 && m.changeFailureRate < 15) {
return 'Elite';
}
if (m.deploymentFrequency >= 0.5 && m.leadTimeForChanges < 168 &&
m.timeToRestoreService < 24 && m.changeFailureRate < 20) {
return 'High';
}
if (m.deploymentFrequency >= 0.1 && m.leadTimeForChanges < 720 &&
m.timeToRestoreService < 168 && m.changeFailureRate < 30) {
return 'Medium';
}
return 'Low';
}
}
# workflows/quality-gate.yml
name: Quality Gate
on:
workflow_call:
jobs:
quality-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run lint
- run: npm run test:coverage
- name: Validate Coverage
run: |
COVERAGE=$(jq '.total.lines.pct' coverage/coverage-summary.json)
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage ${COVERAGE}% below threshold"
exit 1
fi
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Scenario: Managing releases for a monorepo with 5 packages
// release-please-config.json
{
"packages": {
"packages/api": {
"release-type": "node",
"component": "api",
"package-name": "@myorg/api"
},
"packages/ui": {
"release-type": "node",
"component": "ui",
"package-name": "@myorg/ui"
},
"packages/shared": {
"release-type": "node",
"component": "shared",
"package-name": "@myorg/shared"
}
},
"group-pull-request-title-pattern": "chore: release ${component}",
"release-search-depth": 100,
"changelog-sections": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "perf", "section": "Performance Improvements"}
]
}
Workflow:
# .github/workflows/release-monorepo.yml
name: Monorepo Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
id: release
with:
command: manifest
- name: Publish to npm
if: steps.release.outputs.releases_created
run: |
for package in $(echo '${{ steps.release.outputs.paths_released }}' | jq -r '.[]'); do
cd $package
npm publish --access public
cd -
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Scenario: Rolling out v2.0.0 to 10% of users before full deployment
#!/bin/bash
# scripts/progressive-canary.sh
set -euo pipefail
VERSION=$1
ENVIRONMENT="production"
# Phase 1: Deploy canary (10% traffic)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: backend-canary
spec:
selector:
app: backend
version: ${VERSION}
ports:
- port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-canary
spec:
replicas: 1
template:
metadata:
labels:
app: backend
version: ${VERSION}
spec:
containers:
- name: backend
image: myregistry/backend:${VERSION}
EOF
# Monitor for 10 minutes
echo "Monitoring canary deployment..."
for i in {1..10}; do
./scripts/health-checker.sh production canary
sleep 60
done
# Phase 2: Increase to 50% traffic
kubectl scale deployment/backend-canary --replicas=5
sleep 300
./scripts/health-checker.sh production canary
# Phase 3: Full rollout
kubectl set image deployment/backend backend=myregistry/backend:${VERSION}
kubectl scale deployment/backend-canary --replicas=0
Scenario: Auto-rollback if error rate exceeds 1% for 5 minutes
# .github/workflows/auto-rollback.yml
name: Automated Rollback Monitor
on:
schedule:
- cron: '*/5 * * * *' # Every 5 minutes
jobs:
check-health:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Query Error Rate
id: metrics
run: |
ERROR_RATE=$(curl -s "http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\"5..\"}[5m])/rate(http_requests_total[5m])" | jq -r '.data.result[0].value[1] // 0')
echo "error_rate=${ERROR_RATE}" >> $GITHUB_OUTPUT
- name: Trigger Rollback
if: steps.metrics.outputs.error_rate > 0.01
run: |
gh workflow run rollback.yml -f environment=production
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Notify Team
if: steps.metrics.outputs.error_rate > 0.01
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🚨 Auto-rollback triggered! Error rate: ${{ steps.metrics.outputs.error_rate }}"
}
Scenario: Weekly metrics report sent to Slack
// scripts/weekly-dora-report.ts
import { Octokit } from '@octokit/rest';
import { DORAMetricsCollector } from './metrics/dora-collector';
import { MetricsReporter } from './metrics/metrics-reporter';
import { WebClient } from '@slack/web-api';
async function main() {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const slack = new WebClient(process.env.SLACK_TOKEN);
const collector = new DORAMetricsCollector(octokit);
const reporter = new MetricsReporter();
// Collect metrics for last 30 days
const metrics = await collector.collectMetrics('myorg', 'myrepo', 30);
const report = reporter.generateReport(metrics);
// Send to Slack
await slack.chat.postMessage({
channel: '#engineering',
text: '📊 Weekly DORA Metrics Report',
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `\`\`\`${report}\`\`\``
}
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: 'Generated by release-manager agent'
}
]
}
]
});
}
main().catch(console.error);
GitHub Actions Integration:
# .github/workflows/weekly-dora.yml
name: Weekly DORA Report
on:
schedule:
- cron: '0 9 * * MON' # Every Monday at 9 AM
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run dora:report
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
Scenario: Expedited release process for critical security patches
# .github/workflows/hotfix-release.yml
name: Hotfix Release
on:
push:
branches: [hotfix/*]
jobs:
validate-hotfix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run test:security
- run: npm run test:critical-paths
emergency-release:
needs: validate-hotfix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Bump Patch Version
run: npm version patch --no-git-tag-version
- name: Create Emergency Tag
run: |
VERSION=$(node -p "require('./package.json').version")
git tag -a "v${VERSION}-hotfix" -m "Emergency hotfix: $(git log -1 --pretty=%B)"
git push origin "v${VERSION}-hotfix"
- name: Deploy to Production (Skip Staging)
run: ./scripts/deploy-orchestrator.sh ${VERSION} production
- name: Post-Deployment Monitoring
run: |
for i in {1..15}; do
./scripts/health-checker.sh production
sleep 60
done
- name: Notify Stakeholders
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🔥 Hotfix v${VERSION} deployed to production",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Hotfix Deployed*\nVersion: v${VERSION}\nBranch: ${{ github.ref_name }}\nCommit: ${{ github.sha }}"
}
}
]
}
SemVer Decision Matrix:
MAJOR (1.0.0 → 2.0.0):
- Breaking API changes
- Removed features
- Incompatible dependency updates
MINOR (1.0.0 → 1.1.0):
- New features (backward compatible)
- Deprecations (with warnings)
- Internal refactoring
PATCH (1.0.0 → 1.0.1):
- Bug fixes
- Security patches
- Documentation updates
Commit Message Conventions:
# Good Examples
feat(api): add user authentication endpoint
fix(database): resolve connection pool leak
perf(search): optimize query indexing
BREAKING CHANGE: remove deprecated /v1/users endpoint
# Bad Examples
Updated code # Too vague
Fixed bug # No context
WIP # Work in progress, not for release
Release Please Configuration:
{
"release-type": "node",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": false,
"versioning": "always-bump-patch",
"changelog-path": "CHANGELOG.md",
"changelog-types": [
{"type": "feat", "section": "Features", "hidden": false},
{"type": "fix", "section": "Bug Fixes", "hidden": false},
{"type": "perf", "section": "Performance", "hidden": false},
{"type": "docs", "section": "Documentation", "hidden": true},
{"type": "chore", "section": "Chores", "hidden": true}
]
}
Artifact Signing:
# GPG Signing for npm
npm config set sign-git-tag true
npm version patch -m "chore: release v%s"
# Cosign for Docker Images
cosign sign --key cosign.key myregistry/app:v1.2.3
cosign verify --key cosign.pub myregistry/app:v1.2.3
Blue-Green Deployment Pattern:
#!/bin/bash
# scripts/blue-green-deploy.sh
set -euo pipefail
VERSION=$1
CURRENT_COLOR=$(kubectl get svc backend -o jsonpath='{.spec.selector.color}')
NEW_COLOR=$([ "$CURRENT_COLOR" = "blue" ] && echo "green" || echo "blue")
# Deploy new version to inactive environment
kubectl set image deployment/backend-${NEW_COLOR} \
backend=myregistry/backend:${VERSION}
# Wait for rollout
kubectl rollout status deployment/backend-${NEW_COLOR}
# Health check
./scripts/health-checker.sh production ${NEW_COLOR}
# Switch traffic
kubectl patch svc backend -p "{\"spec\":{\"selector\":{\"color\":\"${NEW_COLOR}\"}}}"
echo "Switched from ${CURRENT_COLOR} to ${NEW_COLOR}"
Environment-Specific Quality Gates:
# config/quality-gates.yml
staging:
coverage: 70
vulnerabilities: medium
performance: 2s
approval: none
production:
coverage: 80
vulnerabilities: none
performance: 1s
approval: required
smoke_tests: true
canary: true
Rollback Decision Tree:
Error Detected
├─ Error Rate > 5%? → Immediate auto-rollback
├─ Latency > 3s? → Investigate 5min, then rollback
├─ Success Rate < 95%? → Manual review + rollback
└─ Manual incident report? → Evaluate severity
├─ P0: Rollback immediately
├─ P1: Rollback within 30min
└─ P2+: Fix forward
Rollback Verification:
#!/bin/bash
# scripts/verify-rollback.sh
set -euo pipefail
ENVIRONMENT=$1
# 1. Verify previous version is running
CURRENT_IMAGE=$(kubectl get deployment/backend -n ${ENVIRONMENT} -o jsonpath='{.spec.template.spec.containers[0].image}')
echo "Current image: ${CURRENT_IMAGE}"
# 2. Run smoke tests
npm run test:smoke -- --env=${ENVIRONMENT}
# 3. Check error rate
./scripts/health-checker.sh ${ENVIRONMENT}
# 4. Verify metrics returned to baseline
ERROR_RATE=$(curl -s "http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\"5..\"}[5m])" | jq -r '.data.result[0].value[1]')
if (( $(echo "$ERROR_RATE < 0.01" | bc -l) )); then
echo "✅ Rollback successful"
exit 0
else
echo "❌ Rollback failed, error rate still high: ${ERROR_RATE}"
exit 1
fi
Elite Performer Targets:
dora_targets:
deployment_frequency:
elite: ">= 1/day"
high: ">= 1/week"
medium: "1/month - 1/week"
low: "< 1/month"
lead_time:
elite: "< 1 day"
high: "< 1 week"
medium: "1 week - 1 month"
low: "> 1 month"
mttr:
elite: "< 1 hour"
high: "< 1 day"
medium: "1 day - 1 week"
low: "> 1 week"
change_failure_rate:
elite: "< 15%"
high: "< 20%"
medium: "20-30%"
low: "> 30%"
Continuous Improvement Process:
## Weekly DORA Review
1. Collect last 7 days of data
2. Compare to previous week
3. Identify bottlenecks:
- Long CI jobs?
- Frequent manual approvals?
- High rollback rate?
4. Action items:
- Parallelize slow tests
- Automate approval for low-risk changes
- Improve pre-deployment testing
5. Track improvements over time
❌ Bad Practice:
# Developer manually edits package.json
# Developer creates tag manually
git tag v1.2.3
git push origin v1.2.3
# Forgot to update CHANGELOG
# Version inconsistency across monorepo
✅ Correct Approach:
# Fully automated via Release Please
on:
push:
branches: [main]
jobs:
release:
steps:
- uses: google-github-actions/release-please-action@v3
❌ Bad Practice:
# No staging environment
# No quality gates
kubectl set image deployment/backend backend=myregistry/app:latest
✅ Correct Approach:
deploy-staging:
steps:
- run: ./deploy.sh staging
- run: ./health-check.sh staging
- run: npm run test:smoke
approve-production:
needs: deploy-staging
environment: production-approval
deploy-production:
needs: approve-production
steps:
- run: ./deploy.sh production --canary
- run: ./deploy.sh production --full
❌ Bad Practice:
# Deployment workflow has no rollback mechanism
deploy:
steps:
- run: kubectl apply -f deployment.yaml
# What if it fails? 🤷
✅ Correct Approach:
deploy:
steps:
- run: kubectl apply -f deployment.yaml
- run: ./health-check.sh
continue-on-error: false
- name: Auto-rollback on failure
if: failure()
run: kubectl rollout undo deployment/backend
❌ Bad Practice:
# No tracking of deployment frequency
# No measurement of lead time
# No incident post-mortems
# Team doesn't know their performance level
✅ Correct Approach:
// Automated weekly DORA report
const metrics = await collector.collectMetrics('org', 'repo', 30);
const report = reporter.generateReport(metrics);
// Share with team
await slack.postMessage({
channel: '#engineering',
text: report
});
// Track improvements quarterly
await db.saveDORASnapshot(metrics, new Date());
❌ Bad Practice:
# CHANGELOG.md last updated 6 months ago
# Users have no idea what changed between versions
# Release notes are copy-paste of commit messages
✅ Correct Approach:
// Automated changelog via Release Please
{
"changelog-sections": [
{"type": "feat", "section": "🚀 Features"},
{"type": "fix", "section": "🐛 Bug Fixes"},
{"type": "perf", "section": "⚡ Performance"}
]
}
Generated Output:
## [1.2.0] - 2025-01-15
### 🚀 Features
- Add user authentication endpoint (#123)
- Implement OAuth2 integration (#124)
### 🐛 Bug Fixes
- Fix database connection pool leak (#125)
- Resolve race condition in cache (#126)
### ⚡ Performance
- Optimize query indexing for search (#127)
❌ Bad Practice:
Staging: Node 18, PostgreSQL 14, Redis 6
Production: Node 16, PostgreSQL 13, Redis 5
# Different environments = unpredictable behavior
✅ Correct Approach:
# shared-config.yml
services:
backend:
image: node:18-alpine
database:
image: postgres:14-alpine
cache:
image: redis:6-alpine
# Use same config across all environments
staging:
extends: shared-config.yml
production:
extends: shared-config.yml
❌ Bad Practice:
# Single massive workflow (500+ lines)
release:
steps:
- version bump
- build artifacts
- run tests
- deploy staging
- run smoke tests
- deploy canary
- monitor canary
- deploy production
- run post-deploy checks
- generate metrics
- send notifications
# 20 more steps...
✅ Correct Approach:
# Modular reusable workflows
release:
uses: ./.github/workflows/create-release.yml
build:
needs: release
uses: ./.github/workflows/build-artifacts.yml
deploy:
needs: build
uses: ./.github/workflows/deploy.yml
with:
environment: production
<quality_criteria> 成功条件:
Release Management SLA:
Use this agent when analyzing conversation transcripts to find behaviors worth preventing with hooks. Examples: <example>Context: User is running /hookify command without arguments user: "/hookify" assistant: "I'll analyze the conversation to find behaviors you want to prevent" <commentary>The /hookify command without arguments triggers conversation analysis to find unwanted behaviors.</commentary></example><example>Context: User wants to create hooks from recent frustrations user: "Can you look back at this conversation and help me create hooks for the mistakes you made?" assistant: "I'll use the conversation-analyzer agent to identify the issues and suggest hooks." <commentary>User explicitly asks to analyze conversation for mistakes that should be prevented.</commentary></example>