Architectural Acupuncture: ピンポイント最適化で、システムを“軽く”生まれ変わらせる技術

  1. 大手術じゃなくていい。痛みの原因を見抜く“アーキテクチャの鍼治療”
    1. ■「全部作り直す」は現実的じゃない
    2. ■ その時に出会った考え方:「アーキテクチャの鍼治療」
    3. ■ それは「技術力」よりも「観察力」が問われる仕事
    4. ■ 小さな改善がチーム文化を変える
    5. ■ 「直す勇気」より「見抜く力」を
  2. キャッシュの打ち方ひとつで、システムはここまで変わる
    1. ■ キャッシュの目的は“正確さ”より“体験の滑らかさ”
    2. ■ 3層キャッシュ戦略:どこに“ツボ”を打つか
      1. ① クライアントサイドキャッシュ(WPF側)
      2. ② APIゲートウェイキャッシュ
      3. ③ データベースレイヤーキャッシュ(DB内メモリ構造の活用)
    3. ■ キャッシュの効果は「秒」じゃなく「心理」で測る
    4. ■ キャッシュは「打ち方」で毒にも薬にもなる
    5. ■ キャッシュは“即効性の鍼”、でも根治には「流れの最適化」が必要
  3. データベースを変えずに、クエリと構造を変える
    1. ■ 「クエリを直せば早くなる」と思っていた時期が僕にもありました
    2. ■ クエリを“書き換える”より、“発行される頻度”を減らす
    3. ■ “ボトルネックの見える化”が最大の改善ポイント
    4. ■ スキーマは変えずに“構造”を変える:派生ビューとマテリアライズ戦略
      1. ▸ 派生ビュー(Derived View)
      2. ▸ マテリアライズ戦略(Materialized Strategy)
    5. ■ パフォーマンス改善は“技術”ではなく“構造改革”
    6. ■ 小さな改善が「アーキテクトの筋肉」を育てる
    7. ■ そして次のツボへ──API設計とサービス間通信
  4. 小さな針がチームを変える
    1. ■ “針治療”的思考がチーム文化になる
    2. ■ “全リファクタ”より“全員が一歩前へ”
    3. ■ 「小さな針」が導いたもう一つの成果
    4. ■ 最後に:針を打つ勇気を持とう

大手術じゃなくていい。痛みの原因を見抜く“アーキテクチャの鍼治療”

「このAPI、なんでこんなに遅いんだろう?」
そう呟きながら、モニターの前で眉間にしわを寄せたことがあるエンジニアは多いと思う。
僕が海外で最初に関わったプロジェクトでも、同じような問題に直面した。

機能追加を重ねた結果、アーキテクチャは複雑化し、レスポンスはどんどん重くなっていた。
でも、チームには「フルリファクタリングをする時間も予算もない」。
それでも「パフォーマンスは改善してほしい」と求められる。
そんな“無理ゲー”みたいな状況、あなたも経験があるかもしれない。


■「全部作り直す」は現実的じゃない

多くのエンジニアが陥る罠が、「遅いなら、根本から直そう」という発想だ。
たしかに理想的だ。
でも、現実のプロジェクトでは、根本治療より対症療法の方が求められることが多い

僕が当時担当していたのは、海外の製造業向けの在庫管理システム。
全体はマイクロサービス構成で、C#とWPFでフロントを作り、バックエンドは.NETとSQL Serverだった。
ある日、倉庫オペレーターから「画面遷移が重い」との報告があった。
ログを調べると、API呼び出しのたびに数千件のデータを丸ごと取ってきていた。

開発者としての第一反応は、
「この設計、もうダメだな。リファクタリングしないと。」
だった。

でもPMの一言が現実を突きつけた。

“That’s not an option. We need improvement this week.”

つまり「根本治療」は許されず、“痛み止め”のようなアプローチで結果を出さなきゃいけない


■ その時に出会った考え方:「アーキテクチャの鍼治療」

医者が身体のツボを刺激して、体全体を整えるように、
アーキテクトも“負荷のツボ”を見抜いて、そこを軽くしてあげることができる。

この考え方を、僕は勝手に「Architectural Acupuncture(アーキテクチャの鍼治療)」と呼んでいる。

小さな改善を、戦略的に。
システム全体の流れを変えずに、部分最適で“即効性”を狙う。

