💸

技術的負債の芽は「構造」にある──「しゃあない」の具体例を見ていく

に公開

技術的負債の芽は「構造」にある ──「しゃあない」の具体例を見ていく

みなさん、こんにちは!フロントエンドエンジニアの @nyaomaru です!

https://zenn.dev/nyaomaru/articles/technical-debt-basics

「なんでこの Button.vue、3 種類あるんや…?」
そんな小さな違和感から始まる技術的負債。今回はその“構造的なはじまり”を

なるべく実務ベースで深堀りしていきます。

ほんなら、一緒に見てこな!

💸 技術的負債が溜まるプロセス

初めから dirty な実装は少ないねん。 review で品質を担保してれば、ある程度防げるかな~と思っとる。

コード自体の品質も大事やねんけど、技術的負債は、以下のような「小さな揺らぎ」が積み重なって生まれるんや:

  • 規模が肥大化してきたり
  • 機能追加による改修が重なったり
  • 既存コードが誤解されたり
  • 共通化・汎用化されなかったり

の積み重ねで、技術的負債って指数関数的に増えていくねんな。

ほな今回は、「規模の肥大化」について、Vue を用いて説明していくで~

📦 規模の肥大化

よくあるケースが、ディレクトリ構成がぐちゃぐちゃになること。

Atomic Design ベースで作業を進めた場合の、コンポーネントディレクトリの例を挙げてみるで~。

最初に言っとくけど、Atomic Design 自体が悪いわけやないで。完全な責務の分離ができていれば成立するはずやねんけど、何でもかんでもコンポーネントを Atomic Design でやろうとするとうまくいかへんことが多いねんな。

🧱 あの頃は・・・

最初の頃はみんな美しかった。
atoms にボタン、molecules にフォーム部品、organisms に一画面分のまとまり…そう、完璧やった。

components/
├── atoms/
│   ├── Button.vue
│   ├── Input.vue
│   └── Label.vue
├── molecules/
│   ├── InputGroup.vue
│   └── RadioGroup.vue
└── organisms/
    ├── LoginForm.vue
    └── RegisterForm.vue

当初の正義:「粒度でコンポーネントを整理しよう」

🧟‍♂️ だがしかし:現場で生き残るための小さな妥協の積み重ね

「とりあえず今だけ」って言って、ログイン画面専用の特殊ボタンを atoms に突っ込んだ。

「まあ見た目は似てるし...」と Input.vue を 7 通りの props & Button.vueLabel.vue を用いて無理やり対応させた。

LoginFormWithSocial.vue は特定画面でしか使わんのに、「まあ reusable に見えるしええか」と 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

気が付いたら:分類の崩壊

  • atoms には、もはや粒度ではなく「なんとなくシンプルそうなやつ」が集まる
  • molecules には、「どこにも入れたくないやつ」の墓場に…
  • organisms には、共通性ゼロのデカい塊が山積み

☠️ 負債の正体:構造という正義が、運用とスピードの前に敗北した

正義(Atomic Design)はルール化されてなかったので、いつでも破れる。

命名規則と分類ガイドラインが無いと、属人化して一貫性が消える。

気づけば 「このファイル何に使われてたっけ?」 が頻発して誰も触れなくなる。

✅ 教訓:「構造」って、ずっとそのまま使えると思ったら負けやで

  • ルール決めとかな、あとから「なんでこんなとこにあるん?」ってなる
    • components/atoms から features/ に import したら CI 落ちるようにするとか
    • ガイドラインに「これは OK、これは NG」って書いとくとか
  • ディレクトリ構成も “設計対象” やねん
    • なんとなくで置くんやなくて、「ここに置く理由」まで考えて決めるのが吉
    • 「それどこに置く?」を設計の会話に含めるだけで全体の統一感が変わるで
  • 最初はよくても、半年後には分類が地獄になる可能性あり
    • 半年に一回「この構成、今でもええ感じか?」をチェックする時間作っとこ
    • 「なんか最近、このフォルダ構成気持ち悪いねん」って話せる場があると最高

🧱 共通と業務の分類が消えた日:全部「atoms」に吸い込まれる世界

最初は “再利用できそう” という観点で atoms を育てていたはずやのに、
気づいたら UI コンポーネント(汎用)と、業務ロジックべったりなやつ(ドメイン特化) が混在して、誰も何を信じたらええかわからん状態に。

📌 理想の構成(分離バッチリ)

💡 Atomic Design は UI 粒度にフォーカスした設計手法やけど、業務ドメインとの分離までカバーしきれへん。

そこで最近では、UI 部品(ui/)とドメイン特化部品(features/)を分ける「責務ベースの設計」 も注目されてるで。

これは Feature-Sliced Design に近い考え方やな!

