Lessons from Failure

失敗から学んだ設計判断

「正しかったはず」が壊れるとき

受託開発の現場で踏んだ設計ミスと、そこから得た勘所。

2026年3月 · 読了 約10分 · 中堅エンジニア・設計判断に悩む方向け

この記事のポイント

  • 「正しい設計」をしたはずなのに壊れた、転職直後の3つの実体験
  • 失敗の原因は技術力ではなく「前提の見誤り」だったこと
  • 失敗を繰り返さないために身についた「設計前にやる3つの問い」

Chapter 01

「正解」を疑えなかった頃

インフラエンジニアとして7年。サーバーの構築、監視、障害対応をひたすらやってきた自分が、「何かを作る側」になりたくて受託開発の世界に飛び込んだ。

転職直後、自分がまずやったのは本を読むことだった。デザインパターン、クリーンアーキテクチャ、SOLID原則。読めば読むほど「正しいやり方」が見えてくる気がした。教科書が示す構造に従えば、良いコードが書けるはずだと信じていた。

現場に入ってすぐ、先輩から衝撃的なことを言われた。「分かっていないのに書くより、分からないから書かないほうがマシだよ」と。当時の自分は「とにかく手を動かさなきゃ」と焦っていたが、先輩は「理解してから書け」と教えてくれた。

振り返ると、転職してすぐの自分は毎日が発見の連続だった。インフラの世界では「設計」といえばネットワーク構成図やサーバースペックの話だったが、Web開発の「設計」はもっと抽象的で、もっと自由度が高かった。

自由度が高いということは、間違える余地も大きいということだ。等身大のコードを書くことの大切さに気づくまでに、いくつかの痛い経験が必要だった。背伸びしたコードは、書いた本人にすら読めなくなる。

学ぶ速度と実践のギャップ。本で知識を入れるスピードに、現場で使いこなす力が追いつかない。そのギャップが、これから話す3つの失敗を生んだ。

Chapter 02

失敗①——過剰な抽象化

最初の失敗は、小規模な受託案件でのことだった。

機能としてはシンプルな業務システム。CRUD操作が中心で、ユーザー数も限られている。にもかかわらず、自分は教科書通りにService層、Repository層を細かく分離した。「将来の拡張に備えて」という理由で。

結果はどうなったか。クラス数が膨れ上がり、ひとつの機能を改修するのに5つも6つもファイルを追う必要が出た。コントローラーからサービスを呼び、サービスからリポジトリを呼び、リポジトリからエンティティを……と、処理の流れを追うだけで疲弊する構造になっていた。

チームメンバーから「どこに何があるかわからない」と言われたときの衝撃は、今でも覚えている。自分は「きれいに分離できた」と思っていたのに、他の人から見ると「迷路」だった。

「資料を書くと管理コストが発生する」——現場で学んだこの教訓は、抽象化にもそのまま当てはまる。レイヤーを増やせば、レイヤー間の整合性を維持するコストが発生する。

YAGNIという原則は知っていた。「You Aren't Gonna Need It」——今必要でないものは作るな。頭では理解していたのに、「将来の拡張に備えて」という免罪符が、YAGNIを無力化していた。

「将来」は来なかった。そのプロジェクトは初期リリース後、大きな機能追加なく安定運用に入った。過剰に分離されたレイヤーは、誰にも使われないまま残り続けた。

教訓

抽象化はコストである。今必要な分だけ導入する。レイヤーを増やすことは「整理」ではなく「投資」だ。リターンが見込めない投資は、負債にしかならない。

Chapter 03

失敗②——「自分の追加分」しかテストしていなかった

2つ目の失敗は、医療系ドメインの在庫管理システムの保守で起きた。

既存の在庫管理ロジックに機能追加を行う担当になった。入出庫の履歴表示や発注データの出力など、新しい機能を実装してテストを書いた。正常系のテストは全部グリーン。自分の追加分は問題ないと安心していた。

