skip to content

Your codebase uses both composition directions in different modules - how do you settle on one direction and migrate to it?

level: principalimportance: nice to knowfreq 26%

answer

  1. convention, not correctness
  2. optimise the reader, not the runtime
  3. existing weight usually decides
  4. direction belongs in the name
  5. module by module behind output tests

basics

~20 s

Neither direction is more correct, so decide on reading cost and existing weight: pick the one most chains and reviewers already use, put the direction in the helper's name, then migrate module by module behind tests of each chain's output rather than in one sweep.

solid answer

~40 s

Treat this as a convention decision, not a correctness one - both directions wire identical chains. The inputs worth weighing are which direction the majority of existing chains already read, which one matches the surrounding style so a reader's eye moves with the data, and how much code you would have to touch. Then make the decision cheap to obey: export one helper, name it so the direction is in the name, and leave the other as a local in modules not yet converted. Migrate per module with a behavioural test on each chain first, because a reversed chain is silent. Write down the decision and the one exception you are willing to allow.

go deeper

for a junior

Recall that both directions produce the same chain, so a team choosing between them is settling a reading convention rather than fixing a defect.

for a middle

Explain the concrete cost of a mixed codebase: a listing written for one helper and passed to the other runs backwards while every join still fits.

for a senior

Describe an incremental migration - behavioural test per chain, one helper exported and named for its direction, the other demoted to a local, one module per change.

for a principal

Own the decision: name the inputs you weighed, the exception you will allow, how the standard is made visible at every call site, and how you will know the migration finished.

Both directions build the same chain, produce the same result and cost the same to run. So this is not a question with a technically correct answer; it is a standard-setting question, and the thing being optimised is the cost paid by every future reader and reviewer. ## What is actually at stake - **Reading cost per line.** In a left-to-right listing the reader's eye moves with the data. In a right-to-left listing it moves against it, and the reversal has to be held in the reader's head for the length of the chain. - **Reviewer error rate.** Mixed directions in one codebase produce a specific silent defect - a listing written for one helper passed to the other - which no amount of care eliminates. - **Onboarding.** A newcomer who has to ask "which way does this one go?" is paying the cost the standard exists to remove. - **Familiarity.** Right-to-left mirrors nested application and the spoken "f after g"; left-to-right mirrors a list of steps. Teams genuinely split on which reads better, and the split is not evidence that one side is wrong. ## Inputs to the decision | Input | Why it weighs | How to measure it | |---|---|---| | Existing weight | Fewer call sites to touch, less relearning | Count chains per direction across the codebase | | Surrounding style | A chain that reads like the code around it costs less | Look at how the dominant data-transformation style already reads | | Shape of typical chains | Long chains punish the against-the-data reading more | Median stage count per chain | | Team preference | A standard nobody likes gets quietly violated | Ask, once, and record the answer | Weight of existing code usually decides it, and that is a legitimate reason: the cheapest standard is the one most of the codebase already follows. ## Running the migration 1. **Write the tests first.** One behavioural assertion per chain - a property of the finished output. A reversal produces a wrong result and no error, so a test of the result is the only net under the migration. 2. **Make the chosen helper the obvious one.** Export it; give it a name that carries the direction, built on "after" for right-to-left or "then" for left-to-right, so no call site depends on remembering a convention. 3. **Demote the other.** Leave it as a local in the modules still using it, not as a shared export. It disappears as those modules convert; nothing new can reach for it. 4. **Convert one module per change.** Never mix directions inside a file, and never convert across modules in one commit - a sweeping rewrite of every call site is the change most likely to reverse a chain and hide it among a hundred others. 5. **Delete the demoted helper when the last user is gone.** A pre-production codebase should not keep the old helper around for callers it does not have. ## What to write down The decision record needs three lines, not three pages: the direction chosen, the reason (usually existing weight plus surrounding style), and the naming rule. Add the one exception you are prepared to defend - typically a boundary where a chain must be handed to something that expects the opposite convention - and require that exceptions are named at the call site rather than argued case by case in review. ## Where leads get this wrong - **Arguing correctness.** There is none to argue; insisting otherwise guarantees the standard is resented and ignored. - **Standardising without migrating.** A written standard plus a codebase full of the other direction is the mixed state, with extra paperwork. - **Migrating in one sweep.** It maximises exactly the silent defect the standard exists to prevent. - **Keeping both helpers exported "so people can choose".** That is the status quo with a decision record attached to it. ## What an interviewer is listening for That you separate the convention question from any technical claim, that your decision rests on measurable inputs rather than taste, and that the migration plan is incremental and has a behavioural net under it. The strongest answers also say what they would *not* do: no big-bang rewrite, no permanent second helper, and no standard written without a way to see who is still violating it.

  • Half the team finds the other direction more natural - does that change the decision?
    It changes how you communicate it, not the decision. Uniformity is the whole benefit, so pick one, record the reason, and acknowledge the preference rather than pretending it is wrong. What you cannot do is split the difference by keeping both, because mixing the two is what produces the silent reversed chain in the first place.
  • Is there a defensible case for keeping both directions?
    One narrow case: a boundary where a chain must be handed to something that expects the opposite convention. Keep the exception local, name it at the call site so no reader has to infer it, and do not let it grow into a second house style. Every other "we need both" is a preference wearing a requirement's clothes.
  • How do you know the migration is finished?
    The demoted helper has no remaining users and is deleted. Until then it is countable, which is the point of demoting it to a local rather than leaving it exported: the number of modules still declaring it is the progress metric, and zero is the finish line.

saying these in an interview costs you the question

  • Argues one direction is objectively correct for every team
  • Plans a single sweeping rewrite of every call site
  • Keeps both helpers exported so each author may choose
  • Treats the choice as style with no reading cost
  • Writes the standard without migrating existing chains
  • Records the direction in a comment instead of the name