ブログに戻る

AI がコードを書く時代にコーディングスキルを鈍らせない方法

AI にコードの大半を書かせていると、どのエンジニアリングスキルから衰えていくのか。衰えに気づく方法と、スキルを保つための AI を使わない Go の練習を毎週のルーティンとして紹介します。

AI がコードを書く時代にコーディングスキルを鈍らせない方法

コーディングスキルを鈍らせないためには、今は AI がやってくれている部分を、アシスタントなしで意識的に練習することです。多くのエンジニアの場合、具体的には、助けを求める前に自分でデバッグする、自分が書いていないコードを読む、空のファイルから小さな問題を解く、テストを自分で書く、といった練習です。まずは週に 1〜2 時間から始めて、それを続けるのがおすすめです。残りの時間は、好きなだけ AI を使ってかまいません。

要約

  • 衰えやすいのは手を動かすスキルより考えるスキルです。自動操縦での飛行に慣れたパイロットは、手動で操縦する力は保っていましたが、ナビゲーションや故障の検知には苦労しました(Casner et al., Human Factors、2014 年)。プログラマーでいえば、デバッグとコードリーディングがこれにいちばん近いスキルです。
  • スキルの低下は数か月で表れることがあります。ある観察研究では、大腸内視鏡検査で AI を使っていた医師が AI をオフにして検査すると、前がん病変の発見率が 28.4% から 22.4% に下がりました(Budzyń et al., Lancet Gastroenterology & Hepatology、2025 年)。
  • 定期的に自分を試してください。最後にマージした diff を説明できますか。失敗しているテストを、プロンプトを使わずに 20 分デバッグできますか。できないなら、それが練習すべきスキルです。
  • 毎週のルーティンで十分です。小さな問題を 1 つ手で解く、プロンプトを書く前にデバッグする、AI の diff をジュニアが書いたものとしてレビューする、標準ライブラリのソースを読む、テストを自分で書く。
  • AI には「直して」ではなく「なぜ」と聞きましょう。先に自分で答えを予想してから比べます。新しいライブラリを学ぶエンジニアのうち、AI に質問する形で使った人は、手で書いた人とほぼ同じくらい理解できていました。問題そのものを AI に渡した人がいちばん学べていませんでした(Anthropic、2026 年)。

AI にコードを書かせていると、どのスキルから衰えるのでしょうか?

最初に衰えるのは、デバッグ、コードリーディング、トレードオフの検討のように、自分で考えて答えを出す必要があるスキルです。構文を打つ力はずっとゆっくりしか衰えません。

航空業界の例が参考になります。2014 年の研究では、航空会社のパイロット 16 人がボーイング 747-400 のシミュレーターを操縦し、手動での飛行をテストされました。手で操縦するスキルは「ほぼ保たれて」いました。問題が出たのは考える作業のほうです。マップ表示なしで機体の位置を把握し続けること、次に何をすべきか判断すること、計器の故障に気づくことでした(Casner et al., Human Factors、2014 年)。研究者たちは、自動操縦の監視に積極的に関わり続けることで、パイロットはこうしたスキルを保てると提案しています。

医療の分野でも、2025 年に似た結果が出ています。ポーランドの 4 つの内視鏡センターで、AI の検出ツールを使っていた医師が、ツールなしで行った大腸内視鏡検査の成績を測定されました。腺腫の発見率は、AI 導入前の 28.4% から導入後は 22.4% に下がっていました(Budzyń et al., Lancet Gastroenterology & Hepatology、2025 年)。ランダム化試験ではなく観察研究ですが、この低下は数か月のうちに起きています。

ソフトウェアに当てはめると、これに当たるスキルが 5 つあります。

2 つの列。練習しないと衰えるもの:アシスタントなしのデバッグ、見慣れないコードを読むこと、API を記憶から思い出すこと、システム設計のトレードオフ、作業時間の見積もり。今こそ重要になるもの:自分が書いていない diff のレビュー、明確な仕様を書くこと、テストとエッジケース、アーキテクチャと境界。

デバッグ

