An architecture should be allowed to change
Superseded decisions are evidence of learning, not defects to hide behind a tidy final diagram.
RS Platform has had three answers to the question, “Where should cloud workloads run?”
The first answer was ECS on Fargate. The second was a small GKE cluster. The current answer is self-managed Talos Kubernetes on EC2.
Read only the final diagram and that sequence looks wasteful. Read the decisions in order and it shows something more useful: constraints becoming clearer.
Every design spends a different kind of complexity
Fargate removed node management and looked inexpensive at the scale I had in mind. It also made some of the networking I wanted awkward and kept the platform from exercising Kubernetes as a real operating environment.
GKE solved that. Raw manifests and Argo CD gave workloads a portable contract, while a managed control plane reduced the amount of Kubernetes machinery I had to own. But the platform now crossed AWS and GCP for a single runtime. The extra IAM model, network boundary, bill, and recovery story were real even when the monthly price looked acceptable.
Talos on EC2 brought the runtime back into the gateway VPC. It traded managed convenience for a smaller provider surface, native ARM nodes, and a recovery model I could inspect end to end.
None of those moves made the earlier reasoning dishonest. Each answer was reasonable under a different understanding of the system.
The durable parts were above and below the runtime
Changing orchestrators would have been much more expensive if workloads, infrastructure, and delivery shared one authority.
They did not:
- Terraform owned networks, identities, and compute envelopes.
- GitOps owned workload definitions.
- Application state lived outside disposable compute.
- The home plane kept data-heavy traffic beside its storage.
- Every always-on service still needed an explicit cost and recovery story.
Those boundaries survived even when the runtime did not. Portability was not “this manifest runs everywhere unchanged.” It was the ability to replace one layer without renegotiating the entire system.
Cost is a design input, not a victory condition
The early architecture used a soft monthly target and a hard ceiling. Later decisions revised both when the platform’s purpose changed.
That is healthier than optimizing for the smallest possible bill after the system stops teaching or serving anything useful. A cheap dependency spread across two providers may cost more attention than a slightly larger instance in one provider. A burstable node may look economical until sustained work turns CPU credits into an operational constraint.
The useful question is not, “Which option is cheapest?” It is:
Which costs are bounded, visible, and justified by the capability they buy?
Keep the discarded path
An ADR should not be edited until it appears that today’s architecture was obvious from the beginning. Supersede it. Keep the constraint, the choice, and the consequence that caused the next decision.
The final diagram explains what exists. The sequence explains whether the team knows how to change it.