Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

Agentic Coding用classファイル静的解析ツールをAgentic Codingで実装した

Rust 製の class ファイル静的解析 CLI inspequte を、Agentic Coding を開発の中心に据えて実装しました。結果として、人間がレビューするのは spec とテストケース、そしてリリース内容だけという運用に到達しています。

本稿で紹介するCoding Agent に多くを任せて人間はリリース内容確認に注力するワークフローを通じて、今後の Agentic Coding が目指すべき形がある程度見えてきたと思っていて、このツールのような Coding Agent が Definition of Done を判断するための CLI が洗練されるのではないかと思っています。

11年間追いかけた「ぼくのさいきょうの静的解析ツール」

FindBugs への貢献を始めたころの2015年に、静的解析ツールはこうあるべきだという理想をまとめたことがあります。その実現のために OSS の静的解析ツールを読んだり、その拡張を書いたり、数学的・技術的なバックグラウンドを理解するために学んだり*1ということを続けてきて2度スクラッチ開発に挑戦しましたが、いずれも当時の技術的限界や単純な開発量に圧倒されて未達のままでした。

それが今回 Agentic Coding の登場によって前提が変わり、非 JVM 言語で class ファイルの静的解析を書くハードルが急激に下がったことで、次のような課題が一気に解決されました。

  • 実行可能バイナリに直接コンパイルできる言語(Rust)で Java バイトコードを高速に扱うこと
  • Test Harness によって MCVE をそのまま回帰テストに使える仕組み
  • Kotlin と Java の両対応
  • control flow graph や data flow のスクラッチ実装
  • マルチコアを活かすための並列実行
  • 人間と Coding Agent に向けたドキュメントの自動生成
  • 複数ルールの開発を並行させてもコンフリクトを起こさないプロジェクト構成*2

さらにプロダクトのメンテナンスで必要になる配慮がプロンプトという形で保存・再生できるようになり、ひとりでプロジェクトを回すことのハードルも下がりました。 もちろんリリース内容に責任を持つのは引き続き人間がやるべきなのでリリース前レビュー工程は外せないのですが、静的解析は正解・不正解を仕様やテスト結果から判断しやすいこともあり、ルール開発のレビューはかなり任せられます。たとえば次のような内容をプロンプトやスキルとして保存しておくことで、一人で複数の作業を同時に実行したり、短い時間で進捗を作ったりという価値を出せています:

  • ルールの実装とテストにおける手順
  • ルール検討における観点
  • 仕様作成における観点
  • 実装における保守性の考慮と不具合を作りにくいテストの書き方
  • 実装評価における観点
  • 偽陽性を洗い出すプロセス
  • 複数プロダクトに対して静的解析を実行するスモークテスト
  • 同一条件で継続的に性能テストを実行して性能悪化を早期に把握する体制
  • 同一条件で継続的に他ツールとの性能比較をする体制

グラフにすると次のような感じです。青のところは Coding Agent をはじめとした自動化に任せられるところで、人間は緑の判断だけやるのと、赤のリリース承認だけやればよいという状況です。開発したルールの採用判断はまだ人間がやっていますが、判断基準が言語化できたらこれも自動化できそうです。

graph LR

%% ===== スタイル定義 =====
classDef auto fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px;
classDef semi fill:#E8F5E9,stroke:#43A047,stroke-width:2px;
classDef manual fill:#FFF3E0,stroke:#FB8C00,stroke-width:2px;

class A auto
class B semi
class C manual

%% ===== ルール実装 =====
subgraph ルール実装
  企画 --> 計画 --> 仕様策定 --> 実装 --> 仕様と突合しての評価 --> 実装
  仕様と突合しての評価 --> 採用判断 --> mainに反映 --> ドキュメント更新
  企画 --> 不採用企画を記録して次に活かす
  採用判断 --> 不採用企画を記録して次に活かす
end

%% ===== 定期メンテ =====
subgraph 定期メンテ
  依存バージョン更新
  偽陽性調査と修正
  パフォーマンス低下要因の分析と修正
end

%% ===== リリース =====
subgraph リリース
  rev(リリース内容を整理) --> merge(リリース承認) --> rel[リリース]
end

%% ===== 凡例 =====
subgraph 凡例
  A[自動化可能(Agentに委譲)]
  B[半自動(人がトリガー・承認)]
  C[完全手動(人が思考・判断)]
end

%% ===== 分類 =====
class A,rev,実装,仕様と突合しての評価,mainに反映,ドキュメント更新,企画,計画,仕様策定,不採用企画を記録して次に活かす,依存バージョン更新 auto;
class B,偽陽性調査と修正,パフォーマンス低下要因の分析と修正,採用判断 semi;
class C,rel,merge manual;

AIを開発の中心に据えつつ人間がその説明責任を追うためのワークフローとして、この skill による自動化と人間によるリリース承認を中心に据えたやり方は筋が良さそうだと感じています。

