技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
architectural-modernization【数据与研究】Upgrades legacy technology stacks — framework migrations, database transitions, infrastructure modernization, and API evolution — using incremental strategies that preserve business continuity while eliminating technical debt.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: architectural-modernization description: Upgrades legacy technology stacks — framework migrations, database transitions, infrastructure modernization, and API evolution — using incremental strategies that preserve business continuity while eliminating technical debt. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: legacy system modernization, framework migration, database migration, technology upgrade, API evolution, on-prem to cloud, Flask to FastAPI, how do i upgrade a legacy system archetypes: - tactical - strategic anti_triggers: - brainstorming - vague ideation - code golf - over-engineering response_profile: verbosity: medium directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types: [code, guidance, config, do-dont] related-skills: monolith-refactoring,microservices-architecture,event-driven-architecture,cicd-pipeline-design,database-migrations
Upgrades legacy technology stacks in existing systems — framework migrations, database transitions, infrastructure modernization, and API evolution — using incremental strategies that preserve business continuity while systematically eliminating technical debt. When loaded, the model designs migration plans that minimize downtime, maintain backward compatibility during transition, and validate correctness at each step through automated testing.
Use this skill when:
Avoid architectural modernization when:
monolith-refactoring and test infrastructure first; migration will fail without a safety netInventory Current Technology Stack — Document every framework version, library dependency, database schema, API endpoint, and external integration point. Use pipdeptree, npm list --depth=0, or equivalent tools to generate a complete dependency graph. Identify which dependencies have known CVEs and what EOL dates apply. Checkpoint: Every production deployment must have a documented inventory file (e.g., tech-inventory.yaml) listing all components, versions, license types, and last security audit date.
Select Migration Strategy Based on Risk Profile — Choose one of three strategies:
Checkpoint: The chosen strategy must include a rollback trigger — if error rate exceeds 0.5% or p99 latency increases by more than 2x during migration, automatically revert to the old system.
Build Compatibility Adapters — Create thin adapter layers that translate between the old and new technology contracts. For framework migrations, write WSGI/ASGI adapters, middleware wrappers, and serializer/deserializer bridges. For database migrations, create a schema mapping layer that translates queries. These adapters isolate application logic from technology-specific concerns. Checkpoint: Each adapter must have unit tests verifying it produces identical output to the old technology for the same input.
Implement New System Incrementally — Build new components one at a time behind the strangler proxy or feature flag. Each component must be independently deployable, testable, and rollable back. Never rewrite the entire system in parallel — this is the #1 cause of migration failures. Replace components in order of decreasing coupling (start with the most isolated modules first). Checkpoint: Every new component must pass the full integration test suite before being enabled in production behind a feature flag.
Execute Data Migration with Zero Downtime — If the technology change involves data storage, implement dual-write during an initial sync phase. Use CDC (Change Data Capture) tools like Debezium or logical replication to keep both stores synchronized during the transition window. Perform a full data replay, verify checksums match, then switch reads to the new store. Checkpoint: Run a 24-hour parity check comparing read results from old and new stores before cutover — any discrepancy means do not proceed.
Monitor, Validate, Decommission — After migration completes, monitor error rates, latency percentiles, and business metrics for at least one full business cycle (e.g., one week including peak traffic day). Once confidence is established, disable the old system behind the feature flag. Do not decommission the old infrastructure until 30 days after cutover with zero incidents recorded. Checkpoint: Archive all old configuration files, database schemas, and deployment scripts in version control before removing infrastructure to preserve audit trail.
When upgrading a web framework (e.g., Flask → FastAPI), the compatibility layer ensures that existing clients continue receiving responses in the same format while internal code transitions to the new framework. This pattern uses an adapter class that translates between frameworks without modifying business logic.
"""framework_adapter.py — Adapter for gradual Flask-to-FastAPI migration."""
from __future__ import annotations
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Callable, Protocol
logger = logging.getLogger(__name__)
# ─── Old framework interface (Flask-style) ────────────────────────────
class FlaskRequest(Protocol):
"""Simulated Flask request protocol for the old framework."""
method: str
path: str
headers: dict[str, str]
json_body: dict | None
class FlaskResponse(Protocol):
"""Simulated Flask response protocol."""
status_code: int
body: Any
headers: dict[str, str]
# ─── New framework interface (FastAPI-style) ────────────────────────
@dataclass
class APIRequest:
"""New framework request representation."""
method: str
path: str
headers: dict[str, str]
json_body: dict | None = None
@dataclass
class APIResponse:
"""New framework response representation."""
status_code: int
body: dict | list | str
headers: dict[str, str] = field(default_factory=dict)
# ─── Business Logic (framework-agnostic) ─────────────────────────────
class OrderService(ABC):
"""Business logic interface — works regardless of web framework."""
@abstractmethod
def get_order(self, order_id: str) -> dict[str, Any]: ...
@abstractmethod
def create_order(self, customer_id: str, items: list[dict]) -> dict[str, Any]: ...
# ─── Compatibility Adapter: Flask → FastAPI ───────────────────────────
class FrameworkAdapter(ABC):
"""Base adapter for framework migration. Subclasses handle translation."""
@abstractmethod
def adapt_request(self, raw_request: Any) -> APIRequest: ...
@abstractmethod
def adapt_response(self, result: dict) -> dict[str, Any]: ...
class FlaskToFastAPIAdapter(FrameworkAdapter):
"""Translates old Flask requests/responses to new FastAPI equivalents.
This adapter sits between the existing Flask application and the
new FastAPI business logic layer during the migration window.
"""
def __init__(self, order_service: OrderService) -> None:
self._service = order_service
def adapt_request(self, raw_request: Any) -> APIRequest:
"""Convert Flask request to unified APIRequest."""
json_body = None
if hasattr(raw_request, 'get_json') and raw_request.get_json(silent=True):
json_body = raw_request.get_json()
return APIRequest(
method=raw_request.method,
path=raw_request.path,
headers=dict(raw_request.headers),
json_body=json_body,
)
def adapt_response(self, result: dict) -> dict[str, Any]:
"""Convert new framework response back to old format."""
return {
"status_code": 200,
"body": result,
"headers": {"Content-Type": "application/json"},
}
# ─── ❌ BAD: Rewrite all controllers at once during migration ────────
class BadMigrationController:
"""Anti-pattern: replacing the entire controller breaks existing routes.
This approach deletes old Flask routes and creates new FastAPI ones,
causing a brief period where no route handler exists — downtime.
"""
def __init__(self, app):
# Deletes ALL existing routes — clients get 404s immediately
app.router.routes = []
self._new_service = NewFastAPIService()
async def handle_request(self, request):
# No backward compatibility — old API format breaks
return await self._new_service.process(request.json())
# ─── ✅ GOOD: Adapter preserves both old and new contracts ───────────
class MigrationController:
"""Controls requests during Flask-to-FastAPI transition.
Routes old-format requests through the adapter layer, allowing
gradual enablement of new controllers via feature flags.
"""
def __init__(self, adapter: FrameworkAdapter, order_service: OrderService) -> None:
self._adapter = adapter
self._service = order_service
self._new_api_enabled = False # Controlled by feature flag
async def handle_request(self, raw_request: FlaskRequest) -> dict[str, Any]:
"""Unified request handler supporting both old and new APIs.
When the feature flag is off (default), uses the adapter to maintain
backward compatibility with the old Flask contract.
When enabled, delegates directly to the new FastAPI service.
"""
api_request = self._adapter.adapt_request(raw_request)
if self._new_api_enabled:
# New path — direct to FastAPI service
logger.info("Routing to new FastAPI implementation")
result = await self._service.get_order(api_request.path.split('/')[-1])
return self._adapter.adapt_response(result)
else:
# Old path — through adapter for compatibility
logger.debug("Routing through Flask adapter (legacy)")
result = self._service.get_order(raw_request.path.split('/')[-1])
return self._adapter.adapt_response(result)
# ─── Feature Flag Controller for Gradual Rollout ─────────────────────
class MigrationFeatureFlag:
"""Manages feature toggles during technology migration."""
def __init__(self, flag_name: str = "new_framework_enabled") -> None:
self._flag_name = flag_name
self._enabled = False # Start with old system
def toggle(self) -> bool:
"""Flip the migration feature flag. Call during planned maintenance window."""
self._enabled = not self._enabled
logger.warning(f"Migration flag toggled to {self._enabled}")
return self._enabled
def should_use_new(self) -> bool:
"""Check if requests should route through new technology."""
return self._enabled
When migrating between database systems (e.g., MongoDB → PostgreSQL), dual-write ensures data consistency during transition by writing to both stores simultaneously. A parity checker verifies that reads produce identical results before switching traffic.
"""database_migration.py — Dual-write migration with parity verification."""
from __future__ import annotations
import logging
import time
from abc import ABC, abstractmethod
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any, Protocol
logger = logging.getLogger(__name__)
# ─── Database Port Abstraction ────────────────────────────────────────
class DatabaseConnection(Protocol):
"""Abstract database connection protocol."""
@abstractmethod
def save(self, collection: str, document: dict[str, Any]) -> str: ...
@abstractmethod
def find_by_id(self, collection: str, doc_id: str) -> dict[str, Any] | None: ...
@abstractmethod
def find_all(self, collection: str, limit: int = 100) -> list[dict[str, Any]]: ...
@dataclass
class MigrationState:
"""Tracks migration progress for a specific table/collection."""
table_name: str
writes_in_flight: int = 0
parity_check_failures: int = 0
total_records_compared: int = 0
last_parity_check_time: float | None = None
def record_success(self) -> None:
self.total_records_compared += 1
if time.time() - (self.last_parity_check_time or 0) > 300:
logger.info(f"Parity check passed for {self.table_name} "
f"({self.total_records_compared} records compared)")
def record_failure(self) -> None:
self.parity_check_failures += 1
if self.parity_check_failures > 3:
logger.error(f"Parity check failed {self.parity_check_failures} times "
f"for {self.table_name} — pausing migration")
# ─── Dual-Write Service ───────────────────────────────────────────────
class DualWriteService:
"""Writes to both old and new databases during migration.
Guarantees that every write reaches at least one store. If the new
store fails, a warning is logged but the operation proceeds with only
the old store to avoid downtime.
"""
def __init__(
self,
old_db: DatabaseConnection,
new_db: DatabaseConnection,
migration_enabled: bool = True,
) -> None:
self._old_db = old_db
self._new_db = new_db
self._enabled = migration_enabled
self._states: dict[str, MigrationState] = {}
def _get_state(self, collection: str) -> MigrationState:
if collection not in self._states:
self._states[collection] = MigrationState(table_name=collection)
return self._states[collection]
def save(self, collection: str, document: dict[str, Any]) -> str:
"""Write to both databases with fallback on new-db failure.
Always writes to old_db (source of truth during migration).
Writes to new_db in best-effort mode — failures are logged but
do not block the operation.
Args:
collection: The collection or table name
document: The data to persist
Returns:
The document ID from the old database (authoritative during migration)
"""
doc_id = self._old_db.save(collection, document)
if not self._enabled:
return doc_id
state = self._get_state(collection)
try:
# Map MongoDB-style _id to SQL-compatible ID if needed
if "_id" in document:
safe_doc = {k: v for k, v in document.items() if k != "_id"}
else:
safe_doc = document.copy()
self._new_db.save(collection, safe_doc)
state.record_success()
except Exception as exc: # noqa: BLE001
logger.error(f"New database write failed for {collection}: {exc}")
state.record_failure()
return doc_id
def find_by_id(self, collection: str, doc_id: str) -> dict[str, Any] | None:
"""Read from new store if available, fallback to old store.
During migration reads are gradually switched: first try the new
database, then fall back to the old one for records that haven't
been migrated yet.
"""
try:
result = self._new_db.find_by_id(collection, doc_id)
if result is not None:
return result
except Exception as exc: # noqa: BLE001
logger.debug(f"New DB read failed for {collection}/{doc_id}: {exc}")
return self._old_db.find_by_id(collection, doc_id)
# ─── Parity Verification Service ──────────────────────────────────────
class ParityChecker:
"""Verifies that old and new databases produce identical results.
Runs periodic comparison checks by reading the same records from both
stores and comparing field-by-field. Reports any discrepancies for
manual review before cutover.
"""
def __init__(self, old_db: DatabaseConnection, new_db: DatabaseConnection) -> None:
self._old_db = old_db
self._new_db = new_db
def check_record(
self,
collection: str,
doc_id: str,
tolerance_ms: int = 1000,
) -> tuple[bool, str]:
"""Compare a single record between old and new stores.
Returns (is_match, details).
Args:
collection: Collection or table name
doc_id: Document ID to compare
tolerance_ms: Time-based fields within this window are considered equal
Returns:
Tuple of (match boolean, human-readable details string)
"""
old_record = self._old_db.find_by_id(collection, doc_id)
new_record = self._new_db.find_by_id(collection, doc_id)
if old_record is None and new_record is None:
return False, "Record missing from both stores"
if old_record is None:
return False, f"Record exists in old store but not new (id={doc_id})"
if new_record is None:
return False, f"Record exists in new store but not old (id={doc_id})"
# Normalize timestamps for comparison
old_clean = {k: v for k, v in old_record.items()
if not k.endswith("_at") and not k.endswith("_ts")}
new_clean = {k: v for k, v in new_record.items()
if not k.endswith("_at") and not k.endswith("_ts")}
if old_clean == new_clean:
return True, "Records match"
# Find specific field mismatches
mismatches = []
for key in set(old_clean.keys()) | set(new_clean.keys()):
old_val = old_clean.get(key)
new_val = new_clean.get(key)
if old_val != new_val:
mismatches.append(f" {key}: old={old_val} vs new={new_val}")
return False, f"Mismatch on fields:\n" + "\n".join(mismatches)
# ─── ❌ BAD: Direct cutover without parity check ─────────────────────
class BadDatabaseCutover:
"""Anti-pattern: switches reads to new database immediately.
This causes data loss for any records not yet migrated, and provides
no way to detect or recover from schema mapping errors.
"""
def __init__(self, old_db: DatabaseConnection, new_db: DatabaseConnection):
self._old = old_db
self._new = new_db
self._switched = False
def cutover(self) -> None:
# No verification — if mapping is wrong, data is silently corrupted
self._switched = True
def find_by_id(self, collection: str, doc_id: str):
if self._switched:
return self._new.find_by_id(collection, doc_id) # Could return None!
return self._old.find_by_id(collection, doc_id)
# ─── ✅ GOOD: Parity-verified cutover with rollback ──────────────────
class SafeDatabaseCutover:
"""Safe cutover using parity verification and automatic rollback.
Requires 99.9% record match rate before switching reads. Maintains
dual-write capability for post-cutover reconciliation.
"""
def __init__(
self,
old_db: DatabaseConnection,
new_db: DatabaseConnection,
parity_checker: ParityChecker | None = None,
required_match_rate: float = 0.999,
) -> None:
self._old = old_db
self._new = new_db
self._checker = parity_checker or ParityChecker(old_db, new_db)
self._required_match_rate = required_match_rate
self._readers_switched = False
def verify_and_switch(
self,
sample_size: int = 10000,
collection: str = "orders",
) -> bool:
"""Run parity check on random samples and switch reads if match rate is sufficient.
Args:
sample_size: Number of records to compare
collection: Collection or table to verify
Returns:
True if cutover should proceed, False if more reconciliation needed
"""
matches = 0
total_checked = min(sample_size, 500) # Practical limit per check
for i in range(total_checked):
doc_id = f"record-{i}"
is_match, _ = self._checker.check_record(collection, doc_id)
if is_match:
matches += 1
match_rate = matches / total_checked if total_checked > 0 else 0
logger.info(f"Parity check: {match_rate:.4%} match rate "
f"({matches}/{total_checked}) — threshold: {self._required_match_rate:.4%}")
if match_rate >= self._required_match_rate:
self._readers_switched = True
logger.info("✅ CUTOVER APPROVED — switching readers to new database")
return True
logger.warning("❌ CUTOVER DENIED — match rate below threshold")
return False
def find_by_id(self, collection: str, doc_id: str) -> dict | None:
"""Read from the appropriate store based on cutover status."""
if self._readers_switched:
# Primary reads go to new DB; fallback to old for reconciliation
return self._new.find_by_id(collection, doc_id) or \
self._old.find_by_id(collection, doc_id)
return self._old.find_by_id(collection, doc_id)
def rollback(self) -> None:
"""Instantly revert reads to the old database."""
self._readers_switched = False
logger.warning("🔄 ROLLBACK — readers reverted to old database")
When replacing an API technology (SOAP → REST, or REST → GraphQL), maintain the existing contract so clients don't need to update simultaneously. The adapter translates between old and new formats transparently.
"""api_evolution.py — SOAP-to-REST migration with contract preservation."""
from __future__ import annotations
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__)
@dataclass
class SOAPEnvelope:
"""Represents a SOAP XML envelope structure."""
action: str # e.g., "GetCustomer", "CreateOrder"
body: dict[str, Any]
headers: dict[str, str] = field(default_factory=dict)
@dataclass
class RESTRequest:
"""Represents an HTTP REST request."""
method: str # GET, POST, PUT, DELETE
path: str # e.g., /api/v2/customers/{id}
headers: dict[str, str] = field(default_factory=dict)
body: dict | None = None
@dataclass
class SOAPResponse:
"""SOAP XML response envelope."""
action: str
result: dict[str, Any]
fault_code: str | None = None
fault_string: str | None = None
@dataclass
class RESTResponse:
"""HTTP REST response."""
status_code: int
body: dict | list
headers: dict[str, str] = field(default_factory=lambda: {"Content-Type": "application/json"})
# ─── Service Interface (technology-agnostic) ──────────────────────────
class CustomerService(ABC):
"""Core business logic — independent of SOAP or REST transport."""
@abstractmethod
def get_customer(self, customer_id: str) -> dict[str, Any]: ...
@abstractmethod
def create_customer(self, name: str, email: str) -> dict[str, Any]: ...
# ─── SOAP → REST Contract Adapter ─────────────────────────────────────
class SOAPtoRESTAdapter(ABC):
"""Base adapter for API technology migration.
Translates between the legacy SOAP XML contract and new RESTful
HTTP requests/responses while preserving business semantics.
"""
@abstractmethod
def parse_request(self, soap: SOAPEnvelope) -> RESTRequest: ...
@abstractmethod
def format_response(self, result: dict) -> RESTResponse: ...
class LegacyToModernAdapter(SOAPtoRESTAdapter):
"""Translates SOAP actions to REST endpoints and vice versa.
Maps SOAP action names to HTTP methods and path segments:
- GetCustomer → GET /api/v2/customers/{id}
- CreateOrder → POST /api/v2/orders
- CancelPayment → DELETE /api/v2/payments/{id}
"""
# Mapping table: SOAP action → REST method + path template
ACTION_MAP: dict[str, tuple[str, str]] = {
"GetCustomer": ("GET", "/api/v2/customers"),
"GetOrder": ("GET", "/api/v2/orders"),
"CreateCustomer": ("POST", "/api/v2/customers"),
"UpdateCustomer": ("PUT", "/api/v2/customers"),
"CancelPayment": ("DELETE", "/api/v2/payments"),
}
def parse_request(self, soap: SOAPEnvelope) -> RESTRequest:
"""Convert SOAP action to equivalent REST request."""
if soap.action not in self.ACTION_MAP:
raise ValueError(f"Unknown SOAP action: {soap.action}")
method, path_template = self.ACTION_MAP[soap.action]
# Extract ID from SOAP body if present
customer_id = soap.body.get("CustomerId") or soap.body.get("ID")
order_id = soap.body.get("OrderId") or soap.body.get("ID")
# Build REST path with extracted IDs
if customer_id:
path = f"{path_template}/{customer_id}"
elif order_id:
path = f"{path_template}/{order_id}"
else:
path = path_template
return RESTRequest(
method=method,
path=path,
headers={"Accept": "application/json"},
body=soap.body if method in ("POST", "PUT") else None,
)
def format_response(self, result: dict) -> RESTResponse:
"""Convert service result to REST response."""
status_code = 201 if "created" in result.get("status", "").lower() else 200
return RESTResponse(
status_code=status_code,
body=result,
)
# ─── Contract Versioning Manager ─────────────────────────────────────
class APIVersionManager:
"""Manages API version transitions during migration.
Tracks which clients are still using the old contract and which have
migrated to the new one, enabling targeted communication and phased
deprecation of legacy endpoints.
"""
def __init__(self) -> None:
self._client_versions: dict[str, str] = {} # client_id → API version
def register_client(self, client_id: str, api_version: str) -> None:
"""Register which API version a client is using."""
self._client_versions[client_id] = api_version
logger.info(f"Client {client_id} registered with API v{api_version}")
def get_client_version(self, client_id: str) -> str:
"""Get the API version for a specific client."""
return self._client_versions.get(client_id, "unknown")
def is_deprecated(self, api_version: str) -> bool:
"""Check if an API version is marked for deprecation."""
deprecated_versions = ["v1", "soap"]
return api_version in deprecated_versions
def get_migration_status(self) -> dict[str, Any]:
"""Get summary of migration progress by client."""
total = len(self._client_versions)
if total == 0:
return {"total_clients": 0}
old_clients = sum(1 for v in self._client_versions.values()
if self.is_deprecated(v))
return {
"total_clients": total,
"migrated_clients": total - old_clients,
"legacy_clients": old_clients,
"migration_percent": f"{((total - old_clients) / total) * 100:.1f}%",
}
# ─── ❌ BAD: Remove old API before new one is ready ──────────────────
class BadAPIMigration:
"""Anti-pattern: deletes the SOAP endpoint immediately after deploying REST.
Any client that hasn't migrated yet gets a 404 — no transition path,
no contract preservation. This causes customer-facing incidents.
"""
def __init__(self, app):
# Deletes old routes — clients breaking immediately
self._old_routes_deleted = True
def handle_request(self, method: str, path: str, body: dict | None):
# No fallback to old system — if new API has bugs, everything breaks
raise RuntimeError("Old SOAP endpoint removed — upgrade required")
# ─── ✅ GOOD: Versioned gateway with automatic routing ────────────────
class APIMigrationGateway:
"""Routes requests to old or new API based on client version.
Acts as the single entry point during migration, automatically
directing each request to the appropriate backend based on the
client's registered API version and capability headers.
"""
def __init__(
self,
soap_adapter: LegacyToRESTAdapter | None = None,
version_manager: APIVersionManager | None = None,
) -> None:
self._adapter = soap_adapter or LegacyToRESTAdapter()
self._version_mgr = version_manager or APIVersionManager()
def route_request(self, raw_request: Any) -> RESTResponse:
"""Route each request to the appropriate backend.
If client uses deprecated SOAP, adapter translates it.
If client uses new REST, passes through directly.
Unknown clients default to the most compatible (old) endpoint.
"""
# Determine client identity and version from headers or auth token
client_id = raw_request.headers.get("X-Client-ID", "anonymous")
api_version = self._version_mgr.get_client_version(client_id)
if self._version_mgr.is_deprecated(api_version):
# Translate SOAP to REST, process through new service
logger.info(f"Routing SOAP client {client_id} through adapter")
soap_env = self._parse_soap(raw_request.body) # type: ignore[union-attr]
rest_req = self._adapter.parse_request(soap_env)
result = self._process_with_new_service(rest_req) # type: ignore[name-defined]
return self._adapter.format_response(result)
else:
# Direct pass-through to new REST service
logger.debug(f"Routing REST client {client_id} directly")
return self._handle_rest_request(raw_request) # type: ignore[name-defined]
def _parse_soap(self, raw_body: Any) -> SOAPEnvelope:
"""Parse incoming SOAP XML envelope (implementation-specific)."""
import xml.etree.ElementTree as ET
root = ET.fromstring(raw_body) # type: ignore[arg-type]
body = root.find(".//Body") or root.find(".*//Body") # type: ignore[union-attr]
return SOAPEnvelope(
action="GetCustomer", # Simplified for example
body={"CustomerId": "12345"},
)
def _process_with_new_service(self, request: RESTRequest) -> dict:
"""Process request through new service implementation."""
return {"customer_id": "12345", "name": "John Doe"} # type: ignore[return-value]
def _handle_rest_request(self, raw_request: Any) -> RESTResponse:
"""Handle REST request directly (no translation needed)."""
return RESTResponse(status_code=200, body={"status": "ok"})
| Scenario | Recommended Strategy | Why |
|---|---|---|
| Customer-facing API with SLA requirements | Parallel Run | Zero downtime; feature flag controls traffic split |
| Database migration with schema changes | Dual-Write + Parity Check | Guarantees data consistency before switching reads |
| Internal tool upgrade with low usage | Strangler Proxy | Minimal overhead; replace components at your pace |
| Framework upgrade with few external clients | Phased Feature Flag | Enable new framework behind flag for each endpoint |
| Full infrastructure migration (on-prem → cloud) | Hybrid: Strangler + Parallel | Route traffic incrementally while maintaining parity monitoring |
| Skill | Purpose |
|---|---|
monolith-refactoring | Structural decomposition of legacy codebases — use this BEFORE technology migration to ensure the code is cleanly modularized for incremental upgrades |
microservices-architecture | Service decomposition patterns — after modernizing the framework, apply these patterns to extract independent services from the upgraded application |
database-migrations | Database schema change management (Alembic, Flyway) — complements this skill's dual-write and parity verification strategies for zero-downtime data migrations |
cicd-pipeline-design | CI/CD pipeline design — provides the deployment infrastructure needed to support blue-green or canary releases during migration rollouts |
Authoritative documentation links for architecture modernization practices. The model follows markdown links at load time to resolve external references and inline content.