components/
├── ui/              ← 共通コンポーネント(ボタン・モーダルなど)
│   ├── Button.vue
│   └── Modal.vue
├── features/        ← 業務コンポーネント(ドメイン依存)
│   └── transaction/
│       ├── TransactionStatusBadge.vue
│       └── TransactionActionButton.vue

思想:

  • ui/ は再利用できる汎用的な部品、デザイン的にも整ってる
  • features/ は業務の文脈ありき(ロジック、文言、制約がガチガチ)

🧟‍♂️ だがしかし:たぶんこのへんやろ!が続いていくと・・・

😱 現実の構成(混ぜるな危険)

components/
├── atoms/
│   ├── Button.vue
│   ├── ButtonForOrder.vue ← 🤔業務依存してるのにここ?
│   ├── Modal.vue
│   ├── TransactionStatusBadge.vue ← 🤔見た目はUIでも、責務はドメインロジック寄り
│   └── ConfirmDialogWithReason.vue ← 🤔 顧客IDを参照して確認文言を出し分ける、みたいな業務ロジック入りまくり

なにが起こってるか:

  • 「UI っぽいから atoms に入れとこ」で業務ロジックも放り込まれる
  • 後続の開発者は「atoms = 安心安全な再利用部品」やと勘違いして、仕様ミスが起こる
  • features/ を作る文化が無く、結果的に「全部が共通コンポーネントに見える病」に…

☠️ 負債の正体:“使い回せそう” という幻影の誘惑

見た目が汎用的でも、中身が業務ロジックに縛られてることはよくある

components/atoms の中身に “顧客 ID” や “注文ステータス” が出てきたら、それはもう features/

✅ 教訓:「再利用できる」≠「atoms に置いてええ」

  • 汎用性・依存性・責務 を明示的にチェックする文化が必要
    • 一定のスプリント感覚で設計レビューをみんなでする、とか -「これ atoms に置いていい?」って問いをチームで擦り合わせる機会を設ける、とか
  • ui/features/ の分離は設計段階で決めとかな後で死ぬ
    • 何が共通コンポーネントで、何が業務コンポーネントなのかの線引きをする、とか
    • 業務関心が入った時点で業務コンポーネントとする、とか
  • Atomic Design = デザインの粒度設計 であって、ビジネスロジックの粒度設計じゃない
    • あくまでもデザイン観点なので、業務関心とは切り離して考える、とか
    • 業務に必要なロジックはアトミックデザインの考えを取り込まないほうがやりやすい、とか

再利用できる UI と、ビジネスドメインに寄った UI の「境界」を意識するだけで、技術的負債はグッと減らせるで!

📦 まとめ

コンポーネントの設計は初期段階である程度固める必要がある。

でも、それは確定事項ではなく、組織はビジネスの成長に伴って、定期的に構造の見直しを行うほうがええ。

それを怠ると、気づいたら Atomic Design と称した怪物が生まれてしまう。

ほんで、事実上修復不能 or かなりの工数をかけないと修正できない状況になる。

せやから、構造的な設計も定期的に見直しかけて、みんなが開発しやすい環境を作ることが大事やな。

理想を完全に守るのは無理やけど、立て直すことはできるで~!!!

✅ この記事を読んだ人は、自分のプロジェクトの atoms にドメインロジックが入り込んでないか、今日 1 個だけでも見直してみてな!

今回の話は Vue 中心やったけど、React や他のプロジェクトでも同じ構造的な課題はあるで!

次回は、「機能追加による改修の重複」について、React を用いてじっくり見ていくで~!期待せんと、待っててな!

📌 この記事で言いたかったことまとめ

  • 再利用性だけで atoms に突っ込むと構造はすぐ死ぬ
  • UI とドメインは設計段階で分ける意識が必要
  • 「半年後の誰かが困るかも」で考えよう

おまけ

あくまでもサンプルやけど、指標としては以下のことを見たらええかもな

  • atoms/useAuth() など composables / hooks が出てきたら黄色信号
  • components/atoms/TransactionStatusBadge.vue ← ファイル名にドメイン名が入ってたら要チェック
  • components/organisms/FormVer2Copy.vueCopyVer2 が出てきたらいよいよ地獄の入口

👉 前回の記事(技術的負債ってなんやねん編)

https://zenn.dev/nyaomaru/articles/technical-debt-basics

👉 次回の記事(機能追加による改修の重複編)は公開済やで〜

https://zenn.dev/nyaomaru/articles/technical-debt-code

他の記事もあるで〜👇:

  • 📘 AI 時代を乗り切る設計術 ── Feature-Sliced Design (FSD) 超入門

https://zenn.dev/nyaomaru/articles/learn-fsd-design

  • 📘 AI 時代こそ npm publish

https://zenn.dev/nyaomaru/articles/ai-era-npm-publish

Discussion