水曜日, 10月 15, 2025
水曜日, 10月 15, 2025
- Advertisment -
ホームニューステックニュースVibe Codingで25万ダウンロード超のOSSを開発できた。できたが… ── AIの役割 vs 人間の役割ではなく「協働」で考える

Vibe Codingで25万ダウンロード超のOSSを開発できた。できたが… ── AIの役割 vs 人間の役割ではなく「協働」で考える


はじめに

私の2025年6月ごろからの取り組みとして、Claude CodeによるVibe CodingでRulesyncというOSSツールを公開しました。

そこでかなり自由にClaude Codeでいろんな手法を試すことができましたので、AIコーディング全般のTipsをお伝えできればと思います。

一方で(人間の介入が少なすぎる)Vibe Codingの弊害としてコードベースやドキュメントが崩壊し、途中で開発がストップした場面もありました。ここのプロセスの反省と、どのように開発可能に復帰させたかという点も紹介します。

最後に人間の役割、AIの役割、ソフトウェア開発の未来といったトピックに触れたいと思います。

Rulesync: 主要なAIコーディングツールの設定ファイルを一括管理

まず、私が開発・公開したツール Rulesync について簡単に紹介します。

こちらのXポストをご覧ください。

https://x.com/suin/status/1943203866117574699

cursor/rules, CLAUDE.md, github/custom-instructions.md などなど、思うところがあるのではと。

次々と新しいAIコーディングツールが発表され、トレンドも頻繁に変わる中で、我々ユーザーとしてはそれらのさまざまなファイル形式の対応に迫られるというペインが生じています。それでさっきのようなミーム的なポストにつながったりするわけですね。

最近は AGENTS.md という標準化の流れで、RulesやMemoriesと呼ばれるファイルについては統一・ペイン解消の流れがあります。

ただ、まだ、AGENTS.mdもすべてのツールにおける統一採用には至っていませんし、Custom Slash Command, Subagent, MCP連携ファイル, Ignoreファイルなどがツールごとに分かれている状況は引き続き残っています。さらに別のカテゴリーのファイルが生まれる可能性もあるでしょう。

そこでRulesyncを開発することでこの問題の解決を試みました。

https://github.com/dyoshikawa/rulesync

Rulesyncを使うと、rules, ignore, MCP, commands, subagents…これらの定義を1箇所で行うだけで、各種AIツールのフォーマットに合わせてすべてを一括生成できます。rulesに関してはファイルの分割にも対応していますし、AGENTS.mdの生成にも対応しています。

また、claude codeなどで実装されているcommands, subagentsについて、これは非常に便利で、個人的には搭載必須レベルの機能なのですが、これはAIコーディングツールによって実装状況がまちまちになっています。
そこで、rulesyncではCommands/Subagentsのシミュレート機能を提供しており、例えば普段Claude Codeでcommands, subagentsをフル活用している方がGitHub Copilotを併用する際に、同じようにcommands, subagentを呼び出して活用ができるような開発体験を再現することを目指しています。

RulesyncはClaude Code(Maxプラン)とCursorエディタで開発しました。

TypeScriptで書いています。この言語選定は単純に自分が慣れているのと、Node.jsランタイムは非常に開発者端末側での普及度が高く、配布に適していると考えました。

Rulesyncは自分が思っていた以上に反響があり、GitHub Starは300以上、npmダウンロード数は総計で25万、週間で2万〜と多くの方に使っていただいています。

もともとは自分のペインを解消したかったというところが起点にあったので、Zennチームでも導入しています。

よかったらRulesyncのリポジトリをのぞいてみてください。もしみなさんのペインを解決できそうなものであれば嬉しいです。

Vibe Codingで開発をスタート

では、AIを使った開発の話に移っていきます。

Rulesyncは今年の6月ごろから、原義のVibe Codingに近いスタイルで開発をスタートしました。原義の、というのは、次のポストのような方式です。

https://x.com/karpathy/status/1886192184808149383