いちばん注意して見ておきたいのがデバッグです。Anthropic の 2026 年の試験では、手でコードを書いたグループのほうが AI グループより多くのエラーにぶつかりました。研究者たちは、そのエラーを自力で解決していったことがデバッグのスキルを育てたと考えています(Anthropic、2026 年)。エラーをすべてアシスタントに直してもらっていたら、その練習の機会はありません。

見慣れないコードを読む

エージェントにコードベースを説明してもらうと、自分の頭の中にその地図ができません。説明が間違っていたり、障害対応の最中にエージェントが使えなかったりするまでは、それでも困らないでしょう。GitClear が 6 億 2,300 万件のコード変更を分析したところ、既存コードの呼び出しは 2023 年から 2026 年の間に、変更 1,000 行あたり 343 回から 223 回に減っていました(GitClear、2026 年)。再利用が減っているのは、すでにあるコードを読まなくなっていることと整合します。

API を記憶しておく

ここで挙げるスキルの中では、覚えていることの重要度は低めです。それでもある程度は必要です。strings の関数を全部覚える必要はありません。ただ、context.WithTimeout が返すキャンセル関数は必ず呼ぶ必要があること、http.Error を呼んでもハンドラーから return しないことは知っておく必要があります。そうした知識がないと、提案が間違っていても気づけません。

システム設計とトレードオフ

頼めば、AI は設計を提案してくれます。妥当な 2 つの設計のどちらを選ぶかには判断力が要ります。判断力は、自分で決めてその結果と付き合うことで身につくものです。キューもスキーマもリトライのポリシーもエージェントが選んでいたら、自分の直感のどれが正しかったのかを知る機会がありません。

見積もり

作業の一部をアシスタントがやっていると、作業にかかる時間の感覚がずれていくことがあります。METR の 2025 年の試験では、経験豊富な開発者が AI を使うと作業時間は 19% 延びましたが、本人たちは終わったあとも AI のおかげで 20% 速くなったと思っていました(METR、2025 年)。自分の速さの感覚がこれほどずれうるなら、見積もりもずれます。

スキルが衰えてきたことにどう気づけばよいのでしょうか?

アシスタントなしで自分を試してみることです。アシスタントと一緒に作業している間は、なかなか気づけないからです。Microsoft Research と Carnegie Mellon の調査では、AI への信頼が高いナレッジワーカーほど、その出力について批判的に考えなくなったと回答していました(Lee et al., CHI、2025 年)。

月に 1 回、次のチェックを試してみてください。

  1. 最後にマージした diff を説明する。最近の pull request のうち、ほとんどを AI が書いたものを 1 つ選びます。それぞれの変更がなぜそこにあるのかを、同僚に、あるいは声に出して説明してください。説明できない変更があるなら、それは本当には理解できていません。
  2. プロンプトを使わずに 20 分デバッグする。次に出てきた失敗テストかバグ報告を、ログとデバッガーとソースだけで調べます。最初の 2 分以内にアシスタントに手を伸ばしていないか、意識してみてください。
  3. 空のファイルから小さな関数を書く。設定ファイルをパースする、バックオフ付きで HTTP 呼び出しをリトライする、順序を保ったまま slice の重複を取り除く、といったものです。標準ライブラリの呼び出しを覚えているか、1 つずつ調べる必要があるかを確かめます。
  4. 実行する前に予想する。テストを実行する前に、通るかどうかを書き出します。失敗するなら、エラーに何と出るかも書きます。予想が外れることが多いなら、頭の中のコードのモデルと実際のコードがずれています。
  5. 見積もってから測る。タスクにかかる時間を見積もり、実際にかかった時間と比べます。差が広がっているなら、時間がどこに使われているのかを把握できなくなっている可能性があります。

どれも長くはかかりません。やってみて居心地が悪いと感じたら、それが練習すべきスキルです。

考える作業を「任せる」ことと「丸投げする」ことの違いは何でしょうか?

