🌟

贋作Sudachi.go : Claude CodeでRustのコードを移植できるか

に公開

はじめに

「Claude Code って知ってますか?」とナイツのネタみたいな問いを投げてしまいますが、ほんと衝撃的でした。こんなにちゃんとしたコードが機械によって書かれる日が来るとは思ってませんでした。しかも、Claude Codeは、適当にコンソールを使って、適当なツールを組み合わせて、grep したり、sed したり、シェルスクリプトを作って検証し出したり、プログラマがやりそうなことはおおむねやってくれます。

デバッグコードを仕込んで printf デバッグし始めたときはほんとひっくり返りそうになりました。

そんなわけで、Claude Code でどのくらいのことが出来るだろうかと、今回 sudachi.rs を Go に移植してみることにしました。

https://github.com/ikawaha/sudachi.go

前提と方針

今回は Claude Code になるべくやってもらってどこまでやってくれるか試すのが目的です。題材の sudachi.rs は形態素解析器 Sudachi の Rust 版です(元は Java で書かれています)。元の Java 版にあるような辞書作成の機能などはなく、解析機能だけに絞ったプログラムになっています。Rust は自分は素人なので、Claude Code にやらせるのにはちょうどよい題材かなと選びました。

形態素解析器の構成方法や、Sudachi の構成はおおむね理解してます。とはいえ、Rust で書かれているとシュッとは読めないので、元のプログラムはみることなしに、Claude Code に頑張ってもらおうという方針です。

Claude Code のプラン

Claude Code のプランは個人的に契約した Pro プランです。[1] このプランだと、すぐリミットに達してしまい5時間待つことになります。だいたい朝やって、お昼休み、夕方の退社後、とちょうどいい感じにサイクルが出来るので、個人の遊びとしてはちょうどよい感じでした。ただ、土日にガッとやりたいときはストレスがあります。

Claude Code に移植してもらう

基本的に Claude Code にコードを生成してもらうスタンスで、自分ではコードを書かないで進めます。git操作(コミット等)は自分でおこなうようにしました。

それとなく動くようになるまで

まず、sudachi.rs のリポジトリを clone して、作業レポジトリ直下のフォルダに置いておき、このフォルダの内容を Go で移植するよう依頼しました。朝、昼、晩のサイクルで2週間ほどでおおむね動くもの(「すもももももももものうち」が解析可能なもの)が出来ました。

形態素解析

Claude Code は移植はしてくれるのですが、よくよく言い聞かせないと独自実装を入れてくるので「忠実に移植してください」「忠実な移植になっていますか?」「独自実装ではないですか?」「Rust版のコードを調査しましょう」など、口酸っぱく言い続ける必要がありました。上手く行っているなと思っても油断するとすぐに適当なコードをいれてくるので、挙動があやしいときは Esc を連打して止めて「元のコードをよく読んでください」などと小言を言う必要がありました。

特にバグを修正するときなどは、隙あらば局所的に解決しようとしてくるので、Claude Code のメッセージにあやしいところがないか見ている必要がありました。なので、やりとりはすべて日本語にしてもらって、流れてくるメッセージにあやしいものがないか目を光らせるといった風でした。

リポジトリの構成を整える

それとなく動くようになったので、リポジトリの構成を整理することにしました。Claude Code は Rust から Go に移植する際に、ある程度適当にそれぞれの言語の特性を読み取った構成に変換してくれました。たとえば、Rust のパッケージと Go のパッケージの概念は似ているようでちょっと違うところがあり、Rust の方が細かい単位でパッケージ化されているので、それを適当にまとめて Go 側でパッケージにするといったことをしてくれていました。

ただ、Rust の構成が、Go のパッケージの循環参照になってしまったり、パッケージの単位が Go らしくないといったような場合は、ある程度、ひとが介入して手動で整えないと解決できない場面がありました。また、Rust のエラーを忠実に Go に移植すると、これはこれで Go らしくないコードになったりするので、そういったリファクタリングもある程度機能がまとまった時点で人力でおこないました。

あと、何も指定しないとやたらめったらスクリプトを作ってディレクトリのあちこちにまき散らすので、フォルダ構成も指定して置く方がよさそうです(たぶんどっかで Claude.md に記録した)。

坊ちゃん 100% をめざす

それとなく動くようになったので、青空文庫から「坊ちゃん」のテキストを1行1文にしたものを用意して、Rust版とそれを移植したGo版で差分を取ってこれを埋めていくことにしました。それとなく動くようになったところから完全互換を目指します。