実際、この発想で取り組んだ改善は次の3つだった:

  1. キャッシュを賢く挟む(Smart Caching)
     → クライアント、API、DBそれぞれで「ここにキャッシュを置けば効く」という場所を特定。
  2. DBクエリの最適化(Query & Schema Optimization)
     → 無駄なJOINを削減し、頻繁に使うカラムにインデックスを追加。
  3. サービス間通信の簡略化(API Streamlining)
     → 不要な中間サービスを減らし、直接呼び出せる経路を再設計。

どれも1〜2日の変更でできる範囲だったが、効果は劇的だった。
結果、平均レスポンスタイムは3.2秒から0.7秒に短縮
ユーザーからも「新しい画面みたいに速い!」とフィードバックをもらえた。


■ それは「技術力」よりも「観察力」が問われる仕事

面白いのは、この種の最適化はコードスキルよりも“洞察力”がものを言うということ。
CPUプロファイルを見て、どこで時間が食われているか。
どのAPIがボトルネックになっているか。
ログを追いながら、「本当に必要な処理はどれだろう?」と探る。

まるでエンジニア版の“東洋医学”みたいな仕事だ。

僕が実践して感じたのは、

「最適化は、根性論ではなく観察論だ」
ということ。

リファクタリングで全部作り直すのは簡単だけど、
現場では、限られた時間・予算・チーム体制の中で結果を出す力こそ価値になる。


■ 小さな改善がチーム文化を変える

興味深いのは、この“アーキテクチャの鍼治療”を繰り返すうちに、
チームのマインドセットまで変わっていったことだ。

以前は、
「このシステム、古いからもう直せないよ」
と諦めムードだった開発者たちが、
「小さく手を入れれば、変えられるんだ」
と前向きに議論するようになった。

結果、メンバーの一人が「じゃあ次はメモリキャッシュを導入してみよう」と提案してくれた。
その小さな改善が、また別のボトルネックを解消した。

つまり、小さな成功体験が“改善できる文化”を生む
これは海外でも共通して通じるエンジニアリングの知恵だと感じた。


■ 「直す勇気」より「見抜く力」を

最適化というと、「手を動かす」ことばかりに意識が行きがちだ。
でも本当に大事なのは、「どこを直すかを見抜く」こと。

僕はこの経験から、

“Don’t rebuild the system. Heal it.”
(作り直すな。癒やせ。)
という考え方を持つようになった。

次章「承」では、この“アーキテクチャの鍼治療”をどうやって実践するか──
特にキャッシュ戦略・DB最適化・API設計の3つの観点から、
実際に手を動かした改善プロセスを具体的に紹介していく。

キャッシュの打ち方ひとつで、システムはここまで変わる

僕が「アーキテクチャの鍼治療」という発想に出会ったきっかけは、
キャッシュ戦略を根本的に見直したことだった。

当時のプロジェクトでは、画面遷移ごとに同じデータを毎回API経由で取得していた。
たとえば、倉庫リストや商品マスタ、ユーザー権限情報など。
どれも“1日にほとんど変わらないデータ”なのに、毎回SQLが叩かれ、ネットワークもCPUも無駄に使われていた。

ユーザーの体感としては「画面がモッサリしてる」「1クリックごとに待たされる」。
だけど、裏側の構造を見れば原因は明らかだった。

「キャッシュがない」=「毎回ゼロから世界を作っている」状態だった。


■ キャッシュの目的は“正確さ”より“体験の滑らかさ”

海外のエンジニアと働くようになって、印象的だったことがある。
彼らはよくこう言う。

“Don’t make it perfect. Make it fast enough.”

つまり「完璧に正確じゃなくても、十分に速ければOK」という考え方だ。

日本の開発現場では「キャッシュ=古い情報を出してしまうリスク」として敬遠されがちだが、
海外では「キャッシュ=体験を滑らかにするツール」として積極的に使う。

僕も最初は抵抗があった。
「もし在庫数が変わっていたら?」「もしマスタが更新されたら?」
と、どうしても“正確さ”に引っ張られていた。

でも、ユーザーインタビューで気づいた。
彼らが本当に求めていたのは、

「正確な数値」より「スムーズに動く操作感」
だった。


■ 3層キャッシュ戦略:どこに“ツボ”を打つか

僕が導入したのは、3層キャッシュ戦略(Three-Layer Caching Strategy)
“ツボ”を打つように、負荷が集中している場所にだけキャッシュを挿入した。