コードをほとんど見ずに実現したい仕様を伝えるだけで開発を行い、バグが発生した際もバグの挙動だけを伝えてAIに直させる、自分で実装の確認はしない、というスタイルですね。

あまり多くの時間をかけられないサイドプロジェクトとして始めたのと、実際コードに関与せずにどこまでいけるのか?興味があるテーマだったので、このようなやり方を採用しました。

ほとんどコードは見ないようにしますが、悪意のあるコードを生成していないかという点だけはざっと見ながら進めました。理屈上はAIデータポイズニングがあり得るためです。

https://www.cloudflare.com/ja-jp/learning/ai/data-poisoning/

実際には顕在化した事例を自分はまだ見たことがなく、プロンプトインジェクションなど外部の影響を受けない限り極めて低い確率とは思います。

コードを見ずにできるだけ遠くに行く

コードを見ずにできるだけ遠くに行くために、Claude Codeだけでなく、周辺の仕組みもできるだけ整備をしました。

思いつく限り挙げると、

  • 型とLinterによる静的解析
  • 自動テスト
  • o3-search-mcp
  • serena mcp
  • knip
  • Dev Containers + バイパスモードの有効化 👈
  • AIによるコードレビュー 👈
  • similarity 👈

これらを活用しました。本記事では 👈 で示した下3つに絞って紹介します。

Dev Containers + バイパスモード

まず、バイパスモードでコーディングさせるという点です。

本記事では --dangerously-skip-permissions オプションの有効化をバイパスモードと指します。如何にも使いたくない名前のオプションですが、Vibe Codingの快適性を上げるためには必須レベルと考えています。

バイパスモードを有効にすると、ユーザーに一切承認を求めずに作業するようになるので、Vibe Codingの快適性は大幅に上がります。ただ、これをホストマシン上で実行するのは流石に怖いです。VSCodeやCursorで使用可能なDev Containersでサンドボックス環境を用意し、その中でClaude Codeを実行するようにします。

https://github.com/anthropics/claude-code/tree/main/.devcontainer

Claude Code用のDevcontainer設定は、Anthropic社が上記でサンプルを公開してくれているため、比較的簡単です。基本的にコピペで設定ができます。

オリジナルな工夫として、この長いオプションを都度打ちたくないので、 ~/.zshrc に claude と叩くだけでバイパスモードで起動するようにエイリアスを設定しています。

.devcontainer/Dockerfile

RUN echo "alias claude=\"claude --dangerously-skip-permissions\"" >> ~/.zshrc

claude/settings.jsonpermissions.defaultModebypassPermissions に設定する方法もあるのですが、ホストマシン側でclaudeコマンドを叩いた際に意図せずバイパスモード起動することは避けたいので、このような方法としています。

AIによるコードレビュー

続いて、AIにコードレビューをさせるという点です。

Subagentにレビューをさせて、結果を報告させます。そして、レビュー結果を受けてのマージ可否、実際のマージまでのすべてをClaude Codeにさせるようにしたいので、これらの全てフローをCustom Slash Command一発で実行するように構成しました。

バイパスモードなので、途中の確認・承認もありません。コマンドを走らせたら人間は待つだけとなります。

レビューのためにこのようなcode-reviewerサブエージェントを用意しました。コードの整然さの観点でコードレビューをさせる定義になっています。

.claude/agents/code-reviewer.md

---
name: code-reviewer
description: >-
  包括的なコードレビューを行う際に使用するエージェントです。
  DRY、SOLID、保守性、ベストプラクティスといった一般的なソフトウェア
  エンジニアリング原則に基づいてレビューを行います。
model: opus
---
コードを一般的なソフトウェアエンジニアリングの観点からレビューします。

- DRY 原則の遵守
- 機能開発に応じたテストコードの追加や更新
- .claude/memories/coding-guides.md の遵守

その他の一般的なベストプラクティス。

別途security-reviewerサブエージェントも用意しました。セキュリティの観点でレビューをさせる定義になっています。

.claude/agents/security-reviewer.md

