skip to content

You host a dozen services on one platform - where do you place the portable seam, and what do you leave unportable?

level: principalimportance: should knowfreq 40%

answer

  1. portability is bought per dependency
  2. proprietary weight times fan-out
  3. take the cheapest seam that works
  4. open interface, then self-run, then wrapper
  5. record what you left unportable

basics

~20 s

Place the seam only where a move would otherwise be expensive - usually one or two interfaces over the most proprietary, most widely used dependencies - and leave the rest deliberately unportable, recorded as an accepted cost with a named owner.

solid answer

~40 s

Portability is bought per dependency and each seam carries a running price, so the job is to spend on the few that would dominate a move and refuse the rest. Rank dependencies by how proprietary the interface is multiplied by how many services touch it: a bespoke interface on every service's main path deserves a seam, while a proprietary one used by a single nightly job does not. Then pick the **cheapest seam that works** for each - an open interface the platform already implements first, a self-run component where the operating burden is affordable, an in-house abstraction only where neither exists. Finally, write down what you chose to leave unportable, so it is a decision on the record rather than a discovery later.

go deeper

for a junior

Recall the three places a portable seam can sit: an open interface the platform implements, a component you run yourself on rented machines, and an abstraction your own team writes and maintains.

for a middle

Explain why each seam has a running cost rather than a one-off one, and why the cheapest workable seam is preferred over the most thorough one.

for a senior

Show the ranking in use: proprietary weight multiplied by fan-out, the cheapest seam that works for each, and an explicit boundary around what you chose to leave unportable.

for a principal

Own the register as a standard other teams inherit. Name what re-opens it - fan-out changing, a capability gap, an operating cost outgrowing the team, a commitment renewing - rather than scheduling a review.

## Portability is bought per dependency, not per system "Is the system portable?" has no answer. Each dependency is portable or not on its own terms, at its own price, and the estate's real position is the sum. A lead's job is therefore allocation: which dependencies get a seam, which seam each one gets, and which are consciously left alone. Every seam has a **running** cost, not a one-off one. An open interface costs the capability you gave up by staying inside it. A self-run component costs patching, upgrades, tested restores, failover and capacity work every month. An in-house abstraction costs maintainers, releases, feature lag and a support path. None of the three is free, so a plan that wraps everything is not a cautious plan; it is an expensive one that also slows every team down. ## Ranking: proprietary weight times fan-out Two factors decide whether a dependency deserves a seam, and they multiply. - **How proprietary the interface is.** A protocol several implementations answer is already portable. A bespoke interface with no second implementation anywhere is a rewrite if you move. - **How many services touch it, and how deeply.** A dependency on the main path of every service multiplies the rewrite; one used by a single batch job does not. The extremes take care of themselves. A bespoke interface used everywhere is the obvious seam. An open interface used by one service needs nothing. The judgment lives in the middle, and the tiebreaker is usually direction of travel: is the number of services touching this dependency growing or shrinking? ## Choose the cheapest seam that works | Seam | What it protects | What it costs | When it wins | |---|---|---|---| | An open interface the platform implements | application code on the data path | the capability you decline to use, plus the edges rebuilt on a move | whenever it exists - it is the seam you did not have to build | | A component you run yourself on rented machines | configuration, protocol, data format, runbook | patching, upgrades, tested restores, failover, capacity, permanently | the team can already operate it, or the managed tier lacks what you need | | An abstraction of your own design | whatever you choose to hide | maintainers, releases, feature lag, a bypass problem | nothing else exists, the interface is narrow, and many services use it | Read top to bottom and stop at the first one that works. The common failure is starting at the bottom - reaching for a wrapper because it feels like engineering - over a dependency that already answers an open interface, which buys nothing and adds a layer to maintain. ## What to leave unportable, deliberately Some dependencies should be used fully and left alone: - The **proprietary capability that is the reason you chose the platform**. An abstraction over it either hides the value you are paying for or reproduces it badly. Use it, isolate it behind a module boundary so its reach is known, and record it as the part that would be rewritten. - Dependencies with **small fan-out**, where rewriting two callers later is cheaper than maintaining a layer for years. - Anything in a **service scheduled for retirement**, where the move will never involve it. - The **control path** generally - provisioning, access policy, telemetry wiring - which is rebuilt on a move regardless of seam, and is cheaper to keep plain than to abstract. Leaving something unportable is only a decision if it is written down. Undocumented, it is indistinguishable from neglect, and the difference matters precisely when somebody asks what a move would involve. ## Record it, and say what re-opens it The output of this exercise is a short register: each significant dependency, which seam it sits behind, and for the unportable ones what would have to be rewritten. That register is a standard other teams inherit, so it needs an owner and a version, and it should name the events that re-open it rather than a review date: 1. The number of services touching a dependency changes materially - a seam not worth building for two can be worth it for twenty, and one built for a dozen retiring services should be dismantled. 2. A capability gap appears that the current platform cannot close. 3. The operating cost of a self-run component outgrows the team that took it on. 4. A commitment you are inside comes up for renewal, which is when the question is asked anyway. Re-read the register when one of those moves. Reviewing it on a calendar produces a document nobody trusts; reviewing it on a trigger produces a decision people can act on.

  • What changes the answer as the estate grows?
    Fan-out. A seam not worth building for two services can be worth it for twenty, because the seam's fixed running cost is spread while the rewrite it avoids multiplies. The reverse holds too: a seam built for a dozen services that are being retired should be dismantled rather than maintained out of habit.
  • Is leaving the most proprietary dependency unportable ever the right call?
    Often, yes. Where the proprietary capability is the reason you are on the platform, an abstraction over it either hides the value or reproduces it badly. The honest move is to use it fully, isolate it behind a module boundary so its reach is known, and record it as the part that would be rewritten.
  • How do you keep this decision from going stale?
    Attach it to the events that would change it rather than to a review date: the number of consuming services moving materially, a capability gap appearing, the operating cost of a self-run component outgrowing the team, or a commitment coming up for renewal. Re-read the register when one of those happens.

saying these in an interview costs you the question

  • Wraps the whole platform on principle instead of the few seams that would matter
  • Treats portability as a property of the system rather than of each dependency
  • Ranks dependencies only by how proprietary they are, ignoring how many services touch them
  • Leaves the unportable parts undocumented, so nobody knows a decision was made
  • Builds an in-house abstraction where the platform already implements an open interface