クライアント説明資料を書き直す — インタプリタ/コンパイルと動的/静的型付けを混同していた自分への反省文
TL;DR
- 以前、非エンジニアのクライアント向けに「PHPとGoの違い」を説明する資料を書いたことがある。読み返すと、技術的に不正確な記述がいくつも見つかった
- 一番の問題は、「インタプリタ/コンパイル」と「動的型付け/静的型付け」という独立した2つの軸を混同していたこと。この2軸は本来分けて論じるべきものだった
- 「PHPは実行時にコードを解釈する」「Goは事前に実行ファイルに変換する」という説明はざっくり正しいが、PHPもバイトコードにコンパイルされOPcacheでキャッシュされるという中間の事実を丸ごと省略していた
- 「メモリ管理の自動化はGoの強み」という記述に至っては単純に誤りで、PHPにもGCがある。Go固有の優位性ではなかった
- PHP は 7.0 の
strict_types、8系の JIT、PHPStan/Psalm のような静的解析、FrankenPHP のような新しい実行環境など、資料が前提にしていたPHP像を過去のものにする進化を続けている - 資料がGo優位の根拠にした「実行速度」も、I/Oバウンドな処理が中心のWeb APIではボトルネックの所在が違うため、単純比較には注意が必要だった
- 非エンジニアへの技術説明は、正確性を100%保ったまま行うのは原理的に難しい。だからこそ「何を削って何を残すか」を設計する意識が要るというのが、この記事で一番言いたいこと
概要
以前、非エンジニアのクライアント向けに「PHPとGoの技術的な違い」を説明する資料を書いたことがある。当時は「わかりやすく伝えられた」と思っていたのだが、最近読み返す機会があり、赤ペンで添削したくなるレベルで技術的に不正確な箇所が見つかった。この記事は、その資料を今のエンジニアの目で読み返し、どこがどう間違っていたかを検証し、「では正確にはどう言うべきだったか」を再構成する postmortem である。合わせて、非エンジニア向けの技術説明という、ディレクター兼エンジニアという立場だからこそ書ける方法論にも触れる。
なお、姉妹記事 「完全に理解した」の例のグラフ、原論文に無いってよ の「伝説その4」でも同じ資料に軽く触れている。あちらは「PHP 8 は JIT で速い」という通説の速報的なファクトチェックが主題で、本記事はその一歩先、「では非エンジニア向けの説明資料として、当時どう書くべきだったのか」という再構成に重心を置く。読み比べると面白いかもしれない。
言語間の優劣を断定する記事ではない。PHPを貶めるつもりも、Goを礼賛するつもりもない。一貫して「用途と制約に対する適合」の話として読んでほしい。
本記事の挿絵は特定の作品やミームそのものではなく、日本アニメ風の比喩として生成したものだ。
目次
- 症状
- 環境(当時の資料の前提)
- 誤診履歴(当時なぜ「正しい」と思っていたか)
- 真因(単純化による不正確さの棚卸し・5点)
- 修正(正確にはどう言うべきだったか)
- 再発防止(非エンジニアへの技術説明の方法論・3原則)
- 落とし穴(関連)
- まとめ
- 参考
症状
先日、別の用件でその資料を見返す機会があった。当時は「これでクライアントにも伝わるはずだ」と自信を持って出したものだったが、今読むと、Go推奨側に有利な情報だけが残り、PHP側の不利な情報が実態以上に強調される、偏った資料になっていた。しかも偏りの原因は「意図的な誇張」ではなく、もっと根の深い技術的な混同・省略にあった。
具体的には次のような記述が並んでいた。
| 項目 | PHP | Go |
|---|---|---|
| 実行方式 | インタプリタ(動的型付け) | コンパイル(静的型付け) |
| 実行速度 | 中程度 | 非常に高速 |
| セキュリティ | 実行時のエラーリスクあり | メモリ管理が自動化されており脆弱性を発生させにくい |
| 拡張性 | スケールに限界がある場合も | 大規模システム、高負荷環境に強い |
比喩表現も添えられていた。「Goは高速道路のようにデータがスムーズかつ迅速に流れる」「Goはシンプルで整理された設計図の家」「Goは工場で完璧な状態を作ってから出荷する」という具合である。
一見わかりやすい。だが技術的に見ると、5箇所は明確に不正確で、比喩も「橋」ではなく「結論」として機能してしまっていた。
環境(当時の資料の前提)
- 説明対象: 非エンジニアのクライアント(技術的意思決定者だが、実装の詳細には踏み込まない立場)
- 目的: PHPからGoへの技術選定、あるいは新規開発言語の選定を説明する場面
- 制約: 限られた説明時間の中で、専門用語を避けつつ判断材料を渡す必要があった
- 筆者の立場: ディレクター兼エンジニアとして、技術的な正確さと説明のわかりやすさの両方に責任を持つ立場
この「限られた時間で非エンジニアに伝える」という制約自体は今も変わらず存在する、正当な制約である。問題は制約への対処の仕方にあった。
誤診履歴
当時、なぜこの資料を「正しい」と思い込んでいたのか。これを率直に書くのが、この記事の誠実さの担保だと思っている。
一つ目の理由は、「インタプリタ言語=動的型付け」「コンパイル言語=静的型付け」という大雑把な相関を、そのまま因果関係のように扱っていたことだ。PHPもGoも初期に浅く触った経験があり、「PHPは書いてすぐ動く」「Goはビルドしないと動かない」という体感の違いを、そのまま型システムの違いの説明に流用してしまった。体感としては間違っていないのだが、それを「だからPHPは動的型付けで、Goは静的型付けだ」という説明の根拠に使うのは、論理が飛躍していた。
二つ目の理由は、説明のシンプルさを優先するあまり、PHP側の進化を調べる工程を省略したことだ。当時参照した情報のいくつかは体感や古い知識に基づいており、PHP 7.4以降のJIT導入や、PHPStan/Psalmのような静的解析エコシステムの成熟を十分に踏まえていなかった。「PHPは実行時にしかエラーがわからない」という認識は、当時の自分の中では最新の情報として通用していたが、実際にはアップデートが必要だった。
三つ目の理由は、比較表というフォーマットの魔力だ。表にすると項目ごとに「PHP: ○○」「Go: ××」ときれいに対比できてしまい、実際には独立した軸(実行方式、型検査タイミング、メモリ管理、実行速度のボトルネック要因)が、一枚の表の上で強制的に一列に並んでしまった。表を作ること自体が単純化を加速させる、という副作用に無自覚だった。
真因(1次資料 + 実機検証で確定)
改めて資料を読み返し、「単純化による不正確さ」を5点棚卸しした。それぞれ「元の記述 → 何が問題か → 正確には → クライアントにはどう言えばよかったか」の4点セットで整理する。
1. インタプリタ/コンパイル と 動的/静的型付けの混同
元の記述: 「PHP(インタプリタ/動的型付け)vs Go(コンパイル/静的型付け)」という対比表の見出しで、あたかも「インタプリタ=動的型付け」「コンパイル=静的型付け」がセットであるかのように併記していた。
何が問題か: 「実行方式(インタプリタ実行か事前コンパイルか)」と「型検査のタイミング(動的型付けか静的型付けか)」は、本来独立した2つの軸である。この2軸を1本の対立軸に押し込めたことで、「インタプリタだから型が緩い」という誤った因果関係を読み手に刷り込んでしまった。
2軸で整理すると、実際には次のように組み合わさる。
しかも「インタプリタ/コンパイル」という二値軸そのものが、実は過剰な単純化である(この表を最初に書いたときも二値で書きかけて、レビューで自分が刺された)。実行モデルはおおまかに3種類に分けるのが正確だ。
| 型検査 \ 実行モデル | AOT(事前)ネイティブコンパイル | バイトコードVM実行(+JIT) | 逐次解釈 |
|---|---|---|---|
| 静的型付け | Go, Rust, C++ | Java, C# | (まれ) |
| 動的型付け | (まれ) | PHP(OPcache), Python, Ruby | 初期のシェルスクリプト等 |
正確には: PHP も Python も Ruby も、現代の処理系はソースをバイトコードへコンパイルして VM 上で実行する。PHP は OPcache がそのバイトコードをキャッシュするため、「毎回その場で全部解釈している」という前提そのものが不正確だった。つまり PHP と Java は、事前コンパイルの有無こそ違えど「バイトコード VM 実行」という同じ族に属する。「インタプリタ言語 vs コンパイル言語」という私の資料の対立軸は、二重に壊れていたことになる。
クライアントにはどう言えばよかったか: 「実行の速さ」の話と「バグの発見タイミング」の話は別の質問だと分けて説明し、「PHPは書いてすぐ試せるが、実行中に間違いに気づくことがある」「Goは書いた後の確認作業が手厚く、実行前に間違いに気づきやすい」という体験ベースの説明に留め、技術用語(インタプリタ/コンパイル)を安易に対立軸として持ち出さないのが誠実だった。
2. PHP 8.xの進化(JIT, OPcache, strict_types, 静的解析ツール)を無視している点
元の記述: 「PHPは実行時にコードを解釈しながら実行する」「実行時のエラーリスク」「大規模開発での管理の難しさ」とだけ述べ、PHP自体の対策手段に触れていなかった。
何が問題か: PHP 7.4以降のJIT導入、OPcacheによるバイトコードキャッシュ、PHP 7.0以降のstrict_types宣言、PHPStan/Psalmのような静的解析エコシステムの成熟という、資料の前提そのものを覆す進化を一切踏まえていなかった。
ここで一つ、裏取りの済んだ確定情報を共有したい。「PHP 8はJITが入ったから速い」という主張自体も、実は単純化として危うい。
PHP 8.4 で変わったのは JIT の「無効化の方法」であって、「デフォルトで有効になった」わけではない。8.3 以前は
opcache.jit=tracingかつopcache.jit_buffer_size=0によってバッファサイズ0で実質無効、8.4 以降はopcache.jit=disableかつopcache.jit_buffer_size=64Mという設定そのもので明示的に無効にする形に変わった。JIT は PHP 8.0 から 8.4 まで一貫してデフォルト無効である。— PHP.Watch — Opcache: INI changes on how JIT is enabled (PHP 8.4) / PHP 公式マニュアル — opcache.configuration
つまり「JITが入った」ことと「JITが効いている」ことは別の話で、php.iniを明示的に設定しなければ、PHP 8系でもJITは眠ったままである。
ここに面白い対称性がある。当時の自分は「PHPは進化を踏まえていない」という不正確な資料を書いたが、もし今それを批判する側が「PHP 8はJITでもう速い」と雑に言い切ったら、同じ穴に落ちる。単純化を指摘する側も、自分の言葉が新しい単純化になっていないか、常に自己点検が要る。
正確には: strict_types=1宣言で関数境界の型を厳格化できる。PHPStan/Psalmは実行前に型不整合を検出でき、Goの「コンパイル前検出」に近い体験をエコシステムの力で補える。FrankenPHPはworker modeで常駐実行し、リクエストごとの起動コストを減らせる——ただし PHP エコシステムの大半は「リクエストごとにメモリが破棄される」前提で書かれているため、常駐化はメモリリークや状態汚染と隣り合わせであり、「入れれば Go 同等にスケールする」と言えるものではない。
クライアントにはどう言えばよかったか: 「PHPは昔と比べて、間違いを実行前に見つける仕組み(型宣言や自動チェックツール)がかなり整ってきている。導入と運用のコストはあるが、Goでなくても一定の安全性は積み増せる」という、言語比較ではなくエコシステム・運用体制の話として説明すべきだった。
3. 「メモリ管理の自動化=Go固有の強み」という誤り
元の記述: 「セキュリティ: メモリ管理が自動化されており一般的なセキュリティ脆弱性を発生させにく
い」とGoのメリットとして記載していた。
何が問題か: これはGo固有の特徴ではない。PHPもGC(ガベージコレクション)を持つ言語であり、C/C++のような手動メモリ管理言語との比較で語られるべき優位性を、あたかもPHPとの対比であるかのように書いてしまっていた。比較対象を取り違えた典型的な誤りである。
正確には: メモリ安全性の観点で比較すべきは「手動メモリ管理言語(C, C++)」対「自動メモリ管理言語(PHP, Go, Java, Python等)」であり、PHPとGoは同じ側に立つ。Go固有の話をするなら、並行処理機構(goroutine)まわりのメモリ安全性や、コンパイル時の型検証がメモリ関連バグの一部を防ぐ、といったより限定的で正確な主張にすべきだった。
クライアントにはどう言えばよかったか: 「メモリ管理の自動化」はPHP・Go共通の安心材料として説明し、Go固有の利点を語るなら型の厳格さや事前検証の強さという、既出の別の軸(1, 2)に統合して語るべきだった。
4. 実行速度の比較が文脈(I/Oバウンド vs CPUバウンド)を欠く点
元の記述: 「実行速度: PHP 中程度 / Go 非常に高速」「高速道路のようにデータがスムーズかつ迅速に流れる」という比喩で、Goの速さを一般的な優位性として説明していた。
何が問題か: 多くのWeb APIの応答時間は、言語の実行速度そのものよりもDBクエリ、外部API呼び出し、ネットワークI/O待ちが支配的(I/Oバウンド)であることが多い。CPUバウンドな処理(大量データの変換、暗号化、画像処理等)でなければ、言語自体の実行速度差がユーザー体感速度に直結するとは限らない。この前提の違いを説明せずに「Goは速い」とだけ言うのは不正確だった。
正確には: ここでレイテンシ(1リクエストの応答時間)とスループット(同時にさばける量)を分けて考える必要がある。ボトルネックがDBにあるなら、言語を変えても1リクエストのレイテンシはあまり縮まない。一方、I/O待ちの「さばき方」は言語のランタイムで大きく違う。伝統的な PHP-FPM はリクエストごとにプロセスを占有するため、I/O待ちの間もプロセスを塞ぎ、同時接続数が伸びるとプロセス数の上限に当たる。Go は goroutine と非同期I/Oにより、待ち時間中に他のリクエストを処理できる。つまり「I/OバウンドだからGoでも変わらない」もまた誤った単純化で、I/Oバウンドな高同時接続でこそスループット差が出る。レイテンシとスループットのどちらの話をしているかを常に明示すべきだった。
クライアントにはどう言えばよかったか: 「今回想定しているアクセスパターンでは、どこで待ち時間が発生しているか」を先に切り分けた上で、「言語を変えることでどこが改善するか」を説明すべきだった。「Goは速い」という一般論ではなく、自分たちのワークロードの性質に照らした説明にすべきだった。
5. 「スケールに限界がある」という曖昧で検証不能な主張
元の記述: 比較表の拡張性の項目で「PHP: スケールに限界がある場合も」「Go: 大規模システム、高負荷環境に強い」と記載していた。
何が問題か: 「スケールの限界」が何を指すか(同時接続数、開発チームの人数、コードベースの行数、インフラコスト)が特定されておらず、検証可能な基準がない。PHPを使う大規模サービスが実際に存在する事実とも整合しない、曖昧な主張になっていた。
正確には: 「スケール」を要素分解すると、(a) 同時リクエスト処理の水平スケール(PHP-FPMやFrankenPHPの多プロセス構成でも対応可能)、(b) コードベースの保守性(型の厳格さ・静的解析でPHPも改善余地あり)、(c) チーム人数増加時の一貫性(コーディング規約・CI・静的解析の整備度合いに依存し、言語だけの問題ではない)に分かれる。「PHPだからスケールしない」という単純化は不正確である。
クライアントにはどう言えばよかったか: 「スケールしにくい」という曖昧な言葉を使わず、「アクセス数が増えたときにどこにコストがかかるか」「開発チームが増えたときにどこに手間がかかるか」を具体的な軸ごとに分けて説明し、検証可能な基準を示すべきだった。
修正
以上の5点を踏まえ、当時の比較表を今の自分が書き直すとこうなる。優劣を断定せず、用途ベースで整理し直した。
| 項目 | 当時の記述 | 書き直し後 |
|---|---|---|
| 実行方式と型 | インタプリタ=動的型付け vs コンパイル=静的型付け | 実行方式と型検査タイミングは別軸。PHPは動的型付け+バイトコードコンパイル、Goは静的型付け+コンパイルという組み合わせ |
| PHPの現状 | 実行時にしかエラーが分からない |
strict_types・PHPStan/Psalm等で実行前検出をエコシステムとして補強可能。運用コストとのトレードオフ |
| メモリ管理 | Goのみ自動化されセキュア | PHP・Go共通でGCによる自動管理。手動管理言語(C/C++)との対比で語るべき軸 |
| 実行速度 | Go全般が高速 | ワークロードがI/Oバウンドかどうかで体感差は変わる。ボトルネックの切り分けが先 |
| 拡張性 | Goが大規模に強い、PHPは限界 | 「スケール」を同時接続・保守性・チーム運用の3軸に分解して個別に評価 |
比喩も評価し直す。「工場で完璧な状態を作ってから出荷する」という比喩は、コンパイル時に検証を済ませてから実行するというGoの体験を割とよく捉えている。一方「PHPは現場で調整」という対比部分は、OPcacheによる事前コンパイルの存在を無視しており、比喩として過剰な単純化だった。
正確に言うなら、両言語とも「事前準備」と「現場対応」のバランスが違うだけで、どちらかだけが工場でどちらかだけが現場、という二分法ではない。
再発防止(非エンジニアへの技術説明の方法論)
再発防止として、非エンジニアへの技術説明で守るべき原則を3つにまとめる。
原則1: 比喩は「理解の橋」であって「証拠」ではないと自覚する
当時の比喩(「高速道路」「シンプルで整理された設計図の家」「工場出荷」)はイメージのつかみとしては悪くない。問題は、比喩がそのまま技術的な優劣の論拠であるかのように使われていたことだ。
比喩は理解を助ける「橋」であり、橋を渡った先には正確な事実を用意しておく必要がある。比喩だけで結論まで運んでしまうと、聞き手は比喩の印象(「工場出荷=安心」)をそのまま技術的事実だと誤解する。比喩を使うときは、「この比喩はどこまでは妥当で、どこから過剰な単純化か」を自問する一手間が要る。
比喩の橋は、たいてい途中までしか架かっていない。渡り切る前に足元を確認したい。
原則2: 単純化の許容ラインは「意思決定に影響するかどうか」で引く
非エンジニアへの説明は、100%正確を目指すと情報量が過多になり、逆に意思決定を妨げる。単純化そのものは必要悪ではなく、正当な技術だ。
ただし単純化してよいのは「結論に影響しない詳細」であり、「結論の方向を左右しうる事実」を省略・歪曲するのは単純化ではなく誤誘導になる。当時の資料の問題は、単純化の対象を誤り、結論(Go推奨)に都合の良い情報だけを残し、不都合な情報(PHP8.xの進化、GCの共通性)を削ったことにあった。
「省略してよい情報」と「省略してはいけない情報」を分けるチェックリストを持つとよい。数値の一人歩きを防いでいるか、比較対象は公平か、反証可能性を残しているか、といった観点である。
わかりやすさと正確さは対立項ではなく、釣り合わせる対象だ。
原則3: 「言語の優劣」ではなく「意思決定に必要な軸」を渡す
非エンジニアが本当に知りたいのは「PHPとGoどちらが優れているか」ではなく、「自分たちのビジネスにとってどちらが適切か」という意思決定材料だ。
説明者(エンジニア側)が無意識に「自分が薦めたい技術」の正当化に説明を寄せてしまうと、原則2で述べた誤誘導が起きやすい。非エンジニア向け説明では、説明者自身の技術的な好み・得意領域から距離を置いて、判断軸(コスト、開発速度、将来の変更しやすさ、採用市場の人材確保しやすさ等)を先に提示し、その軸の上でどちらが有利かをフラットに示す設計が誠実だと思う。
「今回このプロジェクトで重視すべき軸は何か」を先に合意してから技術比較に入る、という順序自体が、実務上のちょっとした工夫になる。
補足として4つ目を挙げるなら、「検証可能な形にする」。「スケールに限界がある」のような曖昧な言葉を避け、「〜という条件下では〜という傾向が観測される」という形に言い換える習慣である。これは棚卸しの5点目と直接つながる。
落とし穴(関連)
この手の単純化は、PHP/Go比較に限らず起きる。ベンチマーク数値を一人歩きさせる、フレームワーク比較で「新しい方が優れている」と暗黙に前提してしまう、クラウドサービス比較で移行コストを説明から落とす、といった場面でも同じ構造の罠が仕掛かっている。「結論に都合の良い情報だけが残っていないか」は、技術説明資料を作るたびに振り返る価値のあるチェック項目だと思う。
まとめ
- 以前書いたPHP/Go比較資料には、インタプリタ/コンパイルと動的/静的型付けの混同をはじめ、5点の技術的な不正確さがあった
- 原因は悪意ではなく、「わかりやすさ」を優先するあまり2軸を1軸に押し込み、都合の良い情報だけを残してしまったという、単純化の設計ミスだった
- 言語そのものに優劣をつける記事ではない。PHPにもGoにも、それぞれ向く場面と向かない場面がある
- 非エンジニアへの技術説明は、比喩を証拠にしない、単純化の許容ラインを意思決定への影響で引く、優劣ではなく判断軸を渡す、という3原則を意識すると資料の誠実さが上がる
- そしてこの記事自体も、書きながら「新しい単純化をしていないか」を何度も問い直した。その自己点検こそが、非エンジニア向け技術説明の実務だと思う
参考
公式1次資料
関連記事(筆者)
- 「完全に理解した」の例のグラフ、原論文に無いってよ — 偏屈エンジニアのための都市伝説成仏ガイド — 同じPHP/Go資料に「伝説その4」として速報的に触れた姉妹記事。本記事はその深掘り版






