How do you recognise and break up a shared utility module that every part of an automation suite imports?
answer
- Its name names no responsibility
- It depends both upward and downward
- Everything imports it, nothing can leave it
- Freeze it before draining it
- Move one cohesive group at a time
basics
~20 sRecognise it by its symptoms: a name that names no subject, imports from every layer, and nothing that can be extracted. Break it up by freezing it and moving out one cohesive group at a time.
solid answer
~50 sA dependency magnet is a module with no single responsibility that everything imports and nobody can change — usually called *common* or *helpers*. The checkable smell is its own import list: it depends both upward and downward, so any module importing it has transitively imported the whole suite and the one-way rule is defeated. Break it up incrementally rather than in a freeze. **Freeze** it first: no new members, so it stops growing. Then **group its members by subject** — time control, identifier generation, data builders, the holder for the driver session that operates the application — and move the groups that import nothing first, re-pointing their importers as you go. Each move is one reviewable change with an obvious rollback, and each shrinks the magnet's import list until the direction can be enforced again.
code
pseudocode · 12 linesmodule common_utils: # 41 members, imported by every module
imports -> adapters # for the holder of the driver session
imports -> actions # for an order builder
imports -> cases # for sample-data constants
# depends both ways, so nothing can be lifted out on its own
# after draining, each module names a subject and imports downward only
module clock_control: imports -> nothing
module identifiers: imports -> nothing
module data_builders: imports -> nothing
module comparisons: imports -> nothing
module session_holder: imports -> adaptersgo deeper
Recall that a module named for its position rather than a subject tends to collect unrelated code, and that a helper everyone imports is hard to change safely later.
Explain the mechanics: why a helper that imports both upward and downward defeats a layering rule, and how grouping members by subject reveals which parts can be extracted first.
Show that you have drained one while the team kept shipping: the freeze, the cohesion inventory, moving the leaf groups first, and the members you deleted rather than moved.
Own the tradeoff between a shared module's convenience and the coupling it creates, and the organisational half of the fix — modules without a named owner will collect whatever has nowhere else to go.
## What a dependency magnet is Almost every automation suite grows one module named for what it is not: *common*, *helpers*, *utils*, *core*. It starts as three functions nobody could place and ends as the module every other module imports and nobody can change. That is a **dependency magnet**: a module with no single reason to exist, positioned so that adding to it is always the cheapest available move. The reason it is a boundary problem rather than an untidiness problem is that it **defeats the layering around it**. A helper module that itself imports adapters, actions and case data is a back door: any case that imports the helper has transitively imported everything, and the one-way rule the suite thought it had is now decorative. ## The smells that identify one - **Its name says nothing.** *common*, *utils*, *shared*, *base* — the name describes position in the build, not a subject. A module whose name cannot finish the sentence "this module is responsible for..." has no boundary. - **It imports from every layer.** This is the strongest signal and the most checkable one: run the import graph and look for the module that depends both upward and downward. - **Everything imports it.** Fan-in near the module count of the suite means any change to it touches everything. - **Nothing can be lifted out.** Try to extract one function and the extraction pulls half the module with it, because members were placed by convenience rather than cohesion. - **It has no owner.** Every team adds, no team reviews, and the module's review history is a list of unrelated one-line additions. - **Its members have unrelated lifetimes.** Time control, identifier generation, data builders, formatting and a holder for the driver session that operates the application all live together with nothing in common but the folder. - **Changing it is scary.** People copy a function rather than edit the shared one, which is the clearest evidence that the module has become un-ownable, and it also tells you duplication is already accumulating. ## Breaking it up without stopping the suite The constraint that makes this interesting is that the suite has to stay usable throughout: the team is shipping, and a week-long freeze to reorganise a helper module is never granted. The sequence below keeps the suite running the whole way. 1. **Freeze it.** No new members, effective immediately, enforced by review and ideally by a check on the module's member count. A magnet that is still growing cannot be drained; this step alone stops the problem getting worse and costs nothing. 2. **Inventory by cohesion, not by size.** Group the members by what they are *about* — time control, identifier generation, data builders, comparison helpers, the holder for the driver session — and by which layer legitimately needs each group. 3. **Order the groups by how tangled they are.** Start with the leaves: groups that import nothing from the suite. Each of those can move in one step, and each move shrinks the magnet's own import list. 4. **Move one group at a time and re-point its importers.** One group is a small, reviewable change with an obvious rollback. Ten groups at once is a change nobody can review and every conflict lands on it. 5. **Give each new module a name and an owner.** The name has to finish the responsibility sentence. Without an owner the new modules become small magnets in a year. 6. **Re-assert the direction as you go.** Each extracted module gets its own import rule, and when the magnet's last upward import is gone, the mechanical check can be tightened to forbid the shape coming back. ## What to watch while draining - **Shared mutable holders are not a naming problem.** A member that holds harness state shared across parallel workers is a coupling problem in its own right, and moving it to a better-named module does not fix it — decide who owns that state before you move it. - **Some members should be deleted, not moved.** A magnet always contains functions with no callers and functions with one caller that belong inside their caller. Deleting is the cheapest extraction. - **Duplication found during the sweep is information.** Two nearly identical helpers usually mean two teams needed slightly different behaviour and neither could change the shared one. Merging them without asking why they diverged rebuilds the magnet. - **Stop when the smells are gone, not when the folder is empty.** The last handful of genuinely cross-cutting helpers can stay together if they share a subject and nothing above them. ## Why interviewers ask this It is not a common screening question, which is exactly why it separates people: it needs someone who has watched a suite ossify. A strong answer names the checkable smell — a module that imports from every layer and is imported by every layer — and then gives a sequence that never requires the suite to stop, because that constraint is what makes the problem real rather than a refactoring exercise.
- Which group of members do you move first, and why that one?The group that imports nothing else from the suite — clock control, identifier generation, pure comparisons. Each is a one-step move with an obvious rollback, and each one removes an entry from the magnet's own import list, which is what eventually lets the mechanical direction check be tightened. Tangled groups get easier once the leaves are gone.
- You find two near-identical helpers while draining. Do you merge them?Not before asking why they diverged. Two copies usually mean two teams needed slightly different behaviour and neither could safely change the shared one. Merging without answering that question rebuilds the magnet with a parameter for each difference. Sometimes the right outcome is two clearly named helpers owned by the teams that need them.
- How do you keep a new magnet from forming a year later?Give every extracted module a name that finishes the sentence 'this module is responsible for...' and a named owner, and keep the mechanical check that fails on a module depending both upward and downward. The failure mode is social as much as structural: modules nobody owns collect whatever has nowhere else to go.
It is the junk drawer. Everyone adds one more thing because it is the nearest place, and eventually nobody can move the drawer without moving the house.
saying these in an interview costs you the question
- Says a big shared helpers module is normal in every suite
- Plans a full reorganisation that freezes the suite for a week
- Splits the module by line count rather than by subject
- Moves the module lower down and calls the direction fixed
- Merges duplicated helpers without asking why they diverged