任せるとは、自分がすでに理解している作業を AI に渡し、自分の注意をほかに向けることです。丸投げするとは、理解していない作業を AI に渡し、理解しないまま済ませることです。前者はエンジニアリングではごく普通のことです。スキルが衰えるのは後者です。

Anthropic の試験では、アシスタントを使ったかどうかより、どう使ったかのほうが結果を左右しました。研究者たちは、認知的な努力、「そしてつらいほど行き詰まることさえ」、習熟にはおそらく重要だと結論づけています(Anthropic、2026 年)。どの使い方が高い得点につながったかは、今でもコードは手で書くべき?で紹介しています。

その理由は学習研究で説明できます。Robert Bjork と Elizabeth Bjork は、この種の努力を「望ましい困難」と呼んでいます。その場ではペースが落ちたように感じても、長期的にはよりよく学べるというものです(Bjork & Bjork、2011 年)。その例の 1 つが分散学習です。同じ量の練習でも、1 回に詰め込むより数週間に分けたほうが定着します。下のルーティンを毎週にしているのはそのためです。読み直すより思い出すほうが効果的だということもわかっています(APS Observer on Roediger & Karpicke、2006 年)。この研究については今でもコードは手で書くべき?で詳しく取り上げています。

どちらだったかを確かめるには、1 週間後に自分に聞いてみてください。このコードを見ずに書き直せるだろうか。書き直せるなら、理解している作業を任せたということです。書き直せないなら、その上にさらに積み上げる前に、戻って理解してください。

AI を使わない練習のための毎週のルーティン

以下のルーティンにかかるのは週に 1〜2 時間です。何回かに分けても、まとめて 1 回でやってもかまいません。難しいのは、それを何か月も続けることです。

小さな問題を 1 つ手で解く

週に 1 回、アシスタントをオフにして空のファイルを開き、小さくても実際にありそうな問題を 1 つ解きます。自分の仕事に近いものを選んでください。レートリミッター、引用符で囲まれたフィールドを扱える CSV パーサー、最初のエラーで止まるワーカープールなどです。時間は 30〜45 分に収めます。練習するのは空のファイルから動くコードにたどり着くまでの流れなので、問題は小さくてかまいません。

テスト付きの問題がほしいなら、LevelUpGo の Training Ground に、まさにこのための単発の Go 演習があります。

プロンプトを書く前にデバッグする

何かが壊れたら、アシスタントに聞く前に、20 分などと時間を決めて自分で取り組みます。仮説を立て、バグを再現し、計測して、それから初めて聞きます。

練習にちょうどいいバグを紹介します。遅い上流 API から為替レートを取得し、10 ミリ秒で諦めるサービスです。

example.gogo
package main

import (
	"context"
	"errors"
	"fmt"
	"os"
	"runtime"
	"runtime/pprof"
	"time"
)

type Rate struct {
	Currency string
	Value    float64
}

func fetchRate(currency string) Rate {
	time.Sleep(50 * time.Millisecond) // a slow upstream API
	return Rate{Currency: currency, Value: 1.08}
}

func rateWithTimeout(ctx context.Context, currency string) (Rate, error) {
	ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
	defer cancel()

	result := make(chan Rate)
	go func() {
		result <- fetchRate(currency)
	}()

	select {
	case r := <-result:
		return r, nil
	case <-ctx.Done():
		return Rate{}, errors.New("rate lookup timed out")
	}
}

func main() {
	timeouts := 0
	for range 100 {
		if _, err := rateWithTimeout(context.Background(), "EUR"); err != nil {
			timeouts++
		}
	}
	fmt.Println("timeouts:", timeouts)
	time.Sleep(100 * time.Millisecond)
	runtime.GC()
	fmt.Println("goroutines:", runtime.NumGoroutine())
	pprof.Lookup("goroutineleak").WriteTo(os.Stdout, 1)
}

本番環境での症状はクラッシュではなく、じわじわと増えていくメモリ使用量です。最初に測るべきは goroutine の数です。Go 1.27 で goroutineleak プロファイルが正式に使えるようになったので(Go 1.27 リリースノート)、ランタイムがリークの場所を教えてくれます。このプログラムを Go 1.27 で実行すると、次のように出力されます。

