Platform Code Placement
also called Policy Placement Decision, Where Platform Logic Runs
The choice of which process a platform's shared logic executes in - the application itself, an injected sidecar, a node agent or a remote service - which fixes how fast a change reaches the fleet and who has to act for it to land.
A platform team is asked to add mutual TLS and per-caller rate limiting to service-to-service traffic. The debate is usually about latency and memory. The decision that determines the outcome is who has to press a button for the change to take effect, and that is settled by which process the code runs in.
Put the logic in a library and 180 teams must merge, build and release before the policy is real. Put it in a sidecar and the platform rolls a new image. Put it behind an API and the next call has it. Those three differ by two orders of magnitude in time-to-fleet, with no change to the code.
Why it matters
Platforms exist to change things across an estate, so the placement decision sets the upper bound on how often the platform can change anything, and every later roadmap argument runs into that bound.
It also sets who is blamed, because code inside the application's process shares its memory limit and its crash, and a sidecar killed for exceeding a limit takes the pod with it. The failure is attributed to whoever owns the pod, rarely the team that wrote the code.
Implementation patterns
- In-process library. Lowest latency and full access to application context such as a tenant parsed from a request body. Changes propagate at the speed of 180 release trains.
- Sidecar proxy. Platform-injected and language-blind. Roughly 50 to 150 MB per pod and about 0.5 to 1 ms per hop on each side, so 2400 pods is on the order of 120 to 360 GB bought for uniformity, plus a control plane.
- Node agent. One process per node rather than per pod, so memory falls by the pods-per-node factor, against weaker isolation and an upgrade that hits every pod on the node at once.
- Remote service behind an API. Instant change and no per-pod cost, against a hop and an availability dependency. Right for admission-style decisions; wrong in a hot request path.
- Build-time generation. Code written into each repository by the template: a copy, and a copy cannot be updated by its author, so it is the placement with no update path at all.
Industry example
Envoy, created at Lyft and open-sourced in 2016, is the canonical move from the first placement to the second: networking concerns that lived in each service's client libraries moved into a proxy beside the application, so observability and retry policy became uniform across a polyglot estate without each team reimplementing them. The reason the model spread is operational - a platform team can upgrade a proxy it owns and cannot upgrade a library it merely publishes.
Failure scenarios
- The six-month rollout. A security fix ships as a library version; six months later the fleet is at 70% adoption and the stragglers are the unmaintained services, which is when the auditor asks for coverage.
- The shared memory limit. A platform sidecar buffers during a peak, exceeds its limit and is killed, dropping a product team's requests. That team's error budget pays for the platform's defaults.
- Silent behavioural skew. Two language implementations of one rate limiter round differently at the boundary, and the discrepancy surfaces as a reconciliation mismatch rather than an error.
Trade-offs
| Choose | Gains | Pays |
|---|---|---|
| Library | Lowest latency; application context | Change speed set by other teams; N implementations |
| Sidecar | Platform-controlled rollout; language-blind | Memory per pod; a control plane; fleet restarts |
| Node agent | Cheaper than per-pod | Weaker isolation; wider upgrade blast radius |
| Remote API | Instant change; no per-pod cost | A hop and an availability dependency |
When not to use it
The question is only worth asking when the same logic must exist in more than a handful of services. Below roughly 30 services in one or two languages, a shared library and a code review beat every other option, and a sidecar means operating a control plane to solve what a dependency bump solved. When the policy needs a value that exists only inside the request handler, no proxy can supply it: the library is the only honest answer whatever the deadline says.
Interview question
Q: "You need mutual TLS and per-caller rate limiting across 180 services in six languages within two weeks. Pick a placement, then tell me what you would have chosen if the estate were 20 services in one language, and what single fact would flip you back."
What a strong answer covers: that the deadline rules out anything requiring 180 teams to act; the memory and latency bill quantified rather than waved at; that an ingress gateway sees none of this traffic; that at 20 services in one language the library wins outright; and the flip condition - a policy needing application context, or a per-hop budget low enough that a proxy's millisecond matters.
Quick check
Quiz: A mandatory header must reach service-to-service calls in 180 services and six languages within two weeks. Which placement, and why not a library? Sidecar: the platform rolls it itself, while a library ships only when 180 teams release and six languages means six implementations that will disagree.
Flashcard: Rank library, sidecar and remote API by time-to-fleet. — Remote API: minutes. Sidecar: hours to days, platform-triggered. Library: weeks to months, because every team must rebuild and release.