23
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI駆動開発時代にこそ『リーダブルコード』が効く理由:TypeScript実例で再確認する

23
Last updated at Posted at 2026-02-12

AIでコードを書くのが当たり前になりました。
実装速度は上がったのに、こんな悩みは増えていませんか?

  • AIが途中で詰まり、結局は自分が巻き取る場面が思ったより多い
  • 自分が想定していた書き方と大きく違い、読む前に読み替えコストが発生する
  • 小さな修正でも「どこまで壊れる可能性があるか」が読みにくく、毎回手探りになる

実際、AIでうまく解決できずに途中から自分が巻き取る場面は少なくありません。
そのときコードが読みやすく書かれていないと、原因特定にも修正にも余計な時間がかかり、かなりしんどいです。
逆に、命名や分岐が整理されたコードなら、巻き取り後の立て直しが一気に楽になります。

この時代だからこそ、改めて効いてくるのが『リーダブルコード』です。
結論はシンプルです。

コードは、他人が理解するまでの時間を最小化するように書く。

AIが書いたコードであっても、最後に運用し、保守し、責任を持つのは人間のチームです。
だから「速く書ける」より「速く理解できる」が、以前より重要になります。

Python版はこちら


なぜ今、リーダブルコードが再重要化するのか

AI駆動開発では、コード生成コストが下がる一方で、次のコストが相対的に上がります。

  • 理解コスト: 意図・前提・境界条件の把握
  • 検証コスト: 仕様適合、例外系、副作用の確認
  • 修正コスト: 将来変更時の影響範囲の見積もり

つまり、ボトルネックは「書く」から「理解して判断する」に移っています。
このボトルネックを直接下げる原則が、リーダブルコードの思想です。


この記事の狙い

  • 『リーダブルコード』の要点を、AI駆動開発の文脈で実務化する
  • TypeScriptのBefore/Afterで、改善ポイントを即適用できる形で示す
  • AI協働レビューにそのまま使えるチェックリストを持ち帰る

最重要原則(AI時代版)

  • 判断基準は一つ:「他人が理解するまでの時間」を最小化する
  • ここでいう「他人」には、未来の自分・チームメンバー・レビュー担当者が含まれる
  • AIは生成を速めるが、理解責任は移譲できない

1. 表面上の改善(名前・見た目・コメント)

1-1. 名前で情報を伝える(第2章)

Before

function get(v: string, t: number) {
  return fetch(v, { timeout: t });
}

After

function fetchUserProfile(url: string, timeoutMs: number) {
  return fetch(url, { timeout: timeoutMs });
}

AI時代の観点

  • 生成コードは短名になりがちで、意図が文脈依存になりやすい
  • 呼び出し側が読んだ瞬間に意味が立つ名前へ寄せると、レビューが速くなる
  • 単位(Ms)の明示は、事故の予防コストを激減させる

1-2. 誤解されない名前にする(第3章)

Before

type User = { age: number };

function filterUsers(users: User[]) {
  return users.filter((u) => u.age >= 20);
}

const disableNotification = false;

After

type User = { age: number };

function getAdultUsers(users: User[]) {
  return users.filter((u) => u.age >= 20);
}

const isNotificationEnabled = true;

AI時代の観点

  • 曖昧な命名は、次のAI修正でも曖昧さを再生産しやすい
  • ブール否定は「人間にもAIにも」条件反転ミスの温床
  • 命名段階で誤解可能性を潰すと、後工程の指示精度が上がる

1-3. 美的配慮で読み順を固定する(第4章)

Before

const user = repo.findUser(id);
if (user) {
  const invoice = billing.createInvoice(user);
  notifier.send(user.email, invoice);
}
const log = logger.child({ scope: "checkout" });

After

const log = logger.child({ scope: "checkout" });
const user = repo.findUser(id);
if (!user) return;

const invoice = billing.createInvoice(user);
notifier.send(user.email, invoice);

