The multi-cloud strategy decision record: why the workload placement model you chose determines your provider lock-in surface and your cross-cloud operational complexity ceiling

Published 2026-07-14 · WhyChose

Multi-cloud strategy decisions are made in the infrastructure selection session when a team is building their first production environment and the engineering lead or CTO is thinking about cloud vendor risk. The AI session that produces the multi-cloud strategy is strategic in scope and architectural in output: it evaluates AWS, GCP, and Azure for their managed service maturity, geographic availability, pricing model, and ecosystem compatibility with the team's existing toolchain; it considers the team's skills and familiarity with each platform; it decides on a primary cloud provider and defines what role, if any, a second cloud provider plays. The session produces a documented infrastructure choice: AWS as primary, with GCP for analytics and ML because BigQuery and Vertex AI are genuinely superior for the team's specific workload characteristics; or AWS only with a commitment to use Terraform so that cloud-neutral infrastructure code does not prevent a future migration. The session is correct and the reasoning is sound. The team deploys their first production workloads, the infrastructure is cost-efficient and well-managed, and the engineering decisions made in the founding infrastructure session remain operationally sound for the following twelve to eighteen months.

What the AI session does not produce is the multi-cloud strategy's second half. The session answers "which cloud do we use?" and produces the founding infrastructure decision. It does not ask: what is the workload placement policy — which workload classes are assigned to which cloud, what portability requirement applies to each class, and what is the decision process for placing new services so that the multi-cloud boundary does not become a tightly-coupled dependency that prevents either cloud from functioning independently? What is the provider lock-in inventory — which cloud-specific managed services are in use, what is the equivalent on the alternative provider, and what is the estimated migration effort for each, so that the team's actual lock-in surface is visible and the decision to accept each lock-in is explicit rather than accumulated invisibly over eighteen months? What are the portable abstraction requirements — which service interfaces must use cloud-neutral APIs to preserve portability, which may use native cloud APIs because the migration cost is acceptable, and what is the operational cost of maintaining the abstraction layer versus the cost of accepting provider lock-in? What is the cross-cloud operational model — how is access control managed across two IAM systems, how are secrets federated or replicated between two secrets management services, how is observability data correlated across two sets of cloud-native metrics and logs during a cross-cloud incident, and what is the incident response procedure when a failure spans providers? Each of these questions has an answer that is not derivable from "we use AWS as primary and GCP for analytics" as the team frames the cloud strategy choice. Each answer determines whether multi-cloud is a genuine availability architecture with a tested failover capability or a two-provider billing relationship with no failover capability; whether the lock-in surface grows silently from two cloud-specific services to twenty-three over eighteen months without a mechanism to surface the accumulation; whether "we use Terraform" means infrastructure-as-code hygiene or multi-cloud portability — a conflation that produces architecture decisions whose operational consequences become visible only when a cloud outage reveals that the team cannot fail over the workloads that depend on the affected cloud's specific managed services. The answers exist in the AI sessions. They are the commitments behind the multi-cloud model choice. They are almost never written down.

Two ways multi-cloud strategy decisions produce the wrong outcome at scale

Multi-cloud as two-provider billing with no failover capability

A developer tools startup launches on AWS in month one with a standard managed service architecture: the API runs on ECS Fargate, the database is Aurora PostgreSQL Serverless v2, the job queue is SQS, the cache is ElastiCache for Redis, the file storage is S3, the CDN is CloudFront, and the observability is CloudWatch. The founding infrastructure session evaluates the trade-off between AWS and GCP and concludes that AWS is the correct primary provider: the team is more familiar with AWS, AWS's managed service maturity for their workload class is higher, and the ECS Fargate cost model is more predictable at the team's expected scale than GCP's Cloud Run equivalent. The session notes, as a future consideration, that BigQuery and Vertex AI are genuinely superior for analytics and ML workloads, and that the team "should evaluate GCP for analytics in the future." This is the right decision, documented with the right reasoning, for a team that does not yet have a meaningful analytics or ML workload to serve.

