/note/tech

なぜ優秀なエンジニアほど「データ構造」から考えるのか

要約:

■ 1. 壊れやすいコードの共通点

  • 実務でコードを書いているとこの処理どこから手をつければいいんだろうと立ち止まる瞬間に何度も出会う
  • 少し仕様が変わっただけで影響範囲が読めなくなる
  • 1つ修正すると思わぬ別の場所が壊れる
  • 自分では丁寧に書いているつもりなのにどんどん扱いづらくなっていく
  • 壊れやすいコードの共通点はデータ構造が曖昧なまま実装に入っていること
  • 優秀なエンジニアほどコードを書く前にまず扱うデータをどう設計するかに時間を使う
  • これはセンスではなく壊れにくいシステムを作るうえでの再現性のあるプロセス

■ 2. データから考えると壊れにくくなる理由

  • コードが壊れる原因は処理が複雑だからではない
  • 多くの場合扱うデータが曖昧なまま実装が進んでしまっていることが本質的な原因
  • 処理から考え始めると次のような問題が起きやすくなる:
    • 想定外の値が混ざる
    • null/undefinedが途中で暴れる
    • 状態管理がif文に散らばる
  • これは本来コードを書く前に決めておくべき前提条件がコードの外に残ったままになっている状態
  • この前提の漏れが仕様変更のたびに壊れる構造を生む

■ 3. データを先に設計する利点

  • データを先に設計しておくと以下が明確になる:
    • 何が入力なのか
    • 何が正常な状態なのか
    • 何が起きると不整合になるのか
  • 実装に一貫した軸が生まれる

■ 4. 情報ではなく状態をデータとみなす

  • 多くの人はデータ=文字や数値と捉えるが実務で重要なのは状態
  • 状態の例:
    • ログイン前/ログイン後
    • 未読/既読
    • 有効/無効
    • 下書き/公開
  • これらはUIのラベルではなくアプリケーションが保証すべき前提そのもの
  • 状態が曖昧なまま実装するとif文が散らばり仕様変更のたびに壊れる

■ 5. 値だけでなく関係もデータ構造に含める

  • データ設計で見落とされがちなのは関係性
  • 関係性の例:
    • ユーザーと組織
    • 記事とコメント
    • 予約枠と担当者
    • 商品と在庫
  • 関係性が曖昧なまま実装するとこのデータどれと紐づいてるんだっけという混乱が必ず発生
  • 関係はおまけではなくデータ構造そのものの中心

■ 6. 仕様に書かれていない前提をデータ化する

  • 実務には仕様書に書かれていない重要な前提が必ず存在
  • 前提の例:
    • ユーザーは1つの組織にしか所属できない
    • 予約は担当者×サービス×日時で一意になる
    • メッセージの編集履歴は削除後も保持する
  • こうした暗黙にされているかもしれない前提をデータに落とし込まないと機能がその前提に依存していたときに壊れるという事故が起きる
  • 優秀なエンジニアほど仕様に書かれていない前提を発掘してデータとして扱うという工程を怠らない

■ 7. 自然言語で書ける重要性

  • 最初からER図やクラス図を書く必要はない
  • まずやるべきなのは扱う状態や前提を自然言語で説明できるようにすること
  • 例:
    • ユーザーは複数組織に所属できるがデフォルト組織は1つ
    • 予約は日時・担当者・サービスの3つが揃って成立する
    • メッセージ編集は可能だが履歴は別レコードで保持する
  • 自然言語で曖昧ならコードではもっと曖昧になる

■ 8. Step1 自然言語で状態関係前提を列挙する

  • まずはコードから離れて自然言語で現在の仕様を洗い出すところから始める
  • 例:
    • 予約は日時×担当者×サービスで構成される
    • ユーザーは複数組織に所属できるがデフォルト組織は1つ
    • メッセージ編集は可能だが履歴は残す
  • 曖昧な表現でも構わない
  • 最初は全体像を漏れなく書き出すことが大事

■ 9. Step2 同じ概念をまとめ不要な状態を削る

  • 洗い出した状態や関係を見直し以下を判断して整理:
    • 本当にアプリが管理すべき状態はどれか
    • UIの名前ではなく構造として必要な状態はどれか
  • 状態は多ければ多いほど壊れやすくなるため削る工程こそ設計の核心

■ 10. Step3 変化する状態と変化しない状態を分ける

  • どの値が変化しどの値が不変かを明確にする
  • 例:
    • 予約の作成日時は不変
    • 予約のステータスは可変
    • ユーザーIDは不変
    • メールアドレスは可変
  • この境界線を引くだけで更新ロジックのバグが激減

■ 11. Step4 状態遷移を書く

  • 状態が整理できたらどの状態からどの状態に遷移できるかを明文化
  • 例:
    • draft → reserved → completed
    • draft → canceled
    • reserved → canceled
  • これにより以下がすぐに可視化される:
    • 到達しない状態
    • 不正遷移
    • 不整合の原因

■ 12. Step5 データ構造として定義する

  • 整理した情報を型・スキーマ・ER図に落とし込む
  • 具体的な手段:
    • TypeScriptの型
    • Prisma schema
    • Rustのstruct + enum
    • Swiftのstruct/enum
    • DBのテーブル定義
  • 状態・前提・関係が整理されているのでここから先の実装は驚くほどスムーズ

■ 13. データ構造設計の重要性

  • データ構造の設計は実装の前作業ではなく実装を壊れにくくするための中心工程
  • 重要なポイント:
    • 曖昧なまま書き始めるほど壊れやすくなる
    • データが明確なら処理は自然とシンプルになる
    • 状態・関係・前提が揃えば仕様変更に強くなる
  • 優秀なエンジニアがデータ構造を先に決めるのはセンスではなく後の開発すべてを安定させる最もコスパの良い投資だから