AIにコードを書かせるようになって、開発速度は明らかに上がりました。コンポーネントの雛形、テストコード、型定義、APIクライアント、リファクタリング案など、以前なら数十分かけていた作業が数分で終わることも珍しくありません。
一方で、半年ほど使い続けた頃から別の変化にも気づきました。コードを書く量が減っただけではなく、以前なら自然に行っていた思考の一部まで、AIへ先回りして渡すようになっていたのです。
この記事は、AIコーディングをやめるべきだという話ではありません。便利な道具を使いながら、どの能力が鈍りやすいのかを把握し、意識的に維持するための話です。
きっかけはTypeScriptのルート定義
最近、フロントエンドのルート定義を整理するPRをレビューしていました。URL、認証の要否、パンくずリストのラベルを一つのオブジェクトへまとめ、ルーターとナビゲーションの両方から参照する実装です。
生成されたコードには、ルート定義の末尾にas const satisfies RouteTableが付いていました。そこで同僚から、次のように聞かれました。
ここは
const routes: RouteTable = ...ではだめですか。as constとsatisfiesは、それぞれ何を担当しているのでしょうか。
私は答えかけて、一度止まりました。satisfiesは値が指定した型へ代入可能かを検査しつつ、式から推論された具体的な型を保ちます。as constは文字列や数値をリテラル型として扱い、配列やオブジェクトをreadonly寄りの型へ狭めます。一方、const routes: RouteTable = ...のように型注釈を付けると、変数から見える型はRouteTableになり、正確なルート名やパス文字列の情報を後続処理で使いにくくなる場合があります。
知識としては分かっていました。それでも、その場で推論結果まで含めて説明する前に、TypeScript Playgroundか公式ドキュメントで確認したくなりました。2年前なら、型注釈、型アサーション、satisfiesの違いをその場でコード例にして説明できたはずです。
もちろん、言語仕様を暗記していること自体に大きな価値があるわけではありません。正確性のために公式ドキュメントを確認するのは正しい行動です。ただ今回は、普段からAIに型エラーの説明まで任せていたため、自分の中から知識を取り出す速度が落ちていることも確かでした。
そこから、自分の開発の仕方を振り返りました。
1. 基礎知識を取り出す力
最初に変化を感じたのは、言語やブラウザAPIに関する細かな知識です。たとえば、satisfiesと型注釈の違い、as constが推論へ与える影響、分配条件型が分配される条件、unknownからの型の絞り込み、neverを使った網羅性チェック、React Hooksの依存配列などです。
以前は手が覚えていたものでも、AIの補完を使い続けると一瞬考える場面が増えます。知識が完全に失われたというより、記憶から取り出すための経路が弱くなった状態に近いと感じます。自分で思い出す前に補完候補が表示され、疑問を言語化する前にAIがコードを完成させるため、知識を取り出す機会そのものが減るからです。
暗記より使い分けを説明できること
すべての構文やユーティリティ型を暗記し直す必要はありません。維持したいのは、名前を見たときに役割を説明できることと、どの条件で使い分けるかを判断できることです。
たとえばsatisfiesなら、オブジェクトの構造を検査しながら具体的なキーやリテラル型を残したい場合に向いています。変数を抽象的なインターフェースとして公開したい場合は型注釈が適切で、リテラル型や読み取り専用のタプルを得たい場合はas constが候補になります。3つは似て見えても目的が異なり、どれか一つが常に正解というわけではありません。
API名や構文だけを知っていても、推論結果と保守性への影響を説明できなければ実務では足りません。
2. 実装前に分解する力
AIへ「この機能を作って」と依頼すると、数十秒後には複数のファイルが生成されます。この速度に慣れると、実装前に自分で問題を分解する工程を飛ばしやすくなります。
たとえば、管理画面にCSVエクスポートを追加する場合、本来は対象データの件数、一括取得か分割取得か、現在のフィルター条件を引き継ぐか、文字コード、値のエスケープ、進捗表示、失敗時の扱い、個人情報を含む列の制御などを先に考える必要があります。
AIへいきなり実装を依頼すると、最初の出力は一般的なケースへ寄ります。コードがすぐに動くため、要件分解が不十分なことにも気づきにくくなります。
実装前に短い設計メモを残す
現在は、AIへコードを依頼する前に、目的、制約、未決事項だけを短く書くようにしています。
目的: 現在の検索条件に一致する注文をCSVで出力する
制約: 最大10万件、1,000件単位で取得、UTF-8 BOM付き、キャンセル可能、一部取得失敗時は出力しない
未決事項: API側で非同期ジョブ化するか、個人情報列をどこまでマスキングするか
この程度でも、自分が決めるべきこととAIに任せられることが分かれます。AIへ渡す前の文章はプロンプトであると同時に、自分の設計レビューでもあります。
3. 他人のコードを読む力
AIコーディングでは、書く時間が短くなる一方で、生成されたコードを読む量は増えます。しかし、差分が大きい、テストが通っている、画面も動いている、AIが「実装完了」と説明している、といった条件がそろうと、重要な部分だけを流し読みして「たぶん問題ない」で進めたくなります。
これを繰り返すと、まとまったコードを頭の中で実行し、データや状態の流れを追う力が鈍ります。
説明できないコードは安全に保守できない
AIが生成したコードであっても、リポジトリへ入った時点で保守するのはチームです。最低限、入力はどこから来るか、状態はどこで変わるか、失敗はどこへ伝わるか、副作用はいつ実行されるか、同じ処理が二重に走る可能性はないか、データ量が増えたときに何がボトルネックになるか、テストされていない分岐はどこかを説明できる状態にします。
説明できないままマージしたコードは、障害や仕様変更が起きたときに安全に直せません。最近は、AIが大きな差分を生成した場合、ファイル単位ではなく、ユーザー操作 → イベントハンドラー → 状態更新 → API呼び出し → レスポンス変換 → 画面描画 → エラー表示という処理の流れに沿って読みます。この経路を追えない場合は、マージ前に差分を小さく分割します。
4. 仮説を立ててデバッグする力
エラーメッセージやログをAIへ渡すと、短時間で複数の原因候補が返ってきます。これは非常に便利ですが、何も考えずにログを貼り付ける癖がつくと、自分で仮説を立てる回数が減ります。
デバッグで重要なのは、修正案を大量に出すことではありません。観測した事実から原因候補を絞り、少ない確認作業で仮説が正しいかを確かめることです。
AIへ聞く前に仮説を一つ書く
現在は、AIへ相談する前に、事実、仮説、確認方法を一行ずつ書きます。
事実: 初回表示では成功するが、フィルター変更後だけ古い結果が表示される
仮説: 先に送ったリクエストの応答が後から到着し、最新の状態を上書きしている
確認: リクエストIDと開始・終了時刻を記録し、応答の完了順を確認する
その後でコード、ログ、自分の仮説をAIへ渡します。この順序にすると、AIは答えを出す装置ではなく、仮説をレビューする相手になります。AIの案が自分の仮説と違う場合も、どちらが観測した事実をよく説明できるか比較できます。
修正前に発生パターンを整理する
AIは、与えられたコードを変更することには積極的です。しかし、修正を始める前に、どの操作や環境で問題が起きるのかを整理する必要があります。
ブラウザによって変わるのか、キャッシュの有無で変わるのか、低速回線で起きやすいのか、ボタンを連続操作したときだけ起きるのか、特定の権限やデータ件数に依存するのかを確認します。問題が起きるパターンをつかめないまま修正すると、直ったように見えて別の場所へ不具合を移しただけになることがあります。
5. コードの良し悪しを判断する力
最も危険だと感じたのは、コードを書く力よりも、コードを評価する力の変化です。AIは変数名が自然で、コメントや型も付いた整ったコードを生成するため、見た目の良さに引っ張られて設計上の問題を見逃しやすくなります。
たとえば、小さな機能に不要な抽象化を追加する、現在のプロジェクトにない設計パターンを持ち込む、正常系だけが整っていて失敗時の状態が定義されていない、APIレスポンスを信用しすぎる、大量データを一度にメモリへ載せる、認証情報や個人情報をログへ出す、テストが実装の詳細に密結合している、といった問題です。これらはLintや型チェックを通過しても残ります。
「動く」以外の評価軸
AIが生成したコードは、要件を満たしているか、既存の設計と一貫しているか、失敗時の挙動が明確か、データ量や同時実行数が増えても成立するか、権限と入力値を信用しすぎていないか、変更範囲が必要以上に広くないか、半年後に別の人が修正できるかという観点で確認します。
コードの生成速度が上がるほど、レビュー側には強いブレーキが必要です。AIはアクセルとして優秀ですが、銀の弾丸ではありません。
なぜ能力が鈍りやすいのか
AIを使うこと自体が原因ではありません。問題は、思考を始める前にAIを呼び出せることです。
従来の開発では、要件を読み、処理を分解し、APIや構文を思い出し、実装し、エラーを読み、仮説を立て、修正するという小さな判断が連続していました。AIへまとめて依頼すると、要件を説明した直後に完成した差分を受け取れるため、その途中を一気に飛び越えられます。
飛び越えた工程が不要になったわけではありません。AIが代行したため、自分が練習しなくなっただけです。スポーツで補助器具を使うことと似ていて、補助器具が悪いのではなく、補助される筋肉を別の方法で動かさなければ弱くなります。
AIをやめずに能力を維持する方法
AIを封印する必要はありません。現在は、定型的なコンポーネントの雛形、型定義の下書き、テストケースの候補出し、既存パターンに沿ったCRUD、ドキュメントの初稿、単純な置換や移行作業、ログの整理などをAIへ任せています。
一方で、要件と制約の整理、データモデル、状態遷移、失敗時の挙動、セキュリティ境界、性能上の上限、既存設計との整合性、採用する案と捨てる案の判断は自分で先に考えます。重要なのはAIにコードを書かせないことではなく、実装前の判断と実装後の責任を手放さないことです。
実践している5つのルール
1. 最初の10分はAIを開かない
要件を読み、制約、疑問点、実装案を自分で書きます。10分後にAIを使うと依頼内容が具体的になり、出力の評価もしやすくなります。
2. コアロジックは一度自分で説明する
コードを書くかどうかに関係なく、処理の流れを文章や図で説明します。説明できなければ、AIの出力も正しく評価できません。
3. デバッグは仮説を添えて相談する
ログだけを貼らず、観測した事実、自分の仮説、確認方法をセットで渡します。
4. 週に一度は小さなものを手書きする
複雑なアプリをゼロから作る必要はありません。配列操作のユーティリティ、小さなReact Hook、リクエストのキャンセル処理、イベントエミッター、型ガード、CSSだけのレイアウトなどを30分程度で書くだけでも、知識を取り出す経路を維持できます。
5. マージ前にAIなしで説明する
PRの説明をAIに書かせる前に、何を変更したか、なぜこの方法を選んだか、代替案は何か、どこにリスクがあるか、どう検証したかを自分の言葉でまとめます。書けない項目があれば、理解が足りていない可能性があります。
初学者ほど注意が必要
経験者の場合、AIを使わない期間に積み上げた知識や失敗経験があります。一時的に取り出しにくくなっても、使い直せば戻ることがあります。
初学者は状況が異なります。最初からAIに実装を任せ続けると、エラー文を読む、ドキュメントを探す、小さく実装する、失敗させる、原因を切り分ける、書き直すという土台が作られません。この遠回りに見える工程が、後からAIの出力を評価する力になります。
初学者にAIを使わせないという意味ではありません。答えを受け取るより、自分の実装をレビューしてもらう、エラーの意味を段階的に説明してもらう、ヒントだけを出してもらう、複数案のトレードオフを比較する、テストケースの不足を指摘してもらうといった使い方の方が学習につながります。AIを解答集ではなく、ペアプログラミングの相手として使います。
まとめ
AIコーディングを半年続けて、基礎知識を必要なときに取り出す力、実装前に問題を分解する力、他人が書いたコードを読み説明する力、観測した事実から仮説を立ててデバッグする力、コードの良し悪しを判断する力が鈍りやすいと感じました。
どれもAIが代替できるように見える能力ですが、AIの出力を正しく使うためにも必要です。AIによってタイピングの負担が減ったからこそ、空いた時間をさらに多くのコード生成へ使うだけではなく、設計、検証、コード読解へ戻す必要があります。
目指したいのは、AIなしで何でも書ける状態でも、AIへすべて任せる状態でもありません。AIが書いたコードを理解し、疑い、直し、最後に自分の判断として出せる状態です。それが維持できている限り、AIは能力を奪う道具ではなく、能力を広げるレバレッジになります。