で、ここまでだけなら「夢が叶いました!」なんですが、今回はもう少し風呂敷を広げて Agentic Coding 時代に求められる理想の静的解析ツールを追求しました。

Agentic Coding 時代に不要となったものもある

今回昔から思い描いていたツールを実現したと言っていますが、実際には必須と思っていた機能が不要となっている部分もあります。

2013年に FindBugs 関係の記事を書いたときは変更されたコードとその影響を受ける部分だけを解析する incremental analysis が必要だと思っていましたが、SonarQube のような巨大プロダクトですら10秒で解析できるようになるとシンプルに寄せられます。マルチコアを活かすための並列実行は重要ですが、複数ノードでの分散実行は大仰すぎました。Google Errorprone や Prettier が提供している自動修正はぜひ欲しいと思っていましたが、Coding Agent に十分な情報を提供できれば不要と判断しました。また IDE 統合も不要です。IDE を開発に使う頻度も落ちていますし、仮に使う場合でも Gradle 統合で乗り切れそうだからです。

なお Coding Agent が書いた Rust コードについては、人間はそこまで見ていません。人間は spec とテストケースだけ見るようにしています。共通コードに手を加える場合は既存テストケースを変えていないか厚めにレビューしますが、そうではない場合は spec が合理的で偽陽性検証のテストが書けていれば良しとしています。ルール変更の影響はそのルールに閉じるように設計することで、これを可能としています。つまりコードレビューもほとんど不要にできたということです。

Agentic Coding 時代、静的解析に求められること

Agentic Coding によって我々の開発サイクルはすでに大きな変貌を遂げています。 Coding Agent による PR レビューがすでに実用化され、人間も意識できていなかった観点を踏まえた高品質なレビューが安く早く行えるようになりました。 PR のレビューすら Coding Agent に任せて、リリースという意思決定を高速化しているチームも多いでしょう。

また Claude Code Security という、セキュリティ上の課題を高速かつ広範囲にわたって発見できるサービスも登場しています。不具合を中心に探してくれるBugbotもあります。静的解析を入れなくても、脆弱性や不具合を簡単に見つけられる時代になりつつあります。

さて、ではもう静的解析ツールは不要なのでしょうか?私はそうではないと考えています。静的解析の価値は「全自動マサカリ投擲機」であること、すなわちコードの品質を底上げるために「どこが問題か」「なぜ問題か」「どう改善できるか」を「めちゃくちゃ速く頻繁なスパンで」書き手に返すことです。そして Agentic Coding 時代でもこうした働きは次に挙げる2つの理由から引き続き必要です:

  1. Coding Agent には確実性が期待できないこと。品質底上げ効果にもムラがあるし、Coding Agent が書いたコードにも誤りが入る可能性がある。これは LLM の原理からも言えますし、複数のモデルやハーネスを組み合わせて利用することが一般的となっていることからも言えます。
  2. コードの書き手が人間から Coding Agent に変わっても「書き手にフィードバックを返す」ことの価値は毀損されていないこと。むしろテストやハーネスといった資産を整理することが重視されているように、フィードバックの価値は高まっています。

この2点を踏まえて考えると、いま必要な静的解析ツールは次のような特徴を備えているべきです:

  1. とにかく高速に実行できること。実行に数分かかってしまうようでは、Coding Agent の良さが活きないため。
  2. 結果が揺れないこと(同一入力に対しては常に同じ結果を返す)。
  3. CLI で動くこと。API や MCP を否定するものではないが、Coding Agent が変えたものの検証をその場で高速かつ少ないトークン消費で行うため。
  4. 出力が簡潔で Coding Agent にとって扱いやすいこと。

私は今回、こうした課題を次のようなアプローチで実行できると考えました:

  1. Rust で実装し、マルチスレッドを標準的に採用すること。
  2. ソースコード分析よりも多くの確定度が高い情報をもとにルールを適用するために、バイトコード解析を採用すること。
  3. CLI を実行可能バイナリとして頒布し、JRE のような前提を作らないこと。
  4. SARIF v2.1.0という Coding Agent が“常識”として知っているJSONベースの業界標準書式を採用し、その分析と解釈を助けること。

今のところこれらのアプローチは狙った結果を出しているように思います。Coding Agent からもビルドツール(Gradle)からも使いやすく、業務で開発する規模のマルチモジュールプロジェクトでも高速に分析して Coding Agent に対する入力として使えています。コーディングエージェントはプロンプトで明示的に教えなくても SARIF 2.1.0 を知っているので、SARIF ファイルを渡せば問題を理解して自分で修正してくれます。

実際どのくらい速いのか

「高速」を自称する以上、数値で語れるようにしたかったので、多くの静的解析ツールで実装されているNULLNESSルールに対象を絞って主要ツールと比較したベンチマークを公開しています:

