Agentic Coding - レビューというボトルネック

Agentic Coding における
現状の人間の役割

レビューについて話そう

追記: この資料は?

toridori 社で行われた Agentic Coding のレビュー議論の資料

(2025-08-06)

話者: @amachang

@amachang
  • レビューに対して偏った意見がある
  • それをぶつけて議論したい
  • 会社の価値を上げることが仕事のすべて
  • 趣味: カラオケ、哲学、歴史

レビューの問題

  • 「レビュー待ち」の問題
  • 「レビューの手法」の共有 (今日主に話し合いたい)

目次

  • 1. Sora さんから Serena の紹介
  • 2. 「レビュー待ち」がより重大ではある話
  • 3. レビュー手法 (みんなで議論)
  • 4. どうやって減らしていくか、無くしていくか

まずは

Sora さんから Serena の紹介

Slack の一幕が Serena 発表の経緯となった

Serena について

Sora さんお願いします

Serena について

Sora さんありがとうございます

ナレッジ共有嬉しい

突然の指名の快諾感謝👏

主題の前に...

「レビュー待ち」
より重大という前置きをしたい

待ち時間って怖いよね

  • 誰かを待つという不確実な時間
  • 不要だった仕事を生む
  • 重要な仕事を後に回す
  • 始めてしまったことのサンクコスト
  • 打ち切りの心理負荷

待ち時間問題加速するよね

  • コードが加速
  • レビュー待ち時間も相対的加速
  • チーム単位でここの早めの取り組みが必要
  • ITGC の要件など業界横断での取組も必要

個人的な意見

たぶんペアプロ的セッション

コード生成の横で
リアルタイムレビュー

みたいになりそう

ペアプロをやり切れるチームは少ない

  • ペアプロはきつい (間違いない)
  • まあペアプロじゃなくてもいい
  • チームごとのやり方を模索して共有して欲しい
  • やりたくないことこそ価値があるかもしれない

とにかく
取り組みを共有していこう!

ここまで前置き

今日の主題

レビューで何を見るか

視点3つ

  • 1. 複雑度の削減(循環複雑度、動的状態数削減)
  • 2. 名前空間の管理
  • 3. 未来のレビュー負荷にならないか

CLAUDE.md🔻 にこういう風に書いている

指名するので、どんな CLAUDE.md を書いてるか教えてください

今日の議論

  • 1. バグか、エラーか、取りうる値か
  • 2. LLM の特性: 最小編集バイアス
  • 3. LLM の特性: 防御的プログラミング
  • 4. LLM の特性: YAGNI
  • 5. ロジックを気持ちよく書く
  • 6. 目tail しようぜ

注意

この先のコードは
スライドのため省略してる
ところもあるので注意

追記

この後の部分は
多くの議論があったので
一つの意見のたたき台
として見てください

議論1: 複雑性解消の一丁目一番地

バグか、エラーか、取りうる値か

事前条件の考え方

それらをどう書くか

このコード、どう思う?


function divide(a: number, b: number) {
    if (b === 0) return null;
    return a / b;
}

function divide(a: number, b: number) {
    if (b === 0) throw new Error('Division by zero');
    return a / b;
}

function divide(a: number, b: number) {
    assert(b !== 0, 'Divisor must be non-zero');
    return a / b;
}
                

const ACCESS_TOKEN = process.env.ACCESS_TOKEN;
if (ACCESS_TOKEN !== undefined) {
    throw new Error(...);
}

const ACCESS_TOKEN = process.env.ACCESS_TOKEN;
assert(ACCESS_TOKEN !== undefined);
                

async function loadConfig() {
    // ↓ この条件をどう扱う?
    // await fs.exists(CONFIG_PATH)

    const configSrc = await fs.readFile(path, 'utf-8');
    const configObj = JSON.parse(configSrc);
    const config = ConfigSchema.safeParse(configObj);

    // ↓ この条件をどう扱う?
    // config.success

    return config.data;
}
                

// この as ってなんだろう?
const user = data as User;

