Version Migration Patterns - Deep Dive
🎯 Overview
This comprehensive guide explores advanced patterns for managing version migrations in Claude Code applications, from API evolution to prompt versioning and automated migration tools.
🏗️ Semantic Versioning Implementation
Version Management System
// Comprehensive version management
class VersionManager {
private current: SemanticVersion;
private history: VersionHistory;
constructor(version: string) {
this.current = this.parseVersion(version);
this.history = new VersionHistory();
}
parseVersion(version: string): SemanticVersion {
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(-(.+))?(\+(.+))?$/);
if (!match) throw new Error(`Invalid version: ${version}`);
return {
major: parseInt(match[1]),
minor: parseInt(match[2]),
patch: parseInt(match[3]),
preRelease: match[5],
metadata: match[7],
toString() {
let v = `${this.major}.${this.minor}.${this.patch}`;
if (this.preRelease) v += `-${this.preRelease}`;
if (this.metadata) v += `+${this.metadata}`;
return v;
}
};
}
isBreakingChange(old: string, new: string): boolean {
const oldV = this.parseVersion(old);
const newV = this.parseVersion(new);
// Major version change indicates breaking changes
return newV.major > oldV.major;
}
async getChangeLog(from: string, to: string): Promise<ChangeLog> {
const changes = await this.history.getChanges(from, to);
return {
breaking: changes.filter(c => c.type === 'breaking'),
features: changes.filter(c => c.type === 'feature'),
fixes: changes.filter(c => c.type === 'fix'),
deprecated: changes.filter(c => c.type === 'deprecation')
};
}
}Automated Version Bumping
class AutoVersionBump {
async determineNextVersion(
currentVersion: string,
commits: Commit[]
): Promise<string> {
const current = this.parseVersion(currentVersion);
let bumpType: 'major' | 'minor' | 'patch' = 'patch';
for (const commit of commits) {
// Check for breaking changes
if (commit.message.includes('BREAKING CHANGE:') ||
commit.message.startsWith('!:')) {
bumpType = 'major';
break;
}
// Check for new features
if (commit.message.startsWith('feat:') && bumpType !== 'major') {
bumpType = 'minor';
}
}
return this.bump(current, bumpType);
}
private bump(version: SemanticVersion, type: string): string {
switch (type) {
case 'major':
return `${version.major + 1}.0.0`;
case 'minor':
return `${version.major}.${version.minor + 1}.0`;
case 'patch':
return `${version.major}.${version.minor}.${version.patch + 1}`;
default:
throw new Error(`Unknown bump type: ${type}`);
}
}
}🔄 API Versioning Patterns
1. URI Path Versioning
// Advanced path-based versioning with middleware
class APIVersionRouter {
private versions: Map<string, Router> = new Map();
register(version: string, router: Router) {
this.versions.set(version, router);
}
middleware(): RequestHandler {
return (req, res, next) => {
const versionMatch = req.path.match(/^\/api\/(v\d+)\//);
if (!versionMatch) {
return res.status(400).json({
error: 'API version required',
supportedVersions: Array.from(this.versions.keys())
});
}
const version = versionMatch[1];
const router = this.versions.get(version);
if (!router) {
return res.status(400).json({
error: `Unsupported API version: ${version}`,
supportedVersions: Array.from(this.versions.keys())
});
}
// Add version info to request
req.apiVersion = version;
req.url = req.url.replace(`/api/${version}`, '');
router(req, res, next);
};
}
}2. Header-Based Versioning
class HeaderVersioning {
private DEFAULT_VERSION = 'v2';
private SUPPORTED_VERSIONS = ['v1', 'v2', 'v3'];
middleware(): RequestHandler {
return (req, res, next) => {
const version = req.headers['x-api-version'] as string ||
req.headers['accept']?.match(/version=(\w+)/)?.[1] ||
this.DEFAULT_VERSION;
if (!this.SUPPORTED_VERSIONS.includes(version)) {
return res.status(400).json({
error: `Unsupported version: ${version}`,
supported: this.SUPPORTED_VERSIONS,
header: 'X-API-Version'
});
}
// Version-specific processing
req.apiVersion = version;
res.setHeader('X-API-Version', version);
// Add deprecation warnings for old versions
if (version === 'v1') {
res.setHeader('X-Deprecated', 'true');
res.setHeader('X-Sunset-Date', '2025-12-31');
res.setHeader('X-Alternative', '/api/v2');
}
next();
};
}
}3. GraphQL Schema Evolution
// GraphQL versioning through schema evolution
class GraphQLVersioning {
async buildSchema(version: string): Promise<GraphQLSchema> {
const baseTypeDefs = await this.getBaseTypeDefs();
const versionedTypeDefs = await this.getVersionedTypeDefs(version);
// Merge schemas with deprecation support
const typeDefs = gql`
${baseTypeDefs}
${versionedTypeDefs}
directive @deprecated(
reason: String = "No longer supported"
removeAt: String
) on FIELD_DEFINITION | ENUM_VALUE
`;
const resolvers = await this.buildResolvers(version);
return makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives: {
deprecated: DeprecatedDirective
}
});
}
// Progressive field deprecation
private getVersionedTypeDefs(version: string): string {
if (version === 'v3') {
return `
type User {
id: ID!
email: String!
profile: UserProfile!
name: String @deprecated(reason: "Use profile.displayName", removeAt: "2025-06-01")
}
`;
}
// Older versions maintain compatibility
return `
type User {
id: ID!
email: String!
name: String!
profile: UserProfile
}
`;
}
}🛠️ Breaking Change Management
Automated Breaking Change Detection
class BreakingChangeAnalyzer {
async analyzeChanges(
oldSpec: APISpec,
newSpec: APISpec
): Promise<BreakingChange[]> {
const changes: BreakingChange[] = [];
// Check removed endpoints
for (const [path, methods] of oldSpec.paths) {
if (!newSpec.paths.has(path)) {
changes.push({
type: 'endpoint_removed',
path,
severity: 'breaking',
migration: `Endpoint ${path} has been removed`
});
}
}
// Check parameter changes
for (const [path, newMethods] of newSpec.paths) {
const oldMethods = oldSpec.paths.get(path);
if (!oldMethods) continue;
for (const [method, newDef] of newMethods) {
const oldDef = oldMethods.get(method);
if (!oldDef) continue;
// Check for new required parameters
const newRequired = new Set(newDef.requiredParams);
const oldRequired = new Set(oldDef.requiredParams);
for (const param of newRequired) {
if (!oldRequired.has(param)) {
changes.push({
type: 'new_required_param',
path,
method,
parameter: param,
severity: 'breaking',
migration: `New required parameter: ${param}`
});
}
}
// Check response schema changes
const schemaChanges = await this.compareSchemas(
oldDef.responseSchema,
newDef.responseSchema
);
changes.push(...schemaChanges);
}
}
return changes;
}
async generateMigrationGuide(
changes: BreakingChange[]
): Promise<MigrationGuide> {
const guide = new MigrationGuide();
// Group changes by severity and type
const grouped = this.groupChanges(changes);
// Generate migration steps for each group
for (const [type, typeChanges] of grouped) {
const steps = await this.generateMigrationSteps(type, typeChanges);
guide.addSection(type, steps);
}
// Add code examples
guide.codeExamples = await this.generateCodeExamples(changes);
return guide;
}
}Deprecation Management
class DeprecationManager {
private deprecations: Map<string, Deprecation> = new Map();
deprecate(options: DeprecationOptions) {
const deprecation: Deprecation = {
id: options.id,
type: options.type,
message: options.message,
since: options.since || new Date(),
sunset: options.sunset,
alternative: options.alternative,
autoMigrate: options.autoMigrate
};
this.deprecations.set(deprecation.id, deprecation);
// Schedule sunset notifications
if (deprecation.sunset) {
this.scheduleSunsetNotifications(deprecation);
}
}
middleware(): RequestHandler {
return async (req, res, next) => {
const deprecations = await this.checkDeprecations(req);
if (deprecations.length > 0) {
// Add deprecation headers
res.setHeader('X-Deprecation-Count', deprecations.length.toString());
// Add detailed deprecation info
const warnings = deprecations.map(d => ({
id: d.id,
message: d.message,
sunset: d.sunset?.toISOString(),
alternative: d.alternative
}));
res.setHeader('X-Deprecation-Warnings',
JSON.stringify(warnings));
// Log deprecation usage
await this.logDeprecationUsage(req, deprecations);
}
next();
};
}
async autoMigrate(
code: string,
deprecations: Deprecation[]
): Promise<string> {
let migratedCode = code;
for (const deprecation of deprecations) {
if (deprecation.autoMigrate) {
migratedCode = await deprecation.autoMigrate(migratedCode);
}
}
return migratedCode;
}
}🚀 Migration Workflows
AI-Powered Migration Assistant
class ClaudeCodeMigrationAssistant {
async analyzeCo
debase(
targetVersion: string
): Promise<MigrationAnalysis> {
const files = await this.scanCodebase();
const currentVersion = await this.detectCurrentVersion();
const prompt = `
Analyze this codebase for migration from Claude Code ${currentVersion} to ${targetVersion}.
Files to analyze:
${files.map(f => `- ${f.path}: ${f.summary}`).join('\n')}
Breaking changes in ${targetVersion}:
${await this.getBreakingChanges(currentVersion, targetVersion)}
Provide:
1. Required code changes
2. Risk assessment
3. Estimated effort
4. Migration order
`;
const analysis = await claude.complete(prompt);
return this.parseMigrationAnalysis(analysis);
}
async generateMigrationScript(
analysis: MigrationAnalysis
): Promise<MigrationScript> {
const script = new MigrationScript();
// Generate automated fixes
for (const change of analysis.requiredChanges) {
if (change.canAutomate) {
const transform = await this.generateTransform(change);
script.addTransform(transform);
} else {
script.addManualStep(change);
}
}
// Add validation steps
script.addValidation(async () => {
const results = await this.runCompatibilityTests();
return results.allPassed;
});
return script;
}
async executeMigration(
script: MigrationScript,
options: MigrationOptions = {}
): Promise<MigrationResult> {
const result = new MigrationResult();
// Create backup if requested
if (options.backup) {
await this.createBackup();
}
// Execute transforms
for (const transform of script.transforms) {
try {
await transform.execute();
result.addSuccess(transform);
} catch (error) {
result.addFailure(transform, error);
if (!options.continueOnError) {
await this.rollback();
throw error;
}
}
}
// Run validations
const valid = await script.validate();
if (!valid && options.requireValidation) {
await this.rollback();
throw new Error('Migration validation failed');
}
return result;
}
}Interactive Migration CLI
class MigrationCLI {
async run() {
console.log('🚀 Claude Code Migration Assistant\n');
// Detect current version
const currentVersion = await this.detectVersion();
console.log(`Current version: ${currentVersion}`);
// Show available versions
const availableVersions = await this.getAvailableVersions();
const targetVersion = await this.selectVersion(availableVersions);
// Analyze migration
console.log('\n📊 Analyzing migration requirements...');
const analysis = await this.analyzeMigration(
currentVersion,
targetVersion
);
// Show migration plan
this.displayMigrationPlan(analysis);
// Confirm migration
const confirmed = await this.confirm(
'Do you want to proceed with the migration?'
);
if (confirmed) {
await this.executeMigration(analysis);
}
}
private async executeMigration(analysis: MigrationAnalysis) {
const steps = analysis.steps.length;
for (let i = 0; i < steps; i++) {
const step = analysis.steps[i];
console.log(`\n[${i + 1}/${steps}] ${step.description}`);
try {
await step.execute();
console.log('✅ Complete');
} catch (error) {
console.error('❌ Failed:', error.message);
const action = await this.select([
'Retry',
'Skip',
'Abort'
]);
if (action === 'Retry') {
i--; // Retry this step
} else if (action === 'Abort') {
throw new Error('Migration aborted');
}
}
}
console.log('\n✨ Migration complete!');
}
}🔒 Backward Compatibility Patterns
Adapter Pattern for API Compatibility
class APICompatibilityAdapter {
async handleRequest(
req: Request,
res: Response,
version: string
) {
// Transform request to latest format
const transformedReq = await this.transformRequest(req, version);
// Process with latest handler
const result = await this.latestHandler(transformedReq);
// Transform response back to requested version format
const transformedRes = await this.transformResponse(
result,
version
);
res.json(transformedRes);
}
private async transformRequest(
req: Request,
fromVersion: string
): Promise<Request> {
const transformers = this.getRequestTransformers(
fromVersion,
'latest'
);
let transformed = req;
for (const transformer of transformers) {
transformed = await transformer(transformed);
}
return transformed;
}
private getRequestTransformers(
from: string,
to: string
): Transformer[] {
const transformers: Transformer[] = [];
// V1 to V2 transformations
if (from === 'v1' && to >= 'v2') {
transformers.push(async (req) => {
// Rename old fields to new format
if (req.body.user_name) {
req.body.username = req.body.user_name;
delete req.body.user_name;
}
return req;
});
}
// V2 to V3 transformations
if (from <= 'v2' && to >= 'v3') {
transformers.push(async (req) => {
// Nested structure changes
if (req.body.email && req.body.name) {
req.body.profile = {
email: req.body.email,
displayName: req.body.name
};
delete req.body.email;
delete req.body.name;
}
return req;
});
}
return transformers;
}
}Compatibility Testing Framework
class CompatibilityTestSuite {
async runTests(versions: string[]): Promise<TestResults> {
const results = new TestResults();
// Test all version combinations
for (const clientVersion of versions) {
for (const serverVersion of versions) {
const compatible = await this.testCompatibility(
clientVersion,
serverVersion
);
results.add(clientVersion, serverVersion, compatible);
}
}
return results;
}
private async testCompatibility(
clientVersion: string,
serverVersion: string
): Promise<CompatibilityResult> {
const client = await this.createClient(clientVersion);
const server = await this.startServer(serverVersion);
const tests = [
this.testBasicOperations(client, server),
this.testDataFormats(client, server),
this.testErrorHandling(client, server),
this.testPerformance(client, server)
];
const results = await Promise.all(tests);
return {
compatible: results.every(r => r.passed),
warnings: results.flatMap(r => r.warnings),
errors: results.flatMap(r => r.errors)
};
}
}🚦 Feature Flag Implementation
Advanced Feature Flag Service
class FeatureFlagService {
private evaluator: RuleEvaluator;
private cache: FeatureFlagCache;
async evaluate(
flag: string,
context: EvaluationContext
): Promise<boolean> {
// Check cache first
const cached = await this.cache.get(flag, context);
if (cached !== undefined) return cached;
// Get flag configuration
const config = await this.getConfig(flag);
if (!config) return false;
// Evaluate rules in order
for (const rule of config.rules) {
if (await this.evaluator.evaluate(rule, context)) {
const result = rule.serve;
await this.cache.set(flag, context, result);
return result;
}
}
// Default value
const result = config.defaultServe || false;
await this.cache.set(flag, context, result);
return result;
}
// Gradual rollout with monitoring
async gradualRollout(
flag: string,
options: RolloutOptions
): Promise<void> {
const monitor = new RolloutMonitor(flag);
for (const stage of options.stages) {
// Update percentage
await this.updatePercentage(flag, stage.percentage);
// Monitor metrics
await monitor.waitForStabilization(stage.duration);
// Check health
const health = await monitor.checkHealth();
if (!health.isHealthy) {
// Rollback
await this.updatePercentage(flag, stage.rollbackTo || 0);
throw new Error(`Rollout failed: ${health.reason}`);
}
// Proceed to next stage
console.log(`✅ Stage ${stage.name} complete at ${stage.percentage}%`);
}
}
}
// Feature flag rules engine
class RuleEvaluator {
async evaluate(
rule: Rule,
context: EvaluationContext
): Promise<boolean> {
switch (rule.type) {
case 'percentage':
return this.evaluatePercentage(rule, context);
case 'segment':
return this.evaluateSegment(rule, context);
case 'datetime':
return this.evaluateDateTime(rule, context);
case 'custom':
return this.evaluateCustom(rule, context);
default:
return false;
}
}
private evaluatePercentage(
rule: PercentageRule,
context: EvaluationContext
): boolean {
const hash = this.hash(
context.userId + rule.salt
);
const bucket = (hash % 100) + 1;
return bucket <= rule.percentage;
}
}📦 Prompt Version Management
Versioned Prompt System
class PromptVersionManager {
private prompts: Map<string, VersionedPrompt> = new Map();
async registerPrompt(
name: string,
version: string,
template: string,
metadata?: PromptMetadata
) {
const prompt = new VersionedPrompt({
name,
version,
template,
metadata,
createdAt: new Date()
});
// Validate prompt
await this.validatePrompt(prompt);
// Store with version key
const key = `${name}:${version}`;
this.prompts.set(key, prompt);
// Update latest pointer
await this.updateLatest(name, version);
}
async getPrompt(
name: string,
version: string = 'latest'
): Promise<VersionedPrompt> {
if (version === 'latest') {
version = await this.getLatestVersion(name);
}
const key = `${name}:${version}`;
const prompt = this.prompts.get(key);
if (!prompt) {
throw new Error(`Prompt not found: ${key}`);
}
return prompt;
}
async renderPrompt(
name: string,
variables: Record<string, any>,
version?: string
): Promise<string> {
const prompt = await this.getPrompt(name, version);
// Render template with variables
return this.templateEngine.render(
prompt.template,
variables
);
}
// A/B test different prompt versions
async selectPromptVersion(
name: string,
context: PromptContext
): Promise<string> {
const experiment = await this.getActiveExperiment(name);
if (!experiment) {
return 'latest';
}
// Use consistent hashing for user assignment
const hash = this.hash(context.userId + experiment.id);
const bucket = hash % 100;
let cumulative = 0;
for (const variant of experiment.variants) {
cumulative += variant.percentage;
if (bucket < cumulative) {
return variant.version;
}
}
return experiment.control;
}
// Track prompt performance
async trackPerformance(
name: string,
version: string,
metrics: PromptMetrics
) {
const key = `${name}:${version}`;
await this.metricsStore.record({
promptKey: key,
timestamp: new Date(),
...metrics
});
// Check if we should update the default version
if (await this.shouldPromote(name, version)) {
await this.promoteVersion(name, version);
}
}
}📊 Monitoring and Analytics
Version Usage Analytics
class VersionAnalytics {
async collectMetrics(): Promise<VersionMetrics> {
const metrics = new VersionMetrics();
// API version distribution
metrics.apiVersions = await this.getAPIVersionDistribution();
// SDK version distribution
metrics.sdkVersions = await this.getSDKVersionDistribution();
// Deprecation usage
metrics.deprecationUsage = await this.getDeprecationUsage();
// Migration progress
metrics.migrationProgress = await this.getMigrationProgress();
// Performance by version
metrics.performance = await this.getPerformanceByVersion();
return metrics;
}
async generateReport(): Promise<VersionReport> {
const metrics = await this.collectMetrics();
return {
summary: {
totalUsers: metrics.getTotalUsers(),
versionsInUse: metrics.getActiveVersions(),
deprecationAdoption: metrics.getDeprecationAdoptionRate(),
migrationCompletion: metrics.getMigrationCompletionRate()
},
recommendations: await this.generateRecommendations(metrics),
alerts: await this.checkAlerts(metrics)
};
}
private async generateRecommendations(
metrics: VersionMetrics
): Promise<Recommendation[]> {
const recommendations: Recommendation[] = [];
// Check for old version usage
const oldVersionUsers = metrics.getUsersOnOldVersions();
if (oldVersionUsers.length > 0) {
recommendations.push({
type: 'upgrade',
priority: 'high',
message: `${oldVersionUsers.length} users on deprecated versions`,
action: 'Send targeted migration guides'
});
}
// Check migration velocity
const migrationVelocity = metrics.getMigrationVelocity();
if (migrationVelocity < 0.1) {
recommendations.push({
type: 'migration',
priority: 'medium',
message: 'Slow migration adoption',
action: 'Improve migration tooling or documentation'
});
}
return recommendations;
}
}🎯 Best Practices Summary
1. Version Planning
- Use semantic versioning strictly
- Plan breaking changes well in advance
- Maintain detailed changelogs
2. Communication
- Announce deprecations early (6-12 months)
- Provide clear migration guides
- Use multiple communication channels
3. Testing
- Comprehensive compatibility test suites
- Automated regression testing
- Performance benchmarking between versions
4. Tooling
- Automated migration assistants
- Feature flag management
- Version analytics and monitoring
5. Documentation
- Version-specific documentation
- Migration guides with code examples
- API changelog with examples
📚 Resources
🧭 Navigation
← Back to Version Migration | Feature Flags → | Migration CLI →