skip to content

Your published rate-limit metadata type gains a burst member. How do you keep other teams' existing attachments compiling?

level: middleimportance: must knowfreq 50%

answer

  1. members are the payload
  2. default decides required or optional
  3. adding a required member breaks callers
  4. shorthand is syntax, not optionality
  5. default choice is a behaviour choice

basics

~20 s

Declare the new member with a default value. A member without a default must be supplied at every attachment site, so adding one breaks existing uses; a defaulted member is optional and older attachments keep their meaning.

solid answer

~40 s

Member elements are the metadata type's parameters, and each one either has a declared default or does not. A member without a default is required: every attachment must supply it, so adding such a member to a published type is a breaking change for every team that already uses it. A member with a default is optional, and an attachment that never mentions it behaves as if it had passed the default. So the burst member ships with a default that reproduces today's behaviour - and picking that default is the real decision, because it is what hundreds of untouched attachment sites will now mean. Be careful later, too: changing a default is not cosmetic. It changes the meaning of every site that relied on the old one, in code nobody edited or reviewed.

code

pseudocode · 12 lines
pseudocode
metadata type RateLimited
    targets: method
    member permitsPerSecond: number        // no default -> required
    member burst: number = 0               // default -> optional

mark RateLimited(permitsPerSecond = 10)    // still legal after burst was added
function chargeCard(order)
    ...

mark RateLimited(permitsPerSecond = 10, burst = 30)
function refundCard(order)
    ...

go deeper

for a junior

Know that a member with a declared default may be left out of an attachment, and a member without one must be written every time.

for a middle

Explain the evolution consequence: a new required member breaks every existing attachment at once, while a defaulted one is compatible and quietly configures every site that omits it.

for a senior

Show judgment about the default you pick for a published marker - it is the meaning imposed on attachment sites nobody will reopen - and about what member types a build can express at all.

for a principal

Treat the metadata type as a published interface with its own compatibility policy, and decide what changes to it require coordination across teams rather than a version bump.

## Members are the metadata type's parameters A metadata type is rarely just a name. It declares **member elements**, each with a name and a type, and an attachment supplies values for them. For a rate-limit marker in a shared toolkit that might be `permitsPerSecond`, an optional `burst`, and a `scope`. The members are the whole payload: the attachment carries nothing else, and a reader can use nothing else. Two properties of a member decide almost everything about how the type can evolve: - **Does it have a declared default?** This decides whether an attachment must mention it. - **What kind of value may it hold?** This decides what can be expressed at all. ## Defaults decide what an attachment must say | Member | At the attachment site | Adding it to a published type | |---|---|---| | No default | must be supplied every time | breaking - every existing attachment stops compiling | | With a default | may be omitted | compatible - omitted sites take the default | That table is the whole answer to the question, but the interesting part is the second column read in reverse. When a member is defaulted, the sites that omit it are not "unconfigured" - they are configured, by you, from a distance. A shared toolkit that adds `burst` with a default of zero has just declared what every operation in the company means by burst, and nobody had to approve it in a review. So the design rule is: **the default of a new member on a published type should reproduce the behaviour that existed before the member did.** A default chosen because it seems like a nice value is a behaviour change disguised as an addition. ## The single unnamed member Many metadata systems let a type designate one member whose value may be written without naming it, so a single-member marker reads as a bare value rather than a name-equals-value pair. It is worth being precise about what that convention is and is not: - It is **syntax at the attachment site** - shorthand for writing one designated member's value. - It does **not** make that member optional; only a default does that. - It does **not** change how a reader addresses the member; readers work by name. - The shorthand usually stops being available once the attachment also supplies other members, so a type designed around it can feel inconsistent once it grows. Designing a type around the shorthand is a readability choice with a cost: the moment a second member is commonly supplied, half your attachment sites use the short form and half the long one. ## What a member value may hold Where metadata is a compile-time construct, member values are typically restricted to things the build can write down as constants - numbers, text, simple enumerated choices, references to a type, and arrays of those. You cannot hand a member a live object or the result of a computation, because there is nothing running yet. Where metadata is instead an ordinary construction evaluated when the declaration is loaded, that restriction does not apply, and a member can hold anything the language can build. This is the most common surprise for someone declaring their first metadata type: they want a member that holds a policy object and discover they may only name one. The usual shape is to pass a **reference to a type** and let the reader construct it, which keeps the declaration constant while still being open-ended. ## Evolving a shared metadata type safely 1. **Add members with defaults**, and choose the default to preserve existing behaviour. 2. **Never add a required member** to a type other teams already use - it is a compile break for all of them at once. 3. **Treat a default change as a behaviour change.** Announce it, and expect it to reach sites nobody rebuilt in some designs and only rebuilt sites in others - toolchains differ in whether a default is resolved from the declaration when the metadata is read or copied into the attachment when it is built. 4. **Removing a member is breaking** for the sites that supply it, the mirror image of adding a required one. 5. **Prefer a narrow member type.** A member declared as free text is a member whose legal values you will be validating by hand forever; an enumerated choice is checked for you.

  • You want a member that carries a whole policy object. What shape does a compile-time metadata type force?
    Constants only, so you cannot hand it a live object. The usual shape is a member holding a reference to a type, which a reader instantiates when it needs the policy. The declaration stays writable at build time while the behaviour it selects stays open-ended.
  • Is removing a member from a published metadata type safe if nobody seems to use it?
    No - it is breaking for exactly the sites that do supply it, and those are the ones you cannot enumerate from inside the toolkit. Deprecate in documentation, find the users, then remove. In a codebase you fully control, remove it outright and fix the sites in the same change.

saying these in an interview costs you the question

  • Thinks adding a member without a default is source-compatible
  • Says every member must be supplied at every attachment site
  • Assumes member values may be arbitrary computed objects everywhere
  • Believes the unnamed-member shorthand makes that member optional
  • Treats changing a default as cosmetic rather than a behaviour change