パルカワ2

最近はFlutterをやっています

モバイルアプリの画面をリアーキテクチャする前に仕様書を書き始めた

  • 画面仕様書と呼んでいる
  • リアーキテクチャをするとき「振る舞いは、原則現行通り」となるが、現行通りとはなにかがわからない
  • 自分の場合、とにかくコードを読んで理解するために頑張るわけだが大変。QAEはコードを読めないので、ドキュメントや実際に動作させて調べているらしい
  • 今はClaude Codeがあるし、レガシーの実装から振る舞いを書き出してみるのがいいのではないかとやり始めた
  • Claude Codeで一発で書き出して終わりというわけではなく、1,2時間くらいかけて修正している。人間が1から書くよりは早いかなという印象。あと人間が書いてもAIが書いても100点は取れないなと思っており、間違うことはあるなという前提でいる
  • 実装がそう振る舞っているだけで、意思を持って決めた仕様ではないため仕様と言いたくないという気持ちにはなるが、ユーザーにとってはそんなの知らんがなという話なので、仕様とする!!!とした
  • また、そう振る舞っていることを言語化しただけなので、意図はまったくわからないことが多い。「なぜそういう実装/仕様に?」は全くわからないので、意図負債は残り続けている
  • 最初はレガシーの画面仕様書だけ書いていたが、リアーキテクチャ時にバグを直したり「これはやめよう」と振る舞いを変えたいことがあることに気づいた
  • レガシーの画面仕様書は、as-isだが、to-be も必要になってきたということ
  • ほな、作りましょうということで、レガシーの画面仕様書を元にリアーキテクチャ後の画面仕様書を書き出すようにした
  • レガシーの画面仕様書には元々バグっぽい挙動や変な仕様を書き出す章があり、そこに「リアーキテクチャ時に直す」とかを記載していたので、それを元にto-beな画面仕様書を書き出すようにした
  • to-beの画面仕様書は、リアーキテクチャを行う前に書き出している
  • リアーキテクチャの前に書き出すことで、実装のタイミングで画面仕様書を更新できる。例えば、Golden テストを書いたら画像を貼ったり、リンクを貼れたりするわけですね。
  • このあたりは、すべてスキル化されている。あとはto-beの仕様書と実装を比較するスキルとかも用意した。
  • それぞれレビューややり取りをかなり細かくして、自分が説明可能な成果物が出てくるという感じ。
  • まあ、実装と同じですね
  • これはCritにかなり助けられている

リポジトリ内の技術ドキュメント