① クライアントサイドキャッシュ(WPF側)

まずはユーザー体感を直接改善できる層から。
WPFアプリでは、画面表示時にAPIを呼ばず、ローカルに持つDictionaryキャッシュを優先するようにした。

if (Cache.TryGetValue("WarehouseList", out var data))
{
return data; // キャッシュヒット時は即返す
}
else
{
var response = await ApiClient.GetWarehousesAsync();
Cache["WarehouseList"] = response;
return response;
}

結果、2回目以降の画面遷移が瞬時になった。
この時点でユーザー満足度は大幅に上がった。

② APIゲートウェイキャッシュ

次に、“みんなが叩くAPI”を挟んでキャッシュを仕込む。
Redisを導入し、同じパラメータで呼ばれたAPIレスポンスを数十秒間キャッシュする設定にした。

Redisに載せた理由は、

  • データの更新頻度が低い
  • APIの応答パターンが似ている
  • サービス間通信が多い

この3条件が揃っていたから。

結果、APIの処理時間が平均700ms → 120msに短縮。
サーバー負荷も2割減少した。

③ データベースレイヤーキャッシュ(DB内メモリ構造の活用)

最後に、“一番奥の層”であるSQL Serverに軽いメモリキャッシュを導入。
一部の定常クエリ結果をMemory Optimized Table Variableに載せることで、
同じクエリを繰り返す処理を高速化した。

SQLチューニングは「重い処理を早くする」のではなく、
**「そもそも繰り返さない構造を作る」**のが大事だと実感した。


■ キャッシュの効果は「秒」じゃなく「心理」で測る

キャッシュを入れると、当然パフォーマンスの数値は良くなる。
でも、僕が最も驚いたのは、**“チームの心理的な変化”**だった。

改善前は、

「このシステム、限界だよ」
という諦めムードだった。

改善後は、

「まだやれること、あるかもね」
と、ポジティブな空気に変わった。

ユーザーからも「操作がスムーズになって気持ちいい!」という声が届いた。
このとき僕は、

キャッシュは技術じゃなくて“信頼を取り戻す装置”でもある
と感じた。


■ キャッシュは「打ち方」で毒にも薬にもなる

もちろん、キャッシュは諸刃の剣でもある。
誤ったデータを保持してトラブルを招いた経験もある。

特に痛かったのが、在庫更新後も古いキャッシュが残っていたケース。
顧客が別の倉庫に商品を誤配送してしまい、クレームが入った。

このとき学んだ教訓が、

“キャッシュは入れる場所より、捨てるタイミングが大事”

キャッシュの無効化(Invalidation)をきちんと設計していなければ、
性能改善どころか信頼を失う。

僕が実践したルールは次の3つ:

  1. 更新イベントを検知したら、関連キャッシュを即削除
  2. 期限切れ(TTL)を必ず設定
  3. 「重要データ」はキャッシュしない(マスタ系・トランザクション系は除外)

こうして運用ルールを整備した結果、事故は再発しなかった。


■ キャッシュは“即効性の鍼”、でも根治には「流れの最適化」が必要

キャッシュは即効性がある。
けれど、それは“痛み止め”にすぎない。

根本的には、サービス間通信やDB設計の流れそのものを整える必要がある。
これはまさに、鍼治療が「痛みのある場所に打つ」だけでなく、
体全体の血流を整えるのと同じだ。

データベースを変えずに、クエリと構造を変える

キャッシュによって「即効性のある改善」を得たあと、次に僕が取り組んだのが、
**「データベースの血流を整える」**ことだった。

というのも、キャッシュで応答時間は短縮できても、
キャッシュミス時のレスポンスが依然として重かった
つまり、“痛み止め”は効いたが、根本の炎症が残っていた。

海外のプロジェクトでは、DB構成を大きく変えるのは非常にコストが高い。
スキーマ変更やマイグレーションには、複数チームの承認・テスト・デプロイ調整が必要だからだ。

だから僕たちが取ったアプローチはこうだった。

「データベースを変えずに、クエリと構造を変える。」

これが、“アーキテクチャの鍼治療”の第二のツボだった。


■ 「クエリを直せば早くなる」と思っていた時期が僕にもありました

最初の改善ミーティングで僕が提案したのは、
「SQLのWHERE句を最適化して、不要なJOINを減らす」ことだった。
でも、実際にチューニングしても効果は限定的。