ざっくり言うと、Guava 33.5.0-jre を対象にした比較では、inspequte の中央値は 0.250s で、同条件の nullness 系ツールに対して概ね 数倍〜桁違い の差が見えます(例:NullAway 1.234s / Checker Framework 1.314s / PMD 3.785s / SpotBugs 12.397s)。

また SonarQube 25.6.0.109173 を対象にした比較では、PMD 9.411s / inspequte 13.123s / SpotBugs 466.245s という結果になりました。比較対象や前提の違いもあるので詳細はリンク先に譲りますが、少なくとも「CI とローカルで何度も回す」用途でボトルネックになりづらい速度を狙って出せている、という手応えがあります。

他に試していること

ChatGPT による画像素材の作成も試しています。以前 99designs でプロダクトロゴを描いてもらったことがあるのですが、手間と資金が必要になるわりに思ったような結果は得られなかったんですね。ChatGPT なら当然レスポンスも早く、違ったときにやり直せるので、ズバリの結果が得られなくてもまぁ良いかなと思えるものにできました。ただ縦横のピクセル数だけは何度言っても変わらなくて、トリミング作業は自分でやっています。

ChatGPT に作成させたロゴ画像

ChatGPT に作成させた OGP 画像

また X におけるリリース通知の文面作成も任せてみましたが、こちらは「このリリースの目玉はそうじゃないんだよな〜」と思う結果が多く、結局文面を自分で作成して ChatGPT にレビューさせる運用になりました。

CHANGELOG の生成までは自動化できる(今回はモノレポに対応している release-please を利用*3)のですが、そのリリースに込めた思いのようなものは人間がまとめて伝えていく方法を採りたいと現状感じています。

静的解析以外の課題領域で同じアプローチが使えるか

今回実装だけではなく企画や仕様策定、評価までを Coding Agent に渡せたのは、静的解析やプログラミングにおける知識を彼らが元々持っていたからです。設計意図だけを書けばそれで十分で、data flow であるとか JVM stack であるとかの概念を教える必要はありませんでした。また入力や出力がほぼテキストとバイトコードという、LLM にとってもともと扱いやすいものだったのも今回の成功に強く寄与しているはずです。

これが Coding Agent が詳しくないドメインを扱うとか、人間などの不安定なインタフェースを相手にする必要性とかが生じると、コンテキストを伝えることの比率が上がって難度は上がるでしょう。それでも今後の技術発展によって扱えるコンテキスト量が増えるとか、サブエージェントの活用によってコンテキストを分割統治する体制を組むとか、Definition of Done をコマンドで検証できるようにしていけば、必ずできるようになるでしょう。特にこの「Definition of Done の検証」を CLI で高速に検証することの重要性は、今後静的解析以外のジャンルでも重視されると思います。個人的には品質目標とユーザインタフェースの評価についてこれができるようになり、人間の同期的な介入なしにプロダクトが成長する未来に関心がありますし、遅くとも年内には近い未来が見えるんじゃないかと期待するところです。

まとめ

Agentic Coding 時代の静的解析を再定義し、そのためのツールを Agentic Coding で実装しました。 人間は仕様とテストケース、リリース予定内容だけをレビューする体制になり、コードレビューも不要とすることで、 リリースの判断以外のほぼ全てを Coding Agent に任せられるワークフローが実際に構築できました。

こうした開発ワークフローを静的解析以外の課題領域でも構築できるかはまだわかりませんが、技術革新によってそれが可能になる未来はすぐに来るんじゃないかと期待しています。 その時に決め手になるのはコンテキストを分割統治する手法と、Definition of Done の検証を CLI で高速に検証する技術のはずで、関連する動きを追いかけていこうと思います。

*1:University of Aarhus の情報が役立ちました

*2:今回の静的解析プロジェクトの構成やプロンプトは次回掘り下げます

*3:リリース自動化については以前ブログ記事を書いています

CLIを書いてるなら、まずOTLPでtracesとeventsを吐けるようにするのがオススメ

とりあえず他の人に説明するのに良さそうなので、ChatGPTに書かせた記事です。


Agentic CodingでCLIを実装していると、機能要件がどんどん進められる反面、非機能要件がないがしろになりがちです。自分は最近静的解析CLIを育てているのですが、OpenTelemetryでtracesとeventsをexportできるようにしました。結果として「どこで遅いか」「どの実装が遅いか」を事実として掴めるようになり、最適化を迷わず打てるようになりました。さらにJargerを使えばスクリーンショットやJSONとして結果を取得できるため、人間はもちろんCoding Agentにとっても計測と改善のループを回しやすくなります。

traces(Span)とは

