Skip to main content

API Versioning & Changes

Version Control Strategy

Versioning Schema

interface APIVersion {
  major: number;    // Breaking changes
  minor: number;    // Backward-compatible features
  patch: number;    // Bug fixes
  status: 'alpha' | 'beta' | 'stable' | 'deprecated';
}

// Example: v2.1.3
const VERSION_PATTERN = /^v(\d+)\.(\d+)\.(\d+)$/;

URI Versioning

// Base URL structure
const API_URL = 'https://api.example.com/v{major}/{resource}';

// Version routing middleware
const versionRouter = (req: Request, res: Response, next: NextFunction) => {
  const version = req.path.split('/')[1]; // e.g., 'v2'
  const [major] = version.match(VERSION_PATTERN) || [];
  
  req.apiVersion = {
    major: parseInt(major),
    implementation: getVersionImplementation(major)
  };
  
  next();
};

Change Classifications

1. Breaking Changes

Changes that require client updates.

interface BreakingChange {
  type: 'BREAKING';
  changes: {
    removedFields?: string[];
    modifiedFields?: {
      field: string;
      oldType: string;
      newType: string;
    }[];
    removedEndpoints?: string[];
    authenticationChanges?: string[];
  };
  migrationGuide: string;
  effectiveDate: Date;
}

// Example breaking changes
const BREAKING_CHANGES = {
  'v2.0.0': {
    type: 'BREAKING',
    changes: {
      removedFields: ['deprecated_field'],
      modifiedFields: [{
        field: 'status',
        oldType: 'string',
        newType: 'enum'
      }]
    },
    migrationGuide: 'docs/migration-v1-to-v2.md',
    effectiveDate: new Date('2024-12-01')
  }
};

2. Non-Breaking Changes

Backward-compatible additions or modifications.

interface NonBreakingChange {
  type: 'NON_BREAKING';
  changes: {
    addedFields?: string[];
    addedEndpoints?: string[];
    optionalParameters?: string[];
    enhancedResponses?: string[];
  };
  announcementDate: Date;
}

3. Bug Fixes

Corrections that don't affect the API contract.

interface BugFix {
  type: 'BUG_FIX';
  fixes: {
    description: string;
    affectedEndpoints: string[];
    resolution: string;
  }[];
  deploymentDate: Date;
}

Version Implementation

Version Manager

class APIVersionManager {
  private versions: Map<number, Implementation>;
  
  constructor() {
    this.versions = new Map();
    this.initializeVersions();
  }
  
  private initializeVersions() {
    // Register version implementations
    this.versions.set(1, new V1Implementation());
    this.versions.set(2, new V2Implementation());
  }
  
  public getImplementation(version: number): Implementation {
    const impl = this.versions.get(version);
    if (!impl) {
      throw new Error(`Version ${version} not supported`);
    }
    return impl;
  }
  
  public isDeprecated(version: number): boolean {
    return this.versions.get(version)?.status === 'deprecated';
  }
}

Version-Specific Implementations

interface Implementation {
  version: number;
  status: 'active' | 'deprecated';
  handlers: Map<string, RequestHandler>;
}

class V2Implementation implements Implementation {
  version = 2;
  status = 'active';
  handlers = new Map([
    ['createVault', this.createVaultV2],
    ['getVault', this.getVaultV2]
  ]);
  
  async createVaultV2(req: Request, res: Response) {
    // V2 implementation
  }
  
  async getVaultV2(req: Request, res: Response) {
    // V2 implementation
  }
}

API Documentation

OpenAPI Versioning

openapi: 3.0.0
info:
  version: 2.0.0
  title: Vault API
servers:
  - url: https://api.example.com/v2
paths:
  /vaults:
    post:
      summary: Create Vault (v2)
      description: |
        Changes from v1:
        - Added required 'security' field
        - Modified response structure
components:
  schemas:
    VaultV2:
      allOf:
        - $ref: '#/components/schemas/VaultV1'
        - type: object
          properties:
            security:
              type: object
              required: true