SQLプロファイラで実行計画を追うと、
CPUよりもI/Oコスト(ディスク読み込み)が圧倒的に高かった。
つまり、クエリのロジックではなくデータの取り方
そのものが問題だった。

DBA(データベース管理者)の同僚に相談したら、
彼が静かに言った。

“You’re fixing the SQL. But what you need is to fix how the system uses the data.”

その言葉で、僕の視点が180度変わった。


■ クエリを“書き換える”より、“発行される頻度”を減らす

チームで分析を進めると、

  • 同じデータを複数サービスが別々に取得している
  • 不要なJOINがある(設計当初の名残)
  • データ取得単位が大きすぎる(全部取得して後でフィルタしている)
    という構造的な無駄が次々と見えてきた。

特に目立ったのが、在庫検索API
各サービスが似たようなSQLをそれぞれ発行していて、DBがボトルネックになっていた。

そこで僕たちはこうした。

  1. 共通ビュー(View)を作り、データアクセスを一本化
     → 各サービスが同じロジックを自前で持たないようにする。
  2. 部分的にストアドプロシージャ化
     → データ抽出処理をサーバー側で完結させ、ネットワーク往復を減らす。
  3. クエリパターンをキャッシュ(Query Result Cache)
     → 同じWHERE条件の検索結果を一時保持する。

結果、在庫検索APIの処理時間が3.5秒 → 0.8秒に短縮。
DB負荷も平均30%削減できた。


■ “ボトルネックの見える化”が最大の改善ポイント

最適化を進める上で、最も効果的だったのがクエリの可視化ツールの導入だった。
SQL Server Profiler や Azure Data Studio の「Query Store」機能を活用し、
どのクエリが遅いのか、どのテーブルが過剰に読み込まれているのかを分析した。

ログを数日間収集して、アクセス頻度をヒートマップ化してみると、
意外にも「古い管理画面が呼ぶサブクエリ」が全体の負荷の約40%を占めていた。

つまり、「ユーザーの体感が遅い」箇所と「実際に重い箇所」が一致していなかったのだ。

この経験から学んだのは、

“最適化は感覚ではなく、データで判断する”
ということ。

この「見える化」の仕組みが、次の最適化サイクルを生み出した。


■ スキーマは変えずに“構造”を変える:派生ビューとマテリアライズ戦略

スキーマを変更できない状況でも、“構造”は変えられる。
僕が実践したのは、派生ビュー(Derived Views)とマテリアライズド・データ構造の活用だった。

▸ 派生ビュー(Derived View)

よく使うJOIN構造をあらかじめViewとして定義しておき、
アプリ側では複雑なSQLを組まなくても済むようにした。
これにより、クエリパースのコストも減少し、保守性も向上。

▸ マテリアライズ戦略(Materialized Strategy)

更新頻度の低い集計結果を一時テーブルに保持。
1時間ごとにバックグラウンドで再計算する方式を採用した。

たとえば「商品別の在庫合計数」を毎回集計するのではなく、
定期的に“スナップショット”として保持しておくイメージだ。

結果、レポート系画面の読み込み速度は5秒 → 0.6秒に。

「データを動かすな。データの流れを変えろ。」
これが僕がこの工程で得た最大の気付きだった。


■ パフォーマンス改善は“技術”ではなく“構造改革”

DB最適化というと、SQLチューニングやインデックス調整の話になりがちだ。
でも、本当に効くのはそこではない。

本質は、

「どの層がどの情報を責任もって扱うか」
という構造の再整理にある。

僕が海外のチームで学んだのは、
エンジニアリングの世界でも「権限と責任の分散」がパフォーマンスを生むということ。

たとえば:

  • API層で絞り込みできるなら、DBまで持っていかない
  • フロントでフィルタできるなら、サーバーは最低限のデータだけ渡す
  • 不変データはキャッシュで、変動データはクエリで取得する

この“責務の分離”を意識するだけで、
設計全体の流れが驚くほどスムーズになる。


■ 小さな改善が「アーキテクトの筋肉」を育てる

DBチューニングの過程で感じたのは、
最適化は筋トレみたいなものだということ。

1つのボトルネックを見つけて解消すると、
次に“もっと効くツボ”が見えてくる。
それを繰り返すうちに、システムの呼吸が整っていく感覚がある。