---
name: security-reviewer
description: >-
  セキュリティに特化したコードレビューを行う際に使用するエージェントです。
  特に脆弱性や悪意のあるコードを検出することを目的としています。
  このエージェントはユーザーが明示的に呼び出した場合のみ使用されます。
model: opus
---
コードをレビューし、脆弱性や悪意のあるコードを特定します。
GitHub の PR の URL が指定された場合、その PR をレビューします。指定がない場合は、現在のブランチに関連する PR をレビューします。

注意:
このプロジェクトはユーザーのローカル環境で使用される CLI ツールです。そのため、不特定多数のユーザーが利用するウェブアプリケーションとはセキュリティ上の考慮点が異なる場合があります。プロジェクトの性質に沿ったセキュリティレビューを実施してください。

これらのSubagentをオーケストレーションするCommandとしてjudge-pr Commandを定義しました。レビューする・マージの可否を判定する・マージするのすべてを一発で実行するコマンドとしてこの命名になっています。

.claude/commands/judge-pr.md

---
description: PRをレビューして、合格ならマージする
---
target_pr = $ARGUMENTS
target_pr が指定されていない場合は、現在のブランチの PR を使用する。

まず、対象の PR の GitHub ステータスを確認する。ステータスが "success" でない場合は、エラーを出して終了する。
次に以下を並列で実行する:

- code-reviewer サブエージェントを呼び出し、$target_pr のコード変更をレビューする
- security-reviewer サブエージェントを呼び出し、$target_pr のセキュリティ上の問題をレビューする

各サブエージェントからの実行結果を統合してレポートする。
そして、コード変更に問題がなければ、pr-merger サブエージェントを呼び出して PR をマージする

詳細は割愛しますが、pr-mergerサブエージェントというサブエージェントも別途用意しています。

そして、similarityというツールも導入しました。

https://github.com/mizchi/similarity

similarityはmizchiさんが公開しているOSSです。TypeScriptの場合はsimilarity-tsという実装が用意されており、similarity-tsコマンドを実行することで類似度の高いコードを検出することができます。thresholdオプションをつけることができて、例えば threshold 0.85 のようにすると85%以上の類似度のコードを検出、といった調整ができます。

この検出結果をClaude Codeに渡して共通化を計画・実装させることで、「コードを見ないリファクタリング」がある程度可能になります。

このような開発スタイルのままv0.62.0の8月14日頃、週間ダウンロードは約1万5千となり、GitHub Starも順調に増加していきました。

しかし…コードベースが破綻

このままVibe Codingでどこまででも行けるのでは?と一瞬思ったのですが、v0.67.0、8月23日頃に限界を迎えました。

v0.1.0リリースが6月18日なので、2ヶ月くらいはほぼコード見ずに進むことができました。これ自体はすごいことです。しかし、この段階で、機能追加が安定しない、バグの内容を伝えても直せない、余計壊れてしまう、何時間作業させてもFailしたテストを通せない、といったことが起こり始め、Claude Codeを使った変更が怖くなるという状態になってしまいました。

AIモデル自体の精度向上やAgentの作り込みがすごいのはもちろんのこと、さらにMCPやsimilarityなど周辺ツールを固めることで、Vibe Codingの限界を押し広げることはできました。ただ、限界自体をなくせるわけではないということを痛感しました。

何が良くなかった?

具体的に何が良くなかったのかでしょうか。

なんとなくお分かりかと思いますが、設計について指示をしていないからということに尽きます。コードを見ないと設計の指示を出すのは困難です。

もちろんAIコーディングツールが悪いわけではありません。AIは「こういう機能を足したい」「このバグを直して」としか言われていないので、それを遂行しただけです。人間が設計に対して責任を持たなかったことの帰結といえます。

この問題さえもAIの将来的な進化で解決されるのでしょうか?ひとつ思うのは、AIが自己判断でリファクタリングすることがユーザーにとって快適なのかはわからないなということです。
指示していないことをされると逆に道具として使いづらくなる可能性もあります。