Over eighteen months, the team builds their analytics pipeline. An engineer who previously worked at a company that used BigQuery proposes that they use BigQuery for their analytics data warehouse — the query performance for their access patterns, the separation of compute and storage, and the native integration with Looker are genuinely better for their use case than the Redshift alternative on AWS. The proposal is reasonable and the decision is made: BigQuery is adopted for analytics, with Pub/Sub receiving events from the application and feeding them into a BigQuery streaming insert pipeline. To connect the AWS application to GCP's Pub/Sub, the team uses a small relay service deployed on Cloud Run that receives events from an SQS queue via long-polling and forwards them to Pub/Sub. The relay was described in the adoption session as a "lightweight bridge" with a one-time setup cost; it becomes a persistent, operational service running in GCP that requires maintenance, monitoring, deployment management, and incident response coverage.

By month eighteen, the team is using twenty-three AWS-specific services (ECS Fargate, ECR, Aurora Serverless v2, SQS, ElastiCache, S3, CloudFront, Route 53, IAM, Secrets Manager, Systems Manager Parameter Store, ALB, VPC, CloudWatch, CloudTrail, Config, Lambda — for the relay coordinator — API Gateway, SNS for notifications, EventBridge for internal event routing, CodeBuild for certain batch jobs, RDS Proxy for connection pooling, and Certificate Manager) and four GCP-specific services (BigQuery, Pub/Sub, Cloud Run, Cloud Logging for the GCP-side services). The two clouds are connected through the SQS-to-Pub/Sub relay and through direct database reads: the analytics pipeline reads from Aurora PostgreSQL directly via a read replica exposed through a cross-cloud VPC peering connection. The CTO, writing the annual infrastructure review, notes that the architecture is "multi-cloud" because it uses two cloud providers. What the review does not document is the dependency graph: the GCP analytics pipeline cannot function if the AWS Aurora read replica is unavailable; the SQS-to-Pub/Sub relay is a single point of failure whose absence prevents events from reaching BigQuery; the Cloud Run relay service, running in a GCP region, is not within the team's primary AWS observability dashboard and was not covered by the on-call rotation until an incident three months earlier revealed that it could fail silently without generating any CloudWatch alarm. The architecture is not multi-cloud in the sense of redundancy or failover — it is multi-cloud in the sense of two-provider coupling, where the failure of either cloud degrades the other cloud's workloads rather than protecting them.

In month twenty-two, AWS us-east-1 experiences a partial outage affecting Aurora Serverless v2's serverless capacity management. Aurora instances in auto-pause state fail to resume on the first two resume attempts, producing a three-minute cold start latency for the first database request after a period of inactivity. The application's Aurora cluster is not in auto-pause — it has a minimum capacity floor configured — but the aurora serverless compute provisioning service that manages capacity scaling is degraded across the region, and the Aurora cluster's capacity does not scale above its minimum during a traffic spike that occurs ninety minutes into the outage. The application performs correctly — reads and writes succeed — but with latency degradation above the application's normal p99 because the cluster is operating at minimum capacity during elevated load. The more significant impact is the GCP analytics pipeline: the SQS-to-Pub/Sub relay uses a long-polling loop that reads from SQS and then performs a synchronous read from the Aurora read replica to enrich events with user segment information before forwarding to Pub/Sub. The Aurora read replica, under the same capacity degradation as the primary cluster, responds slowly. The relay's enrichment step times out on 23% of events, producing a dead-letter queue accumulation in SQS and a data gap in the BigQuery analytics tables for the duration of the outage. The on-call engineer receives the analytics data quality alert but cannot immediately identify the cause because the relay's logs are in GCP Cloud Logging, the SQS dead-letter queue metrics are in AWS CloudWatch, and the Aurora connection metrics are in RDS CloudWatch — three separate observability surfaces, none of which the on-call runbook instructs the engineer to check in sequence. The outage post-mortem identifies that the architecture produces cross-cloud failure propagation in the direction that was supposed to be the "benefit" of multi-cloud: GCP analytics is degraded because of an AWS Aurora capacity event, which is the opposite of the intuitive expectation that using two clouds provides isolation. The post-mortem also identifies that the workload placement policy was never written: there is no document that specifies which workloads must be isolated from AWS failures, whether the analytics pipeline is in that category, and if so, what architecture achieves that isolation. The policy that would have prevented the cross-cloud dependency from being built is the document that the founding infrastructure session could have produced in the same session that decided to use BigQuery for analytics.

