本記事は、最初から最後までAIを使わずに手打ちで作成しました。
はじめに
AIが人間よりも上手にコードを書けるようになって、1年近く経過したように感じます。最近は手でコードを書く機会も少なくなりました。私はOSS優待を利用してVSCode上のGitHub Copilotを使用していますが、最近はClaude CodeやCodexも試しています。
ライブラリをCrystal言語向けに移植する
Crystal言語は他のプログラミング言語に比べると、ライブラリが豊富でありません。
しかし、オープンソースのライブラリは自由に移植してよいライセンスを持つものが多く、AIは移植作業がとても上手なため、ライブラリがなくても単に移植すればよい状況がうまれつつあります。
この記事では、ライブラリの移植でどのような作業をしているか、言語化して記録したいと思います。
参照元ライブラリを選定する
まず参照元のライブラリを選定します。
Crystal言語では、CやGoと比較すると高度な抽象化が期待されます。一方でRubyやPythonほどメタプログラミングを活用した高度な記述はできません。
そういう意味で、Crystalは中間的な言語であり、参照元ライブラリを一つに絞れるとは限りません。複数のライブラリを参照して、どうするのが良いか考える必要があります。私の場合、コミュニティの元気なRustやGoを見つつ、RubyやPythonのAPIを参考にすることが多いです。Rustを参照元に選定する場合は、移植よりもバインディングという手法を使う方がよいこともあります。
そういった切り分けを行うのが第一段階で、これは移植がいいだろうとなったら次に進みます。移植とバインディングが混在するケースも考えられます。
ライセンスを確認する
最も重要な点は、ライセンスです。MITライセンスなど、移植可能なライセンスであることを確認します。作成するライブラリは移植元のライセンスを引き継ぐようにします。
オリジナルの開発元情報がはっきりわかるように明示します。私の場合、参照元リポジトリを git submodule でまるごと追加します。こうすることで、Coding Agents が参照するコードのバージョンも固定することができます。また、無用な誤解やトラブルの発生を避けることにつながります。
最終成果物として「ツール」を作成するときは、複数の参照元を設定することも十分に考えられますが、「ライブラリ」を「移植」や「バインディング」で作成する場合は大本の参照元は単一に絞る方が後々メンテナンスしやすいと思います。
Web版ChatGPTで全体計画を立てる
これは2026年5月1日時点でのworkaroundです。
まず最初に、移植元ライブラリの圧縮ファイル(tarballもしくはzip)を、ChatGPTにアップロードし、ライブラリ全体をCrystalに移植する全体方針を聞きます。(圧縮ファイルをアップロードするのは単にアップロードファイル数に制限があるからです。)
なぜ最初からCoding AgentではなくWeb版のChatGPTなのか?
理由は経験的に Coding Agentよりも、Web版で壁打ちした方が、良い結果が出るというのがあります。
推測になりますが、おそらく理由は2つあります。1つ目はインターネット検索の活用効率です。Web版のChatGPTは、ローカルのCoding Agentと比較して検索が得意です。ウェブサイトや技術ブログ、GitHubを検索して幅広い視点で方針を探ってくれます。曖昧なところやわからないところも、自走してググって方針を詰めてくれます。2つ目は、おそらくクラウド環境の方がドキュメントのアクセスの効率がよい点です。ローカルでトークンを削りながらコードベースを検索するより、クラウドでコードを検索する方が効率が良いみたいで、良い結果が得られると感じます。
全体の方針を出したら、わからない点や要望を追加のチャットで詰めます。この段階では、ChatGPTの返答が長くなって脇道にそれないように「3行以内で答えてください」と情報制限するといいかもしれません。
C, C++, Rust など静的言語のバインディングとしてCrystalのライブラリを構成する場合は、バインディング境界をどうするのか、ビルドの仕組みとインフラをどうするか、よく相談して認識をすり合わせるとよいでしょう。
望ましいCrystal言語のAPIデザインは、移植元の言語、例えばRustやGoなどとは異なることが多いです。そういった場合は、参考資料としてRubyのライブラリなどもアップロードし、どういったAPIが望ましいか考察するように依頼するとよいでしょう。ただし、CrystalはRubyとは異なる言語ですので、Rubyと同じAPIにすればそれで良いというわけではありません。本当に良いものを作りたいケースでは、自分でチェックする必要があります。
アーキテクトやデザインについて合意が取れたら、PLAN.md などの形で自己完結して実行可能な計画書に書き下ろしてもらいます。PLAN.md は私が読めるように日本語で作成することが多いです。このファイルはGitHubにはアップロードしないようするか、リポジトリをパブリックにする段階で除去します。
ただし、自分のためだけではなく、広くみんなに使ってほしいツールの場合は 最初から PLAN.md を英語で作成してもらう場合もあります。
ローカルで移植作業を行う
私はCopilotの無料枠を活用したいため、ローカルではVSCodeを使っています。しかし、最近はCLIエージェントも発達しており、Zenなどの好みのエディタを使える状況になってきています。
まずはプロジェクトリポジトリの初期化を行います。
Crystalのプロジェクト名を決め、crystal init lib piyo でひな形を作成し、参照元のリポジトリをサブモジュールで追加します。そして、PLAN.md を配置します。
PLAN.md を TODO 形式に寄せた方がいいか?という点については今のところはっきりわかっていません。
作業が消えてしまうと悲しいので、GitHubで新しいプライベートリポジトリを作成し、プロジェクトをpushできる体制を整えます。あとは、PLAN.md を見てプロジェクトを Coding Agent に進めてもらいます。
作業のサイクル中に、定期的に crystal tool format でフォーマットを行い、crystal spec でテストを実行してもあります。
私の場合はリンターの ameba --no-color はあとから自分で実行して、その結果を Agent に投げてまとめて直してもらっています。
定期的にPLANを見直して、更新したり、終了した箇所を削除したりしてもらいます。
コーディングエージェントは、最初は勢いよく作業を進めてくれますが、途中からステップを刻むようになり、なかなか作業が進まなくなることがあります。
その場合は、一旦止めて「今何が問題になっているのか?」「作業を進める前にリファクタリングした方がいい箇所があるか」「PLAN自体に無理なところがないか」「私に環境構築などでやってもらいたいことがないか」などを聞き取り調査します。
また、場合によっては、出来上がったところまでを tarballにして、ChatGPTに再アップロードして、現状を大局的に評価して PLAN2.md を策定してもらいます。
ただし、このような進め方をするのは個人で、GitHubに全て公開するからであって、複数人で仕事のためにコーディングする場合は不可能なやり方だと思います。
Fixtureを活用してテストを書く
参照元ライブラリによっては、テスト用にFixtureを用意しているものがあります。その場合、submoduleで参照元を追加していると、Fixtureも参照できるため、Crystal側でもそのFixtureを使ってテストを行うことができます。
しかし、浮動小数点演算による誤差や、乱数などの違い、レースコンディションの発生などにより、パリティー(parity)を狙うのは容易ではないというのが率直な感想です。
乱数の差を吸収するためには、オリジナルの言語で乱数を発生させる小さなコードをテスト用に用意し、それで発生させた乱数をFixtureの一種としてjsonなどに保存し、Crystalのテストでは、その数値を利用するという方法もあります。(この方法も常に上手くいくわけではありませんがワークするケースもあります)
GitHub Actions を追加する
ある程度形になったら GitHub Actions にいくつかのワークフローを追加します。
- ドキュメント生成用の
docs.yml - CLIツール生成・リリース用の
build.yml - テスト用の
test.yml - GitHub Actions 更新用の
dependabot.yml(もしくはrenovate)
これらは、毎回追加することになるので、私の場合は、lolcat.cr というトイプロジェクトを用意しており、コピペで追加してから変更を加えることが多いです。
追加し終わったら、テストやビルドが通過するまで調整して、README.md にバッジを追加します。
README.mdを簡潔にまとめる
目的によります。私の場合、AI移植ライブラリを作成する目的の大半は自分自身が使いたいからです。手書きでライブラリを作っていた頃と比較すると「ライブラリが広まってほしいとも思わない」という心境の変化もあります。メンテナンスで時間が削られたり、トークン代金がかかることを本能的に警戒しているのかもしれません。
README.md を着飾ってしまうと、いかにもAIで作った感じが増します。豪華READMEでメンテナンスが停滞しているプロジェクトは、余韻なきテーマパークの残骸みたいで心証がよくありません。
そこでREADME.mdは「簡潔・質素・実用性一辺倒で」書いてもらうようにいつも指定を入れています。
コミットの粒度を決める
一般原則としては、信頼性の高いプロジェクトの運営のために、force push を避けて、透明性の高いコミットログを残すことが望まれます。オープンソース・コミュニティーで集団開発する場合は特にそうです。プルリクエストとレビューを通して世界の参加者と交流し、人となりを知ることができます。これはオープンソースコミュニティに参加する醍醐味と言えるでしょう。
しかし、AIに依存して一人で個人開発する場合の Gitを使ったワークフローのベストプラクティスはまだ確立されていません。どのような判断でそのようなコードを入れたのか記録する必要がありますが、AI専用の記憶システムを外付けするよりも、Git に寄せた方がいいと思います。
ただしGitは時系列であり、コミットの順番を入れ替える場合には最終的なコードの状態が同じでも force push が必要になります。AI時代には、時系列に依存せず rebase できる意味論に基づいたバージョン管理手法の開発が必要であるとも感じます。
現状では個人的には、AIにコミットメッセージを書いてもらい、それを自分で手動でコミットしています。
ここでは人間の目的とAIの作業目標が一致してることを確認するという最低限のチェックとして機能すると思っていますが、AIが作成したコードをあたかも人間が書いたように洗浄・隠蔽しているという批判もあり、ベストプラクティスとは言えないな、とは思っています。
恥ずかしながら、force push も多用しています。
とくにPLANを進めるプライベートリポジトリの初期段階では、--amend して force push を繰り返すことで事実上履歴を残さず「上書き保存」をしています。
おわりに
書いてある内容は、2026年4月時点でのものです。
あとから読み返したときに「あの時代はこうだったんだな」と思い出せるとよいかなと思います。
Coding Agent によって、これまでは理解するのが難しかったアルゴリズムやアイディアを、任意の粒度で解説してもらったり、思いついたことを簡単に実装して試せるようになったのは本当に革命的なのですが、ついついAIコーディングにのめり込んでしまい、長時間の作業で健康を害するような傾向もあって、少し気をつけなければならないと感じています。健康第一ですね。
冒頭に、この記事はAIではなく手打ちで作成しましたと書きました。わざわざそうに書いたのは、最近は文章もAIで生成することが多いため、あえて違うことをしてみたかったからです。
この記事は以上です。