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)