example.texttext
timeouts: 100
goroutines: 101
goroutineleak profile: total 100
100 @ 0x1048c59a8 0x10485e854 0x10485e468 0x10491c5ac 0x1048cbc74
#	0x10491c5ab	main.rateWithTimeout.func1+0x5b	./main.go:29

100 回のルックアップはすべてタイムアウトし、main 以外に 100 個の goroutine がまだ生きています。29 行目は result <- fetchRate(currency) です。タイムアウトしたルックアップはそれぞれ goroutine を 1 つ置き去りにします。その goroutine は、誰も受け取ることのない送信でブロックされたままです。rateWithTimeout はもう return しているからです。修正はバッファを 1 つ持たせることで、そうすれば送信は必ず成功します。

example.gogo
result := make(chan Rate, 1)

この変更を入れると、同じプログラムは goroutines: 1 と goroutineleak profile: total 0 を出力します。このバグなら、アシスタントもおそらくすぐに見つけるでしょう。練習になるのは自分で見つけることです。次のリークは、アシスタントが一度も見たことのないライブラリの中で、しかも自分ひとりで対応する障害の最中に起きるかもしれません。このバグの背景にある channel のルールは、Go の chan キーワードで詳しく解説しています。

AI の diff をジュニアが書いたものとしてレビューする

エージェントが pull request を作ったら、優秀だけれどまだこのシステムを知らない新しいチームメンバーの pull request を読むつもりで読んでください。正しく見えるかどうかに加えて、何を前提にしているかも確認します。エラー時の処理、context の扱い、ロック、そして 2 回目の呼び出しで何が起きるかを確認します。

こうしたツールへの信頼は、もともと高くありません。Stack Overflow の 2025 年の調査では、回答者の 84% が AI ツールを使っているか使う予定だと答えましたが、その正確さを強く信頼していたのは 3.1% だけでした(Stack Overflow、2025 年)。Google の DORA チームは、AI はチームを立て直すものではないと結論づけています。AI は「もともとあるものを増幅する」のです(DORA 2025、2025 年)。レビューが機能しなくなったときに何が起きるかは、AI コーディングツールの負の側面で 2026 年のデータとともに解説しています。

標準ライブラリのソースを読む

週に 1 回、Go の標準ライブラリの関数を 1 つ読みます。丁寧に書かれていて、ドキュメントコメントが設計上の判断を説明していることもよくあります。何もインストールする必要はありません。

example.bashbash
go doc -src net/http.Error
example.gogo
// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
//
// Error deletes the Content-Length header,
// sets Content-Type to “text/plain; charset=utf-8”,
// and sets X-Content-Type-Options to “nosniff”.
// This configures the header properly for the error message,
// in case the caller had set it up expecting a successful output.
func Error(w ResponseWriter, error string, code int) {
	h := w.Header()
	// ...
	h.Del("Content-Length")
	h.Set("Content-Type", "text/plain; charset=utf-8")
	h.Set("X-Content-Type-Options", "nosniff")
	w.WriteHeader(code)
	fmt.Fprintln(w, error)
}

コメントの 2 行目は、よくあるバグを説明しています。http.Error はレスポンスを書き込みますが、ハンドラーを止めはしません。そのあとの return を書き忘れると、ハンドラーはそのまま実行を続けます。次のハンドラーは、JSON をデコードする分岐でまさにそのミスをしています。

example.gogo
type Order struct {
	ID       string `json:"id"`
	Quantity int    `json:"quantity"`
}

func createOrder(w http.ResponseWriter, r *http.Request) {
	var o Order
	if err := json.NewDecoder(r.Body).Decode(&o); err != nil {
		http.Error(w, "invalid JSON", http.StatusBadRequest)
	}
	if o.Quantity <= 0 {
		http.Error(w, "quantity must be positive", http.StatusBadRequest)
		return
	}
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(o)
}