CLIの処理を「区間」に分けて、spanとして刻みます。例えば:

  • 引数解析
  • 入力列挙(ファイル探索など)
  • 入力の読み込み
  • 処理実行(処理ごとにspanを切る)
  • レポート生成
  • 出力(ファイル書き込みなど)

OTLPは、トレース・メトリクス・ログを運ぶための標準プロトコルで、Collectorや各種バックエンドに投げられます。CLIにexporterを組み込んで --otel http://localhost:4318/ などとcollector (Jarger)のURLを指定してやると、こうした情報がCLIのプロセスが終わった後もわかりやすい形で残るということです。イベント情報も紐づけて記録できるので、span処理中に何が起こったかをわかりやすく残しておくこともでき、ログファイルと突合しないと何がおこっていたか判断できない……みたいな状況も防げます。

Jaegerを合わせて使うと「記録」が一気に楽になる

OTLPで通信して情報を残しておくために、Jaegerをコンテナで立ち上げるのがおすすめです。Jaeger UIは、トレースを JSONとしてview/downloadできるので結果をそのままファイルに残せますし、スクリーンショットをPRやGitHub Releasesに添付しておくと人間にもわかりやすい形で改善を説明できます。こうした作業ももちろんCoding Agentに任せられます。

さらに最近、Jaeger本体に MCP(jaegermcp extension)関連の機能追加が入ってきていて、span名探索やcritical path取得など“エージェントから触りたい操作”が揃っていくと期待できます。ここまで来ると、パフォーマンス調査 → レポート化 → 評価、まで自動化できる見込みが立ちます。自分は現時点ではスクリーンショット撮影についてはスキル化しています:

github.com

なおJargerはログの表示に対応してないので、自分はOpenTelemetryをtracesとeventsに絞って利用しています。

Agentic Codingと相性が良い理由:ループが速くなるだけじゃない

Agentic Codingの開発ループって、だいたいこうなります:

  • エージェントに実装させる
  • 動かす
  • 遅い/失敗した/意図と違う
  • 直す

ここでOTelがないと、ループの途中で毎回こうなります:

  • 「多分ここが遅い」→ 当てずっぽう最適化
  • 「ログ増やすか」→ ログが増えて余計見づらい
  • 「再現条件なんだっけ」→ 記録がない

OTLPでtracesとeventsが取れると、ループがこう変わります:

  • 「遅い場所がspanで見える」→ 直す場所が確定
  • 「判断がeventで残る」→ 速さの理由が追える
  • 「JaegerのJSON/スクショをPRに貼る」→ 変更の証拠が残る

自分のケースでも、“観察できる状態”を先に作ったことで、並列化や最適化を適時に打てて、エージェントから気軽に呼び出せる実行時間に寄せられました。

まず最初の一歩(最小構成)

  1. CLIにOpenTelemetry SDKを入れて、トップレベルspanを1本切る
  2. 主要フェーズだけspanを増やす(入力列挙/読み込み/処理本体/出力)
  3. “判断”だけevent(ログ)で残す
  4. OTLP exporterでCollector/Jaegerに投げる(ローカルでOK)
  5. Jaeger UIで遅いtraceをJSON保存(またはスクショ)

この段階でも、「速さの議論」が体感から事実に変わります。

続・codexでJVMバイトコード静的解析ツールを書いた

codexでJVMバイトコード静的解析ツールを書いたの続き。その後2週間ほどで6個のマイナーリリースが出せました。 ユーザ体験を考えなければ充分に本番投入できるレベルに来ていると感じています。

v0.5

opentelemetryでtraceをexportできるように改修
SLF4Jのplaceholderの数を検証するruleを追加
ruleを並列で実行することにより高速化

v0.6

SLF4J ruleを多数追加

v0.7

SARIFレポートの安定化
jarを並列で読み込むことにより高速化

v0.8

大きな入力をファイルを経由して受け取れるように修正(Gradleへの組み込みに対応)
guava解析時に失敗する問題を修正

v0.9

EnumSet の利用を推奨するruleを追加
InterruptedException の扱いを検証するruleを追加

v0.10

SLF4J向けrule群をLog4j2向けに調整したrule群を追加
InterruptedException の扱いを検証するruleを追加
ClassPathにSLF4JがないときはSLF4J用ruleを無効化して高速化
ClassPathにLog4j2がないときはLog4j2用ruleを無効化して高速化
GitHub Code Scanningに対応

人間の役目としての意思決定

ルールの実装についてはskillに要件や方法論をまとめることで、ほぼテストケースをレビューすれば実装の詳細は気にしなくて良いという段階まで来ました。またSLF4J向けルールをLog4j2向けに調整して実装する、などはplan作成からほとんど任せられました。このようなルールの横展開ができるケースは多くないものの、codexであればかなり少ない工数でやれそうです。