// バグ?
assert(isUser(data), 'Bug: data must be User');

// エラー? (主にデータ発生源とかで)
if (!isUser(data)) throw new ValidationError(...);

// とりうる値?
if (isUser(data)) {
  processUser(data);
} else {
  processGuest();
}
                

議論2: LLM の特性

最小編集バイアス

この修正、どう思う?


async function getUsers() {
    return await api.get('/users');
}

// LLM は関数名やシグネチャを変えることを過度に恐れ以下のような修正をする
// 責任範囲や視野を与えていない人間の責任
async function getUsers(includeInactive: boolean = true) {
    if (includeInactive) {
        return await api.get('/users');
    } else {
        return await api.get('/users?filter=status:active)');
    }
}
                

async function getAllUsers() {
    return await api.get('/users');
}

async function getActiveUsers() {
    return await api.get('/users?filter=status:active');
}
                

# 呼び出し元も grep して修正
rg 'getUsers' src/

# または build すれば影響範囲が分かる?
pnpm build
                

(Esc キーを押して無駄なことは直ちにやめさせる)

> その方法は受け入れられないので revert

> /compact 既存の修正を見直すために compact します。
(こういうことにこそ context が必要)

> OK. まず今やっていることの目的に立ち返って @DESIGN.md を見て
既存のコードと追加すべきコードを「もし一から全て書き直したら」
どのようなコードになるかまず詳細に考えて。
その上でそのコードと現状との差に着目して。

消すべきものは消して。名前を変えるべきものは変えて。
影響するすべてのコードを rg などのコマンドで調べて修正して。
大きな変更になっても OK. You own everything. では修正して。
                

人間「LLM, you own everything.」

議論3: LLM の特性

防御的プログラミング

このコード、どう思う?


async function updateUserAge(userId: string | undefined, age: number | undefined) {
    if (typeof userId !== 'string') return;
    if (!UserIdSchema.safeParse(userId).success) return;
    if (typeof age !== 'number') return;
    if (!Number.isInteger(age)) return;
    if (age < 0 || age > 150) return;
  
    try {
        const user = await getUserById(userId);
        if (!user) return;
        await db.updateUser(userId, { age });
    } catch (error) {
        console.error('Failed to update user age:', error);
    }
}
                

async function updateUserAge(userId: string, age: number) {
    assert(0 <= age && age < 150, 'Age must be [0, 150)');

    const user = await getUserById(userId);
    if (user === null) {
        throw new UserNotFoundError(userId);
    }
    await updateUser(userId, { age });
}
                

> 防御的にならないでください Fail Fast してください。

> もし age や userId がそれらの値を含む箇所があるならば
そちらの方を修正してください。

データの検証は、データや誤差の発生源で行うべきであるので、
ロジックやデータの保存箇所で行うべきではありません。

バグに依存したシステムを許容しないため、クラッシュさせるべきです。
                

「Fail Fast.」

議論4: LLM の特性

YAGNI

このコード、どう思う?


const BATCH_SIZE = process.env.BATCH_SIZE ?? 100;

function main(opts: Options) {
    const verbose = opts.verbose ?? false;
    const batchSize = opts.batchSize ?? BATCH_SIZE;
    if (verbose) {
        console.log(`Running with batch size: ${batchSize}`);
    }
    while (...) {
        processBatch(batchSize);
    }
}
                

// 自分は原理主義なのでこの quality も YAGNI に見える
// てか default param 嫌いは YAGNI と最小編集バイアス臭がする
// ハードコードしたい
// ラディカルすぎる?みんなはどう?
async function saveToJpeg(
    path: string, image: Image, quality: number = 75) {
    await fs.writeFile(path,
        await JpegEncoder.encode(image, { quality }));
}
                

「You ain't gonna need その複雑性」

YAGNI の原則

  • 将来必要と思えることの 90% は不要と言われている
  • 体感「これは流石にいる」でも 60% いらん

議論5: ロジックを気持ちよく書く

validate, encode, decode の場所