元々 .claude/rules/*.md に「ーーーはこうあるべし」を色々書いていたが、superpowersを使ってspecやplanを書いた時に参照されなかったりと微妙だなぁと思えてきた*1ので、やり方を変えることにした。

  • docs/ 配下にドキュメントを書く
  • CLAUDE.md から 「ーーーする時は docs/xxx/xxxx.md を参照すること」と記載

という形に変更した。現状単体テスト、Golden テスト、Widget テストについてしか書いてないが、specやplanやレビューでいい感じなのでこのまま他も揃えていく予定。

目的 → 目指す状態 → やり方の流れで書く

元々rulesを書いた時はAI向けだ!と思っていてなるべく短くしたい気持ちでいて「こうするべし」というやり方しか書いていなかったが、docsに移行するにあたって、「目的」と「目的を満たしている状態」と「どうやってその状態にするのか」を書くようにした。

少なくとも人間は、目的を理解していないと行動を変えにくいだろうし、ドキュメントで書かれてることだけやりました*2となるのもおかしな話なので書くようにした。AIもそうなのかもしれない。

例えば、テストも「なぜGolden テストを書くのか?」を理解出来ていないと意味のないテストやテスト範囲が大きすぎるテストをAIが書いてもレビューで指摘出来ないとかがある。このあたりの認識をチームで揃える必要があるし、自分含め常にアップデートしていく必要があるので言語化しておくの大事そう。

スキルにしない

最初はスキルにしようかと思っていたが、CLAUDE.mdで参照するように言えばいいかと気づいてやめた。

1つのドキュメントの長さ

最初は単体テストに関することが複数あるので、それぞれファイルを小分けにしようと思っていたが、単体テストを書くとき・レビューするときに全部参照してほしいなと思ったので分けずに1つのドキュメントにまとめた。最近は1M Contextとかになっているし、気にしすぎなくて良い気がする。とにかく小さく保て!と言われると小さく保ちたくなるが、早すぎる最適化と思って堪える。

*1:正確にはCLAUDE.local.mdなりで指定すればいいのだろうが、ほなrulesで書く意味ないやんと思ったし、設定するしないがあるのは微妙そう

*2:そんな同僚はいません

最近の開発

  • obra/superpowers を使っている
  • brainstormingでぼんやり考えていることを書き出してもらってから進めている
  • 自分の悪いクセでやろうとすることが大きく変更も大きくなりやすいことが多い
  • Claude Codeが賢いので、大雑把に言ったことでも実現出来るが当然やるべきことが多いので変更は大きくなる
  • 変更が大きくなると全ての変更に目を通したつもりが漏れてることがあったりして、理解負債が貯まっていくように感じた
  • なのでまずは分解するためにやろうとしていることを洗い出すというのが自分には向いてそうだった
  • 洗い出したものを眺めて、こう分割して進めようとか考えることが多い。分割するのが決まったらまた改めてbrainstromingから始めることもあるし、writing-plansで範囲指定して実装計画を書き出すこともある
  • specやplanは今のところコミットしていない。おそらく自分しか利用していないので、コミットする価値があるのかよくわかってない

最近のLinter事情

analyzer 9.x.xにアップデート出来た*1ので、元々custom_lintで書いてたものに加えて新たにカスタムルールを書いたりしている。

なぜLinterが必要なのか

  • AIは確率論的に動くため人間と同様に抜け漏れがある。逆にLinterは、抜け漏れが*2ない
  • Linterはルールのテストが書けるというのも自分的には大事
  • 基本的に自分が扱うリポジトリは、Claude Codeを前提にしてルールなどを書いているが、チームメンバーがそれ以外を使って開発することは止めることは出来ないし、プルリクエストが必ずAIを通しているとは限らない

すべてをAIで解決するのではなく、適材適所いい感じに使いたい。プログラムで問題発見出来るものを都度AIで発見するのではなく、問題発見するためのプログラムをAIで書く。手動テストせずに自動テスト書くみたいな感じ。

どういうルールを書くのか

プロジェクト特有のルールやライブラリ特有のルールを書く。 例えば、以下のようなのを書いて設定している。

  • このレイヤーからこのレイヤーは逆流してはいけない
  • このファイルは、こういう命名規則のクラスでなければならない
  • Widgetは1ファイルにつきpublic classは1つでなければならない
  • sealed classは、freezedを利用しなければならない
  • RiverpodのRefをProvider以外に引数として渡してはいけない

どういうルールを作るべきか?もAIと話して見つけることがある。また元々custom_lintを使っていた時からこういうルールがあると良さそうというのを溜めていたのでそこからちまちま作っている。

その他、考えていること

コードレビューをAIに任せようという話があり、自分も任せているし有益である。一方で、Linter、テストカバレッジ、循環的複雑度認知的複雑度Code smellsなどを機械的に示すことで解決できることも多くあり、まずはそっちを頑張ろうかとぼんやり思っている。

*1:アップデートした日に13.0.0が出ましたが...

*2:ルールにバグがない限り

プルリクエスト作成時のAIによるコードレビューの所感

プルリクエストを出す流れが結構決まった流れになってきたのでスキル化した。今のところ、Github Copilotを前提とした流れになっている。

  1. Claude Code) /create-pr
    1. Draftでプルリクエスト作成
    2. Github Copilotへのアサイン
    3. テンプレートを元にbodyを作成
  2. Github Copilot) アサインされたらレビューする
  3. 自分) Github Copilotからのレビューを対応する/しないを決めてGithub上でコメントする
  4. Claude Code) /respond-to-review
    1. コメントされた内容を本当にやるべきかも改めて確認する
    2. Github Copilotに指摘された内容を修正
    3. 変更 -> commit -> push -> Github上での対応しましたコメントまで行う
  5. 自分) Github上の対応しましたコメントを確認して、解決していたらResolvedに変更
  6. 自分) Github上のUIで変更を眺めてOKならReady to reviewに変更
  7. 同僚) レビュー + Approveする(AIを使うかもしれないが自分は認知しない)

Github Copilotによるコードレビューは、大きく変更しない限り基本的に1回しか行わない。変更をpushするたびに再度全体を人間にレビューしてもらうことはしないと思うのだが、それと同様にAIもしなくていいという気持ちに今はなっている。


プルリクエスト作成時のAIによるコードレビューの目的は、人間レビュアーの負荷軽減だと自分は思っていて、人間レビュアーが指摘しそうなことを先に潰しておいてレビュアーがわざわざ指摘しなくてもいいようにしたい。

なので、Draft状態でAIによるコードレビューを先んじて行い、レビュイーがすべて対応してから人間にレビュー依頼を行うという形にしている。

自分は、レビュアーとしてアサインされたプルリクエストにAIによるコードレビューが大量に残ってると「これは対応するのか?対応したのか?対応を待ったほうがよいのか?」など考えることが増えてだるいな〜と思うので。


コードレビューには、レビュイーが持つコストとレビュアーが持つコストがあって、AIによってレビュイーのコストは自然と減っていく気はするが、レビュアーのコストはレビュイーが意識しないと減らない気がするので、減らしていきたい。

プルリクエスト作成時のコードレビュー

最近はClaude Codeでしかコードを書いていない。1月に対して2月は自分が関わったプルリクエストの量は3倍になったらしい。

コードを出力するスピードが上がっているのであれば、コードレビューがネックになるでしょう!と思って、Claude Code ActionによるAIレビューを導入してみたのだがあまりしっくりきていない。

考えてみると、コードレビューの何をAIに期待しているのかを自分の中で整理できておらず、期待しすぎてるかもと思ったので整理してみる。

コードレビューの目的と役割分担

コードレビューの目的はかなり多いし、人や組織やプロダクトによっても異なりそう。例えば、検査の観点でいえばバグ発見、コードの質、テストの質などがあるし、チームメンバーと話して疑問をはっきりさせたり、共通認識にしたりする場でもあるみたいな話もあるだろう。

Coding Agent登場以前は、これらすべてを人間がやっていたのかでいえばそうではなくLinterに頼ってもいた。Linterは、答えが明確になっているルールに沿って警告やエラーを出す。

人間とAIは同じ入力に対して異なる結果を返す可能性があるが、LinterやTestは同じ入力に対して常に同じ結果を返す。それを踏まえると以下のような役割分担になるのではないか。

  • 答えが明確でルール化できる → 主にLinter / Test
  • まだ答えがないので人間の判断が必要 → 主にAI
  • 判断・確定 → 主に人間

Linterによるレビュー

スタイル・フォーマット・命名規則みたいなのは当然として、依存方向の違反や禁止されたimportのようなアーキテクチャルールも明文化できるならLinterに寄せられる。

言語イディオムレベルの冗長なコードもLinterが得意。Dartは、関数参照(tear-off)で書けるのにラムダ式を使っている箇所を検出するルール のunnecessary_lambdas とかそういうのがある。コードの質でいえばDart Code Metricsとかもある。

テストの質も頑張ればいける気がする。ミューテーションテストは、ソースコードを自動的に壊して(a > ba < b など)テストを実行し、壊したのにテストが通ったらアサーションが甘いのでテストの質が低いねというのがわかる。*1

ルールとして明文化できるなら、まずLinterに寄せるのがよさそう。

AIによるレビュー

AIが扱うのは、Linterではルール化できないが人間に「確認してほしい」箇所。例えば

  • 既存のコードと異なるパターンで書かれているがLinterは指摘していない
  • テストが書かれていないが書かなくていいのか判断つかない

一方で、AIはAIが持つわずかなコンテキストを元に「このコードは怪しい」という確度を出しているだけなので、AIはバグや間違いっぽいものを検出するが「ここが間違いです」とは断定できない。加えて、人間の方がいろんな情報にアクセス出来るため、コンテキストを多く持っていて「あえてそうやっている」とか「別にそこまでやる必要ない」とかすでに判断していることが多い気がする。

AIがアクセス出来る場所にコード以外のコンテキストも含めていくのが必要で、難しければAIには暗黙知っぽいところを指摘してもらうくらいしか出来ないのではなかろうか。

人間によるレビュー

AIによるレビューで書いたことは、人間によるレビューにも言えるが、人間にしか出来ないことは、以下のようなものがある。

  • 設計の妥当性の判断
  • ビジネスロジックの正しさの判断
  • AIの指摘に対する判断
  • 知識共有の会話

判断するのは人間で決定したことをLinterで気付けるようにしていくみたいなのをAIに指示するのも人間ですね。

まとめ

Claude Code Actionでのコードレビューは、Linterでやるべきことも指摘しようとしていたからしっくりこなかったんだとおもう。

今まであんまりやりきれてなかったLinterカスタムルールなどを色々整備していくことが今後重要になっていくだろうし、Coding Agentによってそれがしやすい環境になってきた。加えて、AIは答えが不確定で、チューニングや期待値の調整が必要なため効果が読みにくい。Linterで地盤を固めたりリポジトリ内にコンテキストを持てるようになった上でAIによるレビューを導入するか、さくっと使えるGithub Copilotでレビューしたりするほうがよさそうではある。

いま悩ましいのは、custom_lintを使わずにカスタムルールを書くにはanalyzerのアップデートが必要で、そのためにはRiverpodのアップデートやcustom_lintからの移行を真面目に考える必要があるということ!やるしかねえ

*1:もちろんそれだけでテストの質が高い!とは言い切れないとは思うんだけど…