継続的に価値あるサービスを提供するために意思決定者が押さえておくべきコストの話

社内で書いた内容から、一般化できる話だけを抜き出して書いた記事です。

TL;DR

意思決定者は、

  • 仕様が複雑になればなるだけ、指数関数的かつ継続的にコストがかかることを理解する必要がある。
  • ユーザーの要求や利便性について優先順位をつける必要がある。
  • それらを揃えた上で「いっちゃんいいバランスで顧客要望に応える」必要がある。

コストについて

サービスあるいは機能の一生

サービスあるいは機能は以下のライフサイクルを辿る

  • 初回リリース以前
  • 初回リリース以後、クローズ以前
  • クローズ

実は一番コストがかかるのが「初回リリース以後、クローズ以前」の部分。なぜならサービスの一生のうちほとんどの時期がここなので……。

初回リリース以前にかかるコスト

  • いわゆる「初期開発」が行われる。
  • 当然、この時に仕様がシンプルであればあるだけ初期開発にかかるコストは低くなるし、仕様が複雑であればあるだけ初期開発にかかるコストは高くなる。
  • このコストは線形に増えるわけではなく、指数関数的に増える
  • 初期開発のコストは大きく分けて設計にかかるコスト、実装にかかるコスト、テストにかかるコストがある。

設計にかかるコスト

  • 互いに関連する要素の数に対して、指数関数的にコストが上がるのがポイント。
    • 要素が1つならば、一貫性を気にする必要はゼロ
    • 要素がふたつならば、ふたつの間の一貫性を気にする必要がある
    • みっつになったら、AとB、BとC、CとAの間の一貫性を気にする必要がある
    • よっつになったら、いつつになったら、という感じで組み合わせ爆発が起こる
  • 複数の主体(チームや企業)にまたがる設計が生じると爆発的にコストが上がる
    • 異なる立場での優先順位などの調整、異なるポリシーのすり合わせなどが必要となるため
  • 設計をなるべくシンプルにすることで仕様の複雑さによるコストを「軽減」することは可能だけど、取り返すことは不可能である

実装にかかるコスト

  • ここは仕様の複雑さに対して線形にコストが上がることが多い
  • ここだけしか見えていないことでのちのち爆発炎上する事案は世間にすごく多い
  • 実装をなるべく綺麗にすることで、m仕様の複雑さ、設計の複雑さによるコストを「軽減」することは可能だけど、取り返すことは不可能である

テストにかかるコスト

  • 設計と同じく、互いに関連する要素が増えれば指数関数的にコストが上がる
    • なぜなら、考えるべきパターンが組み合わせ爆発を起こすので

初回リリース以後、クローズ以前にかかるコスト

  • 大きく分けて、メンテナンスとサービス運用とサービス改善(追加開発)にかかるコストがある。
  • 前述の通り、サービスの一生の中で一番長い時間留まる部分だし、一番コストがかかる部分である
  • 後述するが、「仕様の複雑さ」がありとあらゆる変更にかかるコストの「ベースライン」を上げてくる。
  • いちばん長い時間、一番コストがかかるところのベースラインをいかに上げないかを考えなければ競合に改善スピードがどんどん追い抜かれていく

メンテナンスにかかるコスト

  • そのサービスが依存しているもの(DBなどのミドルウェア、ライブラリ、フレームワーク、言語など)は、日々アップデートされていく
    • このアップデートについていかないとなにが起こるか
      • セキュリティ上の問題が起こる(いっちゃんやばい)
      • パフォーマンスが相対的に悪くなる(競合はもっといいバージョンを使っているぞ)
      • サービス改善のスピードが相対的に悪くなる(競合はもっと使いやすいバージョンを使っているぞ)
  • このバージョンアップ作業には、「計画、実装、テスト」のコストがかかる
    • 計画については、関連する要素の数に応じて指数関数的に計画が難しくなる。理由は初期開発と同じ。
    • 実装についてはやるだけなので、仕様の大きさに対してかかるコストは大きくみれば線形である傾向がある。
    • テストについては、関連する要素の数に応じて指数関数的にテストすべき内容が増える。理由は初期開発と同じ。

サービス運用にかかるコスト

  • 問い合わせがあったら調査をする必要がある。
    • ここはテストに似ていて、関連する要素の数に応じて、考慮すべきパターンが指数関数的に増える
  • 障害があったらそれに対応する必要がある
    • 障害が起こる可能性は、「障害が起こりうる点」がどれだけ多いかに関連する。障害点はコード量、ミドルウェアの数に応じて増えるし、要素間の関連の数に応じて増える。
    • 障害から復旧するためには、原因調査が必要である。
    • 原因調査の難易度は考慮すべきパターンの数に依存するので、関連する要素の数に応じて指数関数的に増える

サービス改善、追加開発にかかるコスト

  • サービス改善や追加開発は、実は新規開発よりも難しい。なぜなら、新規開発の場合は「すでにある仕様」との一貫性をとる必要がないが、追加開発ではその必要があるからである。
  • つまり、「現状の仕様の複雑さ」が「追加開発にかかるコストのベースライン」となる。
    • このベースラインのコストはいままで見たきた通り、仕様の複雑さに対して線形ではなく指数関数的に増える
    • このベースラインが上がっていくと、どんどん「借金で首が回らない」という状態になっていく
    • 「既存のシステムが絡まっていて、手をつけられない、あるいは競合に対して大変動きが遅い」となっている仕組み、世の中にたくさんありますね。
      • これは別にエンジニアが無能なわけではなくて既存のシステムが「足枷」になっているということ

クローズにかかるコスト

  • サービスをバツっと閉じてしまうならコストはかからない
  • しかし現実はそうではないことが多い
    • 代替となる新機能や新サービスを作るならば、そのための新規開発のコストがかかる
    • 代替となる別サービスに移行するならば、移行のための追加開発のコストがかかる

上述のコストを考えた上で意思決定する必要がある

  • 仕様が複雑になればなるだけ、言い換えればつまり「関連する要素」が増えれば増えるだけ指数関数的にかつ継続的にコストがかかるようになっていく
  • 「ユーザーの利便性のためならなんでもやる」も「コストがかかるから仕様をシンプルにすることだけ考える」も意思決定ではない
  • 「ユーザーの利便性」と「継続的にかかるコスト」を天秤にかけて、「じゃあどこがバランスするか」を決断するのが意思決定である。
    • エンジニア的観点では、「ユーザーの利便性や要求」について理解をしようとする必要がある。その上で、なるべく各要素の関連を少なくするようなシンプルな仕様、実装で、ビジネスに求められるラインを提案し、実現するために知恵を絞るべきである。だし、自分が意思決定者でないのであれば、その仕様にすることでなぜ将来にわたるコストが低いのかを意思決定者に説明する必要がある。
    • ビジネス的観点では、エンジニア的観点から導かれた「将来にわたるコスト」について理解をする必要がある。また、「なにが本当に必要なもので、なにを足すと"将来の足枷"が増えるのか」よく理解した上でユーザーの利便性や要求について優先順位を明確にする必要がある。
    • 良い意思決定は、このふたつ両方が理解されて初めてなされる