When choosing a wire encoding for a new service-to-service integration, which properties of the workload actually decide it?
answer
- match the hop, do not rank formats
- six criteria, not one
- volume, evolution, consumers, readability, safety, migration
- hard constraints eliminate before scoring
- record what killed each alternative
basics
~20 sSix workload properties carry most of the decision: payload shape and volume, how fast the contract will change, who the consumers are and what tooling they run, whether humans read the bytes, decoder safety posture, and migration cost.
solid answer
~50 sStart from the hop, not from a favourite format. I ask six things about the workload. **Volume and shape**: how many messages, how large, how repetitive — that decides whether bytes on the wire are a real cost or noise. **Evolution pressure**: how often the contract changes, and whether producers and consumers deploy together. **Consumers**: who decodes this, across how many ecosystems, and what decoders they already run. **Human contact**: will anyone read a captured message during an incident or hand-edit a fixture? **Safety posture**: the decoder meets caller-controlled bytes before any validation runs, so an encoding that can expand or name things arbitrarily starts behind. **Migration cost**: what already exists on this hop and what moving off it costs. Then eliminate candidates on the hard constraints, score only the survivors, and write down which criterion killed each rejected one.
code
pseudocode · 20 linescandidates = all_encoding_families()
# hard constraints remove candidates outright, before any score is computed
for each family in candidates:
if hop.consumers_span_many_ecosystems and not family.has_maintained_decoders_everywhere:
drop(family, reason = "a consumer cannot decode it at all")
if hop.accepts_untrusted_input and family.decoding_can_construct_arbitrary_objects:
drop(family, reason = "decoder surface too large for this trust boundary")
if hop.contract_changes_independently and not family.has_compatibility_rules:
drop(family, reason = "no way to survive a mixed-version rollout")
# only survivors are scored; weights are stated out loud, not assumed
for each family in candidates:
score(family) = w_size * fit(family, hop.volume)
+ w_human * fit(family, hop.human_contact)
+ w_tools * fit(family, hop.consumer_tooling)
- w_move * migration_cost(hop.current_encoding, family)
choose the highest score
record every dropped family with the reason that dropped itgo deeper
Be able to name the axes a choice is made on: size, readability, whether a schema is required, and who has to decode it. Naming the axes in that order already beats naming a favourite format.
Explain each criterion as a mechanism: why message volume turns per-message overhead into money, why an independently deployed consumer changes the evolution requirement, why a consumer's existing toolchain is a constraint and not a preference.
Show that you eliminate on hard constraints before scoring soft ones, and that you can state which criterion decided and which alternative lost on which axis. Include migration cost rather than treating the hop as empty.
Own the weighting. Say what a criterion is worth to this organisation — a debuggability win measured against a bandwidth bill — and write the standard so it encodes those weights rather than blessing one encoding by name.
## The unit of decision is a hop, not a product A format argument that opens with "which encoding is best" has already gone wrong, because encodings are not ranked — they are matched. The unit of decision is a single **hop**: one place where values leave memory and become bytes something else reads. A request between two services, a message sitting on a queue, a file written today and read in three years, a payload posted by a partner you do not control — each of those is a hop, and two hops inside the same product routinely have opposite constraints. So the honest form of the question is "what does *this* hop need", and the honest form of the answer names the criterion that decided it. ## What the workload tells you | Criterion | The question you actually ask | What it pushes toward | |---|---|---| | Volume and shape | How many messages, how big, how repetitive? | High volume and repetitive fields push toward a compact encoding; low volume makes size noise | | Evolution pressure | How often does the contract change, and do both sides deploy together? | A fast-drifting contract with independent deploys pushes toward an encoding with explicit compatibility rules | | Consumer set and tooling | Who decodes this, in how many ecosystems, with what already installed? | Many unrelated consumers push toward whatever has broad, maintained decoders | | Human contact | Will a person read or edit these bytes under time pressure? | Incident traffic and hand-written fixtures push toward a readable encoding | | Safety posture | Does this hop accept bytes from callers you do not trust? | Untrusted input pushes toward an encoding whose decoder has a small, boring surface | | Migration cost | What is already on this hop, and what does leaving it cost? | An entrenched encoding with many live consumers raises the bar for every alternative | Each row is a property of the **workload**, not of the format. That is what makes the matrix usable: you can fill the left-hand side in before you have named a single candidate, which is exactly the order that keeps the discussion honest. - **Volume and shape** turn per-message overhead into money. A few dozen bytes of field naming per message is invisible at a hundred messages an hour and is an infrastructure line item at a hundred thousand a second. - **Evolution pressure** is really a question about deployment: if both ends ship together, a breaking change is a single release; if they do not, every change must be survivable by a reader that has not been upgraded yet. - **Consumer set and tooling** is the criterion that most often turns out to be a gate rather than a score. A consumer with no maintained decoder in its ecosystem does not integrate slowly — it does not integrate. - **Human contact** is easy to dismiss and expensive to be wrong about. Debuggability is not a luxury on a hop whose failures are diagnosed at three in the morning from a captured message. - **Safety posture** matters because the decoder runs before your validation does. On a hop fed by unknown callers, prefer an encoding whose decoding step does little more than build plain data. - **Migration cost** is what makes a theoretically better encoding the wrong answer. It is a real cost, it is usually paid by people who are not in the room, and it belongs in the matrix rather than in a sigh. ## Hard constraints first, scores second The matrix has two kinds of rows, and mixing them is the most common process error: 1. Write down the **hard constraints** — the conditions a candidate must satisfy to be usable at all. "Every consumer ecosystem must have a maintained decoder." "The bytes must be readable by a person with no special tool." "Decoding must not be able to construct arbitrary application objects." 2. **Eliminate**: remove every candidate that fails any constraint, and note which constraint removed it. This usually takes the field down to two or three. 3. **Score** the survivors against the soft criteria with weights the team states out loud, so that the weighting is arguable rather than implicit. 4. **Record** the winner, the runners-up, and the criterion each runner-up lost on — that last part is what makes the decision re-openable when the inputs change. ## Where the decision goes wrong - Naming a candidate before describing the hop, so the criteria get reverse-engineered to justify it. - Treating payload size as the whole matrix because it is the only criterion with an easy number. - Scoring a candidate that a hard constraint should already have eliminated, which lets a weighted average override a gate. - Counting the wire cost of an encoding and not the cost of the toolchain it obliges everyone to learn and harden. - Leaving the rejected alternatives unrecorded, so the same argument is had again in a year with no memory of what decided it.
- Which of the six criteria is left out of the conversation most often?Migration cost. Teams compare candidates as if the hop were empty, then discover that switching means a dual-write period, a dual-read period, re-encoding whatever is already stored, and a coordinated upgrade of consumers who have other priorities. The better encoding can lose to the incumbent on this row alone, and that is a legitimate outcome rather than a failure of nerve.
- How does the number of distinct consumer ecosystems change the answer?It converts tooling from a score into a gate. With one consumer ecosystem you can pick almost anything and write the missing decoder yourself. With five unrelated ones you inherit five toolchains, five sets of bugs and five upgrade schedules, so breadth and maturity of existing decoders start to dominate every other criterion including size.
- What makes a criterion a hard constraint rather than something you score?Whether failing it makes the candidate unusable rather than merely worse. "Must be decodable in every consumer's ecosystem" and "must be readable by a person during an incident" are pass or fail; "should be compact" is a matter of degree. Constraints act first and remove candidates; scores only order the ones that survived.
Choosing an encoding is like choosing a shipping container: you do not ask which container is best, you ask what is going in it, who unloads it, and whether anyone needs to see inside on the way.
saying these in an interview costs you the question
- Names a favourite encoding before asking anything about the hop.
- Treats payload size as the only criterion that matters.
- Assumes a compact binary encoding is always the professional choice.
- Ignores what decoders the consumers already have and must build.
- Forgets that an encoding already on the hop carries a migration cost.
- Says human readability stops mattering once a system reaches production.