A marker attached to a class declaration changes nothing on its own — what must exist for it to have any effect?
answer
- declaring is not the same as doing
- someone has to look for it
- a consumer, running at some phase
- no consumer, no error, no behaviour
- enumerate, read, then act
basics
~20 sAttached metadata is inert: something has to read it. A consumer — a build-time processor, a startup scanner, or a check at the call site — must look for that marker and act on what it says, or nothing happens.
solid answer
~40 sAttaching a marker to a declaration only records a fact next to that declaration; it compiles away into the artifact and changes no control flow. The behaviour everyone attributes to the marker actually lives in a separate piece of code, the consumer, which enumerates some set of declarations, asks each one whether that marker is attached, reads its member values, and then does something — registers the component, validates it, wraps it, or rejects it. Three things must all hold: the marker must still be readable at the phase the consumer runs in, the consumer must exist, and its candidate set must actually include the declaration you marked. If any of the three is missing, the program behaves exactly as if the marker were not there, and nothing reports a problem.
code
pseudocode · 15 lines// attaching the marker: purely descriptive, no behaviour
marker Scheduled(everySeconds)
Scheduled(everySeconds = 30)
class ReportRefresher:
function run():
refreshReports()
// the consumer. Without this running, nothing above ever fires.
function startScheduler(typesInDeploymentUnit):
for each type in typesInDeploymentUnit:
m = markerOn(type, "Scheduled")
if m == none:
continue // unmarked types are skipped, silently
repeatEvery(m.everySeconds, () -> newInstance(type).run())go deeper
Remember the one-line version: metadata describes, code decides. If you attach a marker and nothing changes, that is the expected result until some consumer reads it.
Be able to name the three steps a consumer takes — enumerate candidates, read the marker, act — and explain that the meaning of a marker lives entirely in the consumer, never in the marker itself.
Show the diagnosis order in a real deployment: confirm a consumer for that marker exists there, confirm it ran, then confirm its candidate set included the declaration. The declaration is rarely the fault.
The trade you are actually approving is decoupling paid for in silence. Decide whether your platform accepts a marker whose consumer may be absent, or requires every marker to have an owning consumer that can be checked.
## Attaching and reading are two separate acts Attached metadata — a **marker** written next to a declaration, sometimes carrying member values such as a name, a number or a flag — is a *description*, not an instruction. Attaching it records a fact about that declaration somewhere another program can find it later. The declaration compiles as it did before, its control flow is unchanged, and no code runs merely because the marker is present. Everything people casually call "what the marker does" lives in a second, entirely separate piece of code: the **consumer** that goes looking for that marker and acts when it finds it. That split is the whole mechanism. It is what lets a marker be added to a codebase that has no idea what it means without breaking anything — and it is also why a marker can sit on fifty declarations for a year doing nothing at all. ## What a consumer has to do Wherever it runs, a consumer performs the same three steps: 1. **Enumerate candidates.** It picks the set of declarations it is willing to look at: everything this build compiles, everything reachable in the deployment unit when the process starts, or just the single target of the call being made right now. 2. **Read the marker.** For each candidate it asks whether that specific marker is attached, and reads the member values if it is. 3. **Act.** It registers the component, validates it, wraps it, records it in a list, or refuses to continue. **The meaning lives here** — in the consumer's code — and nowhere else. Drop any one of the three and the marker is inert. The most common real-world drop is the first: a consumer exists and works perfectly, but the declaration you marked was never in its candidate set. ## Where a consumer can run | Consumer | Runs | Typical act | |---|---|---| | Build-time processor | while the build compiles the declaration | check the marker is well placed, record it, fail the build | | Startup scanner | once, when the process boots and assembles itself | register each marked component it finds | | Call-site check | on each call through a marked member | decide what this particular invocation should do | One precondition sits under all three: a consumer can only read a marker that still exists at the phase it runs in. How long a marker survives — discarded after parsing, kept in the artifact, or queryable while the program runs — is its own subject. The point here is simply that surviving is *necessary but not sufficient*: something still has to look. ## Why nothing complains A missing consumer is silent, and the silence is structural rather than an oversight: - The marked declaration is **well-formed on its own**. Nothing about it is incomplete without a consumer, so no build step and no loader has grounds to object. - **Unrecognised markers are normal.** Several unrelated consumers commonly read different markers on the same codebase, so "a marker nobody here recognises" cannot be treated as an error without producing constant false alarms. - **Nobody owns the question.** There is no component whose job is to ask "should something have read this?", because answering it would require knowing every consumer that might ever exist. - The observable result of *no consumer* is identical to the observable result of *feature switched off*: the plain, unmodified behaviour of the code. ## What the inertness buys The same property that makes the failure silent is what makes the technique useful: - A library can ship marked declarations that are harmless to anyone who has no consumer for them — they cost some space in the artifact and nothing else. - A team can mark declarations before the tooling that reads them is written, or keep them marked after it is removed. - Two independent consumers can read the same declaration for different purposes without coordinating, because neither one changes the declaration. The matching hazard is that two consumers can also read the *same* marker and interpret it differently. The marker itself carries no semantics to arbitrate between them. ## Debugging from the right end Because "nothing happened" is the expected symptom of a missing consumer, diagnosis starts at the consumer and works backwards: does a consumer for this marker exist in this deployment, does it run at all, and does the set of declarations it enumerates include the one you marked? Staring at the marked declaration itself will almost never reveal the fault, since the declaration is usually exactly right.
- If two independent consumers read the same marker, what decides what it means?Each consumer does, separately. The marker carries member values but no semantics, so one consumer may treat it as a registration hint and another as a validation rule, and neither can see the other's interpretation. That is a genuine hazard: the fix is ownership — one team owns the marker and the consumer that defines it, and other readers of it are documented.
- Why can a library ship marked declarations that are harmless to a user with no consumer for them?Because the marker changes nothing about how the code compiles or runs. A user without a consumer gets the plain behaviour of the declarations, and pays only the space the metadata occupies in the artifact. That is what makes optional, opt-in tooling possible without forking the library.
- A consumer exists and runs, but one marked class is still ignored. Where do you look?At the consumer's candidate set. A consumer only sees declarations it enumerates, and that set is a deliberate choice — one part of the deployment unit, one kind of declaration, one naming convention. A class outside that set is invisible no matter how correctly it is marked.
A coloured sticker on a parcel only routes it if someone at the sorting belt has been trained to watch for that colour. The sticker moves nothing by itself, and an unwatched colour is not an error — the parcel simply travels the default route.
saying these in an interview costs you the question
- Thinks attaching a marker itself causes code to run
- Assumes any marker is automatically discovered by the runtime
- Says a marker with no consumer is a compile error
- Believes the marker's member values are executed rather than read
- Cannot say who reads the marker or at what phase
- Blames the marked declaration when nothing happens, never the consumer