「人間がコードを書く時代は終わった」議論は2種類のコードを混同している
ちょうど最近このような投稿を連続して見かけたので、「コード」が何を指しているのかの自分の理解を整理しておきます。
世の中的に見ればソフトウェアエンジニアは未だに売り手市場であると言えると思うので、上記の指摘について「そんなことはないはず」と反論する人も多いと思います。
また、上のRyanさんのツイートでも
That's not to say SWEs don't have work to do
と書かれています。これも「ソフトウェアエンジニアはコードを書く仕事なのだから、AIが書くようになるなら仕事がなくなるだろう」と解釈されがちなところを、そうではないと念を押しているのだと思います。
では、「AIがコードを書く」とはどういうことで、その場合にソフトウェアエンジニアの仕事はどうなるのでしょうか?
ここで、我々がコードと呼んでいるものを区別することが理解に役立つと思います。
「コード」は大きく2種類に分けられる
私は、世の中で語られる「コード」は大きく『仕様』と『技術詳細』に分かれると思っており、両者は必要なスキルも目的も異なるので区別したほうが良いと思っています。
- 仕様: ソフトウェアがどう振る舞うべきかを定義する
- 技術詳細: どのAPIや特定の技術などを用いて実現するかを定義する
例えば、以下のようなシステム要件を満たすコードを書く場合を例にそれぞれを考えてみましょう。
## 新規ユーザー登録機能
システム管理者は、新しいユーザー(メールアドレス)を登録できる。
登録されたユーザーには初回パスワードが記載されたメールが届き、それを使ってログインすることができる。
仕様: ソフトウェアがどう振る舞うべきかを定義する
一般にユースケースやシナリオと呼ばれるものの設計に近いです。
ここの定義次第でユーザーから見えるソフトウェアの挙動が変わるので、PdMやQAなどを含めてチーム内で合意されている必要があるものです。
上記のシステム要件の例であれば、「コード」に落とし込むときにおそらく以下のようなことを定義する必要があるでしょう。
- システム管理者以外がその操作をしようとしたら何が起きるのか。
- 新しいユーザーのメールアドレスが不正なものだったり、既にユーザーとして登録されている場合に何が起きるのか。
要件定義などで抜け漏れてしまいがちなこれらもコードを書くうえでは必要です。
参考までに仕様を表現するサンプルコードを書いてみます。
kotlinで書いていますが、マークダウン記法のような特定の方言だと思ってもらえれば良いかと思います。
fun registerNewUser(request: UserRequest, requester: User): Response {
if (!requester.isAdmin()) {
return Response("管理者しかユーザー登録できません")
}
val email: Email = Email.validate(request.email) ?: return Response("メールアドレスが不正なため登録できません。")
val existingUser = UserDB.findByEmail(email)
if (existingUser != null) {
return Response("既に登録済みのメールアドレスです")
}
// ユーザー情報を永続化
UserDB.add(User.create(email))
// ユーザーに初回パスワード付きのメールを送信
EmailService.sendWelcomeEmail(newUser.email, newUser.password)
return Response("ユーザー登録が完了しました")
}
技術詳細: どのAPIや特定の技術などを用いて実現するかを定義する
さて、上記のサンプルコードだけでは実際に動く「コード」として十分ではありません。例えば以下のようなことも定義する必要があります。
- これはREST APIとして動くのか?
- APIを呼び出した人(requester)の識別はどのように行うのか?
- UserDBではRDBを使うのか?その場合にJDBCを使ってアクセスするのか?
- Emailの送信はAmazon SESを使うのか?API呼び出しはAWS SDKを使うのか?
しかし、これらは上記の『仕様』とは区別した方が良いと思っています。理由は、何を選んでもソフトウェアの挙動にはほとんど影響しない部分なので、チームへの共有もしなくていいし、勝手にコードを書き換えても問題にならないからです。
乱暴に例えるなら、ビデオ会議でイヤホンにAirPodsを使うか有線イヤホンを使うかくらいの違いと言えます。今日たまたまバッテリーが切れていて有線に変えたところで、殆どの場合はビデオ会議に影響は出ないでしょう。
ちなみに、どのコードが『仕様』『技術詳細』のどちらに含まれるかはプロダクトに依るので難しいところです。
ただ、目安としては「PdMやチームメンバーが把握しているべき」であれば『仕様』で、「動けば何でも構わない」のであれば『技術詳細』と言えるでしょう。
「人間がコードを書く時代は終わった」の意味
上記の『技術詳細』のコードに関して言えば、もはやAIに丸投げしても問題ない時代と言えるでしょう。
インターフェースが明確なので、AIが毎回違うコードを生成してきてもテストが通ればOKとしてよいかもしれません。
一方で、『仕様』を表すコードについてはAIが書けるとは思っていません。
ユーザー登録の例のような仕様書をAIに渡したところで、「メールアドレス重複チェックをすべきか?」などを誰かが意思決定しないといけません。
もし仕様書に、
- 既に同じメールアドレスが登録されていたら、処理を中断してユーザーに通知すること
- 「既に登録されている」かどうかはそのemailを持つユーザーがシステムにいるかどうかで判別する。
- ユーザーへの通知文言は「既に登録済みのユーザーです」とする
などと書き足せば、AIも正しいコードを書いてくれるでしょうが、そこまで指示ができるならもはやコードを書いていると言えます。
上記のサンプルコードをもう一度記載するので見比べてみてください。
val existingUser = UserDB.findByEmail(email)
if (existingUser != null) {
return Response("既に登録済みのユーザーです")
}
実はAI時代の前から同じことをしている
LLMの精度が上がってきたことでこういった指摘が増えてきましたが、私はAI時代の前から似たようなことは多くあったと思っています。
いくつか例を出しましょう。
- 以前はjQueryなどを使ってDOMを順番どおりに作ったり消したりすることであるべきDOM構造を組んでいたが、Reactに「こういうDOM構造にしておいて」と伝えるだけでよくなった。
- 以前はサーバーやHDDを買ってデータセンターに持っていって組み立てていたが、今はAWSに「このスペックのサーバーと永続ディスクを東京リージョンに用意して」と伝えるだけでよくなった。
- 以前はSpringサーバーを起動するためにTomcatの準備やxmlなどを書いていたが、今はSpringBootのアノテーションを呼び出すだけでよくなった。
それぞれ『技術詳細』の仕事は代替されていっていますが、ソフトウェアのあるべき『仕様』を組み立てる仕事の重要性は上がり続けているように見えます。
ソフトウェアエンジニアとして、『仕様』を組み立てるという本来のエンジニアリングのスキルで貢献していきたいものだなと思いました。
Discussion
私見として
要件定義にしろ詳細化にしろ実装にしろ、それは人間の知的労働で、それはまさにAIの置き換えるところで、順番はあれど、何処かは置き換わるけどどこかは残る、そういう次元なのはごくごく短期間だけだと予測します。
重複の確認などは既にAIの範疇、指示しなくても実装されます。
今現在この瞬間、コードを書く時代は終わったかもしれないし終わってないかもしれませんが、まあ些細なことだと思います。
それでも残る可能性があると思う部分を私なりに2つ。
1つ、AIよりも人が安いので人を使う。
2つ、AIの知らない全く新しいものを作る。
おっしゃる通りだと思います。
人間の代わりにAIがコードを書くようになるという話は、ともすれば人間vsAIの知能比べの話になりがちなんですが、本質的には自然言語vsコードの話なんですよね。ソフトウェアを記述する上でどちらの方が適しているかという。だってどんなにAIが賢くなっても、AIに入力するのは自然言語なので、その自然言語が不完全だったらどうしようもないという。欠陥だらけの仕様書に泣かされるのが、プログラマーからAIに代わるだけの話で。
自分はいまのアーキテクチャである限り、言い換えるとポチョムキン理解の部分が改善しない限りは、手続き的な解決が必要な、トレードオフがある部分は人間の仕事である程度残るように思いますね。銀の弾丸がある領域は勿論現状のRLHFベースのllmで行けるでしょうが、それ以外も問題ないと考えるのは些か楽観的ではないのかなあと。勿論0,1ではないでしょうけど。まあ、自分も仕事をはやくぶん投げられる時代来てほしいのでブレイクスルーに期待したいですな