今回自分が気にするようにしていたのは機能と非機能のバランスです。v0.5でopentelemetryを使えるようにして、どこで遅くなっているのか、どのルールが遅いのかを事実として捉えられるようにしました。これによってjar読み込みの並列化であるとか、ルールの並列化であるとか、特定ルールの最適化であるとかの高速化を適時に実行できました。今回の開発ではコーディングエージェントから気軽に呼び出せるパフォーマンスを重視していますから、そのための観察と意思決定が高い確度で行えることは非常に有効であると感じました。

いまのところOTLPデータからレポートの作成はjaegerを使った手動となっていますが、ちょうど最近JaegerのMCPサーバ対応がリリースされたため、今後はレポートの作成と評価までを自動化できそうです。

こうなると人間にとっては実装の詳細は正直どうでもよく、目指している速度や機能要件を満たせているか、ルールのfalse positiveやfalse negativeを見つけるためのテストケースが書けているかの2点だけを見れば良いことになります。たまに不要なcopyの削減や共通化できそうなコードの発見・対処といった負債返済のためのプロンプトを投げることは必要だと思いますし、そのskill化には価値があると思いますが、人間が真にやるべきは全体のバランスを見て今何をするべきかを決定づけることだと考えます。

このやりかたで踏みそうな地雷としては、OSS実装をコピって持ってきてしまいライセンス違反となることでしょうか。実際に家庭用ゲーム機エミュレータを実装させるとOSS実装をまんま持ってくるようなことはあるようです。また細かい実装まで見ないということは脆弱性を作り込んでしまう可能性も上がるので、SASTの価値も上がりそうですね。Renovateでお世話になっているMendがこの方向で攻めるようなので期待しつつ、それこそJFlogやSonatypeやSonarといった先駆者が多い領域でもあるのでメシ食うのは大変だろうなぁと思うなどしました。

inspequte 1.0リリースまでにやること

ルールを増やしていくことはもう難しいことではなくなったので、ひとまずユーザ体験向上にコストをかけて良さそうです。だいたい次のようなラインナップになるでしょうか:

  • コンパイル済みバイナリを提供する
  • setup-inspequte actionを提供する
  • Gradleプラグインを提供する

あとできればJava標準APIのnullnessデータベースを組み込んでやりたいですが、そこまでこだわるよりは出しちゃったほうが良い気もしています。ちょっと考えます。

codexでJVMバイトコード静的解析ツールを書いた

今年の目標としてRustでCLIを作りたかったのと、coding agentの台頭で静的解析ツールに求められるものも変わるのではないかと想定したことを受けて、RustでJVMバイトコードを解析するコマンドを作りました。

図1 ChatGPTに描かせたロゴ

開発の背景

SpotBugsへの貢献を止めてから後、実は2度ほど静的解析ツールを開発していました。目的はKotlinコンパイラプラグインとして動くものを書くことです。KotlinのIRを静的に解析することに挑戦したかったですし、独立したツールとして提供するよりもIDEやビルドシステムに統合されていた方が体験が良いのではと思ったからでした。差分コンパイルなどのプラットフォームの最新技術の恩恵も得られそうだと思いましたし。

しかし1度目は自分で書き、2度目はエージェントに書かせる形で挑戦したものの、結局はKotlinコンパイラが充分な情報を提供してくれ無さそうという結論になり、いずれも挫折しています。

そこから月日が経ちcoding agentが一般的になったことで、こうした統合の価値がほぼ無くなってきたのでは?むしろ設定や統合をできるだけ減らしてCLIでサクサク実行できる方がいいよね?ということを思いつき、成人の日を使ってcodexといっしょに書いてみた、というところです。もはやJavaやKotlinを書くのにIDEA使わなくなりましたもんね。

なおRustにしたのは知人と話していたときに、高速起動はやっぱRustかGolangだよな、Rustのほうがシステムプログラミング寄りだし一番速そう、みたいな話で盛り上がっていたのがきっかけです。変な苦手意識を取り除くためにも、自分が詳しくて簡単に検証サイクルを回せる開発テーマで使ってみるべきだ、という直感もありました。

codexと開発するうえで工夫したこと

claude codeのplan modeがいいぞという話を多く耳にしたので、codexで開発するうえでもまず how do you plan ~ という質問をまずして計画から入るようにしました。これはControl Flow Graph (CFG)のような静的解析としては一般的な構造や、厚めに書きたいテストケース周りでは特に役立ったと思います。エージェントと2人で仮説を持って開発に臨めること、結果として出てきたものをどんな観点でレビューすべきかをあらかじめ考えて臨めること、特にこの2点がコード変更量に圧倒されないために有用でした。

