AIに頼ってライブラリ依存度を下げる試み
はじめに
仕事の中で小さなCLIツールを作る機会がありました。スピナーや対話型UIが欲しくなり、使えそうなライブラリを探そうと思ったものの、小さなツールなのでそこまで機能性が高いものは必要なさそうでした。AIに頼ればライブラリに依存しすぎることなく、小さくシンプルに作れるのではと思い試してみました。
ライブラリへの依存を減らすことで、サプライチェーン攻撃のリスクを抑えたり、ライブラリの非推奨化や破壊的アップデートの影響を抑えることで、メンテナンスの手間を抑えたいというモチベーションです。
作ったもの
実験台として PokéAPI にCLIでアクセスするツールを作ってみました。
CLIでは以下のコマンドを提供します。
-
get: 入力した名前のポケモンのデータを表示
-
pick: 選択したポケモンのデータを表示
これらのコマンドは、APIリクエストの間はスピナーを表示したり、ユーザーに入力を促したり、選択肢から選択を促すUIを持ちます。これらのUIは、VSCode Copilot Edits(Claude 3.7 Sonnet)を使って実装しています。ライブラリの実装を参考にしつつ、必要最小限の仕様のみ取り入れています。
今回使ったライブラリは、コマンドの引数解析とルーティングに tj/commander.js 、PokéAPIのクライアントに Gabb-c/pokenode-ts を入れています。
スピナー
お手本として sindresorhus/ora を参考にしました。機能が豊富で使いやすいライブラリです。
- 多様なスピナー
- カラー表示
- プレフィック、サフィックスの表示
- テキストの動的更新
- succeed, fail, warn, infoなど多様な状態を表現可能
- CI/CDなど非インタラクティブな環境のサポート
ここまでリッチでなくとも良いので、今回は以下の仕様に絞ってCopilotに再実装してもらいました。
- スピナーはドット表示1種のみでよい
- 機能は start, stop, succeed のみでよい
- サードパーティのライブラリに依存しないこと
最終的に80行程度の小さなコードに収まりました。LLMとのやりとりは2往復、大体5分ほどでこの形になりました。
対話型の入力
お手本として SBoudrias/Inquirer.js の input を参考にしました。こちらも機能豊富で使いやすいライブラリです。
- 多様な入力形式(input, password, list, checkbox ...)
- カラー表示
- バリデーション
- 複数の質問を一連のフローとして実行
- 入力に応じた条件分岐
今回は以下の仕様に絞ってCopilotに再実装してもらいました。
- 1個の質問ができれば良い
- 回答をトリムして返すこと
- サードパーティのライブラリに依存しないこと
最終的に20行程度の小さなコードに収まりました。これはやりとり1往復で生成され、5分ほどでこの形になりました。
標準ライブラリの readline の強力さに驚きました。スピナーの実装にも使われていましたが、readline があればちょっとしたCLIなら十分そうですね。
対話型の選択
こちらは SBoudrias/Inquirer.js の select を参考にしました。
今回は以下の仕様に絞ってCopilotに再実装してもらいました。
- 1個の質問ができれば良い
- 回答をトリムして返すこと
- 矢印上下キーで選択肢を選べること
- サードパーティのライブラリに依存しないこと
最終的に70行程度のコードに収まりました。やりとりは6往復ほどで、手作業で一部のコードを調整し、大体30分ほどでこの形になりました。カーソルを移動させると表示が崩れるバグがあり、その解消で4往復ほどかかっています。
まとめ
AIによるコード生成を活用し、ライブラリへの依存を低減しつつCLIツールを開発する手法を試しました。
ライブラリへの依存を低減することで、サプライチェーン攻撃のリスクやライブラリの非推奨化などの影響を抑える効果が期待できそうです。ライブラリから必要最小限のコードを抽出するのは従来では以外と手間がかかるものでしたが、AIを活用すればかなり効率化されます。
ただし、この手法は小さくシンプルな機能には適しているかもしれませんが、規模が大きかったり要件が複雑なケースでは成立しづらい面もあります。複雑な要件ほど1度のやりとりで狙ったものを生成するのは難しく、LLMとそれなりにやりとりをする必要がありますが、セッションが長引くとトークン上限にぶつかりやすく、これもまた生成されるコードの質の悪化に影響します。こうなってくると、リスクを受容してライブラリに依存した方が開発やメンテナンスのコストを抑えられるでしょう。
ライブラリの中には豊富で過剰といえるほどの機能性を持つものもありますが、それは多くのフィードバックを受ける中で汎用性が高まった結果と言えます。また、多く利用されるということはそれだけ実戦でテストされ磨き上げられているはずです。巨人の肩に乗ることも大切です。
はじめは小さくシンプルに実装を抽出しつつも、その機能に価値があることが分かり徐々に規模や要件が複雑化してきたら本格的にライブラリを利用するように切り替えるなど柔軟に判断できると良いですね。
つまりこの手法は、機能にどれほどの価値があるのか、今後どれほどの改修が見込まれるのかが未知数な初期フェーズでの開発において、サードパーティライブラリの影響による保守コストを抑えたいケースで効果を発揮するかもしれません(要検証)

ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion