採用面談で、最近こんな質問を受けることがあります。
「AIがコードを書いてくれる時代に、今からプログラミングを学ぶ意味はありますか?」
聞いてくるのは、学生の方や、別の職種からエンジニアを目指している方が多いです。不安になるのは当然だと思います。実際、私たちの開発現場でも、AIに下書きを任せる場面はかなり増えました。
先に結論を書きます。
意味はあります。ただし、「書けるようになること」がゴールだった頃とは、学ぶ意味の中身が変わりました。
この記事では、その「変わった中身」を、現場で実際に起きていることを通してお伝えします。
1章 精度が悪い原因に気づけるか
AIに任せると、たいていのものは8割くらいまで一気に進みます。詰まるのは、いつも残りの2割です。
社内向けの検索機能を作っていたときのことです。それらしい結果は返ってくるのに、肝心の場面でほしいドキュメントが出てこない。「精度を上げたい」とAIに相談すると、プロンプトの書き換え案や、別のモデルを試す提案が返ってきます。どれももっともらしい。試せば、少しだけ良くなったような気もします。
ただ、本当の原因は別のところにありました。ドキュメントを取り込むときの分割位置が悪く、見出しと本文が切り離されていたのです。検索する前の段階で、すでに情報が壊れていた。
別の例もあります。生成されたコードは正しく動いていたのに、本番でだけ極端に遅い。中を見たら、ループの中で1件ずつクエリを投げていました。ローカルの数十件では誰も気づかないコードです。
この2つに共通しているのは、AIに渡せるのは「症状」だけで、「原因」は自分で見つけるしかないということです。「精度が悪い」「遅い」と伝えても、AIはその環境の外側を知りません。原因の当たりをつけるのは、いつも人間の仕事です。
そして当たりをつけるには、システムの内側で何が起きているかの地図が、頭の中に必要になります。その地図は、自分で書いて、動かして、壊して、直した経験からしか描けません。
2章 AとB、どちらが良いか
AIは選択肢を出すのがとても得意です。一方で、選ぶのは苦手です。
実際にレビューで話題になったコードを出します。注文の合計金額を計算する関数です。
A
function calculateTotal(items: OrderItem[]): number {
return (
items
.filter((i) => !i.isCanceled)
.reduce((acc, i) => acc + i.unitPrice * i.quantity, 0) *
(1 + TAX_RATE)
);
}
function calculateTotal(items: OrderItem[]): number {
return (
items
.filter((i) => !i.isCanceled)
.reduce((acc, i) => acc + i.unitPrice * i.quantity, 0) *
(1 + TAX_RATE)
);
}
B
function calculateTotal(items: OrderItem[]): number {
const activeItems = items.filter((item) => !item.isCanceled);
const subtotal = activeItems.reduce(
(sum, item) => sum + item.unitPrice * item.quantity,
0,
);
const total = subtotal * (1 + TAX_RATE);
return total;
}
function calculateTotal(items: OrderItem[]): number {
const activeItems = items.filter((item) => !item.isCanceled);
const subtotal = activeItems.reduce(
(sum, item) => sum + item.unitPrice * item.quantity,
0,
);
const total = subtotal * (1 + TAX_RATE);
return total;
}
やっていることは同じです。同じ入力に対して同じ値を返すので、テストも同じように通ります。行数だけを見れば、Aの方が優れているようにも見えます。
私たちはBを選びます。ただし「読みやすいから」という理由ではありません。
半年後に「合計金額が数円ずれている」という問い合わせが来たとします。
Aの場合、まずどこが怪しいのかが分かりません。キャンセル品の除外が漏れているのか、単価と数量の掛け算のどこかで丸めがずれているのか、税率の当て方が違うのか。切り分けようとすると、結局この式を手で分解するところから始めることになります。
Bなら、subtotal にログを1行足せば済みます。小計が正しければ税率側、ずれていれば明細側。最初の1手で原因が半分に絞れます。テストも subtotal の段階で書けます。
名前のついた中間変数は、デバッグの足場です。 そして名前は、コードと仕様をつなぐ役割も持っています。subtotal と total があれば、「小計」「税込合計」という業務上の言葉とそのまま対応する。Aにはその結び目がありません。
分かれ目は「このコードが何年生きるか」です。一度きりの集計スクリプトならAでも構いません。私たちのプロダクトは長く生きるので、Bを選びます。
さて、ここからが本題です。
この2つをAIに見せて「どちらが良いですか」と聞くと、どう答えると思いますか。
おそらく「Aは簡潔でメソッドチェーンが自然。Bは中間変数によって可読性が高い。どちらも妥当です」といった答えが返ってきます。そこで「Aの方が良いと思うんですが」と付け加えれば、Aを支持する理由を並べてくれます。Bと言えば、Bを支持してくれます。
つまり、自分の中に判断の軸がないままAIと対話すると、AIは鏡になります。 自分の考えが良いのか悪いのか、いつまでも分からないまま進んでしまう。
そして判断の軸は、知識だけでは作れません。「短く書いたあのコードを、半年後に自分が3時間かけて読み解いた」という記憶が軸になります。遠回りに見えますが、これが一番効きます。
3章 求められる力が変わった
ここまでを整理すると、こういうことだと思っています。
価値が下がったもの
- 構文を覚えていること
- ライブラリの使い方を暗記していること
- 定型的なコードを速く正確に書けること
正直に言って、これらはもうAIの方が速くて正確です。ここで勝負しても意味がありません。
価値が上がったもの
- 疑う力 — 「動いた」と「正しい」は違う、と一度立ち止まれること
- 検証を設計する力 — 何をもって正しいとするか。テスト、評価指標、計測。出てきたものを評価できなければ、成果物の品質の上限は自分の評価能力で決まります
- 言語化する力 — 作りたいものを曖昧にしか言えない人からは、曖昧なものしか出てきません。要件を詰める力が、そのまま出力の質になります
- 全体を見る力 — 目の前の関数ではなく、設計、運用、コスト、そしてチーム
面白いのは、後者のリストが、前者を通らずには身につかないことです。テストを設計できるのは、テストが通らなくて困った経験があるから。疑えるのは、動いたはずのものが壊れた経験があるから。近道は、今のところ見つかっていません。
だから、プログラミングを学ぶ意味はあります。ただしゴールが変わりました。ゴールは「書けるようになること」ではなく、「判断できるようになること」。書くのは、その判断力を手に入れるための手段です。
おわりに:私たちが一緒に働きたい人
最後に、求職者のみなさんに向けて。
私たちは「AIを使うな」という方針ではありません。むしろ全員が使っています。使わない理由がない。
そのうえで一緒に働きたいのは、出てきたものに対して「なぜこうなっているんだろう」と一度立ち止まれる人です。
もし今、「AIがあるのに勉強する意味あるのかな」と迷いながら手を動かしている方がいたら。その手を動かした時間は、これから何かを判断するための材料になります。無駄にはなりません。
カジュアル面談では、こういう話もしています。興味を持っていただけたら、ぜひお話ししましょう。