また、ソフトウェアの設計や構造に画一的な正解はないので、AIベンダーからすると単に「仕様通り動くコード」を書くことに比べるとトレーニングがしづらいのかなとは思います。そのため「綺麗なコード」を書くことについては比較的苦手分野と思われます。

大規模リファクタリングで開発可能な状態に復帰させる

話を実際の開発の方に戻します。

ともかく、開発が止まってしまった状態から、開発可能な状態に復帰させる必要がありました。

そのために、設計・構造を大規模に変更する決断をしました。自分自身でコーディングと、Cursor Tabを補助として作業をしました。

Claude Codeに一気にリファクタリングさせるのは困難で、コンテキストが大きすぎるためか、仕様を保ったまま構造を変えることができませんでした。ドキュメントも生成させていたのですが、リファクタリングのついでにチェックすると、存在しない機能の記述や誤りが散見されたため、書き直しをしました。こちらも人間側のこまめな軌道修正やコンテキスト管理が甘く乖離していったと思われます。

大規模なリファクタリングを一気に行ったので、テストハーネスが機能しないことには苦しみました。テストファイルは用意していましたが、対象ファイル、ひいてはそのディレクトリごと削除して再構築するような変更なので、テストハーネスを有効に活用することが難しかったです。


テストファイルはあったが、対象ファイル(というかディレクトリごと)を削除するので…

途中、デグレや破壊的変更もありつつですが、1〜2週間ほどで完了しました。言うまでもないことですが、業務プロジェクトであれば、もっと工数を割いて漸進的なコードべース移行を計画すべきです。今回は個人のOSSプロジェクトという性質と、割ける時間が限られているという背景からこのような形で実施しました。


かなりの書き直しをしました

リファクタリング後の開発スタイル

リファクタリング後の開発スタイルはこのようにしました。

まずはコードの構造を頭に入れた状態で指示をする、AIによる変更をちゃんと読むといったことです。

そして、実際のところ、日本語で指示するより自分で書いた方が速い場合も多いので、自分自身によるコーディングとCursor Tabのスタイルも織り交ぜていきます。立ち止まって考えてみると、AIエージェントオンリーでの開発に拘るのも手段と目的が逆転した本末転倒でした。私たちはAIを使いたいのではなく、良いものを素早く作りたいだけです。

また、0→1のコーディングについては、もちろんこちらも活用するのですが、自分で書くより冗長になるなど、まだムラがある印象です。そのため、どちらかといえば実装の横展開を依頼することを意識しました。

結果的に、伝統的な開発スタイルへの揺り戻しとなりました。

リファクタリング後にできるようになったAIコーディング

いくつか、リファクタリング後にやっているAIコーディングのTipsを紹介していきます。

commit hashベースで指示をする

特に実装の横展開に使えるのが、commit hashベースで指示をするというテクニックです。

commit hash xxxxxxxxxx を参照して、同じ方針で path/to/file.ts を変更してください。

といったプロンプトですね。AIはcommit hashからdiffを取得できるので、注目して欲しい箇所のbefore/afterを簡単かつ正確にAIに伝えることができます。

diff-analyzer Subagent

また、 diff-analyzer というSubagentを定義してよく使っています。

.claude/agents/diff-analyzer.md

---
name: diff-analyzer
description: 現在のブランチと origin/main の差分を分析し、作業の進捗状況を要約する必要があるときに、このエージェントを使用してください。
model: sonnet
---
1. git fetch origin/main を使って最新の main ブランチを取得する。
2. git diff origin/main...HEAD を使って、現在のブランチと main の差分を取得する。
3. git log origin/main..HEAD --oneline を使って、現在のブランチのコミット履歴を取得する。
4. 差分とコミット履歴をもとに作業内容を要約する。

内容的には、mainブランチと現在のブランチを比較して要約するというシンプルなものです。これはcommit hashより長いスパンの変更をまとめて把握させたい時に使えます。

1. diff-analyzer サブエージェントを呼び出し、現状の変更内容を把握してください。
2. 把握した変更内容に沿って、テストコードを追加・変更してください。

