A factory takes a batch job's run prefix and number width once and returns an issuer already configured: what does that buy every later call?
answer
- settings travel inside the value
- the call site carries nothing
- checked once, at construction
- no name means no later change
- a new setting means a new issuer
basics
~20 sCalls carry no configuration at all: it was validated and prepared once at construction, the returned issuer holds it in captured scope where callers cannot change or mistype it, and each issuer built this way owns its settings and its own counter.
solid answer
~40 sThe factory does the setup work once — check the width is sane, precompute anything derived from it, reject bad input immediately — and closes over the result. Every later call then has a smaller surface: no configuration arguments to get wrong, no repeated validation, and no way to pass a different prefix halfway through a run. Because the settings live in a binding no caller can name, they are effectively frozen for the life of that issuer: changing them means asking the factory for a new one. And because each factory call creates its own bindings, two issuers configured differently coexist without interfering — different prefixes, separate counters, one shared code path.
code
pseudocode · 13 linesfunction makeIssuer(prefix, width):
if width < 1:
fail("width must be at least 1") # checked once, here
counter = 0
function next():
counter = counter + 1
return prefix + padLeft(counter, width)
return next
issue = makeIssuer("RUN-", 4) # bad width fails on this line
issue() # RUN-0001 - caller passes no configuration
issue() # RUN-0002go deeper
Recall the payoff in one line: the settings were handed over once and now live inside the returned function, so the call itself takes no arguments and cannot be given the wrong ones.
Explain the ordering — validation and derived work happen when the factory runs, before the first call — and why each factory call yields an independent issuer with its own bindings.
Show the cost side: configuration is no longer visible where it is used, a captured mutable structure is not frozen, and the shared-counter question does not go away because the settings did.
Decide where a codebase should place its configuration boundary: construction-time binding buys uniformity and early failure but hides the settings from every reader of the call site, and that trade should be a team standard rather than a per-file taste.
## What a configured factory is A batch job needs identifiers of the form `RUN-0001`. Two settings decide them: the prefix and the number width. There are two ways to arrange this. Either every call site passes both settings every time, or you call a **factory** once with the settings and it returns an issuer that already knows them. The second arrangement is the subject here: the factory's parameters become captured bindings of the function it returns, so the settings ride along inside the value instead of travelling through every call. The shape is unremarkable — a function returning a function — but what it changes at the call sites is worth being precise about. ## What the call site stops carrying - **No configuration arguments.** The call is `issue()`, not `issue(prefix, width)`, so there is nothing to reorder, mistype or read from the wrong variable. - **No agreement to maintain.** Ten call sites passing the same two values is ten places that can drift apart; one factory call is one place. - **No repeated validation.** If the width must be at least one, that check belongs where the width arrives, not on the hot path of every identifier. - **No mid-run change.** A caller that never receives the settings cannot alter them for the next call, deliberately or accidentally. ## What happens once instead of every call 1. **Validation.** Bad configuration fails at the moment the factory is called, before any issuer exists — so the failure lands next to the code that supplied the setting, not deep inside the run where the first identifier is formatted. 2. **Derivation.** Anything computable from the settings — a padding template, a precomputed maximum before the width overflows — is computed once and captured, rather than recomputed per identifier. 3. **Binding creation.** The counter is created here too, which is what makes the returned value an issuer rather than a formatter: it carries configuration *and* the state that changes. That ordering is the real payoff and is easy to state backwards. The work happens **before** the first call, not lazily on it; that is precisely why an invalid width is reported early. ## The configuration becomes unchangeable Once captured, the prefix and width have no name outside the factory body. A caller holding the issuer therefore has no expression it can write to change them. This is a genuine guarantee rather than a discipline: the way to get a different prefix is to ask the factory for another issuer, and that new issuer comes with its own counter and its own settings. Two coexisting issuers built from the same factory share nothing but the code path. Be careful about what is frozen. What cannot change is the **binding** — which prefix value the issuer will use. If a captured value is itself a mutable structure, whoever else holds that structure can still mutate its contents, and the issuer will see the change. Capturing a setting freezes the choice, not the thing chosen. ## Passing the configuration versus baking it in | | Passed on every call | Baked in by the factory | |---|---|---| | Call-site surface | prefix and width, every time | nothing | | When bad input is caught | on the call that uses it | when the factory runs | | Who can change the settings | any caller, at any call | nobody, without a new issuer | | Cost of validation | per call | once | | Visibility of settings | obvious at the call site | must be traced to construction | The last row is the honest cost. A reader looking at `issue()` cannot see the prefix; they must find where the issuer was built. The configuration has moved from something local and obvious to something distant and implicit, which is the same trade any construction-time binding makes. ## What it does not buy you Three claims are worth refusing explicitly, because candidates reach for them. - It does **not** make the issuer safe to share between concurrent workers. The counter is still a single mutable variable behind a function; coordinating that is a separate problem. - It does **not** validate anything the caller supplies later, because callers supply nothing later. Only the configuration was checked. - It does **not** reduce allocation. Each factory call produces a new function value with its own captured bindings; that is the price of independence between issuers. ## What to say in the interview "The settings become captured bindings of the returned function, so the call site passes nothing, validation and any derived work happen once at construction, and the settings cannot be changed afterwards — a different prefix means a new issuer with its own counter. The cost is that the configuration is no longer visible where it is used."
- Two issuers are built from the same factory with different prefixes. What do they share?Only the code path. Each factory call created its own bindings, so each issuer has its own prefix, its own width and its own counter, and one cannot observe or disturb the other's sequence.
- The captured configuration is a structure the caller also holds a reference to. Is it still frozen?No. Capturing freezes which value the issuer will use, not the contents of that value. If the caller mutates the structure, the issuer sees the mutation on its next call. If that matters, copy the fields you need at construction and capture the copies.
- What does a reader lose when configuration moves from the call site into the factory?Locality. At `issue()` there is nothing on screen saying which prefix or width is in force, so the reader must trace back to where the issuer was constructed. That is the standard cost of construction-time binding, and it is why the construction site deserves a clear name.
saying these in an interview costs you the question
- Says the issuer re-reads the configuration from the factory on every call.
- Claims baking in the settings makes the issuer safe to share across concurrent workers.
- Thinks bad configuration is only reported when the first identifier is formatted.
- Assumes capturing a mutable structure freezes its contents as well as the choice.
- Believes two issuers from one factory must share the configuration they were given.