Cloud neutrality as a requirement that was never specified

A fintech startup builds its first production infrastructure on AWS in months one through six: the payment processing API runs on ECS Fargate, the database is RDS PostgreSQL, the message queue is SQS, the monitoring is CloudWatch with PagerDuty integration, the file storage is S3, and the background job processor uses Lambda. The infrastructure selection session correctly evaluates that AWS's managed services for financial applications — RDS PostgreSQL with automated backups and point-in-time recovery, SQS with dead-letter queues for reliable message processing, Lambda with VPC integration for secure background processing — provide the correct combination of reliability, compliance capability, and operational maturity for the team's use case. The session does not address multi-cloud because the team's first-year focus is building the product, not hedging infrastructure risk.

In month twelve, the team begins its first enterprise sales conversations. The first three enterprise prospects — mid-market financial services companies — each include a line in their vendor questionnaire asking about "multi-cloud availability" or "cloud provider redundancy." The CTO, reading the questionnaires, interprets these requirements as requiring the ability to run the application on two cloud providers simultaneously — an active-active or active-passive multi-cloud architecture. An engineering session is convened to evaluate what it would take to achieve this. The evaluation identifies that five current AWS services are blocking multi-cloud portability: RDS PostgreSQL (AWS-managed; GCP equivalent is Cloud SQL, migration is moderate effort), SQS (AWS-specific queue API; GCP equivalent is Pub/Sub, which has different delivery semantics; migration is moderate effort plus application code changes), Lambda (AWS-specific; GCP equivalent is Cloud Functions or Cloud Run, migration is moderate effort), CloudWatch with PagerDuty (AWS-specific metrics; GCP equivalent is Cloud Monitoring, migration is moderate effort), and Secrets Manager (AWS-specific; GCP equivalent is Secret Manager, migration is trivial). The CTO decides to migrate away from all five AWS-specific managed services to cloud-neutral equivalents: RDS PostgreSQL to self-managed PostgreSQL on EC2 with replication to GCP Compute Engine, SQS to RabbitMQ deployed on EC2 and GCE, Lambda to Kubernetes jobs on EKS, CloudWatch to a self-managed Prometheus and Grafana stack, and Secrets Manager to HashiCorp Vault. The migration is scoped at four months and approved.

The migration takes four months and twenty-four hundred engineering hours across a team of eight. It replaces five working managed services with five self-managed equivalents, each of which now requires dedicated operational engineering: the self-managed PostgreSQL cluster requires a DBA-level operational process for backups, point-in-time recovery testing, replication lag monitoring, and failover; the RabbitMQ deployment requires operational knowledge that no current team member has at production depth; the Kubernetes job migration requires rearchitecting the Lambda functions' trigger model for Kubernetes CronJob semantics; the Prometheus and Grafana stack requires maintenance of the stack itself alongside the infrastructure it monitors. The team completes the migration and now operates a cloud-neutral architecture that could, in principle, be deployed on any cloud provider that runs Kubernetes, PostgreSQL, RabbitMQ, Prometheus, and HashiCorp Vault. In month seventeen — three months after the migration completes — the enterprise prospect whose questionnaire triggered the migration renews their evaluation. The CTO follows up on the multi-cloud question. The prospect's vendor security manager clarifies: their requirement is for a recovery time objective of under four hours in the event of a regional outage, which in their framework means multi-region deployment on a single provider — they are not requiring the vendor to maintain active deployments on two cloud providers simultaneously. "Multi-cloud" in their questionnaire template means "resilient to regional failures," not "running on AWS and GCP." The other two prospects have the same clarification when the CTO asks: two of them require multi-region deployment within AWS; one of them has no specific technical requirement and used "multi-cloud" because it was in their standard questionnaire template. The twenty-four hundred engineering hours and four months of migration work were not required to satisfy the enterprise requirements they were motivated by. The self-managed infrastructure that replaced the managed services now costs four hundred engineering hours per quarter in operational maintenance that the managed services did not require. The document that would have prevented the migration — a clarification of what "multi-cloud" means in each prospect's questionnaire, compared against the team's current architecture and what each interpretation would require — was never written because the founding infrastructure session did not address multi-cloud strategy, and there was no subsequent session that established what multi-cloud meant as an organizational commitment before the migration session that treated it as a given requirement.