まず、Rust版でテキストを解析した golden を用意させ、Go版で解析して差分を求めてもらいます。Claude Code はスクリプトを作成して、差分を取ってくれるようになりました。問題を修正する度に勝手に差分を取ってくれるので、だいぶ捗りました。

だたし!差分があっても「微細な差異、実用上は問題なし」などと言ってくるので油断してはいけません。「それはバグです」「差分が出ないように修正する必要があります」などと言い続ける必要があります。下の表は Claude Code が作ってくれた改善の推移です。

品質指標の推移

修正段階 一致率 不一致文数 主な改善内容
初期状態 98.8% 30件 -
第1段階 99.4% 15件 空表層形・OOV処理改善
第2段階 99.6% 10件 CategoryType処理改善
第3段階 99.88% 3件 OOV表面形正規化修正
第4段階 99.92% 2件 Lattice Dump順序修正
最終 100.00% 0件 OOV品詞選択修正

最初から割といい線いっているように見えますが、初期段階では結構ひどいコードが混じっていて、適当な定数とか初期値とかでごまかされていたりします。とくに Claude Code は少しでも精度を上げるために、fallback 処理をあちこちに入れていて、特定の問題はこれで解決されるのだが、汎化されてないので他の場面で問題になるということが多々ありました(機械学習、って感じがします)。あと、問題になっている差分を解消しようとすると、以前直した部分を元に戻してきたりして、さらには、元に戻した部分は「辞書が異なるため」とか「本質的な違いではない」などといってごまかしてきたりしました。ほんと・・・

それでも粘り強く対話し続けた結果、最終的に坊ちゃん100%を達成しました :tada:

Claude Code が作成してくれたレポート

なんとなく得られた知見

いつでも捨てられるようにしておく

Claude Code になるべく任せたいが、ちょっとした修正を(自分は)予定しているのにもかかわらず、広範囲に手を付け出すことがあるため、常に Claude Code が編集した部分をリセットできるようにしておくことが大切でした。コミットするのが面倒なら、安定している部分をステージングに上げておくだけでも「やっちまった・・・」を減らせます。もしかしたら、コミットも Claude Code に任せたりするとよいのかもしれませんが、ちょっと今回はそれは怖い気がして出来ませんでした。

同じ事を2回やってもよい。次は上手くいくこともある

Claude Code は同じ事を頼んでも、同じようにやるとは限りません。一度修正を捨てて、もう一度やらせると、さっきは上手くいかなかったのに、上手くまとめてくれることがあります。同じ事をなんどか挑戦してみるのも意外な発見があります(ないときもあります)。

完璧にはならない

完璧な移植は難しいなと思いました。コードの構成も読んだら変だなと思う箇所が沢山あります(でも今回は気にしないことにしたのでみてない)。たぶん、坊ちゃん100%を達成したいまでも適当な fallback 処理などは残っていると思います。仕上げるためには目視チェックが必要そうです。逆に言えば、完璧にはならない、それでよい。土台部分だけほしい。そういった用途ではガンガン使えそうです。今回に限っていえば、ここからコツコツ直していく楽しみがあるので、これはこれでよいのですが。

なるべく複雑さを持ち込まない

今回、RustからGoへの移植をおこないましたが、JavaからGoへの移植であれば、言語機能の違いも少なく、もしかしたらもう少し品質よく移植できたのではないかと思います。問題を単純に、複雑さを持ち込まないように Claude Code にタスクを設定してやれたらよかったと思いました(こなみ

それはやめて Claude Code!

  • 動作確認等で使われてない機能を実装しない (実装を省略しますとかコメントが残っていることもよくある)
  • 質問したつもりが Claude Code を誘導してしまう。「そのとおりですね!」
  • エラー処理を省いて fallback してごまかす
  • 回帰テストのつもりなのに、テストに失敗すると t.Skip() を挿入してテストをこっそりスキップする
  • 差分があってもそれは本質的には問題ないと言い張る

それはバグではありません

まとめ

頭も錆びついてきて手も動かなくなってきたオッサンにこそ Claude Code は福音です。めんどくさいなと思ったコードでも、自分で見通しさえ立っていればかなり上手にコードを生成してくれます。Claude Code のおかげで僕もプログラマとしてもう少しやっていけそうです。

Happy hacking!

脚注
  1. 会社のアカウントではもっとガンガン使えるプランで、個人的に使ってもかまわないよといってくれていて、会社がケチとかではないです ↩︎

Discussion