ところが、運用中に問題が浮上した。在庫計算ロジックにエッジケースで不正な結果を返す不具合が潜んでいた。自分が追加した機能ではなく、既存のロジックに元々あった問題だった。結果的に既存コードを丹念に読み解いて原因を特定し、修正することになった。

このとき痛感したのは、自分が書いた部分だけテストして安心していたということだ。先輩に言われた言葉が刺さった。「テストを書くということは、データの動きが正しいかどうかを捉えることだ。自分が書いたコードだけでなく、データが通る道全体を見ないといけない」と。

自分のテストは「自分の追加分が動くこと」を証明するためのものだった。「システム全体として壊れないこと」を証明するためのものではなかった。この違いは大きい。

全パターンを網羅するのは現実的に無理だ。だからこそ「枝刈り」の考え方が必要になる。全部テストするのではなく、壊れやすい箇所を見極めて、そこに集中する。

テストケースの優先順位を完全に誤っていた。正常系100本書くよりも、境界値10本のほうが価値がある場面がある。特に金額や数量を扱うロジックでは、ゼロ、マイナス、最大値、同時アクセスといったエッジケースが最も危険だ。

この経験以降、テストを書くときの順番が変わった。まず「どこが壊れやすいか」を考え、そこから書き始める。正常系は後回しでいい。正常系が動くことは実装中にだいたい分かるからだ。

教訓

「最初に壊れるのはどこか」を考え、そこにテストを集中させる。テストの目的は「動くことの確認」ではなく「壊れないことの証明」だ。

Chapter 04

失敗③——前提が崩れたAPI設計

3つ目の失敗は、外部API統合の設計で起きた。

複数の外部サービスと連携する必要があった。最初は1社だけだったから、その会社のAPIに合わせた個別実装にした。これ自体は、その時点では合理的な判断だった。

2社目、3社目も同じやり方で通した。各社のAPIはレスポンス構造からエラーハンドリングまで根本的に違う。共通化の設計を考えるより、目の前の1社に合わせて書く方が速い。「今回も個別で」を繰り返した結果、よく似た連携処理が3社分並存し、同じ趣旨の修正を3箇所に入れる状態ができあがった。

限界がはっきりしたのは、4社目の連携追加が持ち上がったときだ。特定の1社向けに書かれたロジックを汎化しないと、もう追加できない。個別実装の継ぎ足しが前提としていた「連携先はそう増えない」という暗黙の仮定が、完全に崩れた。

「1箇所コードを直すことの恐ろしさ」——現場で痛感した「変更の重み」が、3社分に増殖していた。1社の仕様変更に手を入れるたび、よく似た他社の処理まで目を配ることになる。

自分が暗黙に置いていた前提は「連携先が増えるとしても、当分先の話」だった。1社目の時点では実際そうだったから、この前提を疑わなかった。しかしそれは偶然であり、連携先は増え続け、4社目で完全に破綻した。

結局、4社目の追加を機に、Adapterパターンで統一基盤へ再設計した。「変換ルール」と「共通インターフェース」を明確に分離し、変換ルール側を各社のAdapter内で完結させる。共通インターフェースは最小限にし、以後の連携追加はAdapterクラス1本の追加で済む構造にした。

教訓

設計の前提を明文化し、「この前提が崩れたら何が壊れるか」を事前に問う。暗黙の仮定は、崩れるまで気づけない。だからこそ、意識的に言語化する必要がある。

Chapter 05

共通点——「前提の見誤り」

3つの失敗を並べてみると、共通するパターンが見えてくる。

過剰な抽象化では、「将来拡張がある」という前提を疑わなかった。テストの不備では、「正常系が通れば大丈夫」という前提を疑わなかった。API設計では、「連携先はそう増えない」という前提を疑わなかった。

どの失敗も、技術力の不足が原因ではなかった。自分が置いた前提を疑わなかったことが原因だった。

