TechLead
Lesson 25 of 30
5 min read
Project Management

Release Management

Master release planning, semantic versioning, feature flags, deployment strategies, release checklists, and rollback procedures

What Is Release Management?

Release management is the process of planning, scheduling, building, testing, and deploying software releases. It bridges the gap between "development done" and "users can use it." A mature release process makes deployments boring (in a good way) — predictable, low-risk, and routine.

Release Cadence Options

Cadence Frequency Best For Risk Level
Continuous DeploymentMultiple times/daySaaS, web appsLowest (small changes)
Continuous DeliveryOn-demand (anytime)Teams with good CI/CDLow
Sprint ReleaseEvery 1-2 weeksScrum teamsMedium
Monthly ReleaseMonthlyEnterprise, B2BMedium-High
Quarterly ReleaseQuarterlyRegulated, desktop appsHigh (big bang)

Semantic Versioning

SemVer: MAJOR.MINOR.PATCH

  • MAJOR (X.0.0): Breaking changes — existing users must update their integration. Example: API v1 to v2.
  • MINOR (0.X.0): New features that are backward compatible. Example: New API endpoint added.
  • PATCH (0.0.X): Bug fixes that are backward compatible. Example: Fixed null pointer exception.

Pre-release: 1.0.0-beta.1 | Build metadata: 1.0.0+20240315

Feature Flags

// Feature Flag Implementation
interface FeatureFlag {
  key: string;
  name: string;
  description: string;
  type: 'release' | 'experiment' | 'ops' | 'permission';
  status: 'enabled' | 'disabled' | 'percentage' | 'user-segment';
  rolloutPercentage?: number;
  targetUsers?: string[];
  targetSegments?: string[];
  createdDate: Date;
  owner: string;
  expiryDate?: Date; // Feature flags should not live forever
}

// Simple feature flag service
class FeatureFlagService {
  private flags: Map = new Map();

  isEnabled(flagKey: string, userId?: string): boolean {
    const flag = this.flags.get(flagKey);
    if (!flag) return false;

    switch (flag.status) {
      case 'enabled':
        return true;
      case 'disabled':
        return false;
      case 'percentage':
        if (!userId || !flag.rolloutPercentage) return false;
        // Deterministic hash for consistent user experience
        const hash = this.hashUserId(userId, flagKey);
        return hash < flag.rolloutPercentage;
      case 'user-segment':
        if (!userId) return false;
        return flag.targetUsers?.includes(userId) ?? false;
      default:
        return false;
    }
  }

  private hashUserId(userId: string, flagKey: string): number {
    let hash = 0;
    const str = `${userId}:${flagKey}`;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return Math.abs(hash) % 100;
  }
}

// Usage in application code
const featureFlags = new FeatureFlagService();

function renderCheckout(userId: string) {
  if (featureFlags.isEnabled('new-checkout-flow', userId)) {
    return renderNewCheckout();
  }
  return renderLegacyCheckout();
}

// Deployment Strategies
type DeploymentStrategy = {
  name: string;
  description: string;
  riskLevel: 'low' | 'medium' | 'high';
  rollbackTime: string;
  resourceCost: string;
};

const deploymentStrategies: DeploymentStrategy[] = [
  {
    name: 'Blue-Green',
    description: 'Maintain two identical environments. Deploy to inactive (green), switch traffic from active (blue) to green.',
    riskLevel: 'low',
    rollbackTime: 'Seconds (switch back to blue)',
    resourceCost: 'High (2x infrastructure)'
  },
  {
    name: 'Canary',
    description: 'Route a small percentage (1-5%) of traffic to the new version. Gradually increase if metrics look good.',
    riskLevel: 'low',
    rollbackTime: 'Seconds (route all traffic to old version)',
    resourceCost: 'Medium (small additional capacity)'
  },
  {
    name: 'Rolling',
    description: 'Gradually replace old instances with new ones, one at a time or in batches.',
    riskLevel: 'medium',
    rollbackTime: 'Minutes (redeploy old version)',
    resourceCost: 'Low (no extra infrastructure)'
  },
  {
    name: 'Big Bang',
    description: 'Replace everything at once during a maintenance window.',
    riskLevel: 'high',
    rollbackTime: 'Minutes to hours',
    resourceCost: 'Low'
  }
];

Release Checklist

Pre-Release Checklist

  • ☐ All stories in the release are "Done" (meet DoD)
  • ☐ All automated tests pass (unit, integration, E2E)
  • ☐ Performance testing completed — no regressions
  • ☐ Security scan completed — no critical/high vulnerabilities
  • ☐ Database migrations tested on staging with production-like data
  • ☐ Feature flags configured for gradual rollout
  • ☐ Rollback plan documented and tested
  • ☐ Monitoring and alerts configured
  • ☐ Release notes written and distributed
  • ☐ On-call engineer identified for the release window
  • ☐ Stakeholders notified of release schedule

Rollback Procedures

  • Define Rollback Triggers: Error rate > 1%, latency > 2x baseline, critical bug reported by 5+ users
  • Automate Rollback: One command or button to revert. No manual steps during an emergency.
  • Test Rollback Regularly: Practice rolling back in staging. An untested rollback plan is not a plan.
  • Database Considerations: Backward-compatible schema migrations only. Never deploy a breaking migration.

Continue Learning