この記事は Perl Advent Calendar 2025 の2日目です。
それは6月の初旬、Claude Code で Perl がどこまで戦えるか試していた頃のこと…
期待に近いコードが出てきて、さあ試そう!という時の Claude と私のやり取りをご紹介します>
🤖 (Claude) entity補完の実装が完了しました。次にテストを追加します。
🤖 (Claude) entity補完のテストを追加します。
# Entity completion tests my $inspector; eval { $inspector = YATT::Lite::Inspector->new(dir => $dir); }; # Test entity macro completion { my @items = $inspector->complete_entities("html/index.yatt", "yatt", "", 0); my @macros = grep { $_->{detail} =~ /entity macro/ } @items; ok(@macros > 0, "Should have entity macros"); my ($if_macro) = grep { $_->{label} eq 'if' } @macros; ok($if_macro, "Should find 'if' entity macro"); is($if_macro->{detail}, "entity macro yatt:if", "if entity macro detail should be correct"); } ...
AIに最初からテストファイルを書かれても、あまり嬉しくない
- そもそもテストコードは 読みやすいとは、言いづらい
- テストコードをさっと読んだだけでは、本当に望みの挙動になっているか、確信が持てない。
- テストコードを実行しても、このテストが成功したか失敗したかしか分からない
- AI に誤魔化されているかもしれない。
- 自分で色々パラメータを変えて試したい。
と思ったので、次のような指示を出しました。
👤 User(hkoba) テストファイルを更新する前に、コマンド行からの同じ意味のテストを行うようにして下さい。
OO Modulino を教えてみた
それに対して Claude は、テスト用のコマンド・ファイルを実装しようとしはじめました。
🤖 Claude コマンドラインからentity補完をテストしましょう。まず、テスト用のコマンドを実装する必要があります。
しかし、私がこの機能の手触りを試したい だけなのに、使い捨てのファイルを増やされても後で手間が増える…ので、 ここは OO Modulino として CLI から直接実行してもらうよう、指示してみました。(読みやすさのために改行を入れています)
👤 User Inspector.pm は modulino なので、メソッドをCLI から試す際にコマンドを実装する必要はありません。このようにして下さい>
./Lite/Inspector.pm --dir=$PWD/samples/basic/1 \ complete_entities html/foobar.yatt yatt '' 15
Claude の反応や、いかに…
🤖 Claude なるほど、modulinoとして直接メソッドを呼び出せるのですね。コマンドラインからentity補完をテストしてみます。
🤖 Claude tool_use: Bash Test entity completion from command line
./Lite/Inspector.pm --dir=$PWD/samples/basic/1 complete_entities html/foobar.yatt yatt '' 15tool_result
{"detail":"entity macro yatt:not","documentation":"Built-in entity macro","kind":12,"label":"not"} {"detail":"entity macro yatt:undef","documentation":"Built-in entity macro","kind":12,"label":"undef"} ... {"detail":"entity macro yatt:dispatch_one","documentation":"Built-in entity macro","kind":12,"label":"dispatch_one"} {"detail":"var a: text","kind":13,"label":"a"} {"detail":"var body: code","kind":13,"label":"body"} ...🤖 Claude 良いですね!entity補完が動作しています。行15にあるfoo widgetの引数である
aとbodyが変数として認識されています。次に、prefix付きで試してみましょう:
ななんと、全て理解した返答をしてくる上に、早速応用を始めてくれました!
🤖 Claude tool_use: Bash Test entity completion with prefix 'i'
./Lite/Inspector.pm --dir=$PWD/samples/basic/1 complete_entities html/foobar.yatt yatt 'i' 15tool_result
{"detail":"entity macro yatt:ifeq","documentation":"Built-in entity macro","kind":12,"label":"ifeq"} {"detail":"entity macro yatt:if","documentation":"Built-in entity macro","kind":12,"label":"if"} {"detail":"entity yatt:is_current_file","documentation":"User-defined entity function","kind":12,"label":"is_current_file"} {"detail":"entity yatt:is_debug_allowed_ip","documentation":"User-defined entity function","kind":12,"label":"is_debug_allowed_ip"} {"detail":"entity yatt:is_current_page","documentation":"User-defined entity function","kind":12,"label":"is_current_page"} {"detail":"entity yatt:ignore","documentation":"User-defined entity function","kind":12,"label":"ignore"} {"detail":"entity yatt:inspector","documentation":"User-defined entity function","kind":12,"label":"inspector"}🤖 Claude prefixフィルタリングも正しく動作しています。
別の行(default widget内)でも試してみましょう:🤖 Claude tool_use: Bash Test entity completion in default widget (line 5)
./Lite/Inspector.pm --dir=$PWD/samples/basic/1 complete_entities html/foobar.yatt yatt '' 5... {"detail":"entity macro yatt:value_selected","documentation":"Built-in entity macro","kind":12,"label":"value_selected"} {"detail":"entity macro yatt:ifeq","documentation":"Built-in entity macro","kind":12,"label":"ifeq"} {"detail":"var a: text","kind":13,"label":"a"} {"detail":"var body: code","kind":13,"label":"body"} {"detail":"entity yatt:is_current_file","documentation":"User-defined entity function","kind":12,"label":"is_current_file"} {"detail":"entity yatt:abspath","documentation":"User-defined entity function","kind":12,"label":"abspath"} ...
出力の理解の自己矛盾(曖昧さ)にも気づいて、勝手にいろいろ試してくれたり。
🤖 Claude あれ、line 5でも
aとbodyが出ています。これは問題です。foobar.yattの内容を確認しましょう:1 <!yatt:args x y> 2 <h2>&yatt:x;</h2> 3 <yatt:foo a=y/> 4 5 <!yatt:widget foo a> 6 <h3>&yatt:a;</h3>🤖 Claude line 5はfoo widgetの宣言行なので、そこでは
aとbodyではなく、default widgetの引数xとyが出るべきです。もう一度テストしてみましょう:
↑この理解は厳密には誤解なのですが、そのまま経過を見守ります。
🤖 Claude tool_use: Bash Test entity completion in default widget (line 2)
./Lite/Inspector.pm --dir=$PWD/samples/basic/1 complete_entities html/foobar.yatt yatt '' 2... {"detail":"entity macro yatt:lexpand","documentation":"Built-in entity macro","kind":12,"label":"lexpand"} {"detail":"var x: text","kind":13,"label":"x"} {"detail":"var y: text","kind":13,"label":"y"} {"detail":"var body: code","kind":13,"label":"body"} {"detail":"entity yatt:script_name","documentation":"User-defined entity function","kind":12,"label":"script_name"} ...🤖 Claude 良いですね!line 2では正しく
x、y、bodyが変数として表示されています(bodyは暗黙の引数)。
ここまで出来るなら大したものです。最後はこんな指示を与えて、テストにも加えてもらいました。
👤 User(hkoba) 惜しいです。
$lineNumberのベースは 0 であるため、cat -nの行番号とは1ずれます。この点に注意しながら、改めてコマンド行からテストを行って動作を確認した上で、同じテストをt/inspector.tにも加えて prove してください。
その後の他のプロジェクトでも、OO Modulino の呼び出し方を教えれば、ちゃんとそれを使いこなしてくれるところを観察しています。
(余談)TDD と OO Modulino 、目的は同じかも??
ところで先日、こんなツイートが目に入りました。
「AIにテストを先に全部書かせる」
— 松岡@AI/DDD/アジャイル (@little_hand_s) 2025年11月26日
これ、実はTDDじゃありません。
従来のTDDでは「よくある間違い」と言われていたものが、AI時代では有効な手法に変わりました。
が、使い所が大事です。
その違いと使い分けを解説しました。https://t.co/29fPPmcFQ2
その記事の中に、(主題の前の)TDD の解説として、以下のような一節がありました。
TDDの特徴は、設計を最初から完璧に決めないこと です。
複雑な問題では最初から正解が見えないため、小さく実装して動かし、得られた知見をもとに設計を改善していきます。この「少しずつ試して学習しながら設計を育てる」アプローチが、不確実性への対処として有効なのです。
恥ずかしながら、正直、TDD の意図、目指すところを誤解しておりました…(テストを先に書くことを何より大事にするという主張かと勝手に思い込んで、敬遠しておりました…)
- 小さく実装して、さっさと動かして、得られた知見を元に設計を改善
- 少しずつ試して学習しながら設計を育てる
もしこれが TDD の目的なら、OO Modulino(CLI_JSON) で私が目指してきたことと同じと言えそうです。 TDD では「小さく実装して、さっさと動かす」ためにテストを書くのに対し、 OO Modulino では全てを CLI から呼べるようにすることで対応します。 さっさと動かして知見を得ることが目的なら、テストを書くよりも CLI の方が早い領域は多いし、パラメータを変えながら試せる幅も増えます。 そうして十分に知見を得られた後で、コマンド履歴からテストをゆっくりコピペして書けば良いかなって…
まとめ
- AIがしばしば確認用としてテストコードを書いてくるけど…確認手段としてテストコードは微妙では?
- CLI から直接、パラメータを変えながら新機能を試せると、コーディングエージェントも人間も、検証がはかどるのでは?
- TDD の人にも、OO Modulino は役立つかも?
OO Modulino はあくまで実装パターンですので、Perl に限らず皆さんの普段の言語でぜひ試して見て下さい。