Three structural properties that multi-cloud strategy decisions determine

The workload placement model and the provider lock-in surface

The provider lock-in surface is not determined by the cloud provider choice — it is determined by the inventory of cloud-specific managed services that the team adopts over time, and whether that adoption is tracked with explicit migration cost estimates or allowed to accumulate without documentation. A workload running on EC2 with PostgreSQL is structurally portable: EC2 is Xen or KVM virtualization with Linux, which is available from every major cloud provider as IaaS compute; PostgreSQL is open-source with direct equivalents as managed services (Cloud SQL, Azure Database for PostgreSQL) or as self-managed installations on any cloud. A workload running on Aurora Serverless v2 with DynamoDB global tables and Lambda@Edge is structurally bound to AWS: Aurora Serverless's auto-pause, auto-resume, and serverless capacity management are proprietary; DynamoDB's query API (GetItem, BatchGetItem, Query with KeyConditionExpression, Scan with FilterExpression), its data model (partition key, sort key, secondary indexes with projection), and its change data capture via Streams have no direct equivalent on any other provider; Lambda@Edge's deployment model (Lambda function deployed to CloudFront edge nodes, triggered on viewer-request and origin-request events with a 128 MB memory limit and 5-second timeout) is specific to CloudFront's architecture and has no equivalent on GCP's CDN or Azure Front Door. The migration cost from DynamoDB to Firestore (GCP's nearest equivalent for document-oriented workloads) is not a database migration — it is an application rewrite of every query that uses DynamoDB's expression API, combined with a data migration that must map DynamoDB's composite key model to Firestore's document hierarchy model, combined with a change data capture replacement for services that consume DynamoDB Streams. The migration cost from Lambda@Edge to a Cloudflare Worker or an equivalent edge function is a rewrite of the function in the target platform's execution model plus a CDN migration from CloudFront to the alternative CDN platform.

The workload placement policy must distinguish workloads by their portability requirement before services are built, not after the services have been operating in production for two years. Three categories serve most organizations: portable workloads that must not use cloud-specific managed services and must be deployable to any Kubernetes-compatible cluster using cloud-neutral infrastructure (object storage accessed through an S3-compatible API abstraction, message queues accessed through an AMQP or MQTT abstraction, databases accessed through standard PostgreSQL or MySQL protocol); provider-specific-acceptable workloads that may use cloud-specific managed services because the migration cost has been evaluated and accepted as justified by the service's operational and feature benefit (Aurora Serverless is accepted for the primary database because the auto-scaling, point-in-time recovery, and operational burden reduction justify the migration cost to Cloud SQL at the time the team would need to migrate); and best-of-breed workloads that are intentionally deployed on the provider with the best capability for their workload class regardless of the primary provider (BigQuery for analytics, Vertex AI for ML model serving, Pinecone for vector search — each chosen for its superior capability rather than for cloud neutrality). The placement policy must specify the decision process for new services: when an engineer proposes a new cloud-specific managed service, the proposal must include the service's placement category and, if the category is provider-specific-acceptable, the migration cost estimate. This converts the lock-in accumulation from a passive process (services accumulate cloud-specific dependencies as engineers make local decisions that seem individually reasonable) into an active process (each cloud-specific service adoption is an explicit decision with a documented migration cost estimate).