また経験上必要になるとわかっている性能検証用の実装( --timing オプションやベンチマーク用スクリプト )や実際のJavaコードを使ったテストを書くための spotbugs test harnessのようなハーネスを早期に実装できたのもかなり有効な工夫だったと思います。ひとつひとつの変更を積み重ねると全体最適でなかったり clone が多発するようなコードになったりして、性能改善のための開発を要所要所で入れていく必要があるのですが、その際にベンチマークによって「どこで、どのくらい」遅いのか、変更でどのくらい改善されたのかということがcodexにとって一目瞭然な状態を組み上げられたので、高速化のための方針策定からcodexに任せられるようになりました。たとえば v0.4.0 リリースではcallgraph edge overheadの2倍近い高速化を実現しましたが、このときのリクエストは次のようにとてもシンプルなものでした:

any idea to shorten analysis_load_ms? it is slow even when we load a single jar, so the cause is not storage I/O I guess.

これだけ書くとコードから仮説を立てて、ベンチマークを都度回しながら有効な手段を特定して実装してくれます。もちろん機能に影響はないことは単体テストとスモークテストで確認できる状態です。最終的には関数実行ごとに clone が実行されている点の修正が提案されましたが、その際もパフォーマンスを評価することをこちらから次のように依頼し、実際に実行されました:

reduce per-call cloning in build_edges then rerun the bench-spotbugs.sh to confirm the perofrmance.

ベンチマークを見ながらパフォーマンス確認できるところまでひとりで持ってくるのは何かと時間がかかり、かつ個人OSSではその内容に確信を持つためのレビューをお願いすることも難しいので、coding agentによってスピードと品質とが両方向上したと感じます。

今後の開発について

本番投入を考えたときに今の実装に足りていないのはルールセットをカスタマイズするための設定ファイルと、ルールをプラグインのような形で増やせる仕組み、そして組み込みルールの数です。そして前者2つについては実装戦略はわかっており、後はやるかやらないかだけという状況です。

ルールを増やすことはcoding agentにとっておそらく容易なことでしょう。既存ツールのルールを並べて評価し、重要そうなものをより本質的なルールへと再定義し、これを実装するだけです。間違いなく人間よりも早く量を用意できると思われます。

のであとはやるだけで普通に既存ツールの土俵で戦えるようになるはず。GradleやIDEAへの組み込みは元々スコープ外ですが、必要なら誰かがやるでしょう。

あと大切そうなのは、いかにdog foodingする場を作ってfalse positiveやfalse negativeについてフィードバックを受けるかという点でしょうか。こればかりはやってみないとわからないですね。ルールセットを増やしてcoding agentを積極的に採用しているOSSを見つけて売り込みに行く必要があるでしょう。

このプロダクトを育てるべきかはちょっとまだ見えてないんですが、継続の意義はあると思うので、しばらく隙間時間に育てていきたいと思います。

2025年の振り返りと2026年の抱負

昨年の振り返りと抱負はこちら。技術書典への個人出展は2回やりましたし、マネジメントスキルの棚卸しや実行も1年間みっちりやりましたので、当初想定はやり切りました。

今年のトピックはだいたいこんなところ↓だと思うので、掘り下げていきます。

  1. 副業をやめた
  2. 執行役員(VPoT)になって成果を出した
  3. 自分の強みと無関心を見つめ直した
  4. 生成AIの使い方が板についてきた
  5. 子どもの親として色々考えた

副業をやめた

今年のはじめ、前職つながりでいただいていた副業をお断りすることにしました。不義理を働いてしまい、当時は結構落ち込んでました。いや業務委託なんてそんなもんでしょという話はあるんですが、それはそれ、これはこれというやつです。

辞めた理由は本業がめちゃくちゃ忙しくなったからで、それは次の理由によるものです。

執行役員(VPoT)になって成果を出した

1月からVPoTを拝命し、3月からVPoEを兼務しました。SREのマネジメントも引き続きやっていたし途中から責務も増えたので、実質4足のわらじを履いてました。

前回の振り返りで書いてた「マネジメントスキルの棚卸し」が必要になっていたのも、転職後に離れていた「マネジメントのマネジメント」を再開する必要があったからです。

自分がマネジメントのマネジメントに思うことは昨年「組織という仕組みで解決することの難しさ、あるいはマネジメントに超人を求めるのは間違っているだろうか」に書いた通りで、助けになるフレームワークやツールはあるものの結局は状況と組織に合わせて様子を見ながら試行錯誤するものであり、とにかくまぁ難しく複雑なものであると思っています。それを兼務なので、明らかにキャパシティを超えた挑戦でした。

とりあえずこの1年ではすべて理想通りにやりきることはできないと早々に諦め、VPoEなのに採用をリードしなかったり、VPoTなのに技術戦略を描かなかったりと、5年後の自分が見たら指さして笑うだろうなという感じの戦術を採りました。ただそのおかげで最も外してはならない権限委譲の実践によるスケールする組織を実現できました。組織は1年間で完成するものではないため引き続きケアは必要ですが、会社として事業戦略をクリアに描けるようになった遠因を作れたことは誇れます。