現場の先輩から学んだ「仮説思考」の大切さが、ここに繋がる。「動かない」という事実に引っ張られるのではなく、「なぜ動かないのか」「自分は何を前提にしていたのか」を紐解く。この思考の習慣があれば、失敗の芽を早い段階で摘める。

「良い設計は良い数式に通じる。例外がないことが美しい数式の条件だ」——先輩のこの言葉が、後の設計判断を変えた。

StrengthsFinderでいう「慎重さ」は、設計の強みになる。リスクを事前に洗い出し、防御的に構造を作る力だ。しかし、一度「正しい」と信じた設計を疑う方向には、慎重さは働きにくい。自分の慎重さは「新しいリスク」には敏感だが、「自分が作ったもの」には甘い。

だからこそ、意識的に自分の設計を疑う仕組みが必要だった。それが次のセクションで話す「3つの問い」だ。

Chapter 06

設計前にやる3つの問い

3つの失敗を経て、設計に取りかかる前に自然と習慣になった問いがある。大げさなフレームワークではない。自分への問いかけだ。

問い①:この設計の前提は何か? それが崩れたら何が壊れるか?

API設計の失敗から生まれた問い。設計には必ず「暗黙の前提」がある。それを言語化し、崩れたときの影響範囲を事前に把握する。前提をドキュメントに書き残すだけで、将来の自分やチームメンバーが同じ轍を踏むリスクを大幅に減らせる。

問い②:この抽象化は今必要か? 未来の自分への借金ではないか?

過剰抽象化の失敗から生まれた問い。レイヤーを増やすたびに管理コストが発生する。「将来のため」という理由は、ほとんどの場合「今は不要」の言い換えだ。抽象化は「投資」であり、リターンが見込めるときだけ行う。

問い③:最初に壊れるのはどこか? そこにテストはあるか?

テスト不備の失敗から生まれた問い。正常系から書き始めるのではなく、「壊れやすい場所」から考える。境界値、同時アクセス、外部依存——壊れるポイントを先に潰す。テストの優先順位を決める基準は「カバレッジ」ではなく「リスク」だ。

この3つの問いは、「段階的詳細化」の考え方にも通じる。いきなりコードを書き始めるのではなく、まず全体像を俯瞰し、前提を確認し、リスクを洗い出してから詳細に入る。

特別なことではない。ただ、失敗しないと身につかなかった。本で読んだだけでは、自分ごとにならなかった。手を動かし、痛い目を見て、初めて骨身に染みた。

失敗を語れるようになるまで

失敗談を書くのは、正直に言えば恥ずかしい。転職直後の自分は「できない自分」を認めるのが辛かった。インフラの世界では一人前だったのに、Web開発の現場では新人と同じスタートライン。そのギャップが、プライドを削った。

でもPLになった今、後輩に伝えるべきは成功体験よりも「踏まなくていい地雷の場所」だと思っている。自分が踏んだ地雷を共有すれば、チームの誰かが同じ痛みを味わわずに済む。

現場で学んだ「準備8割、実行2割」という言葉がある。設計の失敗も、突き詰めれば準備——つまり前提の検証——が足りなかったことから来ている。コードを書く前の30分が、書いた後の30時間を救う。

もうひとつ、現場で教わった考え方がある。「Find One Fix All」——1つの失敗を見つけたら、同じパターンが他にないか全部チェックする。過剰抽象化を1箇所で見つけたなら、他のモジュールも同じ問題を抱えていないか。テストの漏れを1箇所で見つけたなら、他のロジックも同じ観点が抜けていないか。

失敗は、ただ反省するだけでは「経験」にならない。なぜ失敗したのか、どんな前提を見誤ったのか、同じパターンが他にないか。そこまで掘り下げて初めて、次に使える知見になる。

失敗は、構造化できたとき「経験」になる。

設計判断 失敗談 テスト デザインパターン 受託開発

著者: しきぴょんた / 2026年3月

関連する記事