AI時代の観点

  • 人間レビューは「形のパターン認識」で高速化される
  • 一貫した段落構造は、生成コードの混在でも追跡しやすい
  • 読み順の固定は、修正指示(「この段落だけ変える」)もしやすくする

1-4. コメントは「なぜ」を残す(第5章・第6章)

Before

// ユーザーをソートする
users.sort((a, b) => a.createdAt - b.createdAt);

After

// 新規作成ユーザーを先に処理しないと、無料枠判定が過去データで誤作動するため昇順に固定する。
users.sort((a, b) => a.createdAt - b.createdAt);

AI時代の観点

  • AIはコードは読めても、業務背景までは持っていないことが多い
  • 「何をしているか」ではなく「なぜ必要か」を残すと、将来の修正品質が上がる
  • 変更理由が明確だと、AIへの追加指示も短く正確に書ける

2. ループとロジックの単純化(第7章〜第9章)

2-1. ネストより早期return(第7章)

Before

function charge(user: User | null, amount: number) {
  if (user) {
    if (amount > 0) {
      if (!user.isBanned) {
        return payment.charge(user.id, amount);
      }
    }
  }
  return false;
}

After

function charge(user: User | null, amount: number) {
  if (!user) return false;
  if (amount <= 0) return false;
  if (user.isBanned) return false;
  return payment.charge(user.id, amount);
}

AI時代の観点

  • 深いネストは、レビュー時の思考スタックを圧迫する
  • 失敗条件を先に返すと、AIへの修正依頼の粒度も揃う
  • 可読な制御フローは、テストケース作成の漏れも減らす

2-2. 巨大な式は説明変数へ分割(第8章)

Before

if (
  !user.isDeleted &&
  (user.role === "admin" || user.teamId === doc.teamId) &&
  !doc.isArchived &&
  !(doc.visibility === "private" && !user.hasPrivateAccess)
) {
  publish(doc);
}

After

const isActiveUser = !user.isDeleted;
const canAccessTeamDoc = user.role === "admin" || user.teamId === doc.teamId;
const isPublishableDoc = !doc.isArchived;
const isBlockedByPrivateRule =
  doc.visibility === "private" && !user.hasPrivateAccess;

if (isActiveUser && canAccessTeamDoc && isPublishableDoc && !isBlockedByPrivateRule) {
  publish(doc);
}

AI時代の観点

  • 分割命名は「意図をコード内に埋める」行為
  • 人間レビューでもAIレビューでも、どの条件が怪しいかを局所化できる
  • ロジック変更時に影響点を限定しやすい

2-3. 変数は少なく・狭く・不変に(第9章)

Before

let total = 0;
let i;
for (i = 0; i < items.length; i++) {
  const item = items[i];
  total = total + item.price * item.count;
}
return total;

After

const total = items.reduce((sum, item) => sum + item.price * item.count, 0);
return total;

AI時代の観点

  • 追跡すべき可変状態が少ないほど、レビューとデバッグが速い
  • const中心は、AIが追加変更したときの副作用検出も容易

3. コードの再構成(第10章〜第13章)

3-1. 一度に1つのことだけをする(第10章・第11章)

Before

async function register(input: RegisterInput) {
  if (!input.email.includes("@")) throw new Error("invalid email");
  const passwordHash = await hash(input.password);
  const user = await db.user.create({
    data: { email: input.email, passwordHash }
  });
  await mailer.sendWelcomeMail(user.email);
  metrics.increment("user_registered");
  return user;
}

After

function validateRegisterInput(input: RegisterInput): void {
  if (!input.email.includes("@")) throw new Error("invalid email");
}

async function createUser(input: RegisterInput) {
  const passwordHash = await hash(input.password);
  return db.user.create({
    data: { email: input.email, passwordHash }
  });
}

async function register(input: RegisterInput) {
  validateRegisterInput(input);
  const user = await createUser(input);
  await mailer.sendWelcomeMail(user.email);
  metrics.increment("user_registered");
  return user;
}