The lock-in inventory is the operational artifact that makes the policy visible over time. The inventory must be maintained as a living document with one row per cloud-specific service, specifying the service, the provider, the nearest equivalent on each alternative provider, the estimated migration effort (trivial: under one sprint; moderate: one to three sprints; substantial: three to six sprints; prohibitive: over six sprints or requiring application redesign), and the team's acceptance decision (accepted, monitored, or mitigated). The inventory should be reviewed at the same cadence as the quarterly architecture review, and each new cloud-specific managed service adoption should add an entry before the adoption decision is finalized. A team that reviews its lock-in inventory quarterly will discover the accumulation of twenty-three AWS-specific services in month six rather than month twenty-two — not because the inventory prevents adoption, but because the inventory makes the total coupling surface visible when each individual service is evaluated in isolation.

The portable abstraction requirements and the consistency tax

Portable abstraction layers — interfaces that wrap cloud-specific services behind cloud-neutral APIs — allow workloads in the "portable" placement category to use cloud services without coupling their implementation to a specific provider's SDK or API format. An object storage abstraction exposes operations like put(bucket, key, body), get(bucket, key), delete(bucket, key), and list(bucket, prefix) regardless of whether the underlying implementation is S3, GCS, Azure Blob Storage, or MinIO. A message queue abstraction exposes publish(topic, message) and subscribe(topic, handler) regardless of whether the underlying implementation is SQS, Pub/Sub, RabbitMQ, or NATS. The application code uses the abstraction; the infrastructure configuration specifies which implementation to inject.

The consistency tax of portable abstractions is the gap between the abstraction's common-denominator API and the native service's full feature set. S3 provides features — Lifecycle Rules for automatic object expiration, Intelligent-Tiering for cost optimization, S3 Object Lock for WORM compliance, Transfer Acceleration for high-throughput cross-region uploads, S3 Replication for cross-region or cross-account copying — that have no equivalent in GCS or Azure Blob Storage at the same API surface. An S3-compatible abstraction that hides the underlying service must either expose these features through cloud-specific configuration that breaks the abstraction's portability, or not expose them at all, which means the workload cannot use features that would otherwise be available and operationally valuable. The consistency tax also includes behavioral differences: S3 offers strong consistency for GET after PUT since November 2020; GCS has offered strong consistency since 2018; Azure Blob Storage has strong consistency within a region but eventual consistency for geo-redundant storage in some configurations. A portable abstraction that does not document the consistency model of each implementation it wraps may produce workloads that pass tests on one implementation and fail in production on another due to consistency differences that the abstraction made invisible.

The portable abstraction decision must specify, for each service category, whether the team will use a portable abstraction or native cloud APIs. The criteria for choosing a portable abstraction are: the workload is in the "portable" placement category (the placement policy requires cloud neutrality), the team actively maintains deployments on two or more cloud providers and the abstraction is exercised in production rather than being theoretical, and the cloud-specific features foregone by the abstraction are not operationally required by the workload. The criteria for using native cloud APIs are: the workload is in the "provider-specific-acceptable" category (the migration cost has been documented and accepted), or the cloud-specific feature set provides operational value that justifies the lock-in, or the abstraction maintenance cost exceeds the expected value of the portability it provides. The decision must also specify who maintains the abstraction: an abstraction that is not maintained degrades over time as the underlying services evolve and the abstraction's implementations diverge from the current SDK. An unmaintained object storage abstraction that was written for S3 SDK v2 may not correctly implement the GCS implementation when the team discovers they need to use it on GCP two years after the abstraction was written — producing a theoretical portability that is not practically exercisable.

The cross-cloud operational complexity and the observability surface