async function predictUserDemographicsByLlm(userId: string) {
    const user = await getUserById(userId);
    const userReprPrompt = generatePromptFromUser(user);

    return await retry(3, async () => {
        // ここがデータの発生源
        const demographicsSrc = await callLlm(
                    MODEL, SYSTEM_PROMPT, userReprPrompt);
        // JSON, Schema, 値の整合性や正規化、標準化
        // - 後続のロジックにおける検証を全て終える
        // - 検証はここでのみ行う
        // - その他の箇所ではすべて assert する
        return parseDemographics(demographicsSrc);
    });
}
                

async function processMessageStream() {
    // decode はデータの発生源で行う
    const data = await readStream();
    const messages = decodeStreamData(data);

    for (const message of messages) {
        // ロジックは型安全であり、型以外の整合性も取れた状態
        await processMessage(message);
        // 出口があれば出口の形式に encode
        await outputMessage(encodeMessage(message));
        // こういうのも encode の一種かも
        console.log(
            `Processed message: ${safeStringify(message)}`);
    }
}
                

[入口] → [decode/validate] → [ロジック] → [encode/escape] → [出口]
                

> ロジックにその if はおかしくない?
バグ?検証漏れ?防御的プログラミング?
データの発生源どこ?ちゃんと検証してるか確認して

とにかくそこのコードは assert して Let it crash!
                

「ロジックにその if はおかしくない?」

議論6: 目tail しようぜ

LLM の出力を
リアルタイムレビューする

絵としてコードを見る

Ctrl+R Ctrl+E

自分の理想のコードとの
マッチ度"目tail"


function processSomething(user: User) {
  // assert は無料! assert はあって困ることはない!
  // 間違ってても呼ぶだけでクラッシュしてくれる
  assert(...); ...

  // データ準備 (直線で if 無し!)
  const fooBar = getFooBar(user);
  const items = user.items.map(mapItem);

  // 末尾で判断(関数の存在意義っぽい)
  // シンプルな式! 三単元の s !
  if (isActive(status)) {
    return doSomething(user);
  } else {
    return handleInactiveUser(user);
  }
}
                

✅ だいちゅき
[入力] → [処理] → [末尾判断]
                        ├─→ [処理]→ [結果A]
                        └─→ [処理]→ [結果B]

❌ ふぎいい
[入力] → [チェック1] ──return──→ [結果X]
           ↓
         [チェック2] ──return──→ [結果Y]
           ↓
         [処理] → [末尾] → [結果Z]
                

早期 return 大嫌い

(個人の見解です)

末尾以外の if は全てかなり疑わしい


if (process.env.ACCESS_TOKEN === undefined) return
// それはバグでは? assert しよ

if (isUser(data)) return
// 発生源で validate しない? 

if (items.length === 0) totalPrice = 0;
else totalPrice = items.reduce(
    (acc, item) => acc + item.price, 0);
// 下の式は length 0 を扱える
                

function processSomething(user: User) {
    ...

    // この if を単一責任として切り出せそう!!
    if (...) {
        ...
    } else {
        ...
    }

    ...

    if (isActive(status)) {
        return doSomething(user);
    } else {
        return handleInactiveUser(user);
    }
}
                

これらは自分の目 tailing のメタ認知
人それぞれのパターンマッチがあるはず

目 tailing だけじゃなく
腰を据えたレビューもする

議論はここまで

キーワードをまとめます

キーワード

  • Fail fast. そこは assert でいい
  • Let it crash! バグはクラッシュした方がいい
  • YAGNI. その複雑性は 90% 不要
  • You own everything. 最小編集バイアスやめて
  • 目tailing しようぜ

最後に

でも、そもそも

レビューっていつかやめるよね?

次に生産性が x10 になったとき

その時レビューってしてるかな?

その時は何が人間の仕事なのかな?

我々はどうすればレビュー
やめられるのか?

今日生き残りながら
それを考えよう

生き残れば
もっともっと面白いことをやれる

生き残ろう!