AI時代の観点

  • 職務分離された関数は、AIへの指示単位として扱いやすい
  • 「検証だけ直す」「通知だけ差し替える」が安全にできる
  • テストが狙い撃ちしやすくなる

3-2. 思考を言語化してから実装する(第12章)

平易な手順

  1. 在庫がない商品は除外する
  2. 割引対象なら割引価格を使う
  3. 合計金額を計算する

実装

function calcTotal(products: Product[]): number {
  const inStockProducts = products.filter((p) => p.stock > 0);
  const prices = inStockProducts.map((p) =>
    p.isDiscountTarget ? p.discountPrice : p.price
  );
  return prices.reduce((sum, price) => sum + price, 0);
}

AI時代の観点

  • 人間向け手順を先に書くと、AIへのプロンプト品質も上がる
  • 説明とコードの段階を一致させると、差分レビューが速くなる

3-3. 最も読みやすいコードは「書かないコード」(第13章)

Before(自前実装)

function uniqById(users: User[]): User[] {
  const map: Record<string, User> = {};
  for (const user of users) {
    map[user.id] = user;
  }
  return Object.keys(map).map((id) => map[id]);
}

After(標準機能を活用)

function uniqById(users: User[]): User[] {
  return [...new Map(users.map((u) => [u.id, u])).values()];
}

AI時代の観点

  • 生成可能なコード量が増えた時代ほど、不要実装は増えやすい
  • 「書ける」ではなく「持つべきか」で判断する

4. 選択トピック(第14章・第15章)

4-1. テストコードも読みやすく(第14章)

Before

it("test1", () => {
  const a = buildOrder(10000, true, "JP");
  const r = calcShipping(a);
  expect(r).toBe(0);
});

After

it("国内配送かつ会員注文では送料が無料になる", () => {
  // Arrange
  const order = buildOrder({ amount: 10_000, isMember: true, country: "JP" });
  // Act
  const shippingFee = calcShipping(order);
  // Assert
  expect(shippingFee).toBe(0);
});

AI時代の観点

  • テスト名は「意図のラベル」であり、将来の検索キーになる
  • 構造化されたテストは、AIによる追加ケース生成も安定しやすい

4-2. 設計は比較して選ぶ(第15章)

『分/時カウンター』の章が示す本質は、次の2点です。

  • 同じ要件でも、設計案の比較をしないと最適解は見えない
  • 可読性と性能(計算量・メモリ)を両立で評価する

AIが候補を複数出せる時代だからこそ、
比較基準を人間側で持っておくことが重要です。


AI駆動開発で使える「Readable Code運用ルール」

チームで運用するなら、次の6つだけでも効果が出ます。

  1. 命名レビューを最優先にする(単位・境界・意図を名前で表現)
  2. PRテンプレに「なぜこの実装か」を必須化
  3. 早期returnで分岐を平坦化
  4. 複雑条件は説明変数へ分割
  5. 1関数1責務を守る
  6. 「書かない選択」を毎回検討する

レビューでそのまま使えるチェックリスト

  • このコードは、初見の同僚が短時間で説明できるか?
  • 名前だけで意味・単位・境界条件が伝わるか?
  • 分岐と式は、頭の中で反転せずに追えるか?
  • 変数は必要最小限で、スコープは狭く、不変化できているか?
  • コメントは背景・意図・注意点を短く正確に伝えているか?
  • そのコードは本当に必要か? ライブラリで置き換えられないか?

まとめ

AI駆動開発の本質は「実装速度の革命」です。
でもプロダクト品質を決めるのは、今も「理解速度」です。

だからこそ、リーダブルコードは古くなるどころか、むしろ重要になっています。

  • AIが速く書く時代ほど、人間が速く理解できる設計が必要
  • そのための判断基準が『リーダブルコード』には揃っている

迷ったら、この一文に戻れば十分です。

理解までの時間が短い方を選ぶ。

23
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?