こういう提案は、2 つ目の分岐が正しく見えるので、ざっとレビューしただけでは通ってしまいます。ソースを読んだことがあれば、return が抜けていることにすぐ気づきます。次に読むものとしては、sync.Once.Do、errors.Is、context.WithCancel、http.MaxBytesReader がおすすめです。

テストを自分で書く

実装はアシスタントに書かせてもかまいませんが、テストケースは自分で書いてください。何をもって正しいとするかを決めるのは、人に渡すべきではない部分です。上のハンドラーに対するテーブル駆動テストは次のとおりです。

example.gogo
func TestCreateOrder(t *testing.T) {
	tests := []struct {
		name     string
		body     string
		wantCode int
		wantBody string
	}{
		{"valid order", `{"id":"A1","quantity":2}`, http.StatusCreated, `{"id":"A1","quantity":2}` + "\n"},
		{"zero quantity", `{"id":"A1","quantity":0}`, http.StatusBadRequest, "quantity must be positive\n"},
		{"malformed JSON", `{"id":`, http.StatusBadRequest, "invalid JSON\n"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequest(http.MethodPost, "/orders", strings.NewReader(tt.body))
			rec := httptest.NewRecorder()

			createOrder(rec, req)

			res := rec.Result()
			body, _ := io.ReadAll(res.Body)
			if res.StatusCode != tt.wantCode {
				t.Errorf("status = %d, want %d", res.StatusCode, tt.wantCode)
			}
			if string(body) != tt.wantBody {
				t.Errorf("body = %q, want %q", body, tt.wantBody)
			}
		})
	}
}
example.texttext
--- FAIL: TestCreateOrder (0.00s)
    --- FAIL: TestCreateOrder/malformed_JSON (0.00s)
        handler_test.go:35: body = "invalid JSON\nquantity must be positive\n", want "invalid JSON\n"
FAIL

ステータスのチェックは通ります。最初の WriteHeader の呼び出しが優先され、レスポンスは 400 のままだからです。バグを捕まえられるのは本文のチェックだけです。実際のサーバーなら http: superfluous response.WriteHeader call というログも出ますが、誰かがログを読んでいればの話です。本文までアサーションするかどうかは、何が壊れうるかを考えて決めることで、ここで練習しているのはその判断です。return を追加すれば、3 つのケースはすべて通ります。

AI に仕事を奪われるのではなく、AI から学ぶにはどう使えばよいのでしょうか?

AI には自分の考えを検証し、説明してもらいましょう。考えること自体は、先に自分でやります。そのために役立つ習慣が 5 つあります。

  • 先に予想してから比べる。解決策を聞く前に、自分のアプローチを 1〜2 文で書くか、コードをざっと書いてみます。それからアシスタントに聞いて比べます。違っていたところが、自分の知らなかったことです。
  • 「直して」の代わりに「なぜ」と聞く。「この goroutine はなぜ終了しないの?」と聞けば、自分で確かめられる説明が返ってきます。「これを直して」と頼めば、何も学ばないまま受け入れるパッチが返ってきます。
  • ヒントを求める。練習中なのでヒントを 1 つずつほしい、とアシスタントに伝えます。たいていのアシスタントはそれに合わせてくれます。
  • クイズを出してもらう。作業のあとで、今変更したコードについて 3 つ質問してもらいます。答えられなければ、コードを読み直します。
  • 記憶から書き直す。完全には理解していない生成コードを受け入れたら、いったん閉じて、その日のうちにもう一度書きます。詰まったところが、学べていなかった部分です。

ゼロから学んでいるときも考え方は同じです。初心者が AI を家庭教師として使う方法は、2026 年のプログラミング勉強法で解説しています。

重要性が下がるどころか、むしろ上がっているスキルは何でしょうか?

AI に指示を出し、その仕事をチェックするためのスキルは、以前より価値が上がっています。

わかりやすいのはレビューです。エージェントはチームが読むより速いペースで pull request を作れますが、1 つずつレビューしてどれをマージするか決めるのは、やはり人間です。

仕様を書くこともその 1 つです。問題、制約、エッジケースを明確に書けばエージェントの出力はよくなりますし、書くことで自分でも問題を最後まで考えることになります。

