大企業に入ったばかりのエンジニアから、よくこんな質問をされます。「2日で書ける機能なのに、なぜ先に設計書を書いて、何度も会議をしなきゃいけないんですか?」
正直に言うと、私も昔はそう思っていました。でも何度か痛い目を見ました。リリースしてみたら誰も使っていない機能、節約できた額より高くついたクラウドの請求書、誰も理解できないシステムの障害対応で夜中に叩き起こされたこと。そうしてようやくわかったのです。大企業では、コードを書くのはたいてい一番簡単な部分です。難しいのは、書く前に「どこで問題が起きるか」を考え抜くことです。
この記事では、私がシステムを設計するときに自分に投げかける問いを、3つの段階に整理しました。
- 設計を始める前に:そもそも何の課題を解決するのかをはっきりさせる
- 設計するとき:経営者の視点で判断する
- デザインレビューで:よく聞かれる質問と、その裏にあるリスク
どの問いについても「なぜそれを聞くのか」を説明します。問いの裏で何を解決しようとしているのかを理解して、はじめて的確に答えられます。そうでなければ、チェックリストを暗記しているだけです。
抽象的な話にならないよう、最初から最後まで同じ例を使います。
ある会社に 1,000 人のカスタマーサポート担当者がいます。担当者は通話が終わるたびに、CRM に通話メモを 3 分かけて手入力しています。経営陣は「通話録音の自動要約」機能を作りたいと考えています。電話を切った瞬間に、システムが録音を文字起こしし、要約を作り、CRM に書き込むというものです。
PART 1設計を始める前に
設計を始める前に、どんな問いを考えておくべきかを整理します。
1. なぜこの機能を作るのか?
これは一番飛ばされがちで、でも一番大事な問いです。エンジニアは要件を聞くと、すぐアーキテクチャを考え始めがちです。「どのモデルを使う? Kafka は入れる?」。でも、誰が使うのか、何を解決するのかがはっきりしていないと、技術的には美しいのに誰も使わないものができあがりかねません。
私はこれを3つの小さな問いに分けて考えます。
この機能のユーザーは誰か?
「サポート担当者」は表面的な答えにすぎません。もう一段掘り下げると、実は3種類の人がいます。
- サポート担当者:入力の手間を減らしたい。要約は正確であってほしい
- サポートの管理者:クレームが来たとき、その通話で何があったのかを素早く把握したい
- 品質管理(QA)担当者:通話品質を抜き打ちでチェックしたい
3者が求めるものは違い、権限も違います(担当者が他人の通話を見られてはいけない)。これらはすべて、後の設計に直接影響します。
ユーザーのどんな課題を解決するのか?
表面的には「入力時間の削減」です。でも実際に計算してみましょう。1時間に5件、1件あたり3分節約できれば、1時間で15分、1日で2時間の節約です。1,000 人分なら、毎日 2,000 時間分の工数になります。
この数字は重要です。会社がこの機能にいくらまでお金をかけられるかを決めるもので、後でコストを計算するときにも何度も出てきます。
ユーザーは以前どうやっていたのか?
一番見落とされがちで、でも一番救われる問いです。これまで担当者は CRM のメモ欄に手入力し、管理者はクレームを調べるときに録音を聞き直していました。
ここからわかることがあります。要約は最終的に CRM に戻さなければならないということです。独立した Web サイトとして作ると、担当者は2つのシステムの間でコピペすることになります。「入力」が「コピペ」に置き換わっただけで、1か月もしないうちに誰も使わなくなるでしょう。
「技術的には成功、ビジネス的には失敗」という機能を作らないこと。ユーザーの昔からの習慣は、新しいシステムの制約条件です。
2. どう実装するのか?
機能の中核はどこで動くのか?エンドポイント / クラウド / ローカルサーバー
同じ機能でも、中核をどこに置くかで結果は大きく変わります。
| 中核の配置 | 通話要約の場合 | メリット | 代償 |
|---|---|---|---|
| エンドポイント(担当者の PC) | 各 PC で音声認識モデルを動かす | 録音をアップロードしないので、プライバシー面で最も有利 | 社用 PC には GPU がなく動かない。1,000 台のモデル更新は悪夢 |
| クラウド | 録音をクラウドにアップロードして処理 | 開発が最速。スケールはマシンを足すだけ | 録音には個人情報が含まれ、セキュリティや法務が社外持ち出しを認めない可能性がある |
| ローカルサーバー(自社データセンター) | データセンターに GPU サーバーを置く | データが社外に出ない | ハードウェアの調達と運用が必要。スケールは調達プロセス待ち |
サポートの通話では、お客様がクレジットカード番号や住所を読み上げることがよくあります。だからこの問いは、たいていエンジニアだけでは決められません。まずセキュリティ部門と法務に確認しましょう。作り終わってからではなく、先に聞くのがポイントです。
インターフェースは何か?デスクトップアプリ / モバイルアプリ / Web
担当者は一日中 CRM をブラウザで開いているので、CRM 内の Web プラグインにすればよさそうに思えます。
ただし、それは録音がどこから来るか次第です。電話システム(PBX)がもともと録音しているなら、バックエンドがファイルを取りに行けばよく、フロントエンドは要約を表示する Web プラグインで済みます。しかし電話システムに録音機能がなく、担当者の PC 上のソフトフォンから音声を取り込む必要があるなら、ブラウザではできません。デスクトップアプリが必要になり、それはインストール、自動更新、社用 PC の権限管理への対応を意味します。
小さく見える要件のひとつが、フロントエンド全体の作り方を決めてしまうのです。
主な技術スタックは何か?
音声認識や LLM のエコシステムはほぼ Python なので、音声を処理するワーカーは Python が妥当です。大量の同時リクエストをさばく API サービスは、Go のほうがリソースを節約できるかもしれません。フロントエンドが CRM プラグインなら、その CRM プラットフォームが定める開発フレームワークに従うしかなく、ここに選択の余地はありません。
中核をどこに置き、インターフェースを何にするかで、データの流れ、コンプライアンス、運用コストが決まります。どれも後から変えにくい決定です。
3. ユーザー規模は?利用頻度は?データ量は?
必ず数字を出しましょう。「たくさんの人が使う」は規模ではありません。「1,000 人、1人あたり1時間に5回、1回 10MB」が規模です。
今回の例で実際に計算してみます。
- ユーザー:サポート担当者 1,000 人。業務時間(8時間)のみ利用
- 頻度:1人あたり1時間に約5件の通話
- ファイル:10分の録音で約 10MB
(1時間あたり 1,000 × 5 = 5,000 回)
(1.4 × 10MB ≈ 14 MB/s)
(5,000 × 8時間 × 10MB)
(営業日 22 日で計算)
ここが面白いところです。毎秒 1.4 リクエストなら、マシン1台で余裕です。でも毎月 8.8TB の録音は、1年で 100TB を超えます。
このシステムの課題は QPS ではなく、帯域、ストレージコスト、処理時間です。数字を出さないと、見当違いのところに力を注ぎがちです。たとえば2週間かけて API のパフォーマンスをチューニングするとか。
「業務時間のみ利用」というのも重要な情報です。
- 夜間と週末はマシンを最小限まで減らしてコストを節約できる
- ただし朝9時に全員が一斉にログインするので、コールドスタートに注意
- 月初に請求書が届いた後は通話量が何倍にも跳ね上がることがあるので、ピークは別途見積もる
規模がアーキテクチャを決めます。1,000 人のシステムと 100 万人のシステムはまったく別の設計です。数字のない議論は空論にすぎません。
4. システムにはどんな制約があるか?
どの技術にも生まれつきのボトルネックがあります。リリース後に壁にぶつかるより、設計前に知っておくほうがずっといい。
CAP 定理
分散システムでは、一貫性(Consistency)、可用性(Availability)、分断耐性(Partition tolerance)の3つのうち、2つしか同時に満たせません。ただ現実にはネットワークは必ず切れ、分断は必ず起きます。だから本当の選択はこうです。ネットワークに問題が起きたとき、「データが常に正しい」ことと「サービスが常に応答する」ことの、どちらを取るか?
- 課金や在庫:一時的に使えなくても、計算を間違えるわけにはいかない。だから C を選ぶ
- いいね数や閲覧数:数秒遅れて更新されても構わないが、ページが落ちてはいけない。だから A を選ぶ
メッセージサイズとタイムアウトの制限
今回の例では特に重要です。録音ファイルが1つ 10MB あるからです。
| コンポーネント | よくある制限 | 10MB の録音への影響 |
|---|---|---|
| AWS API Gateway(REST) | ペイロード上限 10MB、統合タイムアウトはデフォルト 29 秒 | ファイルサイズはぎりぎり。10分の録音の文字起こしは確実に 29 秒を超える |
| AWS Lambda(同期呼び出し) | リクエスト / レスポンスのペイロード上限 6MB | そもそも入らない |
| Kafka | 1メッセージのデフォルト上限は約 1MB | 入らない |
| DynamoDB | 1アイテムの上限 400KB | 長めの文字起こしなら超える可能性がある |
これらの制限がわかれば、設計の方向性は自然に見えてきます。
- ファイルは API を経由させない。署名付き URL(Pre-signed URL)でクライアントから S3 に直接アップロードする
- キューにはファイルのパスだけを載せる。ファイル本体は載せない(Claim-Check パターン)
- 文字起こしは 29 秒を超えるので、非同期処理にする
設計の途中、あるいはリリース後になって、あるコンポーネントが負荷に耐えられないと判明し、アーキテクチャ全体を作り直す羽目になるのを防ぐこと。
5. ユーザー1人あたりのコストはいくらか?
市場価格はいくらか?自分たちの目標はいくらか?社内ツールでも計算は必要です。経営陣は必ず「既製の SaaS を買う場合」と比べるからです。
- 類似の通話要約サービスは、ユーザー1人あたり月額約 30 ドル
- 自社で1通話あたりにかかる音声認識と LLM 要約のコストは約 0.02 ドル
- 担当者1人あたりの通話は月に約 880 件(5件 × 8時間 × 22 日)
計算コスト:880 × 0.02 = 17.6 ドル/人/月
ストレージコスト:1人あたり毎月 8.8GB 増加。最初の月はわずかだが、削除しなければ積み上がり続ける
ユーザー1人あたりの月額コスト ≈ 18 ドル〜、しかも毎月増えていく
30 ドルと比べれば余裕がありそうに見えます。でも運用の人件費、遊んでいるマシン、積み上がるストレージを加えると、買った場合と大差なくなるかもしれません。こうなると設計の方向性も変わってきます。
- 2分を超える通話だけを要約できないか?(短い通話はたいてい転送か間違い電話)
- まず小さめのモデルで分類し、複雑な通話だけ大きなモデルに回せないか?
- 録音は 30 日後に削除し、テキストの要約だけ残せないか?
外部の顧客に販売するプロダクトなら、SaaS でよくある粗利率の目標は 70〜80% です。つまりユーザー1人あたりのサービス提供コストは、価格のおよそ2〜3割以内に抑えないと、チームを維持できません。
作れることと、作るべきことは別です。コストがこの機能が生き残れるかを決め、逆にアーキテクチャも決めます。自前でホストするか、キャッシュを入れるか、データをどれだけ保持するか。
6. 本番品質に到達するまで、どれだけの人員と期間が必要か?
デモが動いた時点で、だいたい半分です。
本番リリースには、監視とアラート、アクセス制御、セキュリティレビュー、負荷試験、エラー処理、オンコール手順書、ドキュメント……がまだ必要です。これらが残りの半分以上の時間を占めることも多いのに、スケジュールから一番抜け落ちやすい部分です。
見積もるときは、作業を2種類に分けます。
明らかなギャップがあり、自分たちで作る必要があるもの
たとえばサポートの通話では、強いなまりや方言、途中で言語が切り替わる会話がよくあり、既製の音声認識では精度が足りないかもしれません。このように「既存の解決策がない」作業は研究的な仕事です。2週間で片づくかもしれないし、3か月たってもまだ調整しているかもしれません。
この部分は必ず切り出して別に見積もり、不確実性をはっきり伝えましょう。通常の開発作業と一緒くたにしてはいけません。
暫定策で埋められるが、後で直す必要があるもの
- v1 は主要言語の通話のみ対応。認識の確信度が低いものは「要人手確認」とマークする
- v1 は要約を CRM に自動で書き戻さない。担当者が「確認」を押してから書き込み、AI が誤ったデータを書くのを防ぐ
- v1 は5分ごとのバッチ処理にし、後でリアルタイムに切り替える
妥協すること自体は問題ありません。ただし必ずドキュメントに書き、チケットを切り、いつ返済するかを決めておくこと。記録されなかった「暫定」は、最終的に「恒久」になります。
マネージャーにスケジュールへの妥当な期待を持ってもらい、どんな技術的負債を抱えるのかを全員が把握できるようにすること。
7. 既存のどんなシステムと互換性を保つ必要があるか?
大企業は白紙ではありません。あなたが設計するものは、10年以上動き続けているかもしれない環境の中に置かれます。会社が好む言語、システム、ソフトウェア、取引先は、どれも設計の前提条件です。
会社が好むプログラミング言語
社内の人が Python や Go の専門家ばかりなら、Elixir がこの機能にぴったりだと思っても、よく考えましょう。理由は現実的です。
- コードレビューで誰も読めず、レビューが形だけになる
- あなたが休暇中にシステムが落ちても、誰もデバッグできない
- 引き継ぎのときに、誰も引き受けたがらない
みんなが慣れた言語を選べば、問題が起きたときにすぐ誰かがレビューし、助けてくれます。
既存のプラットフォームとデータ形式
たとえば社内のデータ基盤が Starburst で、Iceberg 形式が推奨されているとします。それなら通話要約の履歴データも Iceberg テーブルとして保存すべきです。データチームがすぐに分析に使え、別途 ETL を書く必要がありません。
その他のよくある互換性の問題
- ログイン:会社にすでに SSO(Azure AD / Okta)があるなら、独自の ID・パスワードは作らない
- 監視とログ:会社が Datadog や ELK に統一しているなら、そこに接続する。独自に構築しない
- 取引先:会社があるクラウドと割引付きのエンタープライズ契約を結んでいるなら、乗り換えには何か月もの調達・法務手続きが必要になるかもしれない
- CRM:CRM に書き戻すなら、その API にレート制限があるかを先に確認する
運用とコミュニケーションのコストを下げること。システムは書いて終わりではなく、誰かが長く面倒を見続けるものです。会社が「維持できる」技術を選びましょう。
PART 2設計するとき
経営者の視点で判断する
システムを設計するとき、一番大事な意識の切り替えはこれだと思います。この機能を自分で起業した会社だと想像してください。毎月のクラウドの請求書は自宅に届き、夜中に障害が起きたら自分の携帯が鳴ります。
この視点に立つと、自然と次のことが気になってきます。
1. スケーラビリティ:量が増えたとき、マシンを足すだけで済むか?
来年、会社が別のサポートセンターを買収し、担当者が 1,000 人から 3,000 人になったとします。良い設計なら「ワーカーを5台から15台に増やす」で終わりです。悪い設計だと「データベースが耐えられず、2日間止めてデータ移行」になります。
2. 拡張性:新機能を足すとき、作り直しが必要か?
スケーラビリティは「量」の問題、拡張性は「機能」の問題です。リリースから3か月もすれば、管理者は必ずこう言います。「文字起こしがあるなら、ついでに感情分析もできない? クレームの分類も自動でできない?」
文字起こしが終わったときに「文字起こし完了」イベントを発行する設計なら、新機能はそのイベントを購読するだけで追加できます。文字起こし、要約、CRM への書き戻しがひとつの関数にべったり書かれていたら、機能を足すたびにコアの処理に手を入れることになり、変更するたびに壊れるリスクを負います。
3. 許容できるコスト
先ほど計算したユーザーあたりのコストは、設計中に何度も見返しましょう。コンポーネント(キャッシュ、キュー、リードレプリカ)をひとつ足すたびに、自問します。それがもたらすメリットは、その費用に見合うか?
4. 許容できる保守の難しさ
Kafka + Kubernetes + Cassandra を自前で運用すると聞くとかっこいいですが、チームが3人しかいなければ、バージョンアップやノード障害の対応だけでチームの時間が溶けてしまいます。
多くの場合、クラウドのマネージドサービスに少し多めに払って、チームが夜ぐっすり眠れるようにするのは、とてもお得な取引です。
5. レビュアーに選択肢を与える
意外と見落とされるのがこれです。少なくとも2つの案を設計し、長所と短所を比較すること。
なぜか? 案をひとつしか出さなければ、レビュアーは「いい」か「だめ」かしか言えません。しかも心の中で「他のやり方を考えなかったのかな?」と思うはずです。そうなると会議は、各自が頭の中にある案を出し合う場になり、議論は発散します。
2つの案を並べれば、議論は「どのトレードオフを取るか」に絞られます。
| A案:クラウドのマネージドサービス | B案:自社データセンターでモデルを自前運用 | |
|---|---|---|
| リリースまでの期間 | 約2か月 | 約5か月(GPU 調達を含む) |
| 月額コスト | 利用量に比例して増える | 初期のハードウェア投資は大きいが、量が多いと割安 |
| 個人情報のコンプライアンス | 録音をクラウドに出せるか、セキュリティ審査が必要 | データが社外に出ない |
| 運用負荷 | 低い | 高い。GPU とモデルの面倒を見る人が必要 |
| スケールの柔軟性 | 高い。いつでも増やせる | 低い。調達待ち |
| 方言の認識 | ベンダーの対応状況に左右される | 自分たちでファインチューニングできる |
最後に「どの案を推すか、その理由は何か」を添えます。不採用になった案にも、その理由を一行残しておく価値があります。半年後に「なんで B 案にしなかったの?」と聞かれたとき、ドキュメントが答えてくれます。
(以降のデザインレビューの例では、A 案を推したと仮定します。録音をクラウドに出すことがセキュリティ審査で認められ、まずはクラウドサービスで素早くリリースする、という前提です。)
PoC は道が通じるかの検証であって、システムを作ることではない
これは本当によくある落とし穴です。
PoC(Proof of Concept)が答えるべきなのは、とても具体的な問いです。「この道は通じるのか?」。たとえば:
実際のサポート通話の録音を 50 件用意し(方言、背景ノイズ、怒鳴っているお客様を含む)、音声認識と要約にかけて、精度が 85% に届くかを確かめる。
この PoC にログインもデータベースもきれいな UI も要りません。スクリプトと結果レポートがあれば十分で、1〜2週間で結論が出るべきです。終わったらコードをまるごと捨てても、まったく問題ありません。
よくある失敗パターンはこうです。PoC を作っているうちに UI が付き、データベースが付き、それを経営陣にデモすると「もうできてるじゃないか。来月リリースしよう!」。こうして、アクセス制御も監視もエラー処理もないものが、そのまま本番に出てしまいます。
プロトタイプは、設計が決まった後に、選んだアーキテクチャに沿って作り、少人数の実ユーザーに試してもらうものです。検証するのはフローと体験です。担当者は本当に要約を読むのか? 要約は CRM のどこにあると一番使いやすいのか?
順番はこうです。
最小のコストで最大の不確実性を取り除き、同時に「作りかけのものがうっかり本番に出る」事態を防ぐこと。
PART 3デザインレビューで
デザインレビューは、あなたをいじめる場ではありません。痛い目を見てきた人たちが、システムのリスクを一緒に探してくれる場です。
そして目標はリスクゼロではありません。リスクゼロのシステムなど存在しませんし、仮に存在しても高すぎて作れません。本当の目標は、「知らないリスク」を「知ったうえで引き受けると決めたリスク」に変え、その代償をはっきりさせることです。
たとえば:
- 「管理者向けの統計レポートが最大1分遅れる」:許容できる。代償は小さい
- 「担当者 A が担当者 B の通話内容を見られる」:許容できない。必ず解決する
以下は、デザインレビューでよく聞かれる質問です。それぞれに「その質問が本当に確認したいこと」を添えました。準備のときにまずそこを考えておくと、答えの方向性がぐっと定まります。
Q1システムはどんな課題を解決するのか?どんな機能を提供するのか?想定規模はどれくらいか?
本当に確認したいこと全員が同じものについて話しているか。
たいてい最初に聞かれる質問です。一言二言で説明できなければ、その後の議論はぼやけてしまいます。良い答えには、ユーザー、価値、数字が入っています。
「1,000 人のサポート担当者が、通話終了後1分以内に要約を受け取れるようにし、1通話あたり3分の手入力をなくす。ピーク時は1時間あたり約 5,000 件、録音は月に約 8.8TB。」
あわせて、このバージョンでやらないこと(Non-goals)も明記しておくのがおすすめです。たとえば「リアルタイム字幕はやらない、感情分析はやらない」。書いておかないと、会議の途中で必ず誰かが「ついでに○○もやるのは難しくないよね?」と言い出します。
Q2どんな技術案があるか?それぞれの長所と短所は?
本当に確認したいこと本当に比較したのか、それとも答えを先に決めてから理由を探したのか。
先ほどの「少なくとも2案」の比較表のことです。レビュアーは比較が公平かを特に見ています。推す案には長所ばかり、もう一方には短所ばかり書いてあれば、それが藁人形だとすぐに見抜かれます。
推す案の短所も正直に書くほうが、かえって説得力が増します。
Q3タスクはどう分配するのか?同期か非同期か?キューは必要か?キューの長さとタイムアウトはどう管理するか?
本当に確認したいことピーク時にシステムが崩れないか、ユーザーはどれだけ待つのか、タスクが消えたり二重に実行されたりしないか。
10分の録音の文字起こしには 30〜60 秒かかるかもしれません。前述のとおり API Gateway はデフォルト 29 秒でタイムアウトするので、同期処理は成り立ちません。フローはこうなります。
キューを入れると、続けて必ずこう聞かれます。
- キューの長さ:月初に通話量が3倍になり、キューに2万件たまったらどうする? アラートを設定し(たとえば「最も古いタスクの待ち時間が5分を超えた」)、滞留が多すぎるときに縮退運転するかも決めておきます。たとえば文字起こしだけ先に行い、要約は後で補うなど。
- タイムアウト:キューの visibility timeout(取り出されたタスクが、どれだけ完了しなければキューに戻るか)は、最長の処理時間より必ず長くすること。30 秒に設定したのに処理が 60 秒かかると、同じタスクを2台目のワーカーが拾い、同じ通話の要約が CRM に2つできてしまいます。これは本当によくあるバグです。
- 失敗し続けるタスク:3回リトライしても失敗したら Dead Letter Queue(DLQ)に送り、無限リトライでキュー全体を詰まらせないようにします。あわせて、誰かが調査できるようアラートを出します。
Q4中核のワークロードはステートレスかステートフルか?ユーザーの状態はどう記録・保持・復元するか?セッションスティッキーは必要か?
本当に確認したいことマシンを自由に増やしたり減らしたりできるか。マシンが落ちたとき、ユーザーのデータは失われないか。
ステートレスとは、どのマシンでもどのリクエストでも処理できる状態です。マシンが落ちたら別のマシンに替えればよく、スケールするならマシンを足すだけです。ステートフルとは、一部のデータが特定のマシンのメモリにしかない状態で、そのマシンが落ちるとデータも消えます。
例:ワーカーが「この録音をどこまで処理したか」をメモリに持っていると、オートスケーリングでマシンが削除されたとき、その通話は最初からやり直しになります。正しくは、進捗をデータベースや S3 に書き、ワーカー自体は状態を持たないようにします。
やっかいなのは、担当者に「要約ができました」と知らせる部分です。WebSocket でプッシュするなら、接続そのものが特定のマシンに紐づくので、本質的にステートフルです。よくある方法は2つあります。
- セッションスティッキー:ロードバランサーが Cookie を使い、同じユーザーを常に同じマシンに振り分けます。シンプルですが、そのマシンが落ちると乗っていた全員が一斉に切断されますし、スケールインの際も接続が終わるのを待って(ドレイン)からでないと停止できません。
- 接続と状態を分離する:ワーカーは処理が終わったら Pub/Sub(たとえば Redis)にメッセージを発行し、すべての接続ノードがそれを受け取り、そのユーザーの接続を持っているノードがプッシュします。どのノードが落ちても、クライアントは別のノードに再接続すれば済みます。
復元については、担当者が再接続したとき、フロントエンドが call_id でデータベースから最新の状態を取得します。「さっきの接続が何を覚えていたか」には頼りません。
Q5ユーザーとデータが増えたら、システムをどうスケールするか?ステートレスな水平スケールか、シャーディングか?何の指標でスケールさせるか?
本当に確認したいこと成長したとき「マシンを足す」で済むのか、「設計し直す」必要があるのか。
成長には2種類あり、分けて考える必要があります。
ユーザーが増える(リクエストが増える):ステートレスな水平スケール
担当者が 1,000 人から 3,000 人に増えても、API とワーカーがステートレスなら、マシンを足すだけで解決します。
データが増える(テーブルが大きくなる):パーティション、ホット/コールド分離、シャーディング
1年後、データベースには数千万件の通話記録があり、管理者の「過去1年のクレームをすべて」という検索がどんどん遅くなります。このとき API のマシンを足しても意味がありません。ボトルネックはデータベースです。次の順で検討します。
- 月単位でパーティションを切り、検索では必要な月だけをスキャンする
- 1年を超えたコールドデータは Iceberg に移し、Starburst でクエリする(ちょうど社内の既存基盤です)
- 書き込み量に本当に耐えられなくなってから、シャーディングを検討する
シャーディングで一番重要なのはシャードキーの選び方です。たとえば「日付」をシャードキーにすると、今日のデータはすべて同じシャードに入り、そのシャードだけが常に一番熱く、他のシャードは暇を持て余します。分けていないのと同じです。「サポートセンター ID」や「担当者 ID」で分けるほうがずっと均等になります。シャードキーは一度間違えるとほぼ変えられないので、レビュアーは細かく突っ込んでくるのが普通です。
スケールのトリガーになる指標
多くの人は直感的に CPU を使いますが、今回の例ではそれは間違いです。ワーカーはほとんどの時間、外部の音声認識 API の応答を待っています(I/O バウンド)。CPU は 20% なのに、キューにはすでに 2,000 件たまっている、ということが起こります。CPU をトリガーにしたら、いつまでたってもスケールしません。
より良い指標は次のとおりです。
- キューの深さ ÷ ワーカー数(ワーカー1台あたりの平均滞留タスク数)
- 最も古いタスクの待ち時間
- API の p95 レイテンシ
Q6キャッシュは必要か?TTL はどれくらいにするか?データの不整合をどう防ぐか?
本当に確認したいことキャッシュは「一貫性と引き換えに速度を得る」もの。何を手放したのか、わかっているか?
まず前提として、すべてのシステムにキャッシュが必要なわけではありません。1,000 人規模の社内システムなら、データベースだけで十分さばけるかもしれません。キャッシュを1層足すことは、データ不整合の原因を1つ増やすことです。まず計測し、必要だと確認できてから入れましょう。
本当に必要なら、データの性質で決めます。
- 管理者向けの「今日の通話統計」:毎回計算すると大量のデータをスキャンする。60 秒キャッシュしてもまったく問題なく、1分遅れていても誰も気にしない
- 通話要約の内容:担当者が直したばかりなのに画面には古い内容が表示されると、保存されなかったと思ってもう一度直したり、バグとして報告したりする
後者は、書き込み時にキャッシュを能動的に消す(データベースを更新したら対応するキャッシュキーを削除する)ようにし、TTL は最後の保険として使います。
もうひとつよくある落とし穴がキャッシュの一斉失効(キャッシュアバランシェ)です。大量のキーが同時に書き込まれ、TTL も同じだと(たとえば朝9時に一括ロードし、TTL はすべて1時間)、10 時ちょうどに全キャッシュが一斉に失効し、トラフィックがすべてデータベースに押し寄せます。対策は TTL にランダムな揺らぎ(ジッター)を加えることです。たとえば 55〜65 分にして、失効タイミングをばらけさせます。
Q7データベースはどう設計するか?CAP のトレードオフは?読み書き分離、シャーディング、バックアップとリストア、マルチ AZ、I/O キャパシティは?
本当に確認したいことデータベースはシステムの中で最も後から変えにくい部分。最初に考え抜いておかないと、後の代償が一番大きい。
CAP のトレードオフ
同じシステムの中でも、データごとに違う選択をして構いません。「1通話につき正式な要約は1つだけ」という対応関係には強い一貫性が必要ですが、統計レポートは結果整合性で十分です。
読み書き分離
管理者の重いレポートはリソースを食うので、リードレプリカで実行し、担当者の書き込みを遅くしないようにします。ただしレプリカには遅延(レプリケーションラグ)があります。担当者が要約を保存した直後に画面がレプリカから読むと、「あれ、保存されてない?」となります。だから自分が書き込んだばかりのデータはプライマリから読むようにします。
バックアップとリストア
「バックアップがある」ことは「リストアできる」ことと同じではありません。レビュアーはきっとこう聞きます。実際にリストアしたことはあるか? 2TB のリストアに何時間かかるか? その間システムはどうするのか?
リハーサルしていないバックアップは、たいてい障害が起きてから、あるテーブルがバックアップ対象から漏れていたとか、リストアに 12 時間かかるとかが判明します。
マルチ AZ
あるデータセンター(アベイラビリティゾーン)で停電やネットワーク断が起きたとき、データベースは別のデータセンターに自動で切り替わるか? 代償としてコストはおよそ倍になるので、「1時間止まった場合の損失」がその費用を上回るかを見極めます。
I/O キャパシティ
クラウドのディスクには IOPS の上限があります(たとえば AWS gp3 のベースラインは 3,000 IOPS)。よくあるのが、夜間のバッチレポートが IOPS を使い切り、オンラインの書き込みまで遅くなるケースです。データベースが落ちたように見えるのに、CPU はほとんど暇。I/O の指標を見ていないと、原因究明に長い時間がかかります。
Q8データ同期はどう設計するか?複数人の同時アクセスをどう制御するか?
本当に確認したいこと2人が同じデータを同時に編集したとき、誰かの変更が黙って消えないか。
担当者がある通話の要約を修正している最中に、管理者が同じレコードにメモを追加します。管理者が先に保存し、担当者が後から保存すると、管理者のメモは上書きされて消え、しかも誰も気づきません。これをロストアップデートと呼びます。
よくある解決策:
- 楽観的ロック:各レコードに
versionカラムを持たせ、更新時の条件をWHERE id = ? AND version = ?にします。バージョンがすでに他の人に変えられていれば更新は失敗し、「このデータは他のユーザーによって変更されました。再読み込みしてください」と表示します。 - データを分ける:管理者のメモは、そもそも要約と同じレコードに入れる必要がありません。別テーブルに分ければ、衝突は自然に消えます。
システム間のデータ同期はまた別の問題です。要約は CRM に書き戻す必要がありますが、CRM の API がたまたま落ちていたら?
- まずどちらが Source of Truth(信頼できる唯一の情報源)かを決める。自分たちのデータベースか、CRM か?
- 書き込みに失敗したらリトライできるようにし、そのうえでリトライは冪等(idempotent)でなければならない。
call_idを冪等キーにすれば、10 回リトライしても CRM の要約は1つだけです。
Q9ログはどう収集するか?どんなエラーを分析できるか?システムの異常イベントを検索できるか?ユーザー操作の監査ログは残すか?
本当に確認したいこと何か起きたとき、何が起きたのかを突き止められるか。
シナリオ:担当者から「昨日の午後の通話に要約がない」と報告が来ます。ログがただのテキストの羅列で、何の ID でもつながっていなければ、何百万行ものログから針を探すことになります。
基本原則をいくつか挙げます。
- 構造化ログ(JSON 形式)にして、検索や集計をしやすくする
- 1つのトレース ID でフロー全体を貫く:アップロード → キュー → ワーカー → CRM。ID ひとつで経路全体が見えるようにする
- エラーを分類する:音声認識の失敗、LLM のタイムアウト、CRM への書き込み失敗をそれぞれ集計し、どれから直すべきかがわかるようにする
- ログに個人情報を書かない:クレジットカード番号や電話番号がログに入った瞬間、ログ基盤そのものが情報漏えいの原因になる
監査ログ(Audit Log)はまた別物です。通話録音にはお客様の個人情報が含まれます。セキュリティ監査や情報漏えいの調査では、必ず「誰が、いつ、どの録音を聞き、どの要約をダウンロードしたか」を聞かれます。こうしたログは通常のログとは分けて保存し、改ざんできないようにし、保存期間は社内規程や法令に従って決めます。
Q10どんな指標を監視するか?CPU、メモリ、ディスク、ネットワーク、リクエスト数、キャッシュヒット率、コネクションプールのサイズ、レイテンシは?
本当に確認したいことユーザーより先に、システムの異常に気づけるか。
指標は2つの層に分けて考えます。
- リソース指標(CPU、メモリ、ディスク、ネットワーク):「マシン」が健全かを教えてくれる
- サービス指標(リクエスト数、エラー率、レイテンシ、キューの深さ、キャッシュヒット率、コネクションプール使用率):「ユーザー」の体験が良好かを教えてくれる
これらの指標がなぜ大事かがわかる、実際に起きる例をいくつか紹介します。
- コネクションプール:ワーカー1台がデータベース接続を 20 本張るとします。ピーク時にワーカーが 10 台から 50 台に自動で増えると、接続数は 1,000 本になり、データベースの上限を超えます。スケールアウトしたせいでシステムが落ちるのです。コネクションプールを監視していなければ、なぜマシンを足したら壊れたのか見当もつきません。
- レイテンシは平均ではなく p99 を見る:平均 800ms なら優秀に見えますが、p99 が 15 秒なら、100 件に1件は担当者が長く待たされています。平均値は問題を隠します。
- キャッシュヒット率:90% から 20% に落ちたら、たいていデータベースが限界を迎える前兆です。
- ディスク:ログがディスクを埋め尽くし、サービスが何も書き込めなくなって落ちる。これは超定番の地雷で、ほぼ全員が一度は踏んでいます。
Q11認証と認可はどう動くか?ユーザーが他人のデータにアクセスするのをどう防ぐか?どのログイン方法に対応するか?アカウントはどう登録するか?
本当に確認したいことセキュリティホール、それも最もよくあって最も深刻な種類のもの。
まず区別しておきましょう。認証(Authentication)は「あなたは誰か」、認可(Authorization)は「あなたは何をしてよいか」です。
最もよくある脆弱性は IDOR と呼ばれるものです。担当者が自分の通話を見ようと /calls/12345/summary を開き、好奇心で URL を 12346 に変えてみたら、他人の通話が見えてしまった、というものです。
フロントエンドでボタンを隠すだけでは意味がありません。すべての API で、この人がこのデータを見る権限を持っているかをバックエンドで確認する必要があります。たとえば、自分の通話であるか、あるいは管理者でかつ自分のチームメンバーの通話であるか。
ログイン方法:社内システムなら、会社の SSO(SAML / OIDC で Azure AD や Okta と連携)にそのままつなぎましょう。独自に ID・パスワードを作る最大の問題は、社員が退職して AD のアカウントが無効になっても、あなたのシステムのアカウントは残っていて、ログインできてしまうことです。
アカウント登録:社内システムは通常セルフ登録を開放せず、AD グループから同期します(たとえば SCIM 経由)。人事異動があれば、権限も自動で追従します。
Q12システムのコストはいくらか?どれだけのトラフィックに耐えられるか?サービス単位あたりどれだけ処理できるか?
本当に確認したいことシステムのキャパシティについて「実測した数字」を持っているか、それとも推測にすぎないか。
「サービス単位」とは、ワーカー1台、Pod 1つがどれだけ処理できるかのことです。この数字は負荷試験で測る必要があり、これがあってはじめてスケールとコストを計算できます。
負荷試験の結果:ワーカー1台で、10分の録音を毎分 12 件処理できる
通常のピーク:5,000 件/時 ≈ 83 件/分 → 7 台必要
障害への余裕(2台落ちても耐える)→ 9 台
月初の3倍のトラフィック:約 250 件/分 → 21 台以上必要
台数に単価を掛ければ月額コストになり、それをユーザー数で割れば、先ほどの「ユーザー1人あたりのコスト」の話に戻ってきます。
あわせて、耐えられる量を超えたらどうなるかも明確にしておきます。キューで待たせて遅くなるのか? レート制限で拒否するのか? それとも文字起こしだけに縮退するのか?
Q13各コンポーネントのフェイルオーバーの仕組みは?復旧にどれくらいかかるか?
本当に確認したいことどのコンポーネントもいつか壊れる。壊れたとき、ユーザーには何が見えるのか?
よく使われる指標が2つあります。
- RTO(Recovery Time Objective):どれだけの時間内にサービスを復旧させるか
- RPO(Recovery Point Objective):最大でどれだけの期間のデータ損失を許容できるか
コンポーネントを一つひとつ挙げて、順に答えていくのがおすすめです。
| コンポーネント | 障害のシナリオ | フェイルオーバーの方法 | 復旧時間 |
|---|---|---|---|
| API サーバー | 1台が落ちる | 複数台をロードバランサーの後ろに置き、自動で切り離す | ほぼ気づかない |
| ワーカー | 処理の途中で落ちる | タスクがタイムアウト後にキューへ戻り、別のワーカーが引き継ぐ | その通話が数分遅れる |
| データベース | プライマリが落ちる | マルチ AZ で自動切り替え | 約1〜2分。その間の書き込みはリトライできる必要がある |
| 録音ファイル(S3) | 1つのデータセンターの障害 | S3 自体が複数 AZ に保存し、耐久性が非常に高い | RPO は 0 でなければならない。録音は絶対に失えない |
| 外部の音声認識 / LLM API | ベンダーのサービス停止 | タスクをキューに残して復旧を待つか、予備のベンダーに切り替える | ベンダー次第。その間、担当者は手入力で対応 |
最後の行が一番忘れられがちです。外部依存が壊れたとき、自分たちには直しようがありません。できるのは、あらかじめ代替策を考えておくことだけです。また、要約は作り直せますが、録音は失ったら終わりです。だから両者の RPO の要件はまったく違います。
Q14エラー処理の方針は?入力の検証や制限は必要か?出力のページングは必要か?失敗したらリトライするか?回数と間隔は?
本当に確認したいこと入力は決して信用せず、呼び出しは必ず失敗すると想定しているか。
入力の検証
担当者が電話を切り忘れて一晩中録音し、3GB の録音ファイルができてしまいます。ワーカーが読み込もうとしてメモリ不足(OOM)で落ち、タスクがキューに戻り、別のワーカーが拾ってまた落ちる……と次々に落ちて、キュー全体が詰まります。処理するものを落とし続けるこうしたメッセージを Poison Message(毒メッセージ)と呼びます。
だから入力には必ず制限を設けます。
- ファイルサイズの上限(たとえば 50MB)
- 録音時間の上限(たとえば2時間。超えたら特別なフローに回す)
- ファイル形式:拡張子だけでなく、実際のファイルヘッダーを確認する
出力のページング
管理者が「今月のすべての通話」を検索し、API が 10 万件を一度に返した結果、API はタイムアウトし、ブラウザは固まります。一覧系の API には必ずページングを入れましょう。データ量が多い場合は OFFSET(後ろのページほど遅くなる)ではなく、カーソルベースのページングがおすすめです。
リトライ戦略
- 成功する見込みのあるエラーだけリトライする:タイムアウト、
429 Too Many Requests、503はリトライする価値がある。400のパラメータエラーは 100 回リトライしても成功しない - Exponential Backoff + Jitter:待ち時間を1秒、2秒、4秒……と伸ばし、ランダムな揺らぎを加える。回数の上限はたとえば5回
- リトライの嵐を避ける:外部 API が遅くなったとき、すべてのクライアントが一斉に即リトライすると、トラフィックが一瞬で何倍にもなり、遅いだけだったサービスを完全に倒してしまう。ジッターに加えて、サーキットブレーカー(失敗率が高すぎたら一定時間呼び出しを止める)も入れる
- リトライする処理は冪等でなければならない:前述のとおり、リトライで二重書き込みが起きてはいけない
Q15どのプラットフォームに対応するか?レスポンシブデザインは必要か?
本当に確認したいこと対応プラットフォームがひとつ増えるたびに、開発・テスト・保守のコストも増える。本当に必要か?
- 担当者は全員、会社支給の Windows PC と Chrome を使っている:Web で十分
- 管理者はスマホでレポートを見たい:レスポンシブな Web で十分。ネイティブアプリは要らない
- でも要件が「外回りのスタッフが顧客先でスマホから録音する」に変わったら:オフライン対応、バックグラウンドアップロード、iOS のバックグラウンド実行制限への対応が必要になり、作業量はまったく別次元になる
「対応しない」プラットフォームを明記することも大切です。たとえば「スマホからの録音アップロードには対応しない」。書いておかないと、リリース後にバグとして報告されます。
Q16システムにはどんな限界があるか?どんな状況で到達するか?
本当に確認したいこと自分のシステムの限界がどこにあるか、そこにぶつかったら何が起きるかを知っているか。
設計前に聞いた「制約」は外部技術の制約でした。ここで聞かれているのは自分たちのシステムの限界です。それぞれについて、「どんな状況で到達するか」と「到達したときにシステムがどう振る舞うか」をはっきり書きます。
| 限界 | いつ到達するか | 到達したときの振る舞い |
|---|---|---|
| 1ファイル最大 50MB(約 50 分) | テクニカルサポートの通話は1時間を超えることが多い | 処理を拒否し、「手動で記録してください」と表示 |
| 処理量の上限は毎分約 250 件 | 月初に請求書が届いた後 | キューで待機し、要約の生成が遅れる |
| LLM API の毎分トークン上限 | ピーク時間帯 | 429 を受けたらバックオフしてリトライ |
| 主要言語のみ対応 | 方言が強い通話や他言語の通話 | 「認識の確信度が低い、要人手確認」とマーク |
こうして書いておけば、夜中のインシデントは「ドキュメントに書いてある既知の振る舞い」になり、サポートの管理者も事前に対応方法を把握できます。
Q17システムの問題をどう調査するか?ユーザーとシステムの状態について、何を記録しているか?
本当に確認したいことシステムの「デバッグしやすさ」を設計段階から考えていたか、それとも問題が起きてからログを足しているか。
実際の調査の流れを通して、どんな情報が必要かを見てみましょう。
- 佐藤さんのユーザー ID と時刻から、その通話の
call_idを特定する call_idでトレースを見る:アップロード成功、キューにも届き、文字起こしも完了しているが、文字起こしの長さが 0- 録音ファイルの情報を見る:ファイルサイズは正常だが、サンプリングレートが 8kHz。前日までは 16kHz だった
- 当時のシステム状態を見る:電話システムが前夜にアップグレードされ、デフォルトの録音形式が変わっていた。音声認識は非対応の形式を受け取り、エラーを出さずに空の結果を返していた
- 結論:モデルが壊れたのではなく、上流の形式が変わった。しかも自分たちのシステムは「黙って失敗」していた。これ自体も直すべき問題で、文字起こしが空ならエラーとして扱い、アラートを出すべき
この流れをたどれるようにするには、事前に次の情報を記録しておく必要があります。
- ユーザーについて:ユーザー ID、ロール、所属サポートセンター、クライアントのバージョン、OS とブラウザ
- リクエストについて:
call_id、トレース ID、ファイルサイズ、録音の長さと形式、各段階のタイムスタンプと結果 - システムの状態について:デプロイされているバージョン、フィーチャーフラグの状態、処理したワーカー、当時の依存サービスの健全性
特に大事なのがバージョン情報です。「クライアントの 2.3.1 だけで問題が起きる」「電話システムのアップグレード後から壊れ始めた」といった結論は、バージョンを記録していなければ永遠にたどり着けません。
Q18データの保持期間はどれくらいか?システムとして許容できるか?
本当に確認したいことデータを消さなければコストは際限なく増え、長く持ちすぎれば法的リスクと漏えいリスクも増える。
先ほどの数字を思い出してください。録音は毎月 8.8TB。永久に保存すれば、1年後には 100TB、3年後には 300TB を超えます。S3 Standard を 1GB あたり月 0.023 ドルでざっくり見積もると(実際の価格はリージョンによる)、1年後には録音の保存だけで毎月 2,000 ドル以上かかり、しかも増え続けます。
よくあるのは、アクセス頻度に応じてデータを階層化する方法です。
| 経過期間 | 保存先 | 用途 |
|---|---|---|
| 30 日以内 | 標準ストレージ(S3 Standard) | 担当者や管理者が頻繁に聞き直す |
| 30 日〜1年 | 低頻度アクセス/アーカイブストレージ | クレームで揉めたときに参照。単価はずっと安い |
| 保持期間を過ぎたもの | 自動削除(ライフサイクルポリシー) | コストと個人情報のリスクを下げる |
保持期間はエンジニアだけで決めるものではありません。法務やセキュリティ部門と一緒に決めましょう。
- 短すぎる:監査や業界の規制要件を満たせない可能性がある(業界によっては記録を一定年数保存する義務がある)
- 長すぎる:個人情報を長く持つほど、漏えい時の影響が大きくなる。個人情報保護法でも、利用する必要がなくなった個人データは遅滞なく消去するよう努めることが求められている
最後に、よく見落とされる細かい点をひとつ。削除は完全に。録音を消しても、バックアップ、ログ、そこから派生した文字起こし、要約、検索インデックスが残っていたら、実質的には消していないのと同じです。
WRAP-UPおわりに
たくさんの問いを並べましたが、すべてで満点を取る必要はありません。目的は設計書を分厚くすることではなく、次の3つを実現することです。
課題を理解する
誰が使うのか、何を解決するのか、以前はどうしていたのか、規模はどれくらいか、コストはいくらか。
選択肢を示す
少なくとも2つの案を用意し、トレードオフを正直に比較し、推す理由を書く。
リスクをさらけ出す
どのリスクなら引き受けられて代償は何か、どれは絶対に妥協できないか。
大企業では、ひとつの設計判断が複数のチームと何年分もの運用に影響します。1〜2週間かけてこれらの問いを考え抜けば、リリース後に何か月も地雷を踏み続けずに済みます。
次に設計書で手が止まったら、まず自分がレビュアーになったつもりで、これらの問いを自分に投げかけてみてください。答えられたものは設計書に書く。答えられなかったもの、それこそがまだ考え抜けていないところです。