そして何より、
「フルリプレイスしなくても、今の仕組みを活かして改善できる」
というチームの自信が生まれる。

それこそが、“アーキテクチャの鍼治療”がもたらす最大の価値だと僕は思う。


■ そして次のツボへ──API設計とサービス間通信

データベースの血流が整っても、まだ“全身”は治りきらない。
システム全体を流れる“神経”とも言える、
API設計とサービス間通信が詰まっているケースが多い。

次章「結」では、
「サービス間の無駄な会話を減らす」ことでシステムを軽くする設計術を紹介する。

アーキテクチャを「再構築」ではなく「再調律」する方法──
それが、このシリーズの最終章だ。

小さな針がチームを変える

正直、最初に「アーキテクチャの針治療」なんて言い出したとき、チームのみんなからは「またヒロが変なこと言ってる」と笑われました(笑)。
でも、数週間後には状況が一変しました。
プロジェクトの朝会で、「昨日のレスポンス、めっちゃ速くなったね!」という声が自然に出てきたんです。
しかも、それは誰か一人のヒーロー的努力ではなく、チーム全体で積み重ねた小さな最適化の結果でした。

■ “針治療”的思考がチーム文化になる

僕が気づいたのは、アーキテクチャの最適化って、技術力だけの話じゃないということです。
どちらかというと、「チームが小さな違和感を無視しない文化」を育てることに近い。

たとえば、以前なら誰かが「API遅くない?」と言っても、「まあ仕方ないよ、仕様だから」で終わっていました。
でも、針治療のように「どこを押せば痛いのか」を確かめる癖をつけると、
「ちょっとログ仕込んでみようか」とか「SQLの実行計画見てみよう」といった、行動につながる会話に変わっていきます。

しかも、この“小さな行動”が積み重なると、開発スピードもチームの士気も明らかに上がる
なぜなら、問題を「自分たちでコントロールできる」と実感できるからです。
海外チームでは特に、「問題を共有して、改善をみんなで進める」文化が重視されます。
一人のスーパープログラマーよりも、全員が少しずつ良くする意識のほうがずっと価値があるんです。

■ “全リファクタ”より“全員が一歩前へ”

僕も最初は、パフォーマンス改善と聞くと「根本から作り直さなきゃ」と思っていました。
でも、現実のプロジェクトではそんな時間もリソースもありません。
大規模なリファクタよりも、**「ここだけ直したら今すぐ1秒早くなる」**という改善のほうが、よほど実用的。

実際、僕らのチームでは、

  • API層にキャッシュを1つ追加
  • 無駄なJOINを1つ削除
  • N+1を1カ所解消
    たったこれだけで、エンドユーザーの体感速度が30〜40%改善しました。

「え、そんなことで?」と驚かれるかもしれませんが、現場のリアルはそんなものです。
大事なのは、“やること”より“やり続ける文化”です。
一度効果を出すと、「他にも針を打てる場所ある?」と自然にチームが探し始める。
これが「アーキテクチャの自己治癒力」だと思っています。

■ 「小さな針」が導いたもう一つの成果

面白いのは、パフォーマンスが上がるとコミュニケーションも滑らかになることです。
遅いAPIのせいでQAチームがイライラすることもなくなり、
ビジネス側も安心してリリースを任せてくれるようになりました。

つまり、“小さな針”はコードだけじゃなく、チームの信頼関係にも効くんです。
そして、それは技術的負債の返済よりも、ずっと価値のある「文化的投資」になる。

僕自身、この経験を通して、技術の使い方が少し変わりました。
昔は「最新技術を使いこなすこと」が正義だと思っていましたが、
今は「既存の技術で最大の効果を出すこと」にこそ、エンジニアの腕が出ると感じます。


■ 最後に:針を打つ勇気を持とう

もしこの記事を読んでいるあなたが、
「うちのシステム、重いけどどうせ直せないよな…」と思っているなら、
まずは小さな針を一本打ってみてください。

1つのクエリを見直す。
1つのキャッシュを試す。
1つのログを追加する。

それだけで、きっと何かが変わります。
そして、その“何か”が積み重なったとき、
システムもチームも、まるで別人のように軽やかになります。

僕はそれを、「アーキテクチャの針治療」と呼びたい。
なぜなら、大切なのは構造全体ではなく、痛みのある一点を丁寧に癒すことだから。

コメント

タイトルとURLをコピーしました