Hey folks!
Sometimes I think bugs love me too much — they just keep coming back no matter what I do 🐛❤️
Anyway, I’m @nyaomaru, a frontend engineer.
In that article, I introduced what technical debt is.
This time, let’s dive into how those “just for now” decisions actually accumulate — with a practical, code-based look.
Ready? Let’s go! 🚀
💸 The Process of Accumulating Debt
Very few implementations start out dirty. With reviews and decent quality gates, you can catch a lot.
But debt creeps in through small cracks, like:
- The system grows in scale
- Patches pile up during feature additions
- Old code gets misread or misused
- No one bothers to abstract or generalize
Each by itself seems harmless, but combined they cause debt to grow exponentially.
This time, let’s look at scale growth — using Vue as an example.
📦 Scale Growth
One classic case: directory structures slowly collapsing.
Take an Atomic Design–based component setup, for instance.
⚠️ Disclaimer: Atomic Design itself isn’t the villain.
It works fine if responsibilities are clearly separated.
But when you try to apply it everywhere, things go sideways.
🧱 The Good Old Days…
At first, everything was clean:
atoms had buttons, molecules had form groups, organisms had whole forms. Beautiful.
components/
├── atoms/
│ ├── Button.vue
│ ├── Input.vue
│ └── Label.vue
├── molecules/
│ ├── InputGroup.vue
│ └── RadioGroup.vue
└── organisms/
├── LoginForm.vue
└── RegisterForm.vue
Justice back then: “Sort by granularity!”
🧟♂️ But Then: Survival-Driven Compromises
- “Just for now” → a login-only button sneaks into atoms/
- “Close enough” → Input.vue overloaded with 7 props + extra hacks
- “Looks reusable” → LoginFormWithSocial.vue shoved into organisms/
components/
├── atoms/
│ ├── Button.vue
│ ├── ButtonBlue.vue
│ ├── ButtonBlueSm.vue
│ ├── ButtonWithLoginIcon.vue
│ ├── Input.vue
│ └── Label.vue
├── molecules/
│ ├── Form.vue
│ ├── FormVer2.vue
│ └── FormVer2Copy.vue
└── organisms/
├── LoginForm.vue
├── RegisterForm.vue
└── LoginFormWithSocial.vue
Before you know it: classification collapse
-
atoms/
= “looks simple-ish” bucket -
molecules/
= graveyard of misfits -
organisms/
= pile of unrelated monsters
☠️ Root Cause of Debt: Structure Loses to Speed
- Justice (Atomic Design) wasn’t enforced, so it was broken at will
- No naming rules or classification guide → inconsistency everywhere
- Soon: “Wait, what was this file for again?” — no one dares touch it
✅ Lesson: Structure Must Be Reviewed Regularly
- Enforce rules with CI (e.g., block
atoms/
from importingfeatures/
) - Treat directory layout itself as part of the design
- Expect categories to rot within 6 months if left unchecked
🧱 When “Atoms” Absorbs Everything
Atoms were supposed to be reusable UI parts.
But gradually, generic UI and domain-heavy components got mixed together.
📌 Ideal Separation
Atomic Design focuses on UI granularity — not domain separation.
So recently, a responsibility-based split (ui/ vs features/) is becoming popular, close to Feature-Sliced Design.
components/
├── ui/ ← Generic reusable UI (buttons, modals)
│ ├── Button.vue
│ └── Modal.vue
├── features/ ← Domain-specific UI (business logic included)
│ └── transaction/
│ ├── TransactionStatusBadge.vue
│ └── TransactionActionButton.vue
Idea:
-
ui/
= polished, reusable, domain-agnostic -
features/
= business-context-bound, with logic and constraints baked in
🧟♂️ But Reality: Mix at Your Own Risk
😱 Actual structure that often emerges:
components/
├── atoms/
│ ├── Button.vue
│ ├── ButtonForOrder.vue ← 🤔 domain-specific but placed here?
│ ├── Modal.vue
│ ├── TransactionStatusBadge.vue ← 🤔 looks generic, but logic is domain-bound
│ └── ConfirmDialogWithReason.vue ← references customer IDs, business logic galore
What happens:
- “Looks like UI” → tossed into
atoms/
even if domain-bound - Later devs assume
atoms/
= safe reusable pieces → specs break - No culture of making
features/
→ everything looks “common”
☠️ Root of Debt: The Illusion of Reusability
- Generic-looking code can still be domain-tied
- If
atoms/
contains “customerId” or “orderStatus,” it’s alreadyfeatures/
✅ Lesson: “Reusable” ≠ “Put in Atoms”
- Judge by responsibility, not just looks
- Split
ui/
andfeatures/
early — or regret later - Remember: Atomic Design = UI granularity design, not business logic design
👉 Simply being aware of the boundary between reusable UI and domain-tied UI cuts debt massively.
📦 Wrap-Up
Component architecture needs an initial plan.
But it’s not final — orgs must revisit structure as the product evolves.
Ignore this, and your “Atomic Design” turns into a monster.
Eventually, it’s either unfixable or costs a fortune to repair.
So: review structure regularly, keep it healthy, and keep development enjoyable.
Perfection isn’t possible — but recovery is! 💪
✅ Homework: check your project’s atoms/
today. If you see domain logic creeping in, fix just one thing.
This article focused on Vue, but React and other stacks face the same structural debt traps.
👉 Next time: feature additions and overlapping refactors, explored with React. Stay tuned!
✂️ That’s the gist!
Technical debt doesn’t appear out of thin air — it sneaks in through small “just for now” compromises.
This time we looked at structural drift, especially around Atomic Design gone wild.
Have you seen your own project’s atoms/
or molecules/
slowly turning into junk drawers?
Share your horror stories or survival tips in the comments — I’d love to learn how your team fights debt!
Top comments (0)