といったプロンプトで活用できます。

Claude Code(Agents) SDKの活用

それから、Claude Code SDKの活用です(現在はClaude Agents SDKに改名されました)。

これを使うことできめ細やかな指示とより多くのコーディングのAI自走を両立しやすくなります。

私は次のようなスクリプトを組んで使っています。

tasks.ts

export const model: "opus" | "sonnet" = "sonnet";

const task = ({filePath}: {filePath: string}) => {
  return `
fix-descriptionブランチに移動して作業してください。

commit hash ec22e123912bfb3ec30d99abd8d1182fc17c0b62 の変更を参考に、
${filePath} にも \`description?: string | undefined\` を追加し、 \`getDescription\` メソッドも追加してください。

そして、\`pnpm run cicheck\` の結果をすべてPASSさせること。
終わったらすべての変更をコミット・プッシュしてください。
  `
}

export const tasks: string[] = [
  ...([
    "src/subagents/claudecode-subagent.ts",
    "src/subagents/codexcli-subagent.ts",
    "src/subagents/fix-subagent.ts",
    "src/subagents/cursor-subagent.ts",
  ].map((filePath) => task({filePath: filePath ?? ""}))),
  "call pr-handler to create a PR",
];

tasks.ts で作業対象のファイル一覧の配列と、ファイル名の変数を展開可能な可能なタスクを定義をしています。結果的にファイル一覧を注入したタスク一覧をexportしています。

tasks.ts

import { query } from "@anthropic-ai/claude-code";
import { model, tasks } from "../tmp/tasks/tasks.ts";

const runClaudeCode = async (task: string) => {
  console.log("プロンプト:", task);
  for await (const message of query({
    prompt: task,
    options: {
      abortController: new AbortController(),
      permissionMode: "bypassPermissions",
      model: model ?? "sonnet",
    },
  })) {
    if (message.type === "assistant") {
      console.log("Claude Codeの発言:", message.message.content[0].text);
    }
  }
};

for (const task of tasks) {
  try {
    await runClaudeCode(task);
  } catch (error) {
    console.error(error);
  }
}

そして run-tasks.ts として、tasks.ts でexportしたタスク一覧をimportして、順番にループでClaudeCodeのタスク実行をキックするようなスクリプトとなっています。

これにより、特に似たような変更を複数のファイルに行うような場面で、各ファイルに対する作業ごとにコンテキストを独立させることができます。コンテキスト独立によりロングコンテキストを回避できるため、1つのプロンプトで全ファイルを列挙した指示をするよりも作業の成功率が高くなります。入力するプロンプトがDRYになったり、作業が終わるたびに次のプロンプトを投げる必要がなくなって、単純に人間の負荷が下がり楽というメリットもあります。

コードの詳細を知っているからこそ効果的なAIコーディングができる

これらのTipsはコードベースに関するインデックスが自分の頭にインプットされていることが前提となります。

構造が秩序立っていることも重要です。処理が期待する場所に置かれている状態を保つことで、プロンプトが組みやすくなります。

たくさんのAI動かして開発するエンジニアは1メンバーというよりは「現場監督」のようですが、それでいうとコードを見ずにAIに書かせるスタイルはいわば「現場に来ない現場監督」ということになります。良いものを作るには、自らも手を動かし、細部も把握している「現場にいる現場監督」でいることが重要と思っています。

AIとソフトウェア開発の現在と、少し先の未来

AIとソフトウェア開発の現在と、少し先の未来について触れます。「少し先」としたのは、正直それより先のことはわからないからです。

インターネットやスマートフォン、ソフトウェア開発領域なら、パブリッククラウド、サーバーレスアーキテクチャ、Reactをはじめとした宣言的UIなど…登場初期にその価値に気づいたのは少数でした。例えばReactは、JSX記法が気持ち悪くて流行るわけないとか、それはすでにPHPが通った道だから、とか色々言う人がいました(自分もそういうことを少し思っていました)。

