こんにちは。@m_koshikawa です。
Xで「英語圏以外のエンジニアはどうやってコードを書いてるの?」という問いを見かけました。
自分の答えはこうでした。
日本人にとって、プログラミング言語はそもそもが異次元の言語です。だから関数や変数の宣言、ifやswitchの分岐のたびに、日本語コメントを山ほどつけて「日本語で理解できる状態」にしています。
これは自分自身が長年体験し続けてきたことです。そして多くの日本人エンジニアに心当たりがあるはずです。
この習慣は、人間がコードを読む時代には正しかったと思います。しかし今、コードを読むのは人間だけではありません。AIエージェントがコードを読み、レビューし、修正を提案する時代になりました。
コードの読み手が人間だけからAIにも広がったなら、「丁寧さ」の届け先も変える必要があります。
本記事では、日本人エンジニアのコーディング「あるある」を4つ挙げ、AI時代にどう改善したかを書きます。
日本人のコーディング「あるある」4選
実際のプロジェクトで日常的に見かけるパターンです。どれも「丁寧に書こう」という善意から生まれています。
あるある1: ステップ番号コメント
def upload_file(client, container, path, download):
# 1. ストレージからダウンロード
content = download(container, path)
# 2. 一時ファイルに保存
tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
tmp.write(content)
tmp.close()
# 3. APIにアップロード
with open(tmp.name, "rb") as f:
uploaded = client.files.create(file=f, purpose="assistants")
return uploaded.id, tmp.name
手順を日本語で番号付きに説明しています。コードを上から読めばわかることを、もう一度日本語で書いています。
あるある2: 関数名を日本語で翻訳するdocstring
def apply_header_style(ws, row, col_start, col_end):
"""ヘッダー行にスタイルを適用する"""
for col in range(col_start, col_end + 1):
cell = ws.cell(row=row, column=col)
cell.font = HEADER_FONT
cell.fill = HEADER_FILL
apply_header_style が「ヘッダー行にスタイルを適用する」ことは、関数名がそのまま言っています。docstringが関数名の日本語翻訳になっています。
あるある3: 冗長なArgs/Returns/Raises
def download_text(container_name, blob_path, encoding="utf-8"):
"""
ストレージからテキストをダウンロードして文字列として返す
Args:
container_name: コンテナ名
blob_path: ファイルパス
encoding: テキストエンコーディング(デフォルト: utf-8)
Returns:
str: ファイルの内容
Raises:
Exception: ダウンロードに失敗した場合
"""
client = get_client()
blob = client.get_blob_client(container=container_name, blob=blob_path)
return blob.download_blob().readall().decode(encoding)
Google Style Docstringを丁寧に書いています。しかし、型ヒントがあればArgs/Returnsの大半は不要です。container_name: str と書けば「コンテナ名」であることは明白です。
C/Java系のプロジェクトでは、さらにインパクトのあるバージョンを見かけます。
/****************************************/
/* 関数名: get_user_info */
/* 機能: ユーザー情報を取得する */
/* 引数: user_id - ユーザーID */
/* 戻り値: 0:正常 / -1:異常 */
/* 作成者: 山田 */
/* 作成日: 2024/06/15 */
/****************************************/
int get_user_info(int user_id) {
関数の上に「仕様書」を丸ごと書くスタイルです。Google Style Docstringはこの装飾枠をなくしてモダンにしたものですが、根っこの発想は同じです。どちらも「関数名を見ればわかること」を別の言語(日本語)で繰り返しています。(さらに言えば、この仕様書コメントの閉じ位置やインデントが正しくない、というだけで従来のレビューでは指摘を受けたりしていました。本質ではない装飾にレビューの時間が使われていたのです)
あるある4(良い例): 規程への参照コメント
# 契約社員・派遣社員に管理者以上は禁止(権限管理規程 第5章2項)
if employment_type in ("契約社員", "派遣社員") and permission_type in ("admin", "privileged"):
results.append(RuleResult(
status=Status.NG,
description="権限管理規程 第5章2項: 管理者以上の権限を付与してはならない",
))
これは消すべきではないコメントです。なぜなら、「契約社員に管理者権限を付与してはならない」というビジネスルールは、コードの構造からは読み取れないからです。しかも規程の章・項まで参照先が明記されています。
あるある1〜3は「何をしているか(What)」の説明です。あるある4は「なぜそうしているか(Why)」の記録です。
この違いが、AI時代に決定的に重要になります。
AI時代に何が問題か
問題1: コンテキストの無駄な消費
AIにはコンテキストウィンドウ(一度に読める量)の上限があります。日本語コメントが大量にあると、その分だけ「AIが本質的な処理に使える注意力」が減ります。
さらに厄介なのが、日本語はトークン効率が悪いという事実です。200万以上の翻訳文ペアを分析したCJK言語のトークン効率調査によると、日本語は英語の平均2.12倍のトークンを消費します。
コード内のコメントで比較してみましょう。
| テキスト | トークン数(GPT-4o) | 倍率 |
|---|---|---|
# Download from storage |
4 | — |
# ストレージからダウンロード |
9 | 2.25倍 |
カタカナが1〜2文字単位で細かく分割されるのが主な原因です。あるある1のようなステップ番号コメントが関数ごとにあると、ファイル全体で数百トークンの浪費になります。
ただし誤解しないでください。「だから英語でプロンプトを書け」と言いたいわけではありません。英語が読み書きできるならそれに越したことはありませんが、そうでなければ日本語で読み書きできるようにAIエージェントへ指示するほうが生産的です。これは「認知負荷」の話であり、本記事のテーマからは外れるため深くは触れません。
問題2: 古いコメントがハルシネーションの種になる
コメントが多いほど、この「地雷」も比例して増えます。あるある3のような詳細なdocstringほど、改修時に更新が漏れやすく、AIへの誤誘導リスクが高くなります。
問題3: Whyがないと検証が浅い
よくあるコミットメッセージとPRの記述を見てみましょう。
commit: バグ修正
PR: 〇〇を実装しました
これをAIにレビューさせても、返ってくるのは「コードスタイルは問題ありません」程度です。
なぜこの方法? なぜこの範囲? なぜ今?
この「Why」が記録されていないと、AIは表面をなぞることしかできません。あるある4の規程参照コメントがAIにとって有用なのは、まさに「Why」が書かれているからです。
改善してみた
改善1: コード自体が語る書き方へ
あるある1〜3を改善するとこうなります。
def upload_csv_to_agent(
client: OpenAIClient,
container: str,
blob_path: str,
download: Callable[[str, str], str],
) -> tuple[str, str]:
"""Upload a CSV from blob storage to the OpenAI Files API."""
content = download(container, blob_path)
tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False)
tmp.write(content)
tmp.close()
with open(tmp.name, "rb") as f:
uploaded = client.files.create(file=f, purpose="assistants")
return uploaded.id, tmp.name
変えたこと:
-
関数名が意図を語る(
upload_file→upload_csv_to_agent) -
型ヒントがドキュメント代わり(
Callable[[str, str], str]→ 引数の型と返り値が明白) - docstringはWhyだけ1行(何をする関数かは名前でわかる)
- インラインコメント: ゼロ
ステップ番号コメントの # 1. # 2. # 3. を消しても、コードの読みやすさは変わりません。上から順に「ダウンロード → 一時保存 → アップロード」と読めます。
改善2: Whyの分離先を作る — インストラクションファイルが実質ADR
ADR(Architecture Decision Records)とは、設計判断とその理由をMarkdownで記録する仕組みです。docs/adr/NNN-タイトル.md に置き、Context → Decision → Consequencesの構造で書きます。
しかし、正式なADRをいきなり導入するのはハードルが高いかもしれません。
筆者は、インストラクションファイル(CLAUDE.md)とチーム向けドキュメントがADRの役割を果たせることに気づきました。
たとえば、筆者のプロジェクトの CLAUDE.md にはこう書いてあります。
### エラーハンドリング方針
- 外部API呼び出しは必ずリトライ付きで実装する(最大3回、指数バックオフ)
理由: 本番環境でAPI側の一時障害による500エラーが頻発し、
ユーザーに不要なエラー画面が表示されていた(2026/02 障害対応)
- ただし、認証エラー(401/403)はリトライせず即座にエラーを返す
理由: リトライしても回復しない。トークン期限切れの場合は
再認証フローに遷移させる必要がある
これは立派なADRです。Decision(リトライ付きで実装する)とContext(なぜそうするか=本番障害の経験)とConsequences(ただし認証エラーは除外する)が含まれています。
同様に、設計判断の記録にはこういうエントリがあります。
### typing.Listではなく組み込みlist[]を使う
- 事実: AIが `from typing import List, Dict` を使ったコードを生成した。
Python 3.9以降では非推奨だが、レビューで見落とされた
- 判断: CLAUDE.mdに「Python 3.10+の型ヒント記法を使うこと」を明記。
AIが古い書き方を出力しても、インストラクションで上書きされる
この記録があることで、AIは次回から非推奨の書き方を避けるようになります。正式なADRでなくても、「判断の理由」が構造化されて参照可能な場所に置いてあることが本質です。
改善3: コミットとPRに「背景の記録」を
コミットメッセージのBefore/Afterです。
Before:
バグ修正
After:
fix(user): 退職済みユーザーが有効扱いされる問題を修正
退会済みユーザーのis_activeフラグが未チェックだったため
課金処理が実行されていた。
参照: docs/policy/account-lifecycle.md
Conventional Commits形式で書くと、fix feat refactor などのプレフィックスだけでAIが変更の性質を判別できます。
PR記述には4つの要素を入れます。
| 要素 | 内容 |
|---|---|
| What | 何を変えたか(1行) |
| Why | なぜ変えたか + 関連ドキュメントへのリンク |
| How | どう実装したか(差分の補足) |
| Impact | 影響範囲と確認済み事項 |
改善4: AIレビューにWhyを問わせる
AIへのレビュー依頼にも、効く依頼と効かない依頼があります。
効かない依頼:
このPRをレビューして
→「コードスタイルは問題ありません」で終わります。
効く依頼:
リポジトリ全体と関連ドキュメントを読み込んだ上で、
このPRを以下の観点で検証してください:
1. account-lifecycle.mdとの整合性
2. 課金モジュールへの副作用
3. 退会後30日間のエッジケース
4. この実装への疑問点
問題点・改善提案・承認可否を明確に回答してください。
「不思議に思う点を挙げろ」というフレームが、AIの検証力を引き出します。ただし、これが機能するのはWhyが参照可能な場所に記録されている場合だけです。ADRもPR記述もないリポジトリでは、AIも浅い検証しかできません。
明日から何を変えるか
一気にやる必要はありません。
Step 1: まずここから
- 関数名・変数名を英語で明確にします(
get_user→find_active_user_by_id) - 「コードを読めばわかるコメント」を書かないようにします
- 型ヒントを入れます(Python 3.10以降なら
int | None記法が使えます)
Step 2: PRとコミットにWhyを書く
- Conventional Commits形式を導入します(
fix:feat:refactor:) - What / Why / How / Impact の4要素をPRに書きます
Step 3: インストラクションファイルにWhyを蓄積する
-
CLAUDE.mdやCopilot Instructions、.cursorrulesなどにチームのルールと理由を書きます - 「なぜこのルールがあるのか」をセットで記録します
- フィードバックや失敗事例も構造化して残します
Step 4: AIレビューにWhyを問わせる
- レビュー依頼に「関連ドキュメントを読んだ上で」を入れます
- 「不思議に思う点を挙げろ」のフレームを使います
まとめ
| レイヤー | 何を語るか |
|---|---|
| コード | What(何をしているか)を構造で語る |
| コメント | 最小限のWhy(ビジネスルール・規程参照)だけ残す |
| インストラクションファイル | チームのWhy(ルールの理由・過去の失敗)を蓄積する |
| PR / コミット | 変更のWhy(なぜ今この変更か)を記録する |
| AIレビュー | Whyとの整合性を検証する |
「コメントを減らせ」という話ではありません。Whyの届け先を変えるということです。
日本語コメントを山ほど書いてきた丁寧さは、弱みではありません。「理解しようとする姿勢」は、AI時代でもエンジニアの強みです。その丁寧さを、AIにも届く場所に届ければ、人間もAIも、あなたのコードをもっと深く理解できるようになります。