skip to content

When one language supports several styles, on what basis do you choose one for a given module rather than the whole repository?

level: middleimportance: should knowfreq 50%

answer

  1. match the style to the data
  2. identity and lifecycle versus plain values
  3. does the same input always answer the same
  4. every switch of style adds a seam
  5. choose per module, not per function

basics

~20 s

Choose on the data's properties, not preference: things with identity and a lifecycle suit objects holding state, transformations of plain values suit pure functions, fast-changing rules suit a description one evaluator reads. Decide per module - every switch adds a seam.

solid answer

~50 s

Ask three questions about the module. Does the data have **identity and a lifecycle** that outlives a single call - something that is the same order before and after, with a status that moves? That is state someone must own, and an object-shaped region fits. Is the work a **transformation** of values with no identity - prices in, prices out - where the same inputs should always give the same answer? That is a pure-function region. Is it a **rule set** that changes more often than the code that runs it? Then describe the rules and let one evaluator interpret them. The granularity matters as much as the answer: choose per module, because each switch of style creates a seam that must be converted across, and a per-function free-for-all buys you the conversion cost without the region you were paying for.

code

pseudocode · 9 lines
pseudocode
// identity and lifecycle: this order exists between calls
function markPriced(order, clock)
    order.status = "PRICED"
    order.pricedAt = clock.now()

// no identity: values in, value out, same answer every time
function totalFor(lines, rules)
    return fold(lines, 0, function(sum, line)
        return sum + priceOf(line, rules))

go deeper

for a junior

Recall that some data is a thing with identity that changes over time and some is a plain value that is interchangeable, and that the second kind is what pure transformations are for. Be able to point at one of each in code you have written.

for a middle

Explain the decision on the properties of the problem - identity, reproducibility, how fast the rules change - and note that switching style creates a boundary, which is why the unit of choice is a module rather than a function.

for a senior

Show the split applied to a real pipeline: which region kept state, which became transforms over values, what crosses the line between them, and which module you deliberately left inconsistent because it was too small to earn a boundary.

for a principal

Own the rule the teams follow: how large a region must be before it may adopt its own style, who arbitrates, and how you keep the count of boundaries low enough that the codebase stays readable to people who join later.

## Why the question is not about preference When a language offers several styles, "which style should we use" has no answer at the level of the repository, because different parts of a system have genuinely different shapes. The interviewer is checking that you can name the properties of a problem that make one style fit, and that you know the choice has a price: every place two styles meet is a boundary someone has to convert across. ## The properties that decide it - **Identity and lifecycle.** If two things with identical fields are still not the same thing - two orders that happen to match are not one order - the concept has identity, something must own its current state, and the transitions it may make are the interesting logic. A region built from objects that hold state and guard their own transitions fits. - **Value-ness.** If two things with identical fields are interchangeable - a money amount, a tax rate, a priced line - they are values. Values want to be immutable and passed around freely, and the work over them is transformation. - **Determinism of the work.** If the same inputs must always yield the same output, and you want to test it by handing it arguments, that is a pure-function region regardless of what the rest of the system looks like. - **Rate of change of the rules.** If the policy changes faster than the machinery that applies it, describing the rules as data for one evaluator beats encoding each rule as more branches. - **Machine shape.** A tight loop over a large buffer where layout and per-element cost decide the outcome is honest imperative work, and rewriting it as a chain of transformations for consistency is a choice you should be able to defend on more than taste. ## Applying it to one pipeline An order-processing pipeline usually splits cleanly: | Part of the pipeline | Shape of the problem | Style that fits | |---|---|---| | The order as it moves through states | Identity, lifecycle, transitions to guard | Objects owning their state | | Pricing, discounts, tax | Values in, values out, must be reproducible | Pure transforms over immutable values | | Eligibility and promotion rules | Policy changing faster than the code | Rules described as data, one evaluator | | Batch export of a day's orders | Volume, layout, per-element cost | Straight imperative iteration | Notice that none of those rows names a language feature. That is the test of whether the reasoning is about the problem: if your justification changes when you change language, you were choosing by syntax. ## Why the unit is the module The cost of a style boundary is paid where the styles meet, so the number of boundaries matters more than which style is on either side: 1. **Per-function choice** gives the maximum number of boundaries and the minimum size of region. Nothing can be reasoned about as a whole, and conversions appear in the middle of call chains. 2. **Per-module choice** gives a region large enough that a reader can hold one model in their head while inside it, and a boundary thin enough to name. 3. **Per-repository choice** removes boundaries entirely but forces every problem into one shape, which is where you get objects with no behaviour wrapping values, or a transformation pipeline pretending it has no state. The middle option is usually right, and being able to say *why* - that the seam is the cost, so make regions few and large - is the substance of the answer. ## How the choice goes wrong - **Choosing by the language's reputation** rather than the problem, so every module gets the style the language is famous for even where it fits badly. - **Choosing by the author's last project**, which is how a repository ends up with three styles distributed randomly across files. - **Treating one style as strictly better** and grading modules against it, instead of asking what the data looks like. - **Ignoring the seam**, picking freely per function and then discovering the conversions dominate. - **Choosing a region too small to be worth a boundary** - a two-function pure island inside a mutating module usually costs more in conversion than it earns in reasoning. ## What a strong answer sounds like A strong candidate answers with the pipeline in front of them: this part has identity so it keeps state; this part is a transformation so it takes values and returns values; the line between them is here and here is what crosses it. A weak one answers with a ranking of styles. The question is deliberately a judgement question - there is no universally correct assignment, only a defensible one for the system described.

  • A module has both kinds of data mixed together - how do you split it?
    Pull the value-shaped work out first, because it is the part that can be lifted with no coordination: give the transforms arguments instead of reaching into the object, and let the stateful part call them. The split is done when the stateful part decides *when* things happen and the transform part decides *what* the answer is.
  • Is there a case where consistency should beat fit?
    Yes, when the region is small. A handful of functions that would form their own style island usually cost more in boundary and reader-switching than the fit gains, so they should follow their neighbours. Fit wins once the region is big enough that someone will spend real time inside it.

saying these in an interview costs you the question

  • Ranks styles as better and worse instead of fitting them to data
  • Picks the style the language is best known for, every time
  • Chooses per function, then finds conversions all through the call chain
  • Says a consistent repository has no style boundaries to pay for
  • Cannot name a property of the problem that drove the choice