一方で、当初騒がれたような結末にならなかったものもたくさんあります。自分が覚えているものだと、AWSでインフラエンジニア失業とか、Firebaseでバックエンドエンジニア失業などでしょうか。ChatGPTが登場した3年前も、2,3年でエンジニアの仕事がなくなるという議論もあったと思います。

人間の役割、AIの役割

今のところ思うこととしては、「人間の役割、AIの役割」と言いますが、それぞれの役割は不可分なのではないかと思っています。どこまでがAIにできることで、どこからが人間にしかできないことで…は、実際は非常に曖昧です。

開発文脈だと、「人間は設計までをして、あとはAIにさせる」という意見がありますが、これはソフトウェア開発において設計と製造を分離する発想と非常に近いものがあると思います。

そもそもソースコード自体が設計書ではないかという向きもあると思いますし、プログラミングをせずに良い設計ができるのか、という昔からの議論と再び対峙する必要があります。

https://qiita.com/mdstoy/items/5510f94c9ed981cfbb85

例えば、2019年の記事ですが、元Microsoftエンジニアの中嶋聡さんは自分でプログラムを書かずに良い設計・良い仕様を作ることはできないと述べています。

https://note.com/lifeisbeautiful/n/n1358c3529940

これに関しては、自信を持って言えるのだが、「どんなに優秀なエンジニアでも、決してプログラムを自分自身で書かずに良い詳細仕様を作ることは出来ない」という絶対的な法則があるのだ。私の知っている優秀なエンジニアは、皆それを知っており自ら実行している。もちろん、彼らはプログラムを書き始める前に大まかな設計をするのだが、十分な経験を積んだエンジニアは、その段階でのものが「仮設計」でしかないことを良く知っている。だから、その段階で詳細設計書を書くような時間の無駄使いはせず、すぐにプログラム(もしくはプロトタイプ)の作成にかかるのである。
(中略)
私には、この「自分でプログラムを書かない上流のエンジニアが詳細設計書を作り、下流のエンジニアがコーディングをする」という工程そのものが、根本的に間違っているとしか思えないのだ。

ソフトウェア開発には不確実な要素が多く、実際に手を動かして探索しながらものを作っていく必要があるという意味で、個人的には共感します。

さらにAIベンダーであるOpenAIの中の人も、コーディングを理解することは必要と述べていたりします。

https://www.businessinsider.jp/article/2509-openai-engineer-advice-for-high-schoolers/

私自身の実感としても、「効果的なプロンプトを入力するにはコードベースへの高い解像度が必要になる」と思っています。実装コストや保守性、体験のリッチさのバランスを取った仕様を策定したり、リファクタリングでモジュールを分割したり、いずれも実装の詳細に対する理解がどうしても必要です。

人間に残るのは上流工程?コミュニケーション?

「どんどんAIに仕事をさせて、人間は人間にしかできない仕事をする」といいます。そこでよく言われるのは上流工程、意思決定、それからコミュニケーションといった領域が人間の仕事として残るのではないかという議論です。しかし、自分はこれらも人間の聖域とは限らないと思っています。

コミュニケーション分野でいうとコールセンターの代替が試みられたり、政治領域では意思決定をさせる試みられるといったニュースを見るようになりました。

https://www.nikkei.com/article/DGXZQOUB16BO80W5A610C2000000/

https://www.nikkei.com/article/DGXZQOGR12CYY0S5A910C2000000/

政治より上流の工程というのも、もうなかなかないですよね。

おわりに

私の現時点の考えとしては、「人間は設計、AIが実装」など、あまり役割を分けて考えようとしすぎず、仕様策定、コミュニケーション、設計、実装、テスト…すべての工程で「AIと協働する」という意識が良いのではないかと思います。

そして、AIと協働したり、AIの提案を理解するためには、結局のところ細部に対する理解が必要になります。これはエンジニアに限らず、他の職業でもおそらく同じではないかと考えています。

つまり、良い方針は良い詳細から生まれます。詳細に降りることができる能力を手放さないことが引き続き重要と考えています。



Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -