<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>honb.in</title><description>Satoshi Honda のブログ</description><link>https://honb.in/</link><atom:link href="https://honb.in/rss.xml" rel="self" type="application/rss+xml"/><item><title>テストが通ることと、修正が正しいことは別</title><link>https://honb.in/blog/verify-before-fixing/</link><guid isPermaLink="true">https://honb.in/blog/verify-before-fixing/</guid><description>OSS に不具合修正の PR を出そうとして、実装もテストも用意した。ビルドが通ることも確認した。</description><pubDate>Mon, 19 Oct 2026 05:06:13 GMT</pubDate><content:encoded>&lt;p&gt;OSS に不具合修正の PR を出そうとして、実装もテストも用意した。ビルドが通ることも確認した。&lt;/p&gt;
&lt;p&gt;ところが、あとから調べ直すと、修正そのものが不要だった。それどころか、仮に採用されていたら、別の問題を隠してしまう可能性もあった。&lt;/p&gt;
&lt;h2 id=&quot;ビルドエラーを直そうとした&quot;&gt;ビルドエラーを直そうとした&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/voidzero-dev/void&quot;&gt;Void&lt;/a&gt; というフレームワークで、Markdown だけで構成された静的サイトをビルドすると、クライアントビルドで &lt;code&gt;UNRESOLVED_ENTRY&lt;/code&gt; が発生する、という問題に取り組んだ。
原因を調べると、このケースではクライアントビルドの入力が空になっていた。一方、クライアントビルド自体は静的ページの prerender に必要なので、ビルド処理は実行される。&lt;/p&gt;
&lt;p&gt;そこで、入力が空の場合にだけ、空の仮想モジュールをエントリーとして追加する方法を考えた。実装を追加し、Markdown だけの playground と integration test も用意した。既存のクライアント入力がある場合には影響しないことも確認する方針にした。
修正の説明も書き、あとは PR を出すだけ、というところまで進んだ。&lt;/p&gt;
&lt;h2 id=&quot;検証した環境と対象ブランチのずれ&quot;&gt;検証した環境と対象ブランチのずれ&lt;/h2&gt;
&lt;p&gt;最初に問題になったのは、検証した環境と PR の対象ブランチが一致していなかったことだ。
手元で確認していたのは、公開済みの Void のパッケージに修正相当の変更を加えた環境だった。PR の対象ブランチそのものではなかった。&lt;/p&gt;
&lt;p&gt;さらに調べると、対象ブランチでは、関連する問題がすでに上流の変更によって解消されていた。つまり、このブランチで修正が必要であることを確認できていなかった。&lt;/p&gt;
&lt;p&gt;手元の環境で仮想モジュールが解決され、ビルドが成功すること自体は事実だった。問題は、その結果を、修正が必要であることの根拠として扱ってしまったことだった。検証した環境で何が確認できたのかと、対象ブランチで何が必要なのかは、別の話だった。&lt;/p&gt;
&lt;h2 id=&quot;空のエントリーが代替できなかったもの&quot;&gt;空のエントリーが代替できなかったもの&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/voidzero-dev/void/pull/481&quot;&gt;上流の実装&lt;/a&gt;を確認すると、修正の捉え方自体にも問題があることが分かった。
既存の変更は、単にクライアントビルドの入力を用意するためのものではなかった。アイランドページや Markdown ページからコンポーネント経由で参照される CSS を収集し、HTML にリンクする役割も担っていた。&lt;/p&gt;
&lt;p&gt;一方、自分が追加しようとしていたのは、何も import しない空のモジュールだった。これなら空入力によるビルドエラーは回避できるが、本来のクライアントエントリーが担っていた CSS の収集まで代替できるわけではない。
仮にこの修正が採用されていたら、ビルドは成功しても、必要な CSS が成果物に含まれない可能性があった。表面上のエラーを解消することと、フレームワークが必要とする成果物を正しく生成することを、同じものとして扱ってしまっていた。&lt;/p&gt;
&lt;p&gt;本来確認すべきだったのは、入力が空になるという現象だけではない。クライアントエントリーがなぜ必要で、どのような役割を担っているのか、ということだった。&lt;/p&gt;
&lt;h2 id=&quot;テストが緑であることは何を保証するか&quot;&gt;テストが緑であることは何を保証するか&lt;/h2&gt;
&lt;p&gt;テストで確認できたのは、仮想モジュールの &lt;code&gt;resolveId&lt;/code&gt; と &lt;code&gt;load&lt;/code&gt; が動作すること、Markdown だけのサイトがビルドされて HTML が生成されること、そしてページからクライアント JavaScript が読み込まれないことだった。
どれも事実として意味がある。ただし、これらはこの修正が必要であることや、成果物が本来の要件を満たしていることまでは証明しない。&lt;/p&gt;
&lt;p&gt;テストは、設定した条件を満たすかどうかを確認するものだ。その条件が問題の本質を捉えていなければ、テストが緑でも、間違った修正を正しいと思い込むことがある。
ビルド成功とゼロ JS は確認した。しかし、クライアントエントリーが担う CSS の収集という役割は、テストの確認条件に含まれていなかった。テストが通ることと、問題が解決していることの間には、まだ確認すべきことが残っていた。&lt;/p&gt;
&lt;h2 id=&quot;ai-と開発して自分で判断すること&quot;&gt;AI と開発して、自分で判断すること&lt;/h2&gt;
&lt;p&gt;今回の作業では、AI を使って調査や実装を進め、テストを考え、PR の説明を整理した。作業を進めるうえで役に立った一方、検証結果から何を結論づけられるかについては、自分で判断する必要があった。&lt;/p&gt;
&lt;p&gt;失敗は、テストが足りなかった、というだけの話ではない。テストは通り、仮想モジュールの読み込みやビルドが成功することも確認できていた。それでも、そのモジュールが本来の役割を代替できるのか、という視点が抜けていた。&lt;/p&gt;
&lt;p&gt;最終的に、修正本体は取り下げることにした。&lt;/p&gt;
&lt;p&gt;何を確認できたのかと、何が正しいと言えるのか。その間には、まだ自分で考えなければならないことが残っていた。&lt;/p&gt;
</content:encoded></item><item><title>開発の過程を、あとから振り返る</title><link>https://honb.in/blog/dtrail/</link><guid isPermaLink="true">https://honb.in/blog/dtrail/</guid><description>AIと開発していると、実装だけでなく、設計の検討や問題の調査など、さまざまなやり取りがセッション履歴に残る。 コードやコミットを見れば変更内容は追えるけれど、なぜその方針にしたのか、どんなことを検討したのかは、あとから確認しにくいことがある。</description><pubDate>Sat, 17 Oct 2026 06:18:43 GMT</pubDate><content:encoded>&lt;p&gt;AIと開発していると、実装だけでなく、設計の検討や問題の調査など、さまざまなやり取りがセッション履歴に残る。
コードやコミットを見れば変更内容は追えるけれど、なぜその方針にしたのか、どんなことを検討したのかは、あとから確認しにくいことがある。&lt;/p&gt;
&lt;p&gt;こうした開発の過程を、あとから振り返りやすくしたいと思った。&lt;/p&gt;
&lt;p&gt;そこで、Claude Code と Codex のセッション履歴をまとめて読める TUI、&lt;a href=&quot;https://github.com/honbin/dtrail&quot;&gt;dtrail&lt;/a&gt; を作った。
セッション一覧と会話を並べて表示し、過去のやり取りを読み返せる。&lt;/p&gt;
&lt;p&gt;その後、Cursor のセッション履歴にも対応した。&lt;/p&gt;
&lt;p&gt;何かを検索して答えを見つけるというより、セッションをざっと眺めながら、そのとき何を考え、どう進めていたのかを振り返る。
そんな使い方を想定している。&lt;/p&gt;
</content:encoded></item><item><title>個人サイトとアイコンを新しくした</title><link>https://honb.in/blog/site-and-icon-renewal/</link><guid isPermaLink="true">https://honb.in/blog/site-and-icon-renewal/</guid><description>以前から使っていた個人サイト honb.in を作り直した。 もともとはプロフィールといくつかのリンクを載せただけのシンプルなサイトで、長い間ほとんど手を入れていなかった。 今回はプロフィールを整理するのとあわせて、ブログもこのサイトに移すことにした。</description><pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以前から使っていた個人サイト &lt;a href=&quot;https://honb.in/&quot;&gt;honb.in&lt;/a&gt; を作り直した。
もともとはプロフィールといくつかのリンクを載せただけのシンプルなサイトで、長い間ほとんど手を入れていなかった。
今回はプロフィールを整理するのとあわせて、ブログもこのサイトに移すことにした。&lt;/p&gt;
&lt;h2 id=&quot;自分の記録を置く場所&quot;&gt;自分の記録を置く場所&lt;/h2&gt;
&lt;p&gt;これまで記事は、はてなブログに書いていた。
自分にとってブログは、考えたことや試したことを残しておいて、あとから振り返るための場所でもある。
はてなブログには長くお世話になっているけれど、以前の記事を読み返すときに、どうしても広告が気になるようになってきた。
せっかく自分の記録を残すなら、もう少し落ち着いて読み返せる場所に置いておきたい。
それで今回、以前からあった個人サイトを作り直すのにあわせて、ブログも自分のサイトに置くことにした。
直近の記事は新しいサイトに移して、はてなブログ側は下書きに戻した。古い記事はそのまま残して、これからは新しいサイトに書いていくつもり。&lt;/p&gt;
&lt;h2 id=&quot;astroで作り直す&quot;&gt;Astroで作り直す&lt;/h2&gt;
&lt;p&gt;サイトの構築にはAstroを使った。他にもいろいろ選択肢はありそうだったけれど、ひとまずこれにした。
今回必要だったのは、プロフィールを表示するページと、Markdownで記事を公開する仕組みくらい。動的な機能は特に必要ないので、静的なHTMLを生成できれば十分だった。
AstroはHTMLに近い形でページを書けるし、Markdownの記事も扱いやすい。
CSSもできるだけシンプルにした。凝ったデザインにするより、文章が読みやすくて、必要なリンクが見つかればいい。
細かい余白や見出しの扱いは調整したけれど、基本的には余計なものを増やさない方針にした。&lt;/p&gt;
&lt;h2 id=&quot;プロフィールも整理する&quot;&gt;プロフィールも整理する&lt;/h2&gt;
&lt;p&gt;プロフィールも、これまでの経験を振り返りながら、これからどんな仕事をしていきたいかを踏まえて書き直した。
バックエンドを中心に、必要に応じてフロントエンドやインフラにも取り組んできたこと。
新しい機能を作るだけでなく、サービスが使われ続ける中で見つかる課題を調べたり、改善したりする仕事が好きなこと。
これまでの経験を整理しつつ、今後の活動にもつながるような内容にした。&lt;/p&gt;
&lt;h2 id=&quot;アイコンも新しくする&quot;&gt;アイコンも新しくする&lt;/h2&gt;
&lt;p&gt;長く使っていたプロフィール画像も変えることにした。
以前の顔写真には馴染みもあって少し迷ったけれど、そろそろ変えてみたいと思っていた。
まったく別のものにするのではなく、元の写真から色を取り出して、幾何学的なアイコンにしてみた。
赤橙、茶色、ベージュ、クリーム色。顔の輪郭や表情はなくなったけれど、色だけは残っている。
形や色の配置をいくつか試して、中心を少し上にずらしたデザインに落ち着いた。
できあがったアイコンを眺めていると、夕方の光が差し込むようにも、折り紙を組み合わせたようにも見えてくる。
暖色の柔らかさと、直線で区切られた幾何学的な形。中心が少し上にあることで、どこか動きも感じられる。
最初からそういう意味を込めて作ったわけではないけど、眺めているうちに、そんなふうにも見えてきた。
以前の画像から何かを残しながら、新しいものにできたのはよかったと思う。&lt;/p&gt;
&lt;p&gt;これからは、この場所に少しずつ記録を残していこうと思う。&lt;/p&gt;
</content:encoded></item><item><title>AIとの対話を振り返って、改善の種を探す</title><link>https://honb.in/blog/reviewing-ai-sessions/</link><guid isPermaLink="true">https://honb.in/blog/reviewing-ai-sessions/</guid><description>最近、Claude Codeとの対話を振り返るための小さなスキルを作って試している。</description><pubDate>Sun, 11 Oct 2026 03:36:31 GMT</pubDate><content:encoded>&lt;p&gt;最近、Claude Codeとの対話を振り返るための小さなスキルを作って試している。&lt;/p&gt;
&lt;p&gt;普段、AIは目の前の問題を解決するのに便利だけれど、作業が終わると、そのとき何をしていたかをあまり振り返らずに次へ進んでしまう。 でも、仕事をしていると、同じような確認や調査を何度も繰り返していることがある。&lt;/p&gt;
&lt;p&gt;以前、ログを使った調査で、取得したデータが意図した範囲をカバーしているか、何度も確認していたことがあった。そこを少し楽にできないかと考え、取得結果を検査する小さなツールを作った。 一方、最近はAIに作業を任せることも増え、以前なら面倒に感じていた繰り返しも、それほど負担ではなくなっている。でも、負担が減ったことで、同じような作業を繰り返していること自体に気づきにくくなっているのかもしれない。 だったら、AIとの対話を後から振り返ることで、そういう繰り返しも拾えるんじゃないか。&lt;/p&gt;
&lt;p&gt;作ったスキルはシンプルで、現在のセッションを振り返り、その中で繰り返していた具体的な作業を拾い出す。 何度も確認していたことや、同じような調査・比較を繰り返していたことなどが対象になる。 そこから、何か改善できることはないかを考える。 ただし、AIに改善案を自由に考えてもらうわけではない。実際の対話に根拠がある作業だけを対象にして、次の3つを分けるようにした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Observed&lt;/strong&gt; — 実際に繰り返していた作業&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inferred&lt;/strong&gt; — その背後にありそうな問題&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opportunity&lt;/strong&gt; — そこから考えられる改善の問い&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;改善の問いは最大3件。根拠がなければゼロ件でもいい。 また、繰り返しているからといって、必ずツール化するわけではない。既存の方法で十分なこともあるし、仕組みを増やすことでかえって複雑になることもある。 今のところ自動実行はせず、調査やレビューが一区切りしたところで手動で呼び出している。&lt;/p&gt;
&lt;p&gt;まず、小さな開発ツールを作っていたセッションを振り返った。 整形チェックやテストの実行、編集後の再インストール、公開前の確認など、繰り返していた作業はいくつか見つかった。 その中から、公開前の確認をもう少し機械的にできないか、という問いが出てきた。 ただ、これはもともと意識して確認していたことでもある。今の手作業でもそれほど困っていないし、仕組みを作ることで確認漏れがなくなるわけでもない。 改善の問いは出たものの、今すぐ何かを作りたいとは思わなかった。&lt;/p&gt;
&lt;p&gt;次は、ログを使った調査のセッションを振り返った。 こちらでは、もう少し具体的な改善の問いが見つかった。 一つは、データを取得する際に、取得結果が十分かどうかを確認し、必要に応じて取得条件を調整する作業。 もう一つは、以前と同じ条件で調査をやり直すために、取得条件を手作業で組み立て直していたことだった。 どちらも、調査そのものではなく、その準備や確認に関わる作業だった。 そこから、取得結果に応じた作業をもう少し簡単にできないか、同じ調査条件を再利用できないか、という問いが出てきた。 一つツールを作って終わりではなく、作業を振り返ることで、その周辺に残っている別の改善機会が見えてきた。&lt;/p&gt;
&lt;p&gt;もう一つは、UIの見た目や動きを比較するために、単体のモックを作っていたセッション。 候補を追加するたびに、複数の箇所を手で揃えたり、編集後の確認作業を繰り返したりしていた。 そこから、候補の定義を一箇所にまとめることで、試行錯誤をもう少し楽にできないか、という問いが出てきた。 これは独立したツールを作るほどの話ではないかもしれない。モックの構造を少し変えるだけでも、改善できる可能性がある。 一方、実際に起きた問題でも、繰り返した根拠が弱いものはあった。そうしたものは、今回の目的とは少し違うので、いったん保留にした。&lt;/p&gt;
&lt;p&gt;異なるセッションで試してみて、繰り返していた作業から改善の問いがいくつか見つかった。 ただ、繰り返しているからといって、必ず改善する必要があるとは思っていない。今のままで十分なこともあるし、仕組みを増やすことでかえって複雑になることもある。AIには改善の問いを考えるところまで手伝ってもらい、実際に取り組むかどうかは自分で判断する。&lt;/p&gt;
&lt;p&gt;まだ試した回数は少なく、見つかった問いが実際の改善につながるかは分からない。 今は一つのセッションを振り返っているだけだけれど、いずれは複数のセッションで見つかった改善の種を持ち寄って、共通する問題がないかも探してみたい。 しばらく、この使い方を続けてみようと思う。&lt;/p&gt;
</content:encoded></item><item><title>集計の前提を確かめる</title><link>https://honb.in/blog/verifying-aggregates/</link><guid isPermaLink="true">https://honb.in/blog/verifying-aggregates/</guid><description>ログを取得して集計するとき、計算自体は正しくても、その前提になるデータや集計方法によって、結果の解釈が変わってしまうことがある。 最近の分析で気になったことが2つあったので、それぞれ小さなツールとして切り出してみた。</description><pubDate>Sat, 10 Oct 2026 09:44:15 GMT</pubDate><content:encoded>&lt;p&gt;ログを取得して集計するとき、計算自体は正しくても、その前提になるデータや集計方法によって、結果の解釈が変わってしまうことがある。 最近の分析で気になったことが2つあったので、それぞれ小さなツールとして切り出してみた。&lt;/p&gt;
&lt;h2 id=&quot;取得したデータを確認する&quot;&gt;取得したデータを確認する&lt;/h2&gt;
&lt;p&gt;障害調査などでCloudWatch Logs Insightsからログを取得するとき、結果が10,000件の上限に達していないかを、その都度確認していた。 指定した時間範囲のログを取得しても、その範囲のデータがすべて取れているとは限らない。件数の上限に達していれば、クエリのソート順によっては、時間範囲の一部に偏っている可能性もある。 そこで、Rustで作っている &lt;a href=&quot;https://github.com/honbin/tskit&quot;&gt;tskit&lt;/a&gt; に &lt;code&gt;window-audit&lt;/code&gt; を追加した。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; .time&lt;/span&gt;&lt;span&gt; alb.jsonl&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  incident-window-audit&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    --from&lt;/span&gt;&lt;span&gt; 2026-09-03T10:20:00+09:00&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    --to&lt;/span&gt;&lt;span&gt; 2026-09-03T10:30:00+09:00&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    --limit&lt;/span&gt;&lt;span&gt; 10000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定した時間範囲と、実際に観測された最初と最後の時刻を比較し、両端の差や取得件数を表示する。 例えば、入力件数が指定した上限に達していれば &lt;code&gt;LIMIT_REACHED&lt;/code&gt; を出す。 ただし、上限に達したからといって、実際にデータが打ち切られたとは限らない。逆に、観測範囲が指定した時間範囲の両端に届いていても、途中のデータがすべて取得できている保証はない。 あくまで取得したデータから確認できることを表示して、原因の判断はクエリの条件やソート順と合わせて考える。&lt;/p&gt;
&lt;h2 id=&quot;集計単位を確認する&quot;&gt;集計単位を確認する&lt;/h2&gt;
&lt;p&gt;もう一つは、イベントログをセッション単位で集計していたときのこと。 セッションという単位は、もともとのイベントログに存在しているわけではない。一定時間イベントがなければ別のセッションとして扱う、というルールをこちらで決めている。 例えば、30分空いたら別のセッションとするのか、90分空いたら別のセッションとするのか。それによってセッション数も変わるし、セッションを分母にした指標も変わってくる。&lt;/p&gt;
&lt;p&gt;そこで、セッション化の処理を &lt;a href=&quot;https://github.com/honbin/sessionize&quot;&gt;sessionize&lt;/a&gt; というCLIツールとして切り出した。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sessionize&lt;/span&gt;&lt;span&gt; --key&lt;/span&gt;&lt;span&gt; user_id&lt;/span&gt;&lt;span&gt; --time&lt;/span&gt;&lt;span&gt; timestamp&lt;/span&gt;&lt;span&gt; --gap&lt;/span&gt;&lt;span&gt; 30m&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; events.tsv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;キーとタイムスタンプを持つイベントに対して、指定したアイドルギャップに基づいてセッションIDを付ける。 入力はTSVで、出力もTSV。元の行順を維持したまま &lt;code&gt;session_id&lt;/code&gt; 列を追加する。やっていることはそれだけ。 例えば、アイドルギャップを30分から90分に変えて、それぞれの結果を集計すれば、セッションの区切り方によって数字がどのくらい変わるのかを確認できる。 もちろん、どちらの区切り方が正しいかを、このツールが判断するわけではない。指定したルールに従ってセッションIDを付けるだけで、集計や比較は後段の処理に任せている。&lt;/p&gt;
&lt;p&gt;今回作った2つのツールは、どちらも集計結果の正しさを保証するものではない。 取得したデータがどの時間範囲を表しているのか。イベントをどんなルールで区切っているのか。 集計された数字を見る前に、そうした前提を確認するためのもの。&lt;/p&gt;
&lt;p&gt;やっていることは単純だけれど、実際の分析で使いながら、少しずつ検証していきたい。&lt;/p&gt;
</content:encoded></item><item><title>サービスを育てるのが好きなんだと思う</title><link>https://honb.in/blog/growing-a-service/</link><guid isPermaLink="true">https://honb.in/blog/growing-a-service/</guid><description>年月が経つのは早く、あっという間に時間がすぎる。</description><pubDate>Tue, 06 Oct 2026 12:14:42 GMT</pubDate><content:encoded>&lt;p&gt;年月が経つのは早く、あっという間に時間がすぎる。&lt;/p&gt;
&lt;p&gt;最近はAIの進化も早い。できることがどんどん増えて、自分自身もかなり使うようになった。以前なら時間をかけて調べたり、考えたり、実装していたことが、驚くほど短い時間でできるようになった。 便利になった一方で、今後自分がやっている仕事はどうなっていくんだろう、という不安も少しある。&lt;/p&gt;
&lt;p&gt;コードを書くことがなくなるとは思っていないけれど、これまでと同じように技術を身につけて、コードを書けるようになっていけばいい、というだけでもない気がしている。 そんなことを考えながら、最近はいろいろなものを作ったり、新しい技術を触ったりしている。 何か次につながるものがないかな、と少し模索しているところもある。 ただ、次に何をやりたいのかを改めて考えてみると、まったく新しいことを探したいわけでもないことに気づいた。&lt;/p&gt;
&lt;p&gt;自分はやっぱり、サービスを育てることが好きなんだと思う。 新しい機能を作るのも好きだけど、作って終わりではなく、その後も見ていたい。 実際にユーザーに使われて、使いにくいところがあれば直す。問題が起きれば原因を調べる。運用して初めて見えてきた課題があれば改善する。 ときには仕様を見ながら、そもそもこれでいいんだろうか、と考える。 そうやって一つのサービスに関わりながら、少しずつ良くしていく仕事が、自分は好きなんだと思う。&lt;/p&gt;
&lt;p&gt;振り返ってみると、自分は昔から一つの技術だけをひたすら掘るタイプではなかった。 フロントエンドもやるし、バックエンドもやる。必要ならインフラも見る。 その時々で問題になっているところに入っていって、どうすればよくなるかを考える。 以前は、そうやっていろいろなことをやってきたことを、「一つの専門に特化していない」と少し弱みに感じていた。 でも最近は、それもそんなに悪くないのかもしれないと思っている。&lt;/p&gt;
&lt;p&gt;特にAIによって実装すること自体のハードルが下がっていくなら、どの技術を使えるかだけではなく、何が問題なのかを見つけて、必要なものを使って解決していくことは、これまで以上に大事になるのかもしれない。 何をAIに任せるのか。何を自分で確かめるのか。何を作るのか。そして、作ったものが本当に良くなったのか。 そういうことを考えながら仕事をするのは、やっぱり面白い。 だから次のキャリアを考えるときも、「AIに代替されない仕事は何だろう」と考えるより、自分がこれからもやっていたいことを軸にしたい。 次の現場はまた、自社サービスの開発に重きを置いて探してみたいと思っている。&lt;/p&gt;
</content:encoded></item><item><title>AIに判断させる前に、事実を取り出す</title><link>https://honb.in/blog/extract-facts-first/</link><guid isPermaLink="true">https://honb.in/blog/extract-facts-first/</guid><description>最近、AIが書いたコードをどう理解するかについて、何本か記事を書いた。</description><pubDate>Tue, 06 Oct 2026 05:58:32 GMT</pubDate><content:encoded>&lt;p&gt;最近、AIが書いたコードをどう理解するかについて、何本か記事を書いた。&lt;/p&gt;
&lt;p&gt;AIは日常的にかなり使っている。設計について相談したり、コードをレビューしてもらったり、知らないことを調べてもらったりしている。 一方で、少し前から、AIに頼りすぎているんじゃないかという感覚もあった。要件を伝えて、設計してもらって、実装してもらって、レビューまでしてもらう。便利ではあるけれど、全部をそのまま渡していると、自分がどこで考えたのかが分からなくなる。それで最近は、少し使い方を変えている。&lt;/p&gt;
&lt;p&gt;こういう考え方の種は、以前からあったのかもしれない。障害調査では、想像で数値を埋めず、まずログやメトリクスから事実を取ることを大事にしていた。 そのあと、Rustの勉強をきっかけに、調査で使う小さなツール群を作った。 ログの発生数を数えたり、時間で切り出したり、2つの時系列が近い時間に発生しているかを調べたり、複数の時系列を同じ時間軸に並べたりする。 作っていて気づいたのは、これらのツールは原因を判断してくれないということだった。「CPUが上がったからエラーが増えた」とは言わない。ただ、いつ何が起きたのかを数えたり、揃えたり、並べたりする。事実を見やすくするだけだ。&lt;/p&gt;
&lt;p&gt;そのあと、コードレビューでも似たようなことを考えるようになった。 大きなdiffをAIに渡して「重要なところを教えて」と聞くことはできる。でも、その前に、変更されたシンボルは何か、参照箇所はいくつあるか、分岐がどれくらい増えているか、といったことは機械的に調べられる。 そこで、git diffからそういう情報だけを取り出す小さなツールを作り始めた。ここでも、ツールには「この変更は危険だ」と判断させないことにした。どこから読むかを決めるための材料だけを出す。 &lt;a href=&quot;https://honb.in/blog/reading-large-diffs/&quot;&gt;前の記事&lt;/a&gt;では、これを &lt;code&gt;signals → prioritize → interrogate&lt;/code&gt; と書いた。コードレビューのために考えた流れだったけれど、もう少し広く捉えると、自分のAIの使い方にも同じことが言えそうだった。 大事なのは、どこまでが観測された事実で、どこからが解釈なのかを見失わないことだと思う。&lt;/p&gt;
&lt;p&gt;AIは、曖昧な情報から意味を考えたり、複数の可能性を出したりするのが得意だ。でも、何件あったのか、どこが変わったのか、どのシンボルが参照されているのか、といったことまで推論してもらう必要はない。 機械的に取れるものは、小さなプログラムで取り出せばいい。もちろん、そこで取れる事実も完全ではない。実際、参照数が名前の一致を拾いすぎたり、変更していない関数を変更箇所として扱ってしまったこともあった。だから、出てきたものをそのまま正解とはせず、必要なら元のコードやログに戻って確かめる。 全部を自動化したいわけではない。人間が判断するために必要な事実を、できるだけ安く取り出したい。その面倒なところを小さなツールに任せる。AIには、その先を一緒に考えてもらう。&lt;/p&gt;
&lt;p&gt;Rustの勉強をきっかけに始めた小さなツール作りだったけれど、思っていたより広い話になった。 AI時代にどういう技術を身につければいいのか、まだよく分からない。でも少なくとも今は、 事実を取り出す道具を作り、その事実をもとにAIと考え、最後は自分で判断する。 このくらいの距離感が、自分にはちょうどよさそうだと思っている。&lt;/p&gt;
</content:encoded></item><item><title>大きなdiffを、どこから読むか</title><link>https://honb.in/blog/reading-large-diffs/</link><guid isPermaLink="true">https://honb.in/blog/reading-large-diffs/</guid><description>最近、AIが書いたコードをどう理解するか、ということを何度か書いていた。コードについて問い直す仕組みを作ってみたり、AIの説明をそのまま信じず、前提や失敗条件を確認するようにしたりしている。 ただ、実際の変更が大きくなると、その前に別の問題がある。そもそも、どこから読めばいいのか。</description><pubDate>Tue, 06 Oct 2026 04:23:20 GMT</pubDate><content:encoded>&lt;p&gt;最近、AIが書いたコードをどう理解するか、ということを何度か書いていた。コードについて問い直す仕組みを作ってみたり、AIの説明をそのまま信じず、前提や失敗条件を確認するようにしたりしている。 ただ、実際の変更が大きくなると、その前に別の問題がある。そもそも、どこから読めばいいのか。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git diff&lt;/code&gt; を上から全部読めばいいのだけど、変更が複数のファイルや関数に散らばっていると、それだけでもそれなりに負荷がかかる。かといって、AIに全部読ませて「重要なところだけ教えて」とすると、今度はその判断自体をAIに預けることになる。&lt;/p&gt;
&lt;p&gt;そこで、&lt;a href=&quot;https://github.com/honbin/change-signals&quot;&gt;change-signals&lt;/a&gt; という小さなCLIをRustで作った。 やっていることはかなり単純で、&lt;code&gt;git diff&lt;/code&gt; から変更されたシンボルを拾って、いくつかの粗い情報を付ける。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;src/cache/store.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  getOrFetch        refs=14, branch+5, cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  evictExpired      refs=6, branch+1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;src/db/records.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  loadRecord        refs=12, db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;refs&lt;/code&gt; はその名前がリポジトリ内に何行あるか、&lt;code&gt;branch&lt;/code&gt; は変更で増減した分岐の数、&lt;code&gt;cache&lt;/code&gt; などは境界らしき文字列が変更に含まれていたことを表している。&lt;code&gt;branch&lt;/code&gt; は符号つきで、減っていれば簡素化や移設の手がかりになる。 どれもかなり粗い。参照解析をしているわけではないし、構文を正確に解析しているわけでもない。リスクスコアも出さないし、「これは読まなくていい」とも言わない。 &lt;code&gt;git diff --stat&lt;/code&gt; でもファイルごとの変更量は分かる。ただ、そこはファイル単位で止まるし、行数しか見ていない。変更したシンボルがリポジトリのどこからどれだけ参照されているか、といったdiffの外側の情報までは出ない。そこを、粗くてもシンボル単位で足したかった。 目的は、変更を理解することではなく、どこから読むかを決める材料を作ることにした。&lt;/p&gt;
&lt;p&gt;考えている流れはこんな感じになる。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;signals → prioritize → interrogate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓           ↓             ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;事実を集める   読む順を決める   実際に理解する&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;途中、実際のコードに使ってみたら、変更していない関数に別の変更が紐づいてしまい、その関数を「読むべき場所」として選んでしまうことがあった。これはまずかった。 情報が粗いことより、違う場所へ誘導することの方が問題だった。 そこで関数の範囲を追跡するようにして誤った帰属は直した。一方で、細かい構文まで正確に解析するところまではやらなかった。そこまでやるなら、&lt;code&gt;Tree-sitter&lt;/code&gt;のようなちゃんとしたパーサーを使う話になってくる。 今回はそこには行かず、誤誘導は直す。粗さは残す。くらいで止めることにした。&lt;/p&gt;
&lt;p&gt;AIを使えば、コードの説明はいくらでも増やせるようになった。でも、説明が増えるほど理解できるとは限らない。全部を自動化するのではなく、自分が判断する場所は残したまま、読むための負荷だけを少し減らしたい。 &lt;code&gt;change-signals&lt;/code&gt; は、そのためのかなり小さな道具になった。 まだ v0.1.0 なので、しばらく実際の変更に使いながら試していこうと思う。&lt;/p&gt;
</content:encoded></item><item><title>コードを問い直すための仕組み</title><link>https://honb.in/blog/interrogate-skill/</link><guid isPermaLink="true">https://honb.in/blog/interrogate-skill/</guid><description>前の記事で、「AIが書いたコードに、自分は何を問えるか」という話を書いた。AIが書いたコードを理解するには、ただ読むだけではなく、「なぜこうなっているんだろう」と自分から問いを返す必要があるんじゃないかと。</description><pubDate>Mon, 05 Oct 2026 12:21:06 GMT</pubDate><content:encoded>&lt;p&gt;前の記事で、「&lt;a href=&quot;https://honb.in/blog/questioning-ai-code/&quot;&gt;AIが書いたコードに、自分は何を問えるか&lt;/a&gt;」という話を書いた。AIが書いたコードを理解するには、ただ読むだけではなく、「なぜこうなっているんだろう」と自分から問いを返す必要があるんじゃないかと。&lt;/p&gt;
&lt;p&gt;最近、そのための質問を &lt;code&gt;interrogate&lt;/code&gt; という小さなskillにまとめて使っている。きっかけになったのは、kawasimaさんの「&lt;a href=&quot;https://scrapbox.io/kawasima/Generation-then-comprehension&quot;&gt;Generation-then-comprehension&lt;/a&gt;」という記事だった。たとえば、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;このコードが前提としている条件は何か&lt;/li&gt;
&lt;li&gt;その前提は誰が保証しているのか&lt;/li&gt;
&lt;li&gt;前提が崩れたらどうなるのか&lt;/li&gt;
&lt;li&gt;もっと単純な実装ではだめなのか&lt;/li&gt;
&lt;li&gt;この条件分岐を消したら何が起きるのか&lt;/li&gt;
&lt;li&gt;正常系のテストだけでは見つからない問題は何か&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といったことを聞く。&lt;/p&gt;
&lt;p&gt;前にも触れたRustのコードでは、ある処理が「入力はソート済み」という前提で動いていた。実装を読むだけなら、それで終わる。でも、「なぜソート済みである必要があるのか」「誰がそれを保証するのか」「ソートされていなかったらどう壊れるのか」と聞いていくと、コードの見え方が変わってくる。コメントに書いておけば十分なのか。&lt;code&gt;debug_assert!&lt;/code&gt; を入れるのか。それとも型で表現するのか。&lt;/p&gt;
&lt;p&gt;ただ、質問の内容以上に意識したのが、AIにどう答えさせるかだった。「なぜこの実装にしたの？」と聞けば、AIは今ある実装をうまく説明してくれる。でも、それだけだと、あとからもっともらしい理由を付けているだけなのか、本当に他の選択肢より良かったのかが分からない。&lt;/p&gt;
&lt;p&gt;そこで、このskillでは「正当化ではなく反証から組む」というルールを入れた。今の実装が正しい理由を並べる前に、正しく動かない条件、採用を見送るとしたらどこか、もっと単純な方法で十分ではないか、テストで確認できていない前提はないか、という方向から考えてもらう。正しさを説明してもらうより、まず壊しにいく。&lt;/p&gt;
&lt;p&gt;もちろん、これで自分がコードを理解できるようになる、という話でもない。skillが質問して、AIが答えて、それを読んで「なるほど」で終わったら、結局やっていることはあまり変わらない。答えそのものより、そこで「本当に？」「じゃあこの入力なら？」「その前提はコードのどこにある？」と自分がもう一度引っかかれることのほうが大事なんだと思う。&lt;/p&gt;
&lt;p&gt;だからこのskillには、ライブラリの挙動や呼び出し箇所のように確認できるものは実際に確認すること、より単純な実装で十分ならそう認めること、自分が書いたコードを自分でレビューするときはそのバイアスを意識すること、といったルールも入れた。AIに説明させるためというより、AIの説明をそのまま信じないための仕組みに近い。&lt;/p&gt;
&lt;p&gt;以前、「AIに何を教えるべきか」を考えていたとき、AIに知識だけでなく「問い」を持たせるのがいいんじゃないかと書いた。その問いが、最近は少し違う使い方になってきた。AIに良いコードを書かせるためだけではなく、AIが書いたコードを自分が理解するためにも使う。 問いをskillにしたことで理解を自動化できるわけではない。むしろ、自動化しすぎて通り過ぎそうになるところで、一度立ち止まるための仕組みなのかもしれない。&lt;/p&gt;
</content:encoded></item><item><title>AIが書いたコードに、自分は何を問えるか</title><link>https://honb.in/blog/questioning-ai-code/</link><guid isPermaLink="true">https://honb.in/blog/questioning-ai-code/</guid><description>6月に「実装が速くなるほど、理解の価値は上がる」という記事を書いた。そのあと「AIに何を教えるべきか」で、AIに渡すのは知識と問いなんじゃないか、と考えていた。 最近になって、この二つが自分の中で少しつながってきた。</description><pubDate>Mon, 05 Oct 2026 08:22:19 GMT</pubDate><content:encoded>&lt;p&gt;6月に「&lt;a href=&quot;https://honb.in/blog/value-of-understanding/&quot;&gt;実装が速くなるほど、理解の価値は上がる&lt;/a&gt;」という記事を書いた。そのあと「&lt;a href=&quot;https://honb.in/blog/what-to-teach-ai/&quot;&gt;AIに何を教えるべきか&lt;/a&gt;」で、AIに渡すのは知識と問いなんじゃないか、と考えていた。 最近になって、この二つが自分の中で少しつながってきた。&lt;/p&gt;
&lt;p&gt;AIに細かいルールをたくさん与えるより、その背景にある知識を渡したほうがいい。「indexをkeyに使わない」みたいなルールだけではなくて、なぜそうするのかを伝える。さらに、知識を渡すだけではなく、「この要素のidentityは何だろう？」「この責務はここでいいんだろうか？」みたいな「問い」も大事なんじゃないか、と。 ルールは品質を安定させる。知識は判断を支える。問いは、その知識を引き出す。当時はそんなふうに考えていた。&lt;/p&gt;
&lt;p&gt;それから3か月くらい、かなりAIと一緒にコードを書いた。設計を相談して、そのまま実装してもらって、レビューして、また直してもらう。便利だし、速い。 ただ、AIが書いたコードを見て「なるほど、これでよさそう」と思っているけど、本当に自分はこのコードを理解しているんだろうか、と思うことが増えた。AIに良いコードを書いてもらうことと、自分がそのコードを理解することは、たぶん別なんだと思う。&lt;/p&gt;
&lt;p&gt;最近、Rustを勉強しながら小さなツールを書いている。 たとえば、ある関数が「入力はソート済み」という前提で動いていた。コードとしては正しく動いている。でも、なぜソート済みである必要があるんだろう。誰がそれを保証するんだろう。前提が崩れたらどうなるんだろう。コメントだけでいいのか。&lt;code&gt;debug_assert!&lt;/code&gt; くらい入れたほうがいいのか。それとも型で表現するのか。&lt;/p&gt;
&lt;p&gt;こうやって一度立ち止まると、AIが書いたコードが少しずつ自分の言葉になっていく感じがする。 以前の記事では「AIに何を問わせれば、より良いコードを書けるか」を考えていた。今は「AIが書いたコードに、自分は何を問えるか」を考えている。 前に書いたことが間違っていたとは思っていない。ただ、それだけでは足りなかった。&lt;/p&gt;
&lt;p&gt;最近は、要件や設計をAIと一緒に考えて、実装は一度自分で軽く書き、AIにレビューしてもらうくらいが自分には合っているのかもしれない。もちろん、AIに実装してもらうこともある。どちらが書いたかは、そんなに重要じゃない。 最終的に「なんでこうなっているの？」と聞かれたときに、自分の言葉で答えられるか。 書いてみると、ずいぶん当たり前の話をしている気もする。でも、AIがかなりのところまでコードを書いてくれるようになって、この当たり前を意識することが以前より難しくなった。&lt;/p&gt;
&lt;p&gt;AIに何を教えるかを考えていたら、いつの間にか、自分が何を問えるかを考えるようになっていた。&lt;/p&gt;
</content:encoded></item><item><title>ぜんぶ timestamp value にする</title><link>https://honb.in/blog/everything-timestamp-value/</link><guid isPermaLink="true">https://honb.in/blog/everything-timestamp-value/</guid><description>Rustで作った障害調査用の小さなツール群を、過去の障害を題材に試している。</description><pubDate>Sun, 04 Oct 2026 08:00:12 GMT</pubDate><content:encoded>&lt;p&gt;Rustで作った&lt;a href=&quot;https://github.com/honbin/tskit&quot;&gt;障害調査用の小さなツール群&lt;/a&gt;を、過去の障害を題材に試している。&lt;/p&gt;
&lt;p&gt;前回は &lt;code&gt;incident-timeline&lt;/code&gt; を使って、いくつかの時系列データを同じ時間軸に並べてみた。数字だけ眺めていたときより、かなり分かりやすかった。「ある時間帯にエラーが増えたとき、ほかの指標はどう動いていたか」といったことが、形として見える。 こういうグラフを見るだけなら、AWSのダッシュボードでもできる。 では、わざわざ手元で見るツールを作った意味はなんだろう。&lt;/p&gt;
&lt;p&gt;AWSのダッシュボードは、CloudWatchにあるメトリクスを見るのには当然よくできている。一方で、障害調査では、それ以外の数字も一緒に見たくなることがある。 たとえば、ログから数えた502の発生数。CloudWatch Logs Insightsで1秒ごとに数えた結果も、時系列データとして見れば、&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-01-01T10:00:00+09:00 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-01-01T10:00:01+09:00 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-01-01T10:00:02+09:00 3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;という、ただの &lt;code&gt;timestamp value&lt;/code&gt; になる。CPUも同じ形にできる。レイテンシもタスク数も同じ。 さらに、既存のデータから自分で計算した値も同じ形にできる。&lt;code&gt;incident-align&lt;/code&gt; で2つの系列を時刻で揃えて、&lt;code&gt;awk&lt;/code&gt; で計算する。その結果も &lt;code&gt;timestamp value&lt;/code&gt; にすれば、CloudWatchから取ってきたメトリクスと同じように扱える。 ログなのか、メトリクスなのか、自分で計算した値なのかは、そのあとのツールにとってはあまり重要ではない。 ぜんぶ &lt;code&gt;timestamp value&lt;/code&gt; の形で扱えばいい。&lt;/p&gt;
&lt;p&gt;振り返ると、これは途中で &lt;code&gt;incident-timeline&lt;/code&gt; の入力を種類ごとに分けず、全部 &lt;code&gt;--series&lt;/code&gt; にした判断ともつながっていた。取得元をツールが知る必要はない。必要なデータを手元に持ってきて、同じ形にしてから、切ったり、並べたり、計算したりする。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;window | burst&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;align | awk | series&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;障害調査では、「この時間だけ見たい」「この2つを揃えたい」「ここから別の値を計算したい」ということを何度も繰り返す。そういう作業には、小さな処理をパイプでつないでいく形が合いそうだと思っている。 もうひとつ、手元に持ってくることでできることがある。 調査に使った入力ファイルと、&lt;code&gt;incident-timeline&lt;/code&gt; が出力したHTMLを残しておけば、「そのとき何を見て判断したか」も残せる。これは生のログやメトリクスをずっと保存しておきたい、という話ではない。必要になったら元の場所からまた取ってくればいい。 でも、ひとつの障害を調べたときに使ったデータと、そのとき見ていたグラフは、ひとまとまりの成果物として残せる。&lt;code&gt;incident-timeline&lt;/code&gt; のHTMLをJavaScriptや外部のライブラリに依存しない形にしたのも、結果的にはここにつながっていた。&lt;/p&gt;
&lt;p&gt;こうして見ると、このツール群はAWSのダッシュボードを置き換えるものではなさそうだ。普段の観測は、AWSのダッシュボードでいい。 調査が必要になったときに、必要なデータを手元に持ってくる。出どころの違うデータを同じ形にして、切ったり、並べたり、重ねたりする。そして必要なら、そのとき見ていたものを残す。&lt;/p&gt;
&lt;p&gt;最初からこういう設計を考えていたわけではない。入力を &lt;code&gt;--series&lt;/code&gt; に揃えたり、HTMLをself-containedにしたり、表示する時刻だけoffsetを変えられるようにしたり、そのとき必要だったことをひとつずつ決めていった。 あとから並べてみると、それらが同じ方向を向いていた。 実際の障害調査で必要になったとき、どこまで使えるか試してみたい。&lt;/p&gt;
</content:encoded></item><item><title>完成してから、使う側に戻る</title><link>https://honb.in/blog/back-to-using-it/</link><guid isPermaLink="true">https://honb.in/blog/back-to-using-it/</guid><description>Rustで作っていた小さなツール群が、ひとまず完成した。</description><pubDate>Sun, 04 Oct 2026 07:10:50 GMT</pubDate><content:encoded>&lt;p&gt;Rustで作っていた&lt;a href=&quot;https://github.com/honbin/tskit&quot;&gt;小さなツール群&lt;/a&gt;が、ひとまず完成した。&lt;/p&gt;
&lt;p&gt;障害調査で使うためのもので、ログの発生数を眺めたり、時間で切り出したり、分布を見たり、複数の系列を並べたりする。最後に作った &lt;code&gt;incident-timeline&lt;/code&gt; は、たとえば502の発生数、CPU、レイテンシ、タスク数みたいな時系列データを、同じ時間軸に並べてHTMLにしてくれる。 過去の障害を題材に試してみると、数字だけ眺めていたときより、かなり分かりやすかった。「ある時間帯にエラーが増えたとき、ほかの指標はどう動いていたか」といったことが、形として見える。 よし、完成。 ……と思ったのだけど、使っていてひとつ面倒なことに気づいた。時刻がUTCだった。&lt;/p&gt;
&lt;p&gt;CloudWatchから取ってきたデータがUTCなので、ツールもそのoffsetをそのまま使って表示している。これは仕様としては正しい。ただ、自分が見たいのは日本時間なので、「05:45だから、14:45か」と毎回頭の中で9時間足していた。 地味だけど面倒だった。 そこで &lt;code&gt;--display-offset +09:00&lt;/code&gt; というオプションを足した。これを指定すると、データそのものやグラフ上の位置は変えず、軸とツールチップの時刻だけを &lt;code&gt;+09:00&lt;/code&gt; で表示する。内部の値には触らない。表示だけ変える。 実装も小さかった。&lt;/p&gt;
&lt;p&gt;タイムゾーンを扱うなら、&lt;code&gt;Asia/Tokyo&lt;/code&gt; みたいな名前を指定できたほうがいいかもしれない。DSTも考えたほうがいいかもしれない。そう考え始めると、いくらでも機能は増やせる。 でも今回、実際に困ったのはそこではなかった。UTCをJSTに読み替えるのが面倒だった。それだけだった。 日本にはDSTがないので、今の自分の用途なら固定offsetを指定できれば十分だった。新しい依存もいらない。なので、そこまでで止めた。&lt;/p&gt;
&lt;p&gt;以前だったら、完成度を上げようとして先回りでいろいろ考えていたかもしれない。でも今回は、いったん完成とする。実際に使って、面倒を踏んだら、その面倒だけ取る。という順番になった。 これは、今回Rustでツールを作りながら何度か意識していたことでもある。「あとで必要になるかもしれない」では作らない。実際に必要になってから考える。 もちろん、最初から考えておいたほうがいいものもある。でも、小さな道具を自分で作っていると、使う前に想像していた不便と、実際に使って感じる不便はけっこう違う。 今回も、完成したあとに機能を追加した。でも、それは完成していなかったからではない気がする。&lt;/p&gt;
&lt;p&gt;完成というのは、もう何も足さないという意味ではないのかもしれない。一度そこで終わりにして、使う側に戻る。使ってみて、不便だったら直す。ただし、そのとき困った分だけ。 今回は、それくらいがちょうどよかった。&lt;/p&gt;
</content:encoded></item><item><title>理解した前提をコードに戻す</title><link>https://honb.in/blog/encoding-assumptions/</link><guid isPermaLink="true">https://honb.in/blog/encoding-assumptions/</guid><description>前回、AIと一緒に書いたコードを読み直して、別々の場所にあるコードが、実はひとつの設計になっていた、という話を書いた。</description><pubDate>Sat, 03 Oct 2026 07:21:06 GMT</pubDate><content:encoded>&lt;p&gt;前回、AIと一緒に書いたコードを読み直して、別々の場所にあるコードが、実はひとつの設計になっていた、という話を書いた。&lt;/p&gt;
&lt;p&gt;題材にしたのは、2つのイベントが近い時間に起きているかを調べる処理だった。両方のイベントをあらかじめソートしておくことで、2つの列を前から順番に見るだけで判定できる。 コードを読んでいて面白かったのは、「ソートする処理」と「ソート済みであることを前提にした処理」が、別の場所にあったことだった。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;nanos&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort_unstable&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この一行があるから、その後の &lt;code&gt;count_matched&lt;/code&gt; では一度通り過ぎたところに戻らなくていい。 そこまで理解して、ひとつ気になった。 この「ソート済みである」という前提は、コードのどこに書いてあるんだろう。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;count_matched&lt;/code&gt; にはコメントがあった。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/// Both slices must be sorted ascending.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; count_matched&lt;/span&gt;&lt;span&gt;(events&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i64&lt;/span&gt;&lt;span&gt;], others&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i64&lt;/span&gt;&lt;span&gt;], window&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; i64&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt; usize&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちゃんと書いてある。でも、関数のシグネチャだけを見ると、普通の &lt;code&gt;&amp;amp;[i64]&lt;/code&gt; なので、ソートされていない値を渡すこともできる。 同じようなところが、別のツールにもあった。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;pub&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt; percentile&lt;/span&gt;&lt;span&gt;(sorted&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;f64&lt;/span&gt;&lt;span&gt;], p&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; f64&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt; f64&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    debug_assert!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;sorted&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_empty&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&quot;percentile of empty slice&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こちらは引数名が &lt;code&gt;sorted&lt;/code&gt; になっている。コメントにもソート済みであることが書かれていた。でも、それをコードでは確認していなかった。 しかも &lt;code&gt;percentile&lt;/code&gt; は、空の配列については &lt;code&gt;debug_assert!&lt;/code&gt; で確認している。 空ならその後かなりわかりやすく壊れるのに、ソートされていない場合は、それっぽい間違った値を返してしまう可能性がある。考えてみると、確認したいのはむしろこっちかもしれない。 じゃあ、どうするか。 最初に考えたのは、型にすることだった。 たとえば &lt;code&gt;Sorted&amp;lt;T&amp;gt;&lt;/code&gt; のような型を作って、生成時にソートし、ソート済みの値しか &lt;code&gt;count_matched&lt;/code&gt; や &lt;code&gt;percentile&lt;/code&gt; に渡せないようにする。そうすれば未ソートの値はそもそも渡せなくなるので、関数側は前提を信頼できる。 でも、少し考えてやめた。 今のコードでは、ソート済みのデータが、多くの場所を経由して持ち回されるわけではない。ソートして、すぐ使う。こういう前提を持つ関数もまだ少ない。 ここで &lt;code&gt;Sorted&amp;lt;T&amp;gt;&lt;/code&gt; を作ると、型の定義だけでは終わらない。どう作るか、どう借用するか、&lt;code&gt;i64&lt;/code&gt; と &lt;code&gt;f64&lt;/code&gt; のソートをどう扱うか、と考えることが増えていく。もちろん、それが必要になることもあると思う。でも、今はそこまでではなさそうだった。 結局、追加したのはこれだった。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;debug_assert!&lt;/span&gt;&lt;span&gt;(events&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_sorted&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&quot;events must be sorted ascending&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;debug_assert!&lt;/span&gt;&lt;span&gt;(others&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_sorted&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&quot;others must be sorted ascending&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;percentile&lt;/code&gt; にも追加した。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;debug_assert!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    sorted&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;is_sorted&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;percentile requires an ascending-sorted slice&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;追加したassertは3つ。 &lt;code&gt;debug_assert!&lt;/code&gt; なので、debugビルドでは前提が破られたら気づける。一方、デフォルトのreleaseビルドではチェックされないので、&lt;code&gt;is_sorted()&lt;/code&gt; のために毎回配列を走査することもない。 型ほど強い保証ではない。でも、今のコードにはこれくらいでよさそうだった。 さらに「このassert自体のテストも書くべきか」と考えた。でも、それもやめた。&lt;code&gt;debug_assert!&lt;/code&gt; と &lt;code&gt;is_sorted()&lt;/code&gt; の動作を確認するだけのテストになりそうだったし、assertのためにテストコードのほうが大きくなるのも少し違う気がした。&lt;/p&gt;
&lt;p&gt;前回は、AIが書いたコードの「なぜ」を追いかけて、ソートとTwo Pointersのつながりを理解した。今回は、その理解したことを、どこまでコードに戻すかを考えた。 コメントだけでも伝わる。assertにすれば実行時にも確認できる。型にすれば、もっと強く守ることもできる。 強くできるなら、強くしたほうがいいようにも思える。でも、型にできることと、型にすべきことは別なんだと思う。 必要になったら、そのとき型にすればいい。 今回はassertを3つ追加して止めた。&lt;/p&gt;
&lt;p&gt;AIとコードを書いていると、生成されたコードを理解することに目が向きがちだけど、そのあとに「理解したことをコードにどう戻すか」を考えるのも面白い。 そして、戻しすぎないことも、たぶん設計のひとつなんだと思う。&lt;/p&gt;
</content:encoded></item><item><title>AIが書いたコードを自分の言葉にする</title><link>https://honb.in/blog/restating-ai-code/</link><guid isPermaLink="true">https://honb.in/blog/restating-ai-code/</guid><description>前回、O&apos;Reillyのサブスクに登録して「作る → AIに聞く → 気になったら本を読む」でやってみる、と書いた。</description><pubDate>Sat, 03 Oct 2026 06:22:17 GMT</pubDate><content:encoded>&lt;p&gt;前回、O’Reillyのサブスクに登録して「作る → AIに聞く → 気になったら本を読む」でやってみる、と書いた。&lt;/p&gt;
&lt;p&gt;最近、Rustの勉強を兼ねて、業務でも使えそうな小さなCLIツールを作っている。基本的には自分で考えつつ、わからないところはAIに聞きながら進めている。 今回は、AIと一緒に書いたコードをあとから読み直してみた。&lt;/p&gt;
&lt;p&gt;題材にしたのは、2つのイベントが近い時間に起きているかを調べる処理。自分だったらどう書くだろう、と考えてみると、最初に浮かんだのは二分探索だった。片方のイベントをソートしておいて、もう片方の各イベントについて、近くにあるものを探す。たぶんそれでも書ける。 でも、実際のコードはそうなっていなかった。&lt;/p&gt;
&lt;p&gt;両方のイベントをあらかじめソートして、2つの列を前から順番に見ていく。片方の位置を示すポインタは前に進むだけで、一度通り過ぎたところには戻らない。いわゆるTwo Pointers（2ポインタ）のやり方だった。 最初は「なるほど、こっちのほうが速いのか」くらいに思った。でも、もう少しコードを追ってみると、面白かったのはアルゴリズムそのものではなかった。 なぜ「戻らなくていい」のか。 調べる側のイベントがソートされていて、時刻が順番に大きくなっていくから、一度通り過ぎたところには戻らなくていい。もう片方もソートされているから、前から順番に見ていけば判定できる。 当たり前といえば当たり前なんだけど、この「ソートされている」という前提が、コードの別の場所にあった。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;rust&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;nanos&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort_unstable&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最初に読んだとき、この一行は単に入力を整えているだけだと思っていた。でも、ここでソートしているから、その後の処理では「もう見たところには戻らない」と決められる。 別々の場所にあるコードが、実はひとつの設計になっていた。&lt;/p&gt;
&lt;p&gt;AIとコードを書いていると、こういうことが結構ある気がする。コードは動く。テストも通る。実装としても悪くない。だから、そのまま次に進めてしまう。 でも、自分がそのコードを理解しているかというと、また別の話なんだと思う。今回も、Two Pointersという名前を覚えたことより、「なぜこの実装が成立するのか」を追いかけたことのほうが収穫だった。 コードを読んでいると、一見すると独立して見える処理が、別の場所の実装を成立させていることがある。さっきのソートが、まさにそれだった。設計を理解するというのは、コードを一行ずつ説明できることより、こういう処理同士のつながりを見つけることなのかもしれない。&lt;/p&gt;
&lt;p&gt;AIはかなりいいコードを書いてくれる。だから最近は、「自分で全部書けたか」だけを気にしても、あまり意味がない気がしている。それより、「自分だったらどう書くか」を一度考えて、AIが書いたものと比べる。違っていたら「なんでこっちなんだろう」と調べる。そこで本を読む。 今回も、二分探索だと思っていたものがTwo Pointersだった、という小さな違いから、ソートと計算量の話を調べて、最終的には「この処理とこの処理は、どうつながっているんだろう」というところまで戻ってきた。&lt;/p&gt;
&lt;p&gt;こういう寄り道なら、悪くない。 「動いた。よし次」で進めるのも速いけど、たまには立ち止まって、AIが書いたコードの「なぜ」を自分なりに追いかけてみる。しばらくは、そんな感じでやってみようと思う。 O’Reillyのサブスクも、たぶんこういう使い方なら元が取れる。&lt;/p&gt;
&lt;p&gt;たぶん。&lt;/p&gt;
</content:encoded></item><item><title>O’Reillyのサブスクに登録した</title><link>https://honb.in/blog/oreilly-subscription/</link><guid isPermaLink="true">https://honb.in/blog/oreilly-subscription/</guid><description>前からちょっと気になってはいたんだけど、まあまあいい値段するし、本なら必要なときに買えばいいかなと思っていた。ただ最近Rustを触り始めて、読みたい本がちょこちょこ出てきたので、試しに入ってみることにした。</description><pubDate>Sat, 03 Oct 2026 05:16:31 GMT</pubDate><content:encoded>&lt;p&gt;前からちょっと気になってはいたんだけど、まあまあいい値段するし、本なら必要なときに買えばいいかなと思っていた。ただ最近Rustを触り始めて、読みたい本がちょこちょこ出てきたので、試しに入ってみることにした。&lt;/p&gt;
&lt;p&gt;今はRustの勉強も兼ねて、業務で使えそうな小さいCLIツールを作っている。ログのタイムスタンプを集計したり、時間で絞ったり、2つのイベントが近い時間に起きているか調べたり。もともと仕事でシェルスクリプトや &lt;code&gt;awk&lt;/code&gt;、&lt;code&gt;sort&lt;/code&gt; あたりを組み合わせてやっていたことを、Rustで少しずつ道具にしている感じ。これが思ったより楽しい。&lt;/p&gt;
&lt;p&gt;ただ、Rustはまだ全然わからない。AIに聞けばコードは出てくるし、それを見ながらなら結構先まで進める。でも、せっかくRustを勉強するためにやっているのに、「動いた。よし次」だけだと、ちょっともったいない。 実際に作っていると、標準入力を1行ずつ読む &lt;code&gt;BufRead&lt;/code&gt; や、時刻順にデータを扱う &lt;code&gt;BTreeMap&lt;/code&gt; みたいなものが、AIと一緒に書いているうちに自然とコードに入ってくる。でも「動いているからOK」ではなくて、「なんでここではこれを使うんだろう」はちゃんと理解しておきたい。&lt;/p&gt;
&lt;p&gt;ちょうどO’ReillyはMCPも提供されているので、Claudeから書籍を検索できるようにもしてみた。実装中に気になったことが出てきたら、そのまま関連する本や読むところを探せる。「このコードを書いているなら、このへんを読んでおくとよさそう」みたいな使い方ができるのは結構よさそうだ。 最初はRustの本を一冊ちゃんと読もうかとも思ったけど、たぶん自分は途中で飽きる。なので、無理に最初から読まないことにした。 コードを書く。わからないことが出てくる。AIに聞く。気になったら、その場でO’Reillyから関連する本を探して読む。&lt;/p&gt;
&lt;p&gt;最近、AIを使ってコードを書くことがかなり増えたけど、本を読まなくてよくなった感じはあまりしない。むしろAIで先に進めるようになったぶん、「これ、なんでこうなってるんだ？」というところを本であとから埋めるのは、結構相性がいい気がする。 作る → AIに聞く → 本で掘る。 しかも、その本を探すところもAIからできる。しばらくこれでやってみる。 問題は、O’Reillyのサブスク代の元を取ろうとして、読まなくていい本まで読み始めないかどうか。&lt;/p&gt;
</content:encoded></item><item><title>AIに教えるべきものは何だろう</title><link>https://honb.in/blog/what-to-teach-ai/</link><guid isPermaLink="true">https://honb.in/blog/what-to-teach-ai/</guid><description>最近、AIエージェントを活用する現場では、「Skill」やプロンプトを整備し、AIの振る舞いを細かくルール化する取り組みをよく目にする。命名規則を書く。実装手順を書く。レビュー観点を書く。AIが安定して期待どおりに振る舞うよう、ルールを積み重ねていく。</description><pubDate>Sat, 04 Jul 2026 08:08:06 GMT</pubDate><content:encoded>&lt;p&gt;最近、AIエージェントを活用する現場では、「Skill」やプロンプトを整備し、AIの振る舞いを細かくルール化する取り組みをよく目にする。命名規則を書く。実装手順を書く。レビュー観点を書く。AIが安定して期待どおりに振る舞うよう、ルールを積み重ねていく。&lt;/p&gt;
&lt;p&gt;この取り組みは、とても重要だと思う。現時点のAIは、ガードレールがあるほど品質が安定する。チーム開発でも、共通のルールは欠かせない。だから、ルールを書くこと自体を否定したいわけではない。&lt;/p&gt;
&lt;p&gt;ただ、その様子を見ているうちに、私はある疑問を抱くようになった。本当に育てているのはAIなのだろうか。それとも、私たちは「設定ファイル」を育てているだけなのだろうか。&lt;/p&gt;
&lt;p&gt;正直に言うと、私はこのルール整備に、どうも乗り気になれずにいる。なぜだろうと、しばらく考えていた。&lt;/p&gt;
&lt;p&gt;気になっていたのは、ルールを書くことではない。ルールをどれだけ充実させても、AIの判断を支える知識を育てている感覚が、どうにも持てなかったのだ。&lt;/p&gt;
&lt;p&gt;ソフトウェア開発では、新しいライブラリが登場し、設計手法が変わり、ドメインへの理解も深まっていく。変化は、まずルールではなく、その背景にある知識の側で起きる。そして、新しい状況に出会っても、ルールは知識から導き直せる。だとすれば、長期的に価値を持つのは、ルールそのものよりも、判断を支える知識のほうではないだろうか。&lt;/p&gt;
&lt;p&gt;ルールと知識は似ているようで違う。例えば、「Reactでは &lt;code&gt;index&lt;/code&gt; を &lt;code&gt;key&lt;/code&gt; に使わない。」というのはルールだ。一方で、「なぜ安定した識別子が必要なのか。」という理解は知識である。また、「この層にビジネスロジックを書く。」というのもルールだが、「なぜその境界で責務を分けるのか。」という設計思想は知識である。&lt;/p&gt;
&lt;p&gt;知識があれば、新しい状況でも判断できる。ルールしかなければ、ルールの外側に出た瞬間に立ち止まってしまう。ルールは知識から生まれる。しかし、知識はルールだけでは生まれない。&lt;/p&gt;
&lt;p&gt;では、その知識をAIにどう活かしてもらえばよいのだろう。知識をそのままルールに書き換えてしまえば、結局は答えを列挙することになる。もちろん、それも必要だ。しかし、それだけでは知識は使われない。知識は、必要な場面で引き出されて初めて価値を持つ。だから最近は、AIに与えるべきものは答えではなく、「問い」なのではないかと思うようになった。&lt;/p&gt;
&lt;p&gt;例えば、「この設計は変更しやすいだろうか」「この責務は本当にここに置くべきだろうか」「ドメインを正しく表現できているだろうか」「他の選択肢は検討しただろうか」。これらは答えを固定するルールではない。AIが知識を引き出し、状況に応じて判断するための問いである。&lt;/p&gt;
&lt;p&gt;優れた設計者は、膨大なルールを暗記しているわけではない。設計原則や経験をもとに、その場に応じた問いを立てながら判断している。AIにも同じことが言えるのではないだろうか。&lt;/p&gt;
&lt;p&gt;ルールは品質を安定させる。知識は判断を支える。問いは、その知識を引き出す。この三つは対立するものではなく、それぞれ役割が違う。&lt;/p&gt;
&lt;p&gt;それでも、もし「AIに何を育てるべきか」と問われたら、私はまず判断を支える知識に時間を使いたい。そして、その知識を必要な場面で引き出せる問いを、一緒に育てていきたい。それが、今の私にとっての「AIを育てる」ということなのだ。&lt;/p&gt;
</content:encoded></item><item><title>実装が速くなるほど、理解の価値は上がる</title><link>https://honb.in/blog/value-of-understanding/</link><guid isPermaLink="true">https://honb.in/blog/value-of-understanding/</guid><description>「AIが設計して、AIが実装する未来」の話をよく見かける。</description><pubDate>Thu, 18 Jun 2026 08:53:24 GMT</pubDate><content:encoded>&lt;p&gt;「AIが設計して、AIが実装する未来」の話をよく見かける。&lt;/p&gt;
&lt;p&gt;AIによる実装支援は素晴らしいし、自分も日々その恩恵を受けている。&lt;br&gt;
それでも時々、ソフトウェア開発とは設計を実装へ変換する作業なのだろうか、と考える。&lt;/p&gt;
&lt;p&gt;現場での開発は、設計してから作るというより、作りながら学ぶことのほうが多い。&lt;br&gt;
実装して、気づいて、設計を変える。その繰り返し。&lt;/p&gt;
&lt;p&gt;だとすれば、開発は正解を作る活動というより、学習する活動だということになる。&lt;/p&gt;
&lt;p&gt;Peter Naur の theory building と近い話かもしれない。&lt;br&gt;
プログラミングとはコードを書くことではなく、そのシステムについての理論を築く活動だという考え方。&lt;/p&gt;
&lt;p&gt;AIが加速しているのは主にコードを書く工程である。&lt;br&gt;
一方で、その理論を作る工程は、作りながら気づき、理解を深める過程そのものなので、そう簡単には短縮できない。&lt;/p&gt;
&lt;p&gt;もちろん、自分も例外ではない。&lt;/p&gt;
&lt;p&gt;AIのおかげで以前よりずっと速くコードを書けるようになった一方で、その速度に理解が追いついているかと聞かれると、自信はない。 実装が速くなること自体は歓迎している。 そのうえで、速くなるほど「なぜこうなっているのか」を説明できることの価値は、むしろ上がるのだと思う。&lt;/p&gt;
&lt;p&gt;開発が学習する活動なのだとすれば、最後に希少なのはコードではなく、その背後にある理論なのかもしれない。&lt;/p&gt;
</content:encoded></item><item><title>観測者は割り込まない ― WebView 向け dev コンソールを作って見えてきたこと</title><link>https://honb.in/blog/noninvasive-webview-console/</link><guid isPermaLink="true">https://honb.in/blog/noninvasive-webview-console/</guid><description>自前の WebView 用 dev コンソールを作っていて、何度も同じ失敗をした。</description><pubDate>Fri, 12 Jun 2026 12:44:59 GMT</pubDate><content:encoded>&lt;p&gt;自前の WebView 用 dev コンソールを作っていて、何度も同じ失敗をした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;レスポンスを観測したつもりが、body を壊す。&lt;/li&gt;
&lt;li&gt;エラーを拾ったつもりが、framework の制御フローまで赤く表示する。&lt;/li&gt;
&lt;li&gt;デバッグツールが、アプリと一緒に落ちる。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原因はどれも同じだった。観測者が、対象に割り込んでいた。&lt;/p&gt;
&lt;p&gt;補足すると、オンデバイスで使える定番として eruda / vConsole があり、実装からは多くを学んだ（後で出てくる技法のいくつかは、ソースを読んで参考にしたもの）。ただ自分の用途とは前提が噛み合わない部分があって、最終的に &lt;code&gt;fetch&lt;/code&gt; / &lt;code&gt;console&lt;/code&gt; を patch する自前のコンソールを vanilla（依存ゼロ）で作ることにした。&lt;/p&gt;
&lt;p&gt;この記事は「ツールの紹介」ではなく、そこで得た「割り込まない観測」の知見をまとめたもの。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fetch&lt;/code&gt; のレスポンスは &lt;code&gt;clone()&lt;/code&gt; で裏読みすると壊れることがある → Proxy 観測&lt;/li&gt;
&lt;li&gt;既存 OSS は「利用する」だけでなく「ソースを読む」ことで得られるものがある（技法を参考にし、設計上のトレードオフを理解する）&lt;/li&gt;
&lt;li&gt;dev ツールは対象から独立させる（vanilla + shadow DOM + global patch）&lt;/li&gt;
&lt;li&gt;「見えてるこの要素は DOM のどこ？」を一発で解決する（Pick）&lt;/li&gt;
&lt;li&gt;エラーバッジの精度は &lt;code&gt;preventDefault&lt;/code&gt; を尊重して保つ&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-fetch-のレスポンスは-clone-で読むと壊れることがある&quot;&gt;1. fetch のレスポンスは clone で読むと壊れることがある&lt;/h2&gt;
&lt;p&gt;ネットワークを覗くには &lt;code&gt;window.fetch&lt;/code&gt; を patch して body を記録したい。&lt;code&gt;Response.body&lt;/code&gt; は一度しか読めないストリームなので、&lt;code&gt;clone()&lt;/code&gt; して裏で読むのが定石に見える。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;window.&lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; origFetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  res.&lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; record&lt;/span&gt;&lt;span&gt;(body)) &lt;/span&gt;&lt;span&gt;// 裏で body を読む&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ところが &lt;code&gt;fetch&lt;/code&gt; を使うライブラリ（自分の場合は connect-web + React Query）は、レスポンスを読み終えると &lt;code&gt;AbortController.abort()&lt;/code&gt; で後片付けをする。これ自体は正しい振る舞い。問題は裏で走らせていた &lt;code&gt;clone().text()&lt;/code&gt; の方で、まだ読んでいる最中に中断され、&lt;code&gt;AbortError&lt;/code&gt; で失敗する。body が取れないどころか、誰も catch しない rejection としてコンソールに残る。&lt;/p&gt;
&lt;h3 id=&quot;proxy-観測&quot;&gt;Proxy 観測&lt;/h3&gt;
&lt;p&gt;「裏で先読みする」のをやめて、原本の Response を &lt;code&gt;Proxy&lt;/code&gt; で包み、消費側が &lt;code&gt;text()/json()/arrayBuffer()&lt;/code&gt; を呼んだ“結果”を観測する。差し替えないので透過で、先読みしないので abort レースも起きない。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-dimmed&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; proxyResponse&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  new&lt;/span&gt;&lt;span&gt; Proxy&lt;/span&gt;&lt;span&gt;(res, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;prop&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (prop &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;json&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; target.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          record&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(v)) &lt;/span&gt;&lt;span&gt;// 消費側が読んだ瞬間に観測&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          return&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      // text / arrayBuffer も同様にフック&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Reflect.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(target, prop, target)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; typeof&lt;/span&gt;&lt;span&gt; value &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;function&apos;&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt; value.&lt;/span&gt;&lt;span&gt;bind&lt;/span&gt;&lt;span&gt;(target) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;window.&lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; proxyResponse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; origFetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;肝は「観測者は割り込まない」こと。消費側から見ると返ってくる Response は本物と区別がつかない（フックしないメソッドは全部 &lt;code&gt;Reflect.get&lt;/code&gt; + &lt;code&gt;bind&lt;/code&gt; で素通し）。フックしたメソッドだけ薄く包んで、実際に読まれた値を記録する。先読みしないので、誰も読まなければ何も起きない＝壊れない。&lt;code&gt;res.url&lt;/code&gt; / &lt;code&gt;redirected&lt;/code&gt; / body ストリーム / &lt;code&gt;bodyUsed&lt;/code&gt; も保たれる。&lt;/p&gt;
&lt;p&gt;振り返ると、&lt;code&gt;clone()&lt;/code&gt; 自体が悪かったわけではなく、問題は「観測のために、対象を読む“もう一人の消費者”を増やした」こと。観測者が消費者として振る舞った瞬間に、もう割り込んでいる。Proxy 観測は「消費者を増やさず、本来の消費者が読んだ結果に相乗りする」という、その一点を守るための道具。&lt;/p&gt;
&lt;p&gt;ちなみに XHR（axios 等）は &lt;code&gt;responseText&lt;/code&gt; がそのまま読めるので、Proxy も abort 対策も不要。&lt;code&gt;fetch&lt;/code&gt; だけが特殊だった。&lt;/p&gt;
&lt;h2 id=&quot;2-既存-oss-は利用するだけでなくソースを読む&quot;&gt;2. 既存 OSS は「利用する」だけでなく「ソースを読む」&lt;/h2&gt;
&lt;p&gt;オンデバイス dev コンソールには eruda / vConsole といった定番がある。最初はそのまま使えばと思っていたが、自分の用途（API が connect-web、データ取得が React Query）では噛み合わない点があり、結局 vanilla で自作することにした。&lt;/p&gt;
&lt;p&gt;その過程で効いたのが、既存 OSS を「利用する」だけでなく「実装のリファレンスとして読む」ことだった。読むと、採用したい技法もあれば、自分の用途では前提が合わない実装も見つかる。たとえば 1 の Proxy 観測は既存実装から参考にした技法だし、一方で「レスポンスを clone で先読みする」アプローチは、自分のケースでは abort レースの原因になり得ると分かった。どちらも、自分の設計判断に根拠を与えてくれる。&lt;/p&gt;
&lt;p&gt;ソースを読む過程で気づいた点は、実際に upstream へ修正提案として送ったりもした。車輪の再発明をしながらでも、読む習慣があると各所の「なぜ」が埋まっていく。&lt;/p&gt;
&lt;h2 id=&quot;3-dev-ツールは対象から独立させる&quot;&gt;3. dev ツールは対象から独立させる&lt;/h2&gt;
&lt;p&gt;dev ツールを React コンポーネントとしてアプリのツリーに載せると、アプリが壊れた時にツールも一緒に死ぬ。一番ツールが欲しいのは「アプリが壊れた時」なのに、だ。&lt;/p&gt;
&lt;p&gt;なので UI も含めて vanilla DOM + global patch（&lt;code&gt;fetch&lt;/code&gt;/&lt;code&gt;console&lt;/code&gt; の差し替え）+ shadow DOM で組み、アプリのフレームワークに一切依存させない。shadow DOM はアプリの CSS と相互汚染しないための隔離でもある。eruda / vConsole が vanilla なのも、突き詰めると同じ理由だと思う。&lt;/p&gt;
&lt;h2 id=&quot;4-見えてるこの要素は-dom-のどこを一発で&quot;&gt;4. 「見えてるこの要素は DOM のどこ？」を一発で&lt;/h2&gt;
&lt;p&gt;小画面で DOM ツリーを手で展開して目的の要素を探すのは苦痛だ。DevTools の inspect（要素ピック）が効く。&lt;/p&gt;
&lt;p&gt;実装は意外と素直：inspect モード中、ページのタップを capture phase で横取りして（&lt;code&gt;document.addEventListener(&apos;click&apos;, handler, true)&lt;/code&gt; ＋ &lt;code&gt;preventDefault&lt;/code&gt; でアプリの onClick を抑制）、&lt;code&gt;event.target&lt;/code&gt; の要素を取り、その &lt;code&gt;getBoundingClientRect()&lt;/code&gt; で枠を被せるだけ。eruda はこれをリッチな overlay でやっているが、ただの枠なら数十行で済む。&lt;/p&gt;
&lt;p&gt;「画面で見えてるこれ → DOM のどこ？」を一発で解決できると、モバイルの DOM デバッグは一気に楽になる。&lt;/p&gt;
&lt;p&gt;そしてこれも、突き詰めると観測者の話。DOM ツリーを書き換えて目的の要素を探すのではなく、ユーザーが見ている要素を観測するだけに留めている。ハイライトの枠も &lt;code&gt;pointer-events: none&lt;/code&gt; でページ操作を邪魔しない。inspect 中だけはタップを &lt;code&gt;preventDefault&lt;/code&gt; で借りるが、これは恒常的な副作用ではなく「その瞬間だけの明示的な横取り」で、DOM には手を入れない。「探す」ために対象をいじらない、という意味で 1・3・5 と地続きな気がしている。&lt;/p&gt;
&lt;h2 id=&quot;5-エラーバッジの精度は-preventdefault-を尊重して保つ&quot;&gt;5. エラーバッジの精度は preventDefault を尊重して保つ&lt;/h2&gt;
&lt;p&gt;コンソールに「エラー件数バッジ」を出すと、&lt;code&gt;window.error&lt;/code&gt; / &lt;code&gt;unhandledrejection&lt;/code&gt; を拾って数えたくなる。が、そのまま数えるとフレームワークの制御信号まで赤くカウントしてしまう。&lt;/p&gt;
&lt;p&gt;たとえば Next.js の &lt;code&gt;redirect()&lt;/code&gt; は内部的に &lt;code&gt;NEXT_REDIRECT&lt;/code&gt; を throw する。これは制御フローであってエラーではない。framework 側は &lt;code&gt;event.preventDefault()&lt;/code&gt; でこれを「handled」扱いにし、ネイティブのコンソールには出さない。&lt;/p&gt;
&lt;p&gt;ところが自前のリスナは preventDefault されても発火するので、何もしないと「ネイティブには出ないのに自前ツールにだけ赤いエラーが出る」状態になる。&lt;/p&gt;
&lt;p&gt;対処は、&lt;code&gt;event.defaultPrevented&lt;/code&gt;（先に別ハンドラが handled 扱いにしたか）を見て、handled なら warn に格下げ＝エラー件数に数えない。「見せる」自体は残す（dev コンソールの価値は「ネイティブが隠すものを見せる」ことなので）が、赤いバッジ＝本当に未処理の異常、という線をはっきりさせる。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;レスポンスを覗くなら clone より Proxy 観測（透過・abort レースを踏まない）&lt;/li&gt;
&lt;li&gt;既存 OSS は「利用する」だけでなく「ソースを読んで技法を参考にし、設計上のトレードオフを理解する」対象にできる&lt;/li&gt;
&lt;li&gt;dev ツールは対象から独立（vanilla + shadow DOM + global patch）させると、アプリが壊れても死なない&lt;/li&gt;
&lt;li&gt;inspect（Pick）は &lt;code&gt;click&lt;/code&gt; capture + &lt;code&gt;getBoundingClientRect&lt;/code&gt; で素直に作れる&lt;/li&gt;
&lt;li&gt;エラーバッジは &lt;code&gt;preventDefault&lt;/code&gt; を尊重すると「未処理の異常」アラートとして信頼できる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;冒頭の三つの失敗は、結局ひとつの原則に還元できた。観測者は割り込まない。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;対象を壊さない&lt;/strong&gt; ── body を消費しない（Proxy 観測）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;運命を共にしない&lt;/strong&gt; ── アプリと一緒に死なない（vanilla + shadow DOM + global patch）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;意味を上書きしない&lt;/strong&gt; ── 対象が handled とした信号を再解釈しない（&lt;code&gt;preventDefault&lt;/code&gt; を尊重）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この三つを守ると、観測ツールは「アプリが壊れた時にこそ使える」道具になる。逆に言えば、ブラウザを patch して観測する footgun は、たいてい「どこかで対象に割り込んでいる」に還元できた。その一つ一つに「なぜ割り込んではいけないか」を詰めるのが、そのまま勉強になった。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;面白かったのは、この原則を意識する前は、全部が個別のバグに見えていたこと。&lt;code&gt;fetch&lt;/code&gt; の &lt;code&gt;AbortError&lt;/code&gt;、Next.js の &lt;code&gt;redirect&lt;/code&gt; が赤く出ること、DevTools の UI をアプリから分離すること──最初はバラバラの問題として、その都度パッチを当てていた。&lt;/p&gt;
&lt;p&gt;個別の対処を覚えるより、「観測者は割り込まない」という原則を持っておく方が応用が利く。今回の三つの問題は、結局その一文に還元できた。&lt;/p&gt;
</content:encoded></item></channel></rss>