生成AIが手に馴染んできた

今年もXでは生成AIの話はほとんどしてませんでした。このスピードについていく心理的コストは払えないから、レイトマジョリティになって長いものに巻かれようというスタンスです。実際自分が役割を演じる上で重要なのは応用なので、そういった情報のキャッチアップは同僚に任せて、製品化された事例を主に追っていました。

プロダクトとしてはCursor, OpenAI, Anthropic, Perplexity, Slack AI, Notion AI, Dify に Vertex AIあたりを薄く広く触っていましたが、コーディングに常用しているのはGitHub Copilotです。個人的にいまのMicrosoftとGitHubを信頼しており、技術的には最先端ではないかもしれないが体験を統合させたプロダクトを提供してくれるだろうと期待していて、実際Agent HQは良いものだと思います。

自分もMacbook ProのMAXを個人端末として所有しているので手元で色々動かそうと思えばできるはずですが、今のところはGitHubのUIからCopilot使ったり、Codespacesを活用してそもそもコードのチェックアウトすら省いたりとできるだけクラウド側で完結させるようにしています。Codespacesを使い倒せば全部インターネットの向こう側で完結して便利そうなんですが、流石にランニングコスト高そうですね。

あとSRE業務への応用という観点では7月のSRE Nextで見たTopotalさんのデモが良かったのと、SRE自動化ハンドブックで紹介されていた業務自動化がいま想像できる理想だなと思っています。HoneycombとDevelocityのMCPサーバが実装されたようなので、来年色々試してみたいです。

2026年の抱負

子どもが学校でPython3をやっているので、共通の話題としてのPythonをしばらくやっていこうと思います。ひとまずpgzeroで壁打ちゲーム(pong)をフルスクラッチで書くところまで来ました。

またバイナリを作成・配布する基盤としてのRustも再入門が必要だと考えて、今週 rustup で環境のセットアップを終えました。作りたいCLIはまだ見つかってないんですが(ちょいちょい思いつくけどコレジャナイになりやすい) tokio くらいちゃんと使えるようになろうと思います。

2025年に読んでよかった本

前回に続いて、今年読んでよかったなと思った本をいくつか。

医療関係

病院という組織の動きと未来を知るうえで「病院経営の教科書」が良かったです。3版まででていて、定番として信頼されているのでしょうね。実際にはヒトが占めるウェイトがかなり大きいとので現地訪問必須とは思っているんですが、現地訪問時の観察眼や質問を研ぎ澄ませるためにもこういう知識は有効だろうと思いました。

エンジニアリングで地域医療を支えるうえでその構造を理解することが重要なのは疑いないことですが、「地域医療の経済学」は今ある構造や課題だけでなく解決の提案も含まれていて面白かったです。その解決が日本の未来かというとたぶん違うんだろうなぁと思いつつ、かかりつけ医の機能を日本の医療でどう実現していくか?は間違いなく今後のアツい課題のひとつだと思うので継続的に考えていきたいです。

自分には機序が明確な印象のある西洋医学に対して、東洋医学にはあんま根拠が明確でない印象があります。自分は昔読んだインタビューマンガの「絶望に効くクスリ」で東洋医学を研究してエビデンスを作っていく動きがあることは知っていたのですが、このブルーバックス「東洋医学はなぜ効くのか」を読むことでその精度がかなり高まっていて納得できる範囲がかなり広くなっていると感じました。 鍼も漢方も、ここまで判明させるのはかなり大変だったろうなと門外漢でも容易に想像できるところです。特に漢方についてはかなり体質によるという点と、生薬の流通・管理に課題があるとは個人的に感じているところなので、体内で作られる成分そのものの精製や関連する腸内菌とセットで服用できるような工夫が今後一般的になると良いなと思います。

その他

図書館で読んだ古めの本です。自分は社会課題解決をする破壊的イノベーションをもたらすスタートアップで働きたいと思っているヒトなのですが、この本を読んで事業そのものが社会課題解決になるスキームそのものがかなり新しいのかもしれないと思いました。というのもこれに掲載されているのは、従業員に対する福利厚生や売上を利用した寄付を用いて社会課題解決に繋げている話なんですよね。もちろんチョコレート製品という食品を通じて社会貢献していたことは想定されますが、カカオ生産に様々な問題があったことは知られているとおりなので、色々と考えさせられました。

「誰が書いたコードか当てまShow!!」出題者による解説

勤務先のブログで紹介されていますが、Kotlin Fest 2025で出題したクイズに向けてKotlinコードを書いていました。本記事では自分で自分のコードを解説してみます。企画のネタバレを含みますので、先に企画紹介記事を読んでいただければ幸いです。

ヒントは「メソッドチェーン」「Kotlinのプロ、じゃない」