When workloads span two cloud providers, the operational surface spans two cloud providers. Access control must be managed in two IAM systems with different permission models (AWS IAM uses policy documents with JSON Effect/Action/Resource/Condition structure; GCP IAM uses roles attached to principals at resource hierarchy levels with a different role and permission naming convention). Secrets must be either replicated between two secrets management services (AWS Secrets Manager and GCP Secret Manager), federated through a provider-neutral secrets management service (HashiCorp Vault deployed outside both clouds), or managed with cloud-specific access per cloud (workloads in AWS read from Secrets Manager, workloads in GCP read from Secret Manager, and the secrets are synchronized by an operational process). Certificates must be provisioned and renewed across two providers' certificate management services or through a provider-neutral CA (Let's Encrypt, HashiCorp Vault PKI). Cost must be analyzed across two providers' billing systems with different billing models (AWS uses a combination of on-demand, reserved, and spot pricing across thousands of SKUs; GCP uses sustained use discounts, committed use contracts, and preemptible instances with different discount structures) that cannot be directly compared in a single report without a unified cost model.

Observability is the most operationally critical cross-cloud complexity. In a single-provider architecture, the operational team uses one set of observability tools — CloudWatch for metrics, CloudWatch Logs for log aggregation, X-Ray or OpenTelemetry for distributed tracing — with one access control model, one query language, and one alerting configuration. In a multi-provider architecture, the operational team must correlate observability data across two sets of cloud-native tools that are not aware of each other. An incident whose root cause is a GCP Pub/Sub subscription backlog (visible in GCP Cloud Monitoring) and whose customer-visible symptom is increased API latency (visible in AWS CloudWatch) requires that the on-call engineer can move between the two observability surfaces, identify that the backlog exists, determine that it is caused by a specific subscriber falling behind, and understand that the subscriber's processing delay is propagating to the API response time. If the on-call runbook does not specify which observability surface to check and in what order, the engineer must rediscover the investigation path during the incident — adding resolution time at the moment when time cost is highest. The observability strategy decision record for a multi-cloud architecture must resolve cross-provider observability before the first production workload crosses the cloud boundary, not after the first cross-cloud incident reveals that the team cannot correlate events across two providers' log systems under time pressure.

The two models for cross-cloud observability — provider normalization onto one cloud's native tools, and provider-neutral observability using an independent stack — make different trade-offs on setup cost, operational burden, incident-time availability, and data transfer cost. Provider normalization forwards GCP Cloud Monitoring metrics to AWS CloudWatch via the Amazon CloudWatch metric stream or via a Prometheus remote write intermediary; it concentrates the team's observability expertise in one tool and eliminates the per-incident context switch between two observability surfaces. Its failure mode is the one the developer tools startup encountered: if the primary cloud's observability service is degraded during a cross-cloud incident, the forwarded metrics from the secondary cloud are also unavailable. Provider-neutral observability — Grafana with Prometheus, Loki, and Tempo; Datadog with agents on both clouds; New Relic with multi-cloud integration — provides observability data from all clouds in a single interface that is independent of both providers. Its setup cost is higher, and a self-managed provider-neutral stack adds a third operational surface (the observability infrastructure itself) to maintain alongside the two cloud workloads. For most teams at growth stage, a managed provider-neutral observability service (Grafana Cloud, Datadog, New Relic) configured with lightweight agents on both clouds provides the correct balance: the observability surface is independent of both cloud providers, the vendor manages the observability infrastructure, and the per-seat or per-data-volume cost is predictable. The incident response procedure for cross-cloud failures must specify which observability surface to use as the primary investigation starting point, how trace context is propagated across the cloud boundary so that distributed traces can be followed from the customer-visible symptom in one cloud to the root cause in the other, and how the on-call engineer accesses both clouds' observability surfaces without requiring separate authentication steps during the incident.

What a multi-cloud strategy decision record must document