テストは、自分で書いていないコードほど重要になります。何が正しいのかをエージェントに伝え、間違えたときにそれを捕まえるのがテストなので、テーブル駆動テスト、ファジング(Go のファジングチュートリアル)、race detector を使う場面が増えます。

最後はアーキテクチャです。エージェントは明確な境界の内側ではうまく働きますが、その境界をどこに引くか、どのパッケージが何を受け持ち、どの interface を安定させておくかを決めるのは、今でも自分の仕事です。

この 4 つのどれにも、言語の選択が効いてきます。Go の厳しいコンパイラ、小さな言語仕様、標準で付いてくるツールは、生成されたコードのミスの多くをレビュー前に捕まえてくれます。詳しくは Go が AI の書くコードに最適な言語である理由で解説しています。その選択をキャリアの面から見た話は、Go 言語は 2026 年に学ぶ価値があるか?で取り上げています。

LevelUpGo の役割

LevelUpGo は、このルーティンのうち AI を使わない部分に取り組める場所です。どのレッスンも左に解説、右に自動補完のない本物のエディタがあり、書いた Go のコードがコンパイルを通ってテストに合格すると次へ進めます。無料で始められる Go Basics コースは基礎からスタートします。Professional Go Testing ではテーブル駆動テストを自分で書く練習ができ、Concurrency Fundamentals では goroutine、channel、キャンセルを扱います。どれも手でデバッグできるようにしておく価値がいちばん高いコードです。

よくある質問

AI を使わないコーディングには、毎週どれくらい時間をかけるべきですか?

まずは週に 1〜2 時間から始めて、何か月も続けるのがおすすめです。学習研究では、たまに長くやるより、短いセッションを間隔を空けて続けるほうがよいとされています(Bjork & Bjork、2011 年)。毎週、小さな問題を 1 つ手で解き、バグを 1 つプロンプトなしでデバッグし、標準ライブラリの関数を 1 つ読めば、いちばん衰えやすいスキルをカバーできます。

チームに AI 並みのスピードを求められているときは、どうやって練習すればよいですか?

クリティカルパスから外れたところで練習します。デバッグを先にする習慣には、誰の作業も止めていないバグやチケットを選びましょう。手で解く問題は、自分の時間か、会社に学習予算があればその枠で取り組みます。diff のレビューとテストを自分で書くことは普段の仕事に組み込めて、ほとんどペースも落ちません。チームが出力量だけを見ているなら、レビューの質とオンコール対応の備えについてリードに話してみてください。どちらも同じスキルに支えられています。

シニアエンジニアも AI でスキルを失いますか?

失います。経験があっても守られるわけではありません。航空の研究のパイロットも大腸内視鏡の研究の医師も経験豊富なプロでしたが、どちらのグループも、自動化が作業の一部を肩代わりしていたところでスキルが落ちていました(Casner et al.、2014 年、Budzyń et al.、2025 年)。シニアはそもそも失うスキルが多く、特に気をつけるべきなのは考えるスキルです。

AI のコードをレビューしていれば、スキルは保てますか?

それだけでは足りません。レビューで使うのは再認で、自分で解決策を生み出すより簡単です。正しい答えを見て正しいとわかる力は、自分で書く力を失ったあとも長く残ります。レビューに加えて、ゼロからコードを書いてデバッグする練習を定期的に行ってください。

AI で失ったスキルは取り戻せますか?

この点についての研究はまだ少なく、誰も約束はできません。上で紹介した研究が測ったのはある時点でのスキルで、回復ではありません。わかっているのは、使い続けていたスキルは保たれていたということです。パイロットの手動での操縦がその例です(Casner et al.、2014 年)。実際的な答えとしては、上のセルフチェックから始めて、いちばん弱いスキルを見つけ、まずそこを練習することです。

出典

シニアエンジニアのように Go を書く

ブラウザで学べるインタラクティブなレッスン。最初のレッスンは無料です。

無料レッスンを試すまたは無料アカウントを作成