まず自分のコードを再掲します。パッと見でメソッドチェーン風だということがわかり、リアクティブプログラミングの経験があるひとにはAっぽいなと伝わる雰囲気を醸し出しています。

fun solve(input: String): String {
    var prevScore: Int? = null
    var prevRank = 1
    return input.split("\n")
        .map { it.split(" ") }
        .groupBy({ it[0] }, { it[1].toInt() })
        .mapValues { it.value.sum() }
        .toList()
        .sortedWith(compareByDescending<Pair<String, Int>> { it.second }.thenBy { it.first })
        .mapIndexed { index, (name, score) ->
            val rank = if (prevScore == null || prevScore > score) {
                index + 1
            } else {
                prevRank
            }

            (rank to "$name $score").also {
                prevScore = score
                prevRank = rank
            }
        }.takeWhile { it.first <= 3 }
        .joinToString("\n") {
            "${it.first}. ${it.second}"
        }
}

ひとつずつ見ていきましょう。まず目につくのが変数名です:

fun solve(input: String): String {
    var prevScore: Int? = null
    var prevRank = 1

prev は previous の短縮形です。このくらいなら短縮せずに previousScore としたほうが良いかもしれませんね。このくらいの変数なら特にコメントで意図を説明する必要はないかなと思って、ドキュメンテーションコメントは何も書いていませんでした。

なおBのコードでは previous ではなく last が使われており、どちらが良い命名なのかを議論するのも面白そうです。

さて次の行が大きなヒントになっています:

    return input.split("\n")

これとてもJavaっぽい書き方で、実際他の方の回答だと lineSequence() が使われているんですよね。ここで少なくとも「AはKotlinのプロであるタケハタのコードじゃないな」とバレるようになっています。なぜ lineSequence() を使わなかったのかって?だって、知 ら な い そ ん な 函 数!勉強になりました。

なおここで「OSがCRLFのような他の改行コードで動いてたらどうするんだ」ってツッコミは可能なんですが、この手の出題でそこが問題になることはないだろうと判断しました。厳密には System.lineSeparator() あたりを参照したほうが良いですね。

ちなみにJavaで似たような問題を解くと必ずと言っていいほど Scanner が出てくるんですが、さすがKotlin、流れるように処理を書き下せました。便利。

さて次のコードです:

        .map { it.split(" ") }
        .groupBy({ it[0] }, { it[1].toInt() })
        .mapValues { it.value.sum() }
        .toList()
        .sortedWith(compareByDescending<Pair<String, Int>> { it.second }.thenBy { it.first })

ここはまぁ、普通ですね。 Pair を引き回すと可読性が落ちるので、Dのようにクラスを定義して使うのも良さそうですが、今回はまぁいいでしょという気持ちになりました。今見返すと、 .groupBy({ it[0] }, { it[1].toInt() }) がだいぶ厳しいか。

あと compareByDescending() については、3分くらい「GuavaならOrderingなんだけどな〜」って検索してました。無事に便利函数が見つかって良かったです。

さて次は誰からも突っ込まれなかったコードです:

            (rank to "$name $score").also {
                prevScore = score
                prevRank = rank
            }

この also の使い方、だいぶ独特な感じに仕上げたつもりなんですが、見事にスルーされました。クリーンアップ処理を他のコードと違う場所にまとめて書ける、かつ finally と比較したときに例外の発生を想定してないことを伝えられる、ということでわりと悪くない書き方だと思うんですけどね。

最後はこのコードです:

        }.takeWhile { it.first <= 3 }
        .joinToString("\n") {
            "${it.first}. ${it.second}"
        }
}

お題を見たときに takeWhile() が出てくることは決めていました。一番最初に書けたコードだとも言えますね。あとは特に変わったところはなく、普通に joinToString() して終わりです。

他の人のコードへのツッコミどころ

ということで、以上で出題者による解説でした。楽しんでいただけたでしょうか。

なお他の人のコードにもツッコミどころというか、個性を感じるところは多々あります。ぜひ皆さんも考えてみてください。私が思ったのは次のような感じでしたが、人によって意見は異なると思いますので、ご参考まで:

  • 名前つき引数、いい。わかりやすい。IDEAで書いてるとなかなか気付けない気配りポイント。印刷前提だったので、自分も書いたほうが良かった。
  • String.split(String) は正規表現のコンパイルが走る可能性があるので、ループ内では呼ばないクセをつけたほうが良いかも
  • return@foobar は可読性が落ちるので、個人的には避けたい
  • tailrec すごくいい
  • コメントがいちいち意味ない!(たぶんツッコミ待ちなんだけど気になる)
  • is-a と has-a は違うんですよ!(たぶんツッコミ待ちなんだけど気になる)
  • 函数の引数の型はできるだけ抽象に寄せたいなぁ、なんで HashMap なんだっけ……(たぶんツッコミ待ちなんだけど気になる)