The first section documents the multi-cloud strategy and cloud provider selection. The motivation for multi-cloud must be specified as one of: regulatory compliance (specifying the jurisdiction, the data category, and the compliance framework driving the multi-provider requirement), best-of-breed capability (specifying the workload class, the capability gap between primary and secondary provider, and the evaluation evidence), negotiating leverage (specifying the current annual spend level, the discount differential achieved or expected, and the minimum spend level at which the strategy remains justified), or vendor lock-in risk mitigation (specifying the failover scenario the strategy is designed to address, the RTO and RPO targets, and how the architecture achieves them). The primary cloud must be specified with the rationale for selection. The secondary cloud, if any, must be specified with the workload classes assigned to it and the rationale for the assignment. The document must explicitly state what multi-cloud means for this organization in operational terms: active-active (both clouds serve production traffic simultaneously with failover capability between them), active-passive (one cloud serves production traffic, the second is a warm standby that can accept traffic within the RTO), best-of-breed (specific workload classes run on the cloud with the best capability for that class, with no failover capability between the two), or best-effort (one cloud is primary; the secondary is used for specific services with no architectural commitment to failover or portability). Stating the model explicitly prevents the conflation that produced the developer tools startup's two-provider billing architecture with no failover capability — a conflation where "we use two clouds" was stated as the strategy without specifying what the two-cloud architecture was designed to achieve.

The second section documents the workload placement policy. The three placement categories — portable, provider-specific-acceptable, and best-of-breed — must be defined with the criteria that determine which category applies to a new service. The decision process for new services must be specified: what information is required before a service is placed in the provider-specific-acceptable category (the migration cost estimate, the team's explicit acceptance of the lock-in, and the lock-in inventory entry), what information is required before a service is placed in the best-of-breed category (the capability evaluation evidence, the cross-cloud coupling surface the placement creates, and the operational requirements for the coupling). The policy must address the cross-cloud coupling risk explicitly: a best-of-breed workload on a secondary cloud that reads from a primary cloud's database creates a coupling that propagates the primary cloud's failures to the secondary cloud — the opposite of the isolation intuition behind multi-cloud. The placement policy must specify whether cross-cloud coupling is acceptable for the architectural goals of this deployment, and if so, which cross-cloud coupling patterns are permitted (read replicas to secondary cloud, event forwarding from primary to secondary, synchronous API calls from secondary to primary) and which are not (synchronous writes from secondary to primary database during normal operations, where the latency and failure modes are inconsistent with the workload's SLA requirements).

