DEV Community

Nadeem Ur-Rehman
Nadeem Ur-Rehman

Posted on

Your Utils Folder Is a Crime Scene

Your Utils Folder Is a Crime Scene

If your interviewer asks "how do you decompose a large frontend?" and you answer "components, hooks, and utils," you have just told them exactly where your architecture goes to die.

Watch the 40-second version: Q4: Frontend System Design Interview: Decompose

Every senior I have ever interviewed has inherited the same repo. Two hundred folders, neatly named by file type: components, hooks, utils, helpers, shared. It looks organized for about ten minutes, right up until you try to change one feature and discover it lives in six different folders, none of which talk to each other, all of which import from a utils.ts that has grown to 3,000 lines and is essentially the code equivalent of a junk drawer.

That is decomposition by file type. It answers the question nobody asked: "what kind of thing is this file?" The question that actually matters is: "what business capability owns this?"

Here is the non-obvious part. Decomposition is not a file-layout exercise, it is a seams exercise. A seam is a boundary between two capabilities that can change independently. If changing checkout requires touching pricing, your seam is in the wrong place, and no folder naming convention will save you. Interviewers know this. When they ask you to decompose, they are watching for one instinct: do you slice by capability with contracts between the slices, or do you alphabetize junk?

The rule I use on real codebases, and in interviews: one feature, one folder. Checkout owns its UI, its state, and its API calls. If I can hand the checkout folder to another team and it survives, the decomposition is real. If deleting a folder leaves 400 broken imports across the repo, it was never decomposed, it was just categorized.

The seam-drawing checklist

Use this before you draw a single box:

  1. List the capabilities, not the pages. Checkout, pricing, auth, search. Pages are navigation; capabilities are seams.
  2. Give each capability a home: UI, state, and data access live together. Colocation beats convention.
  3. Ban the junk drawers. No new utils, helpers, or shared folders without a named owner and an expiry date.
  4. Draw the contracts between capabilities, not just the boxes. What does checkout import from pricing? If the answer is "everything," your boundary is fiction.
  5. Test the seam: could one capability be rewritten or swapped without touching the others? If yes, you are done.

Get the seams right and every diagram after them is believable. Get them wrong and the rest of your system design is fiction dressed up in boxes and arrows.

So tell me: what is the worst junk-drawer folder you have inherited? I have seen a common.tsx with 8,000 lines. I am still not over it.

Top comments (0)