最近、他人が書いたソースコードを以前よりも多く読んでいる。環境の変化*1によるものだが、とにかく、自分以外の人のコードを読み、理解し、時に問題点を指摘する、という機会が増えた。
同時に、相変わらず自身でもコードを書いている。仕事で書いて、時折プライベートでも書いている。肩ひじ張って開発してプロダクトに組み込んだモジュールもあれば、ちょっとした痛痒を解消するために適当にでっち上げた個人的な小スクリプトもある。
自分がガリガリとコードを書いているなか、世間ではLLMベースの生成AIを用いたソフトウェア開発が産声をあげたようだ。門外漢なので実情は全く分かっていないが、少なくとも産声をあげたといえる段階には到達しているように見える。ここから無事に成長するかどうかは分からないけど。
興味はある。しかし手を出してはいない。何か事情がある訳でもなく、単に興味の矛先が他にあるだけだ*2。
つまりは、生成AIについては門外漢な訳だ。
門外漢ながらに思うことに、特にバイブコーディングあたりで顕著だが、現状のAIを用いた開発においては、外部仕様が割と重要な地位を占めているように見える。
LLMが自然言語処理の分野の代物であるため、AIへの指示は自然言語で記述することになる。この特徴に起因してなのかどうかは不明だが、AIと対話して仕様を策定して文書化して、文書化した仕様を元にコードを生成させて、自動化されたテストで検証する――といった感じで、少なくとも「何を作るか」については「自然言語で記述された外部仕様」で規定しているように見える。
単体テストの自動生成についても、例えば自然言語で記述された「関数のインタフェース仕様」に基づいてテストケース・テストコードを生成する、といったアプローチが見られる。テスト対象のコードを読み込ませてテストケースを生成するアプローチも存在するが、この場合、コードを読み込む際にAIが対象を批評することはなく、所与のものとして扱われる。
総じて、AIの比重を大きくする方向に進むにつれて、現状では外部仕様の重要度が高まっていく傾向にあるように見える。
外部仕様の比重が大きい、という私の見方がある程度正しいと仮定するならば、品質の観点では、少なくとも外部品質は一定以上担保されているのだろう。
ここで門外漢の私が少し気になるのは、内部品質の取り扱いがどうなるか、という点である。
C/C++プログラマの職業病かもしれないが、とかく「外部仕様を満たしているバグありプログラム」を目にしてきた身としては、現状において外部仕様の比重が大きく見えるバイブコーディングが行きつく先において、内部品質のあり方がどうなるのか、気になるものである。
私見だが、内部品質に関する本質的な部分は、半世紀以上前にダイクストラが "Testing shows the presence, not the absence, of bugs." と喝破した頃からあまり変化していないように思う。
外部仕様(インタフェース仕様)にもとづく適切なソフトウェア・テストは、バグの存在を燻り出すことはできるが、バグが無いことを保証しない。それは論理として成立しない。*3
この問題にたいするダイクストラの解答は、ソースコードという「プログラムの静的構造」を解析することでバグの不在を証明しよう、というものである。初期の論文では数え上げ・数学的帰納法・抽象によるソースコードの解析を提案している*4。
ここで、半世紀前においては、プログラムの静的構造を解析するためにも人間がソースコードを読む必要があったのだが、「人間の知性の限界」という観点より「解析しやすいソースコード」と「解析しにくい/解析不能なソースコード」に分類することができる――という点に着目して、「では静的構造を解析しやすいソースコードとはどのようなものか?」を論じたのが、ダイクストラの構造化プログラミングである。
あれから半世紀以上が経過した。我々は未だに長い時間をコードリーディングに費やしているし、コーディングにおいて「理解しやすさ」という観点で内部品質を担保しようと努めている。
もちろん何も進展がなかった訳ではない。C/C++よりも遥かに安全なプログラミング言語の割合は増えた。人間によるコードリーディング以外にも、コンパイラの警告機能や、Lintのような静的解析ツールを使う「機械による静的構造の検証」というジャンルが登場して、今ではエディタ上でオンザフライで稼働している。ソフトウェア・テスト(動的テスト)においても、サブルーチンやモジュールのようなミクロな単位でのテスト――単体テストが広まっている。
それでも、だ。今でも主力は人間の目なのだ。この傾向は要求品質が高くなるにつれて顕著になる。組み込み開発でホワイトボックステストと網羅基準の話が出てくるのは、単なる偶然ではない。高品質を求めるなら静的構造の検証は必須であり、検証の主力が人間である以上、内部品質を担保してコードを読み易くすることは静的構造の検証の容易化に繋がるのだ。
まあしかし、現代は、単体テストの議論において「プライベート関数を個別にテストするか否か?」を議論できる時代である。そういう議論が成立するほどに、ソフトウェア開発の分野が広がり、結果としてプロダクトごとに要求品質の差が大きくなったと言えるだろう。
外部品質のテストと機械的な静的構造の検証だけでも十分間に合う程度の要求品質のプロダクトと、人間の目でソフトウェア内部に踏み込む必要のある要求品質のプロダクト、これらが混在しているのが2025年の状況である。
さて、仮に外部仕様駆動のバイブコーディングが広まっていくとしたら、これからどうなるだろうか?
要求品質がそれほど高くない分野では、仕様に基づく外部品質のテストと、静的解析ツール、これらの組み合わせだけで要求品質をそこそこクリアできるだろう。どちらの方法も比較的容易に自動化できそうだ。
このような分野ではAIが人間のプログラマにとって代わる可能性は高いだろう。また、プログラムにバグがあった時、小さなパッチを当てる直し方だけでなく、AIに作り直させる余地もあるだろう――自動化されたテストで要求品質をクリアできるからだ。
こうなると、淘汰されずに生き残ったプログラマはプレイングマネージャーと化すのかもしれない。外部仕様を考え、文書化してAIに与えて、AIが高速かつ頻繁に提示する開発物をさばきつつ、いざとなったら自分自身も打って出る――というスタイルが目に浮かぶ。
一方で、高品質が求められる分野では、静的構造を検証するためのコードリーディングと、その容易性を高めるための内部品質の担保は、依然として重要であり続けるだろう。
この場合は、人間のプログラマとAIの共存路線が考えられる。例えば、AIに下書きさせてから人間が手を加えて形を整えていく、旧来の「サンプルコードをコピペして改変」の発展版のようなアプローチが考えられる。
どんなにAIが高速かつ頻繁にソースコードを生成したとしても、それを人間が読んで理解する必要があるならば、開発速度は人間に律速したままだろう。また、理解を深めるためにも、単にコードを読むだけでなく、コードに手を入れることも効果的だろう。
どうせ人間がボトルネックとなるならば、プログラマがより静的構造を把握しやすい方法――AIと協調してコードを書く、という戦略は十分に考えられる。
コードリーディングにおいてもGitHub Copilotのコードレビューのような仕組みが出てきている。学習データの出どころを考えると、カバーされるのは一般的な問題点に絞られる気がするが、しかしその分だけプログラマの負担は減るだろう。より重大な、複雑な問題に焦点を当てたコードリーディングに時間を割くことができるはずだ。
まあしかし、この分野のプログラマも幸せにはなれないかもしれない。元より品質確保のために技量が求められる分野ではあるものの、より高い技術力が要求されるようになる気がしてならない。
AIに下書きさせたコードに手を入れるならば、AIよりも一段上の「コードへの審美眼」が求められるだろう。他人のコードを読んで良し悪しを判断できる技術水準に達していなくてはならない。AIが軽微な問題を指摘するならば、AIではカバーしきれない「難しい部分」が人間のプログラマに回ってくることになる。
――ここまでは門外漢の妄想だ。さてはて、一体どうなることやら。
非常に興味深い時代だ。同時に、1人のプログラマとしてキャリアの先が不透明で困惑させられる時代でもある。もっと気楽に、へらへらしながらコードを書きたいのだけどなあ。
*1:新しいコードベースを相手にしている、メンバーが増えてコードレビュー回数も多くなった、等々。
*2:個人的経験に基づけば、中途半端に興味がある状態で手を出しても良い結果にはならない。手を出すには、まだまだ興味ゲージが足りていないようだ。
*3:三段論法「もしPならばQである。Pである。従ってQである」に誤謬はないが、「もしPならばQである。Qである。従ってPである」は後件肯定である。経験的に正しいと思われる「もしこのソフトウェアにバグが無ければ、テスト結果は全てOKである」という命題の下において、「テスト結果は全てOKだった」という事実から「このソフトウェアにはバグが無い」と主張することは、形式的誤謬が過ぎるというものだ。そして我々は、これまた経験的に「もしテスト結果が全てOKならば、このソフトウェアにはバグが無い」という命題が現実世界において成り立たないことを知っている。
*4:後には最弱事前条件も出てきて、ホーア理論などの形式手法や、契約プログラミング(契約による設計)に繋がっていくことになる。