The third section documents the provider lock-in inventory. The inventory format must be specified — per-service rows with service name, provider, nearest alternative-provider equivalent, migration effort estimate, and team acceptance decision. The review cadence must be specified: the inventory is reviewed at each quarterly architecture review, and new cloud-specific service adoptions require an inventory entry before the adoption decision is finalized. The team must establish which migration effort threshold triggers an escalation review beyond the team that is adopting the service — a "prohibitive" migration cost entry that is added without broader review represents organizational lock-in that the adopting team may not have the authority to accept on behalf of the broader organization. The infrastructure-as-code decision record must be consistent with the lock-in inventory: if the IaC strategy specifies that all infrastructure is provisioned through Terraform, the lock-in inventory must distinguish between Terraform-manageable portability (the IaC code can be adapted to provision the equivalent service on an alternative provider) and application-level portability (the application code uses the service's API in ways that require changes when migrating to the alternative provider). A DynamoDB table provisioned by Terraform is not portable because of Terraform — the Terraform resource block can be replaced with a Firestore or Bigtable equivalent resource block — it is coupled because the application code uses DynamoDB's proprietary query expression syntax, and the coupling is in the application, not the IaC.

The fourth section documents the portable abstraction policy. The section must enumerate each service category where a portable abstraction is used (object storage, message queuing, secrets management, distributed caching), specify the abstraction interface and which implementations are supported, and specify the maintenance owner and review cadence for the abstraction. The section must also enumerate each service category where native cloud APIs are used despite a possible alternative portable abstraction, with the rationale for the native API choice (the workload is in the provider-specific-acceptable category; the cloud-specific feature is required; the abstraction maintenance cost is not justified by the expected migration probability). The criteria for adding a new portable abstraction versus accepting native API coupling must be specified, so that future engineers can make the same trade-off consistently without rediscovering the reasoning. The disaster recovery decision record must be consistent with the portable abstraction policy: if the disaster recovery plan requires the ability to fail over to an alternative cloud, the portable abstraction policy must ensure that the workloads in the failover path use abstractions that are exercised and tested on the alternative cloud, not just theoretically portable. A portable abstraction that has never been deployed against its GCP implementation is not a disaster recovery capability — it is a migration starting point.

The fifth section documents the cross-cloud operational requirements. The access control model must specify how identities are managed across providers — whether IAM roles in each cloud are provisioned independently, federated through an identity provider (Okta, Google Workspace, Azure AD), or restricted to machine identities (service accounts and IAM roles for workloads, with human access via cloud-specific console authentication). The secrets management model must specify whether secrets are replicated between providers, federated through a provider-neutral vault, or managed independently per provider, and the synchronization process if replication is used. The observability model must specify the observability architecture (provider normalization or provider-neutral), the tooling, the agent configuration for each cloud, the trace context propagation model for distributed traces that cross the cloud boundary, and the incident response procedure for cross-cloud failures including which observability surface to use as the starting point and how to correlate events across providers. The cost model must specify how costs from both providers are aggregated for reporting and optimization review — whether through a cloud management platform (CloudHealth, Apptio Cloudability, CloudZero), through custom cost attribution tooling, or through provider-specific cost export to a common data warehouse. The cost optimization decision record must address multi-cloud cost attribution explicitly: reserved instance or committed use discount decisions on each cloud interact — the optimal commitment level on each provider is determined by the expected workload distribution between providers, and an over-commitment on the primary cloud relative to the multi-cloud workload split can eliminate the cost advantage that motivated the secondary cloud adoption. The infrastructure decision record for the primary cloud must be read as the prerequisite to the multi-cloud strategy decision record: the primary cloud selection and the managed services adopted for the initial architecture are the starting point for the lock-in inventory, and the multi-cloud strategy's workload placement policy must be retroactively applied to the services already in use before it is applied to new services going forward.

The founding infrastructure selection session, the analytics platform adoption session, and the enterprise requirements evaluation session each produce multi-cloud strategy decisions whose long-term operational cost — a cross-cloud failure propagation pattern that degrades GCP analytics when an AWS Aurora capacity event occurs, because the two clouds are coupled through a synchronous cross-cloud database read rather than isolated; or twenty-four hundred engineering hours replacing working managed services with self-managed equivalents to achieve cloud neutrality for a requirement that actually meant multi-region deployment within a single provider — exceeds what a workload placement policy, a lock-in inventory with migration cost estimates, a portable abstraction decision, and a cross-cloud operational model would have cost at the time the founding decisions were made. The decisions are in the AI sessions: the infrastructure selection session that decided "we'll use AWS for primary and GCP for analytics" without specifying what "multi-cloud" means as an operational commitment, the analytics adoption session that added BigQuery and Pub/Sub without documenting the cross-cloud coupling they created, the enterprise requirements session that treated "multi-cloud" as a technical requirement without clarifying whether the prospect meant provider redundancy or regional redundancy. WhyChose's open-source extractor surfaces these founding infrastructure and strategy sessions as structured decision records before a cross-cloud incident reveals that the team's two-provider architecture propagates failures rather than isolating them, or before an enterprise requirement is misinterpreted as an architectural mandate and twenty-four hundred engineering hours are spent on a migration that was not required to satisfy the requirement. The new CTO onboarding problem in a multi-cloud architecture is a lock-in audit problem: an incoming technical leader will find two cloud bills, an undocumented workload placement policy, a lock-in surface spread across twenty-three cloud-specific managed services with no migration cost estimates, and a cross-cloud coupling that was built incrementally over eighteen months without a mechanism to surface the total coupling cost. The decisions that were never written down in the multi-cloud context are the workload placement policy that would have distinguished between isolated best-of-breed workloads and tightly-coupled cross-cloud dependencies, the lock-in inventory that would have made twenty-three AWS-specific services visible as a total lock-in surface rather than twenty-three individually-reasonable adoption decisions, and the cross-cloud operational model that would have resolved observability and incident response before the first cross-cloud failure required the team to improvise them under pressure.

Further reading