
2026/04/24 23:28
過剰な思考、スコープクリープ、そして構造的相違によって引き起こされるプロジェクトへの sabotaging です。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は「やってみるか」という哲学を提唱し、広範な調査よりも即時の作成を優先することで楽しみを保ち、スコープの蔓延を防ぐことを重視する。この転換は、ハードウェアのプロトタイピングインターフェース、Clojure+Rust 融合言語、CAD プログラミングといった長年にわたる技術的関心を扱うが、焦点のない成功基準により数百時間の投入にも関わらず合成された解決策をもたらさず、こうしたサイクルを打破するために著者は迅速なプロトタイピングに注力する。友人の Marcin と一緒に週末プロジェクトとして製作した合板の棚は、機能的成果に絞って完璧な仕様ではなくてはしごを作ったものであり、逆にリソースが不要な機能や過剰な調査に浪費されるときには失敗する。具体的には、LLM エージェントプロジェクト(Finda スタイルのファイルシステム検索)でアンカー機能を見捨てること、difftastic、semanticdiff.com、diffsitter などのツールを数時間レビューして高レベル構造を正しく処理できないことが判明した例などが挙げられる。こうした限界に失望した著者は、Tyvek/ライトディフューザー材料の EU ベンダーを探したり、Coinbase クリプト破産分析から酵母ワクチンや Loon Lisp まで幅広い話題に触れたりする雑多な更新事項も記録している。
本文
こんにちは、みなさん!
5 月 8 日に Babashka Conf に、5 月 9 日には Dutch Clojure Days に参戦します。どちらか(あるいは単にアムステルダムを訪れるだけで)行く予定の方、ぜひご連絡ください!
何か新しいプロジェクトのアイデアを持った際、私の行動は大きく分けて次の 2 パターンのどちらかに収まっています:
- ただやってみる。 少し細かい修正を加えることもありますが、多くの場合、私が想像していた通りになり、それで十分に満足します。
- 「類似作(prior art)を探すべきだな」と思う。 これには当初考えていたよりもはるかに広いスコープを扱っている既存のものが多く存在しています。「その広範な範囲を取り込むべきか?」「既存の類似解の上に自分のものを重ねて構築すべきか?」「あるいは、ただ人気のあるものを使えばいいのではないか?」と頭がごっちゃになります。もちろん、それらのツールに勝る実装も作れますが、そのためには膨大な時間が必要です。しかし、実際には大規模で人気のプロジェクトをメンテナンスしたり、これほどの時間を割いたりしたくはありません。うーん、やめたことが後悔になりそうですが、結局元の課題の解決にも至らず、創造することの喜びも得られずに多くの時間を無駄にしてしまいます(Uh oh!)。
私は 1 番目の結果の方を選びたがりますが、その鍵は自分自身の成功基準をどの程度内面化しているかにかかっています。
例 1:木工棚
先週末、友人の Marcin が家に遊びに来ました。木工でも遊ぼうと盛り上がったので、私のキッチン用にこんな棚と、3D プリンターで製作したフックを作りました:
非常に成功したプロジェクト:
- カフェでアイデア出し(ブレインストーミング)を行った。
- イケアのゴミ箱用フック用として、いくつかの 3D プリンティングの試作を行った(OnShape で CAD データを作成したので、ご自身でも 3D プリントできます)。
- 作業台に眠っていた材料を活用した。
- 角をハンドサンダーで目視に合わせて丸めた。
- 友人が余っていた塗料を使って未塗装のパイン材の端をコーティングした。
- これで週末終了。
主な成功基準は「友人と木工を楽しみながら制作すること」でした。その一点に集中することで、作品自体の細かい成功基準を過剰に考えず、「ただ、私のキッチンのために棚を作る」ということに集中できました!
例 2:構造的・意味的な差分ツール
対照的に、先週の金曜日、
difftastic があまりうまく機能していないのに気づきました。そこで、構造的な差分や意味的な差分を扱うツールとそのワークフローを探すことにしました(私がこれまで勉強したことのなかった分野で、LLM で生成されたコードのレビューが増えるにつれて関心が高まっている話題です)。
週末の 4 時間、既存のツールの調査を行いました(以下にメモを残しています)。「意味樹の差分は博士号レベルの複雑な問題だ」とか、「なぜみんな MCP サーバーを入れているんだ?私は MCP サーバーがほしくない」といった暗い時期を過ごしましたが、やがて理性を取り戻し、もともとの成功基準に気づきました。「Emacs で自分にとって使いやすい差分ワークフローが欲しいだけだから、自分で作ればいい。おそらく 4 時間かからないはずだ」そう思い直したのです。
この「目標を再認識してスコープを最小限に固める」という教訓があり、さらにモチベーションの喪失を防げれば、動機が尽きる前にプロトタイプを作り上げられると期待しています。
しかし、私が長年抱いてきた興味のある他の分野は:
- ハードウェアのプロトタイピング用インターフェース(2023 年 9 月に議論)
- クロージャとラズスの良さを融合させるプログラミング言語(2023 年 11 月)
- CAD 用のプログラミング言語(制約条件、双方向編集など、いくつかの思惑に満ちたアイデア)
これらはすべて「成果 #2 の沼」の深处に沈んでいます。つまり、数百時間に及ぶバックグラウンドリサーチと小さなプロトタイプを作成してきましたが、当初の課題に対する実用的な解決策をまだ合成できていないのです。
その時間を後悔しているわけではありません(本を読むことで学ぶことも大好きだからです)。しかし、「失敗への恐怖」といったような内部の批判家が創造的な衝動を抑え込み、より楽しく(そして生産的!)「行うことによる学習」から遠ざけられていると感じる不安が常に拭えません。
これらのケースでは、成功基準がかなり曖昧でした:
- 自分の Rust/Clojure の使用を置き換えるための試みか?
- 一部の問題領域への適用のみを考えているのか?
- それとも、単に言語設計・実装についての遊び場(プレイグラウンド)が必要で、最終的にその言語を使う必要はないのか?
CAD についても同様です:
- 商業用の CAD ツールを自分のもの置き換えるための試みか?
- 単純な部材や特にパラメトリックな部品の一部の領域での利用のみか?
- 他の人が有用だと感じるかどうかに関心があるのか?
- 既存のオープンソースツールと明確に異なる必要があるのか?
これらを考慮するのは確かに重要です。しかし、結局のところ、「たくさん考えること」よりも「実際にたくさんやってみること」の方がずっと良い結果をもたらすだろうと思います。
なので、かつての無知な 20 歳の自分の内なる声を呼び起こし、ただ「やってみる」ことに全力を注ごうとしています。たとえ後から見て「明らかに失敗だった」と思うプロジェクトでも、その過程から何かを得た分だけ、全体としてプラスになるはずです =D(ネットメリットあり)。
スコープCreep の保存則
もちろん、「ただやってみる」ことには時間の限界があり、バランスが必要になります。私のキャリアを通じて YAGNI(You Ain't Gonna Need It=必要なんてないよ)の教訓を何度再学習するかわかりませんが、LLM エージェントを使って大量のコードを書き、やがて理性を取り戻してそれをすべて廃棄した経験から、改めてこの原則に気づかされました。
プロジェクト:Emacs 用の Finda 風ファイルシステムワイドファジーパス検索
Emacs 向けに、Finda 風のファイルシステム全体のファジーパス検索機能を実現したいと考えていました。私は以前(手書きでコードを入力!)、まさに同様の機能を既に実装したことがあります(ファイルシステムを探索してパスを収集し、三元組でインデックス化し、ビットマップの交差計算で高速なファジークエリを行うため)。その経験から、LLM にコードを書かせて監修するだけで数時間で行えだろうと判断しました。
最初は「計画モード」でのチャットから始めましたが、LLM は私が Finda を書いたのが 10 年前であることを考慮して Nucleo というライブラリを提案してくれました(驚き!)。Nucleo のドキュメントを読み込み、設計が良く、機能的だと評価したので採用することにしました。これにより、大文字小文字の区別や Unicode ノーマライゼーションなどの利便性を活用できます。(例:クエリ
foo は Foo と foo 双方にマッチしますが、Foo のクエリは foo にマッチしません。同様に cafe と café の場合も同様です。)
素晴らしいライブラリを見つけることこそが問題ではなく、問題は Nucleo が追加機能(アンカー:
^foo は行の先頭でのみマッチ)もサポートしていたことです。これはファイルパスからなるコーパスにおいてどう意味を持つのか?行の先頭にアンカーを設けるのは役に立たない(すべて / で始まるため)、と考え、代わりにパスセグメントに対してアンカー解釈を試みました。例:^foo は /root/foobar/ にマッチしますが、/root/barfoo/ にはマッチしません。
これを効率的に行うためには、インデックスがセグメント境界を追跡する必要があり、各セグメントに対してクエリをチェックできるようになります。しかし同時に、アンカー付きクエリにスラッシュが含まれる場合(例:
^foo/bar)も対応する必要があります。なぜなら、個々のセグメント (root, foo, bar, baz) だけを見るとマッチしないパス /root/foo/bar/baz/ も存在するからです。
この課題を解決するには数時間かかりました。LLM とデザインのアイデアを出し合い、Nucleo の型をラップするコードを書いてもらい、それが膨張気味で「喜び」を与えないことに気づいたため、最終的に自前ですべて小さく簡素なラッパーを実装しました。
その後、休憩を取って考え直したところ:
- Finda にアンカー機能があったいと願ったシチュエーションは思い当たらない。
- パスコーパスに対しては、クエリの先頭または末尾に
を追加することでアンカーを実現できる(ファイル名の最後でのみアンカーを行う場合を除く)。/
そのようにして、すべてのアンカー関連コードを廃棄しました。
LLM や他者との議論なしにすべてを自前で作成した場合と比較すれば、結果的にプラスだったことはほぼ確実だと思われますが、100% 断定はできません。ここで種の保存則的な現象が働いているのかもしれません:「プログラミング速度の向上は、必ずしも不要な機能の増加、ループホールの掘り起こし、そして distractions と相殺される」という法則でしょうか。
構造的差分(Structural Diffing)
さて、不要な distraction についても触れながら、最近学んだ構造的差分に関するすべてをお話しします。この分野で意見や感情、参照をお持ちの方がいらっしゃいましたら、ぜひお聞かせください!
コードを扱う場合、「diff」とは通常、2 つのバージョン間の行ごとの変更要約を指します。これは変更された行に
+ または - を付加して追加または削除を示す「ユニファイドビュー」で表示されることが多いです。例:
- We removed coffee and added apple.
同じ diff は、より複雑な変更の場合は横並びビューでも表示されます:
File A File B function foo() function bar() { x = 1; y = 2; return x; return y; } }
しかし、この行ごとの差分の問題は、関数や型などの上位構造に無自覚であることです。もし両バージョンで brace が偶然一致してしまえば、それが異なる関数の brace であっても表示されないことがあります。
素晴らしいツール
difftastic は、TreeSitter による具体的な構文木を使用することでこの問題を解決しようとします。これは行ベースの差分 مقارنة に比べて劇的な改善ですが、残念ながら常にバージョン間のエンティティ(実体)をうまくマッチングしません。このプロジェクト全体を動機づけたのが以下の diff です:
※注:difftastic は
struct PendingClick をマッチしていません。左側で削除、右側で追加と表示しています。*
なぜ
difftastic がここで失敗するのか詳細に調査していないのですが、結果として間違ったと感じています。たとえ全体としての差分が長くなっても、両側にわたって PendingClickRequest と PendingClick を正しくマッチングさせる方がましだと感じます。
この分野のツール・参考文献の概要:
-
semanticdiff.com 最も完成度が高く、考案された semantic diff ツールは、おそらく意外に思わないかもしれませんが、ドイツの小規模企業によるもので、無料の VSCode プラグインと GitHub PR の差分を表示する Web アプリを持っています。残念ながら、私が望むワークフローの基礎となるコードライブラリはありませんでした。
-
ブログ記事:SemanticDiff vs. Difftastic SemanticDiff と Difftastic を比較したこのブログ記事には、素晴らしい詳細(Python において、difftastic が意味的に重要なインデントの変更さえ表示しない!)が含まれています。著者の一人は堅実な背景知識に基づいた硬質な HN コメントを残しており、TreeSitter から離れるようになった理由も説明されています:
特に文脈依存型のキーワードは絶え間ない悩みの種でした。文法自体は正しいように見えても、 Lexer の働き方により解析に失敗してしまいます。パラメータ名を「async」と命名したためにツールが中止になるようなことは望みません。
-
diffsitter TreeSitter ベースで、MCP サーバー付き。README には類似プロジェクトのリストが含まれています。多くの GitHub stars を持っていますが、ドキュメンテーションが特に良くない印象です。仕組みの説明は見つからず、difftastic の wiki によると「ツリーの葉に対する LCS(共通部分列)を計算する」とのことです。
-
gumtree 2014 年に研究/学術的な起源を持つツールです。Java を必要とするため、Emacs から直接使える簡易ツールという用途には適合しません。
-
mergiraf: Treesitter ベースの Rust で書かれたマージドライバ 非常に素晴らしいアーキテクチャ概説が用意されており、Gumtree アルゴリズムを使用しています。ドキュメントと愛らしいイラストから、このプロジェクトは明らかに thoughtful な人間によって作られたことがわかります。
semanticdiff.com の著者が HN コーメンターとして述べたこと: 「GumTree は結果を高速に返す点で優れていますが、我々にとって常に悪いマッチングを返してしまうケースが少なからず存在し、改良のための追跡論文を実装しても解決されませんでした。最終的には、マッピングのコストを最小化しようとするダイクストラ法に基づくアプローチへの移行を決めました。」
-
weave: Treesitter ベースの Rust で書かれたマージドライバ 「HN 最適化」を感じさせます(派手なランディングページ、多くの GitHub stars、MCP サーバーなど)。エンティティ抽出 crate (
) を検討しましたが、コア差分コードは問題ないもののやや冗長です。Greedy エンティティマッチングアルゴリズムを採用しています。データモデルではファイル内での移動を検出できず、これも重要な変更であっても検出できません。多数のヒューリスティックな「影響度」分析が含まれており、これには言語とのより緊密な統合が必要になるため、信頼するには過大スコープであると感じています。sem
を実行するとバグのある出力に遭遇しました。実際に変更されていない行が変更されたように表示されていました。sem diff --verbose HEAD~4仮想的に有用で 80% 完成度の機能が多いですが、私にとって基盤として使用するにはまだ早すぎるかもしれません。ただ、3 ヶ月という短期間でこれらを構築した大学生や学生には敬意を表します。
-
diffast: 2008 年の学術論文に基づく AST の Tree Edit Distance "Python, Java, Verilog, Fortran, C/C++ を専用のパーサを通じてサポート"。美しいサンプル AST の違いのギャラリーがあります。tuples 形式で情報をエクスポートでき、datalog に使用できます。
-
autochrome: Clojure 専用差分(動的計画法に基づく) 素晴らしい視覚的な説明と実例のウォークスルーがあります。Tristan Hume が 動的計画法と A* を用いたツリー差分アルゴリズムの設計 に関する優れた記事を書いてくれました。
私の計画とワークフロー
私の主な使用ケースは、LLM の出力をターン-by-ターンでレビューすることです。私はアジエン(エージェント)やそれらが多数存在する場合でも放っておいて、一度に 10k ライン以上のコードを生成させることを許しません。むしろ、アジエンにスコープされたタスクを与え、数分後に戻り、何を行ったかを見直し、手動で Emacs で修正・調整するか、あるいはすべて廃棄して再度試みる(あるいは自分で書く)というワークフローを望んでいます。
したがって、望むワークフローは以下の通りです:
- デフォルトでエンティティ(型/関数/メソッド)の追加・削除・変更に関する高レベルの概要を見る。
- エンティティごとのテキスト diff を素早く表示する(上記の概要の一部を「展開」する)。
- 変更を素早く編集できること(別場所へ移動せずに行う=インライン編集、diff モードとファイルモードの切り替え不要)。
つまり、Magit のようなファイル/行レベルではなくエンティティレベルで変更をレビュー・ステージングするための何かのようなものを望んでいます。
先日、何度も学び直した「最小スコープでプロジェクトを完了する」という教訓を踏まえ、私の計画は以下の通りです:
- Treesitter ベースのエンティティ抽出フレームワークを自前で作る(まずは Rust に限定)。
- とりあえず単純な Greedy マッチングを行う。
- 差分情報をコマンドラインにレンダリングする。
これが妥当と判断できれば(つまり、特定のコミットに対して
difftastic よりも優れていれば):
- さらにインタラクティブな Magit ライクな Emacs ワークフローに連携する(Magit そのものを再利用できるかもしれない!)。
- 必要に応じて新しい言語への対応を追加する。
- 単純な Greedy マッチングではなく、より高度なスコアベースのグローバルマッチングを試みるかもしれません。
うまくいけば何かリリースすることになるかもしれませんが、GitHub stars や HN karma を集めることを目指しているわけではありません。したがって、自宅で静かに使用することも厭いません。「商業化しよう」とは思いません。
結局のところ、時にはただ棚が欲しいこともありますから。
その他のこと
- Light Diffuser Material: 光拡散器を作るのに適したテヴェック(Tyvek)や他の半透明の不織布を数平方メートル購入したいと考えています。EU へ配送可能なおすすめのベンダーをご存知の方がいらっしゃいましたらお知らせください。
- How They Made This - Coinbase Commercial Breakdown: Crypto は生産的な経済活動への寄生体であり負の和であるものの、奇抜な創造的な人々に資本を集めるという銀色の面があります。
- A Tail-Call Interpreter in (Nightly) Rust
- The Easiest Way To Design Furniture…. Laura Kampf: computer から離れて、テープや小さな木片、紙箱を使って物理空間を設計する方法について。
- Hotel California - Reimagined on the Traditional Chinese Guzheng
- C is not a low-level language: Your computer is not a fast PDP-11
- I Taught My Dog to Vibe Code Games - Caleb Leak
- Loon is a Lisp: Clojure と Rust を組み合わせた欲望を持つ唯一の人ではないことを発見し、大いに興奮しました。現在の実装は狂気じみた vibe coding で作成されたらしく、すぐにひどいバグに遭遇しましたが、存在していることから、ハターになることはしません。
- Print in Place Box: 印刷した蜜蜂🐝を簡単に配布できるように、in-place プリンティングボックスを作成しました。「結果に対して非常に満足しており、蜜蜂がพอดี にフィットし、箱の開閉もスムーズです」とのことです。
- Yeast-based vaccines are a big deal for biosecurity
- How to buy a gas mask under state repression: 国家の抑圧下での生活のためにガスマスクを購入する方法についての信頼できる情報は少ないですが、このガイドを読むことで教育された判断を下すための準備ができるはずです。
- BYTE Magazine Zoomable Map: このズーム可能な地図は、BYTE の各号の各ページを表示しています(最初の号の表紙が左上で、最後の号の最終ページが右下)。検索バーでは全 10 万ページの全文に対して RE2 レギュラ式を実行します。ユーザーインターフェースが極めて高速で情報密度が高いことを示す愛らしい例です。
- How Every Milk Product Is Made
- In Favour of Trying New Things – Daniel Frank
- Staring into the abyss as a core life skill