「AIはコードが得意だから、ソフトウェア開発ではAI活用が進んだ」。よくそう言われる。
だが、本当の理由はそこだけではないと思う。
この分野が、コードだけでなく、手順も、設定も、検証も、その履歴も、早くから文書やデータに残してきたからだ。
その下地が整えば、AIは他のどんな仕事でも同じように働く。
そこで問われるのは、その仕事をどう捉え、何をどこまで切り分けて渡すか、という段取りの力になる。
プログラミングは、そんなに高度な仕事なのか
書けない人から見ると、プログラミングは高度な作業に映るらしい。
暗号のような記号が並び、少し打ち間違えれば全体が動かなくなる。
その様子を見れば、書ける人は特別な思考をしていると感じても無理はない。
しかし手を動かしている側の実感は、これとかなり違う。
業務システムやよくあるサービスで書くコードの大半は、難しい思考をほとんど必要としない。
データを受け取り、条件で振り分け、別の形に整えて返す。その繰り返しだ。
では、時間はどこへ消えているのか。
プログラムを書く作業ではない。
何をどう表現するかを決める設計と、それが正しく動くと確かめるテスト。
食っているのはこの二つだ。
そしてこれは、AIが開発に加わるずっと前から、変わらない。
難しさが無いわけではない。場所が違うだけだ。
問題そのものが持つ難しさ(本質的複雑さ)と、作り方の都合から生まれる難しさ(偶有的複雑さ)は、区別できる(Brooks「No Silver Bullet」)。
前者は逃げられない。だが現場で日々ぶつかる複雑さには、後者、つまり設計しだいで増えも減りもする側が、かなり混じっている。
機能追加と複雑性爆発
この後者の複雑さは、なぜ手に負えなくなるのか。
増え方に理由がある。
正確な言い方ではないが、感覚をつかむために単純化する。
何も考えずに機能を足すと、複雑さは一機能ごとに掛け算で増えていく。
新しい機能は、既存のすべてと組み合わさりうるからだ。
状態を表す切り替えが一つ増えるだけで、あり得る組み合わせは倍になる。
この、掛け算で膨らむ現象を複雑性爆発と呼ぶ。
具体的に見てみる。
通販の注文の状態を、はじめは「支払い済みか」のオン・オフだけで持つ。
やがて「発送したか」が要る。返品も、キャンセルも出てくる。必要になるたび、印を一つずつ足す。
一つひとつの追加は、そのときの要望に対して正しい。
だが印が四つ並ぶと、「支払っていないのに発送済み」という、本来ありえない組み合わせまで表現できてしまう。
そうならないための確認が、プログラムのあちこちに散らばる。
印が一つ増えるたびに、散らばった確認を全部直して回る。
一つの追加が、既存のすべてへ波及する。これが複雑性爆発の実際の姿だ。
うまく設計すれば、この増え方を足し算の側へ近づけられる。
注文の状態を、「支払い済み」「発送済み」「返品」のように、同時に一つしか選べない一個の値にする。
すると「支払っていないのに発送済み」は、そもそも書き表せなくなる。
防ぐべき組み合わせが、最初から存在しなくなる。
ありえない事態を、対処ではなく定義ごと消す。こうした設計の考え方を、Ousterhout は説いている(『A Philosophy of Software Design』)。
手近だからと印を足す道は、いつでも簡単(easy)だ。だが絡まない形に畳む道だけが、単純(simple)だ(Hickey「Simple Made Easy」)。
単純とは、機能を削ることではない。絡めないことだ。
AIに丸投げすると、複雑さは速く積み上がる
複雑なプログラムを書きこなす人ほど、優れたプログラマーと思われているかもしれないが、本当に効くのは逆の力だ。
要求を抽象的に捉え、単純な設計へ落とし込み、そもそも複雑なものを作らずに済ませる。
込み入ったコードを書けることより、書かずに済む形を見つけることのほうが、ずっと難しく、ずっと価値がある。
AIは、この一番大事な工程を、たやすく飛ばせる。
新しいモデルが出るたびに賢さが話題になるが、モデルの賢さと、いま言った設計の良さは、別の軸だ。
複雑な依頼をそのまま渡せば、複雑な実装がそのまま返ってくる。
畳む工程を省いたまま丸投げすれば、複雑性爆発はかつてない速さで進む。
賢いAIほど、雑に渡すと複雑さも速く積み上がる。
これは、扱う問題の性質にもよる。
AIが何時間も自走してコードを書き続ける、という話が時折ニュースになる。
ああした成果が出やすいのは、正解のかたちがあらかじめわかっている、閉じた問題だ。
あるプログラミング言語で書かれたものを別の言語へ置き換える。
オープンソースの、外との境目がはっきりした部品を作る。
こうした仕事には、参照できる正解と、明確な完成条件がある。
業務システムは、様子が違う(ここでの話は一般的な業務システムやサービスが対象で、研究段階の問題や新規アルゴリズムは別だ)。
すでに動いている別のシステム、溜まったデータ、現場の業務の流れ。過去の改修の経緯。
こうした事情が例外として次々に入り込み、素直な設計には収まらない、不自然な仕様が生まれる。
私の実感では、これを丁寧に文書へ起こして渡しても、丸ごと任せるやり方では、最新のモデルでもつまずくことがある。
足した機能が、離れた別の箇所と食い違う。
逆に、文脈を絞り込んで設計してから指示すれば、一世代前のモデルでも筋の通ったアウトプットが返ってくることは多い。
効いているのはモデルの新しさではなく、渡す前にどれだけ問題を畳んでおいたかだ。
プログラミングを超えて
この話は、コードだけのものではない。
冒頭で触れた「下地」とは、こういうことだ。
環境の構築手順も、設定も、変更のやりとりも、動作の確かめ方も、この分野は何年も前から、コードと文書に落とし、履歴を残して管理してきた。
渡せる文脈が、すでにそういう形で揃っていた。それが、AIが早くから力を発揮できた大きな理由だと思う。
書く作業のコストが下がった分、価値は残ったものへ移る。
複雑な要望を、いかに簡単な形へ畳むか。
その判断だけは、まだ肩代わりされていない。
これから、あらゆる仕事でAI活用が進む。そして私は、ソフトウェア開発で起きたのと同じ流れが、そこでも起きると思っている。
仕事の中身が文書やデータとして残り、AIがそれを文脈として扱えるようになるほど、同じ問いが立ち上がる。
何も考えずに要望を足せば、複雑さは掛け算で膨らむ。
それをどう畳んで足し算に収めるか。
畳む相手がコードから他の仕事に変わっても、問いの形は変わらない。
もっとも、複雑な実装さえ低いコストで手に入るいま、こうした考え方そのものが老害なのかもしれない。
それでも、少なくとも現時点では、複雑さが増したものに整合性を持たせる仕事は、最新のモデルにとっても易しくない。
畳んでから渡すか、畳まずに渡すか。
その差は、モデルがこれから賢くなっても、消えずに残ると思う。
参考
- Fred Brooks「No Silver Bullet」(1986):問題そのものに固有の難しさ(本質的複雑さ)と、道具や作り方から生じる難しさ(偶有的複雑さ)を区別した論文。
- Rich Hickey「Simple Made Easy」(2011):simple(絡み合っていない状態)と easy(手近で扱いやすいこと)を、別の概念として腑分けした講演。
- John Ousterhout『A Philosophy of Software Design』(2018):ありえない状態を最初から表現できなくするなど、「エラーを定義ごと消す」設計を説いた書籍。