Change Management Process

1. Planning Phase

interface ChangeProposal {
  type: 'BREAKING' | 'NON_BREAKING' | 'BUG_FIX';
  description: string;
  justification: string;
  impact: {
    clients: string[];
    endpoints: string[];
    estimatedEffort: string;
  };
  timeline: {
    developmentStart: Date;
    betaRelease: Date;
    stableRelease: Date;
    deprecationDate?: Date;
  };
}

2. Communication Strategy

interface VersionAnnouncement {
  version: string;
  type: 'NEW_VERSION' | 'DEPRECATION' | 'END_OF_LIFE';
  details: {
    summary: string;
    changes: string[];
    migrationGuide?: string;
    timeline: {
      announcementDate: Date;
      effectiveDate: Date;
      endOfSupportDate?: Date;
    };
  };
  distributionChannels: string[];
}

3. Implementation Guidelines

Version Transition

// Graceful version handling
app.use('/v:version/*', (req, res, next) => {
  const version = parseInt(req.params.version);
  
  if (version < MINIMUM_SUPPORTED_VERSION) {
    return res.status(410).json({
      error: 'Version no longer supported',
      minimumVersion: MINIMUM_SUPPORTED_VERSION,
      migrationGuide: getMigrationGuideUrl(version)
    });
  }
  
  if (isDeprecated(version)) {
    res.setHeader('Warning', '299 - "Deprecated API Version"');
  }
  
  next();
});

Feature Flags

interface FeatureFlag {
  name: string;
  enabled: boolean;
  version: number;
  rolloutPercentage: number;
  conditions?: {
    clientIds?: string[];
    environments?: string[];
    startDate?: Date;
  };
}

const featureManager = {
  isEnabled: (feature: string, context: RequestContext): boolean => {
    const flag = getFeatureFlag(feature);
    if (!flag) return false;
    
    return (
      flag.enabled &&
      meetsVersionRequirement(context.version, flag.version) &&
      meetsRolloutCriteria(context, flag.rolloutPercentage) &&
      meetsConditions(context, flag.conditions)
    );
  }
};

Deprecation Strategy

1. Deprecation Timeline

interface DeprecationSchedule {
  version: string;
  announceDate: Date;
  deprecationDate: Date;
  endOfLifeDate: Date;
  alternativeVersion: string;
  migrationDeadline: Date;
}

const DEPRECATION_POLICY = {
  minimumNoticePeroid: 180, // days
  supportWindow: 365, // days
  maxVersionsSupported: 2
};

2. Deprecation Notices

// Response headers
app.use((req, res, next) => {
  const version = req.apiVersion;
  
  if (isDeprecated(version)) {
    res.setHeader('Deprecation', 'true');
    res.setHeader('Sunset', getEndOfLifeDate(version));
    res.setHeader('Link', `<${getUpgradeGuideUrl(version)}>; rel="deprecation"`);
  }
  
  next();
});

3. Migration Support

interface MigrationTool {
  validateRequest: (req: Request, targetVersion: number) => ValidationResult;
  transformRequest: (req: Request, sourceVersion: number, targetVersion: number) => Request;
  transformResponse: (res: Response, sourceVersion: number, targetVersion: number) => Response;
  generateMigrationPlan: (currentVersion: number, targetVersion: number) => MigrationStep[];
}

Monitoring and Analytics

Version Usage Tracking

interface VersionMetrics {
  version: string;
  requests: number;
  uniqueClients: number;
  errorRate: number;
  avgResponseTime: number;
  deprecationStatus: 'active' | 'deprecated' | 'sunset';
}

const trackVersionUsage = async (req: Request, res: Response, next: NextFunction) => {
  const startTime = Date.now();
  const version = req.apiVersion;
  
  res.on('finish', () => {
    const duration = Date.now() - startTime;
    metrics.recordVersionUsage({
      version,
      duration,
      status: res.statusCode,
      clientId: req.auth?.clientId
    });
  });
  
  next();
};