2 ポイント 投稿者 GN⁺ 2025-01-26 | 1件のコメント | WhatsAppで共有
  • パッケージのインストールが標準になった開発文化は、更新、パッチ、監査、推移的依存関係の管理へとつながる dependency churn を増やし、生産性における隠れたコストを生む
  • JavaScript や Rust のようにパッケージングがよく整備されたエコシステムほど影響が大きく、新しい Tokio プロジェクトでは 28 個の crate、Rocket では 172 個の crate、MiniJinja の CLI 版では 142 個の依存関係が生じる例がある
  • terminal_size のように長く安定している機能であっても、プラットフォーム抽象化ライブラリ の変化によって追加の crate と繰り返しのリリースを抱え込むという逆説が生まれる
  • 小さな機能なら、ChatGPT や Cursor で 依存なしの実装 を作るほうが、依存関係を探して継続的にアップグレードするより速いことがあり、外部コードをコンパイルする負担も減る
  • HTTP、QUIC、グラフィックス、tokio のような難しい領域のライブラリは必要だが、1 つの関数のために大きな依存グラフを受け入れる選択はもっと疑ってよい

依存関係の増加が生むメンテナンス treadmill

  • 開発者は生産性を理由にパッケージを気軽にインストールするが、その結果として更新、パッチ、監査、推移的依存関係の管理が終わりなく続く dependency churn に置かれる
  • パッケージングの仕組みがよく整ったエコシステムほどこの問題は目立ち、JavaScript と Rust は特に大きな影響を受けている
  • Rust の例:
    • 新しい Tokio プロジェクトは 28 個の crate を取り込む
    • 新しい Rocket プロジェクトは 172 個の crate にまで増える
    • MiniJinja 自体は単一の依存関係で存在できるが、CLI 版では 142 個の依存関係 を取り込む
  • 小さな機能 1 つのために、実際に必要なコードよりはるかに多くの外部コードをビルドし管理する構造こそが中核的な問題である

terminal_size が示す安定コードの逆説

  • terminal_size は、その名のとおり端末のサイズを取得する crate である
  • この機能が使う基盤 API はコンピューティング端末の初期から事実上安定していたにもかかわらず、OS によっては 3〜4 個の追加 crate を引き込む
  • 端末が 80x25 なのか 120x40 なのかを確かめるために、何千もの別の関数をコンパイルする状況が生じる
  • この crate は 26 回リリース されているが、同じ機能のために 10 年前のプロジェクトへ入れた自前実装は更新なしでもいまなお動作している
  • リリース回数が多い理由は機能自体が変わったからではなく、土台となる プラットフォーム抽象化ライブラリ が継続的に変化しているためである
  • UNIX では libc 依存が必要になるという例外がある
    • Rust がプラットフォームの libc 定数を公開しておらず、それらの定数が標準化されていないためである
    • ただし libc は一般的で軽量な依存関係であり、避けるのは難しい

セキュリティとコード再利用の文化が依存関係を強める

  • 「big supply chain」的なアプローチは、関数をコピーして貼り付けたり自分で unsafe を使ったりすることを警戒し、プラットフォーム抽象化レイヤーへ任せるよう圧力をかける
  • 依存関係の問題を扱うツールを提供する企業も存在し、セキュリティの名のもとに依存関係を維持し最新化するよう促す
  • しかし多くの依存関係そのものが、セキュリティ問題の主要な発生源 になり得る
  • コードの目標は、ある時点で安定性に達し、更新を必要としないように書かれることであるべきだ
  • Rust エコシステムでは、安定して動作していても、バグトラッカーがやや不活発な依存関係は RUSTSEC で低く評価されることがある
  • 企業的なコードレビュー文化もオープンソースに影響している
    • 新しくて目新しいライブラリを持ち込むエンジニアは、叱られるより報われる可能性が高い
    • その結果 Dependabot のようなツールが生まれ、プロジェクトには継続的に依存関係更新の PR が送られてくる
    • 企業内部では、vendoring、内部監査、全社的アップグレードだけでもエンジニアリングチームを継続的に忙しくさせうる

小さな機能は自分で作れる

  • より単純な道は、必要なコードを自分で書くことだ
  • 初期の作業は増えるかもしれないが、書き終えたコードには新しい crate も不要で、upstream の作者が edge case を直すまで待つ必要もない
  • 自分の用途でコードが壊れたなら自分で直せばよく、動いているコードが必ずしもメンテナンス treadmill に乗る必要はない
  • 2025 年時点では、ChatGPT や Cursor が一般的な小機能の 依存なし実装 を素早く作ってくれる
  • 多数の小さな関数はメンテナンスのオーバーヘッドが小さく、継続的な依存関係アップグレードより負担が軽い場合がある
  • 数行のコードで済むなら、単一機能のために他人の何千行ものコードをコンパイルする必要はない

低依存をより高く評価する

  • すべての依存関係が悪いわけではない
    • 複雑なドライバを抽象化するグラフィックスライブラリ
    • HTTP や QUIC のようなプロトコル実装
    • tokio のように取り除けず、取り除くつもりもない重要なライブラリ
  • ただし 1 つの関数しか使わないのに何百もの関数をコンパイルしているなら、警告サインと見るべきだ
  • 小さな関数を自分で書いて 推移的依存グラフ を避ける選択を、もっと評価すべきである
  • 大きな crate グラフにはもっと懐疑的であるべきで、数年間手を入れなくてもよい単純で安定したコードを前向きに捉えるべきだ
  • sha1-smol はもともと sha1 という名前で SHA1 ハッシュ計算の標準 crate になったが、その後 rust-crypto に名前を譲り、より大きな crypto エコシステムに合わせるよう圧力を受けた
    • 新しい sha1 crate を使うと 10 個の依存関係 が付いてくる
    • レジストリ上の名前が重要で、trait 互換性の要求もあったため、避けるのは難しかった
  • MiniJinja は README で低依存を強調している
$ cargo tree
minimal v0.1.0 (examples/minimal)
└── minijinja v2.6.0 (minijinja)
    └── serde v1.0.144
  • MiniJinja には最後の依存関係を取り除くための PR がある
  • 適切な状況では 自分で作る ことを称賛し、依存関係が少ない、あるいはないオープンソースライブラリを作る作者にもっと多くの評価を与えるべきだ

1件のコメント

 
GN⁺ 2025-01-26
Hacker News のコメント
  • Rust という言語自体は良いが、Rust の依存関係エコシステムは好きではない。C++ は依存関係を追加しにくいとよく不満を言われるが、むしろその点が機能のように感じられる。本当に必要なのかを考えさせてくれるからだ。
    C++ では依存関係を自分で管理できるが、Rust ではすぐに100個を超えて諦めることになる。セキュリティの観点から見ると、正直、自分が何を配布しているのか分からない。
    さらに Rust にはABI 互換性もなく、共有ライブラリの文化もない。そのため、OS のパッケージ配布モデルを壊しているように感じる。Linux ディストリビューションを選ぶときは、そのディストリビューションをビルドしている人たちを信頼するということだが、Rust は誰でも PyPI に何かをアップロードできる Python により近く見える。

    • Debian/Ubuntu はメンテナが署名し、そのメンテナたちが検証されているため、安全だと見なせる。結局のところ、Debian/Ubuntu が信頼に足る人にだけパッケージ署名を許可していると信じる構造だ。
      Docker/Python/Rust はどうだろうか。自分の Docker イメージ、PyPI パッケージ、Rust クレートを作った人たちのことをまったく知らない。
      実質的には、EXE と DLL を ZIP でやり取りしていた時代に戻ったようなものだ。ただ今ではそれをコンテナと呼び、誇らしげに root 権限で実行しているだけだ。
      投稿者が言うように、時には依存関係のソースコードをそのまま取り込むのが最善の場合もある。以前はこれを**ベンダリング(vendoring)**と呼び、Rails/Ruby ではかなり重要だった。後から悪意あるパッケージ乗っ取りの影響を受けないという大きな利点があり、望むならアップストリームのセキュリティパッチを自分でマージできる。
    • こういう見方をする人たちが一体どんな仕事をしているのか分からない。架空のリスクが怖いという理由だけで、プロジェクトごとに車輪を再発明できるのだろうか。そのリスクの大半は社内で再実装してもそのまま残るし、バグも同じだ。
      NIH 症候群が正味でプラスの効果を生んだプロジェクトは思い浮かばない。必須ではない依存関係でさえ、大きく時間を節約してくれる。
      任意の依存関係の一部をプロジェクトごとに作り直すなら、追加のバグや境界条件、下位依存関係の複数バージョン、複数プラットフォーム対応を直す時間はどこから出てくるのか気になる。
    • 最初からパッケージマネージャがあり、パッケージ追加と配布の摩擦を大きく下げると、言語エコシステムは非常に有用なものを失う。つまり、自然選択の良い圧力だ。
      そこに PyPI、npm、Cargo のように誰でもアップロードでき、キュレーションが弱いリポジトリと、小さな標準ライブラリが組み合わさると、苦痛を覚悟しなければならない。
    • 「簡単に取り込めるライブラリ群」という機能が、サポートライブラリを簡単に取り込めるようにするという理由で反機能になるのだろうか、と思う。これは Rust や JS よりも開発者の選択の問題に近いように見える。
      どの依存関係を取り込むかは開発者が選ぶ。誰も使わない依存関係は消えていくだろうし、多くのライブラリに依存関係が多いのは、多くの開発者が依存関係の上に何かを作ることを好むからだ。
      ビルドシステムの話であれば、Cargo は Crates.io の使用を強制していない。簡単にしているだけだ。CMake/Vcpkg、Conan のようにパスベースの依存関係も使えるし、ライブラリを自分で作ることもできる。
      Crates.io を使うとしても、変化が嫌ならバージョンを固定すればよい。最新バージョンを簡単に取得できるようにしているだけだ。
      Rust では既存ソフトウェアの上にソフトウェアを作りやすい。既存ソフトウェアやその変化の速さが気に入らないなら、Cargo のせいにせず、望むやり方でやればよい。
    • C++ で依存関係を追加しにくい本当の問題は、「すべての人が問題なくビルドできる方法」で追加するのが難しい点にある。自分のコンピュータで動いても、潜在的なユーザーやコントリビューターの環境では動かない可能性があり、問題になる。
      ディストリビューションは今でも Rust パッケージをソースからビルドし、クレートの依存関係をリポジトリにベンダリングしている。依存関係がより多く、更新も頻繁なのでより苦痛だが、これは共有ライブラリとは別の話だ。
  • 端末サイズを取得する API が50年間安定していたという話は正確ではない。TIOCGWINSZ ioctl は、私の知る限り標準化されたことがなく、Unix や BSD ごとに名前もいくつかある。
    tcgetwinsize() 関数が POSIX に入ったのは2024年になってからで、このトピック全体にはかなり残念な歴史がある https://news.ycombinator.com/item?id=42039401。Windows 側に行く前からすでにそうだという意味だ。

    • 記事から受けた印象は、代替実装がより優れているから単純なのではなく、自分のユースケースだけを扱っているから単純なのだ、というものに近い。
      それで問題ない場合もあるが、別のユースケースを持つ人や、本人が理解しておらず使ってもいないシステムで実行する人にとっては、ソフトウェアがより悪くなる可能性がある。
      ライブラリの本当の価値は、単純に見える問題にも実は多くの複雑さがあることに対処してくれる点にある。Windows/Linux の両方で動くライブラリに依存関係が3〜4個あることが、それほど多いとも思わない。良い場合なら、そのライブラリを使う人はその領域で自分より経験が多く、自分の知らない未知の部分まで処理してくれる可能性がある。
    • 標準化はされていないが、そうした呼び出しが変わることはない。特にWindows の呼び出しは無数のバイナリにコンパイルされて組み込まれているため、ABI 安定性が保証されている。
      ioctl には確かに問題があるが、terminal-size やその依存関係で複数のリリースを生んだ変更は、ioctl/TIOCGWINSZ 定数や winsize 構造体とは無関係だ。そのコードは変わっていない。
    • この場合、terminal-size クレートは単に Rustix の tcgetwinsize を呼び出し、Rustix はさらに libc の tcgetwinsize を呼び出している。だから同じことを直接やれば依存関係をかなり減らせるし、その代償はWindows 対応くらいだ。
      この API が50年なのか25年なのか安定していたかは細部の話だ。その依存関係はその複雑さを処理しているふりすらしておらず、その関数が近い将来に変更または削除される可能性も低い。
    • 端末はとても単純に見えるが、初期にベンダーが多すぎ、業界標準が定着しなかったために大きな厄介事になった好例だ。最も近いものが VT100 なのか VT102 なのかも曖昧だ。
      たいていはそこに直接書き込むが、端末サイズや raw モードのような機能はめちゃくちゃで、ioctl のようなものが必要になる。正直かなり微妙だ。
      ただ、ライブラリはさらに微妙だ。ncurses にリンクしたくないなら、神のご加護が必要だ。
  • 最近、2006年に作った最初のスタートアップのWebアプリを復活させた。メディア共有中心のソーシャルメディアサイトで、当時としてはかなり単純な LAMPスタックだった。PHP 5、MySQL 3.2だったが、当時のソーシャルメディア機能はおおむね備えていた。
    新しいCI/CD技術を自分で試してみたくて、このアプリを学習用に過剰にエンジニアリングしながらデプロイ過程を作っている。WordPressやHello Worldアプリを使うこともできたが、こちらのほうがずっと面白い。
    PHPはほぼすべて自分で書いた。認証/認可、テンプレート、フォーム処理などのためのライブラリも自作し、メール送信にだけPEARライブラリを1つ使った。フロントエンドは素のHTMLで、JavaScriptはほとんどなく、メディア再生にはFlashを使っていた。2006年には、たいていこうやって作っていた。
    19年前のアプリを再び動かすのに、1時間ほどしかかからなかった。古いPHPの mysql ドライバを mysqli に置き換え、MySQL 8に合わせてスキーマと一部のクエリを調整した。主には、今では予約語になった単語をバッククォートで囲み、より厳格になったカラムのデフォルト値を直した程度だった。動かなかったのはFlashだけだった。
    一方、今の職場ではJava 8で書かれたSpring Bootアプリを何十個も運用しているが、数十個の依存関係に由来する脆弱性リストがページ単位で積み上がっている。1つを更新すると他のライブラリも芋づる式に上げる必要があり、推移的依存関係のせいで悪夢になる。だから最も致命的な脆弱性だけを最小限対処していて、全体を上げる現実的な計画はない。
    面白いのは、2006年のPHPアプリがやっていたことと、今のSpring Bootアプリ群がやっていることが大して違わない点だ。結局は全部CRUDで、その周辺にエンタープライズ向けの飾りやツールがはるかに多く付いているだけだ。

    • GoやC系のLinux世界を見て、厚いライブラリという哲学に納得するようになった。ある領域の問題を解くライブラリを1つ取り込み、そこに必要な部分を足していくやり方だ。
      チェックリストの各項目ごとに依存関係を1つずつ引っ張ってくることはしない。重複作業は生じ得るが、アップグレード経路はずっと楽になる。
    • 新しいCI/CD技術の大半は、サードパーティ依存関係と変わり続ける実行環境を維持するために生じた複雑さのせいで標準になったものだ。scp でデプロイする古いLAMPアプリケーションでは、たいてい問題にならない。
    • 一部は同意するが、車輪を再発明して、そこにバグを入れることに時間を使いたくはない。
      たとえば FHIR やHL7のような標準的な通信メッセージ形式については、すでに複雑な標準定義全体を自分で実装したいとは思わないはずだ。
      暗号化関数も自分で書けば、たいてい自分の足を撃つことになり、長年発見されてきた深刻なセキュリティ問題がそれを証明している。
      今は、ソリューションがどのように正しく作られるかより、ビジネス課題の解決に集中したい時代だ。AIの登場で、すべてのコードが目をつぶってつなぎ合わせたもののように感じられるため、この点はいっそう重要になっている。
      すべてを自作することに時間を使えば長期的には有利かもしれないが、まずは競争を生き残らなければならない。競合は初期に速くて捨てられるコードで、すでに市場を取っているかもしれない。
    • 職場にはかなり厳格な 静的解析ポリシー があり、4月からさらに厳しくなる予定だ。依存関係を自動アップグレードするのに https://docs.openrewrite.org/ を見たことがあるか気になる。
      最近、Java 8、Spring Boot 2、SwaggerからJava 17、Spring Boot 3.3、OpenAPI 3へ移行したが、かなり painless だった。
      まだ一部の直接依存関係と推移的依存関係をさらに上げる必要はあるが、最大の障害はマイグレーションで処理できた。
    • ここで重要なのは、Rustには推移的依存関係の問題が事実上ないという点だ。あるクレートを上げたら公開依存関係も上がり、その依存関係がAPIに露出していて相互作用しなければならないなら、当然一緒に上げる必要がある。
      しかしCライブラリにリンクしない限り、非公開の推移的依存関係はまったく関係ない。同じ依存関係ツリーにSemVer互換でないクレートのバージョンを好きなだけ置けるし、必要なら複数バージョンに直接依存することもできる。
      Java式の大規模な一括アップグレードは不要で、脆弱性のあるものを1つだけ上げればよい。C#にも似た機能があると理解しているが、もう少し冗長だ。
  • 100%同意する。NodeJSは自分のキャリアに大きな影響を与えたが、NPM はひどかった子ども時代以上のトラウマを与えた。
    新しいプログラマが、友人に見せる新しいアプリを作っているところを想像すればいい。新しい依存関係を追加しようとしたら他の依存関係と合わず、全部更新することにする。次の瞬間、何も動かなくなり、Babelが悲鳴を上げる。
    それでも何とか解決できるだろうと思うが、すぐに基本的なことすら文字どおり動かないという未解決のGitイシューを見ることになる。たとえばExpoには、デフォルトの新規React NativeプロジェクトがAndroidでビルドできないというイシューが開かれている。
    半分くらいは誰も気にしていないようで、その場合の解決策もNodeエコシステムではなくAndroidエコシステムのどこかにある。下まで全部ダクトテープだ。それでも、数十億ドル規模のプロジェクトが動かないテンプレートを配布できるなら、自分のサイドプロジェクトが半分動かなくてもインポスター症候群を感じる理由はない、という自信を与えてくれもする。

    • これはNodeの問題であって、Rustの問題ではない。依存関係同士が互いに「気に入る」必要はない。すべてのバージョンを同時に置けるし、何も壊れない。
    • Node.jsとnpmが制御不能になったことは、約15年前ごろ自分にとって警鐘になった。
      最初の依存関係を取り込む瞬間を、個人的な失敗のように見るようになった。その瞬間、単にscriptタグで普通のJSを読み込む代わりに、package.json 1つを書くためだけに、パッケージングと組織化がすべて必要になったからだ。
  • Go から Rust エコシステムに移ってきて驚いた点です。成熟した Go プロジェクト、たとえばどこかの会社の本番用 Web バックエンドでも、推移的依存関係まで含めて 10〜20 個の依存関係しか持たないことがあります。
    記事で述べられているように、小さな Rust プロジェクトでもそれよりはるかに多くなる可能性が高く、非同期処理をするとほぼ確実です。
    これが文化のせいなのか、言語機能のせいなのかはよく分かりません。たとえば Go ではインターフェースが暗黙的に満たされるため、実装していることを示すために何かを取り込む必要がありません。

    • 大きな理由の一つは、Go には優秀で完成度の高い 標準ライブラリがある一方、Rust には本当にそうしたものがない点です。
      Go では言語や標準ライブラリに入っているのに Rust では依存関係が必要になるものとして、グリーンスレッド、チャネル、正規表現、HTTP クライアント、HTTP サーバー、時間、コマンドラインフラグ、ロガー、アニメーション GIF の読み書きなどがあります。
      Go 言語そのものが大好きというわけではありませんが、ツールと標準ライブラリでは先頭を走っており、私が使ったものの中では間違いなく最高です。
    • Rust と Go の違いは、とりわけ 標準ライブラリにあります。Rust の標準ライブラリは意図的に小さく作られています。
    • Go の膨大な標準ライブラリは、依存関係の数を減らすうえで大いに役立っています。
  • 論旨には大いに同感です。他のライブラリの抽象化を自分のライブラリの外へ露出させることを許すのには、隠れたコストが多くあります。絶対にやってはいけないという意味ではありませんが、判断する時点で将来のコストをバランスよく見る必要があります。
    ラップしているパッケージが設計を頻繁に変えたり、目標を変えたりすると不安定性が生まれます。本来はソフトウェア工学における最も単純な保証の形だった機能が、消えたり分割されたりします。
    また、キャリアのあるオープンソース貢献者ではないものの特定分野に詳しいオーナーが、エコシステムに参加しにくくなります。「X をきれいに行う方法を作った」が、「人々は Z を好むかもしれないので、X は Y の下に合わせるべきだ」という数週間の議論、交渉、政治に発展するなら、全員の時間の無駄です。
    人生で学んだことの一つは、最も単純なものが最も長く生き残るという点です。ミニリス(minilith) とモノリスはもっと頻繁に称賛されるべきです。Rust だけの問題でもなく、さまざまな言語で見てきました。オープンソースコミュニティは原子単位の大きさのパッケージを強く推すことが多いですが、実際のユーザーコミュニティの利益というより、旗を立てることや所有権の移転が主目的なのではないかと、よく疑問に思います。

  • 2025 年には、よくある関数の依存関係なしの実装を ChatGPT や Cursor に作らせるほうが速い、という見方にある程度偶然たどり着き、ますます同意するようになりました。特に大規模な React アプリの 依存関係地獄を経験してからはなおさらです。
    SaaS やサードパーティサービスへの依存関係も考える必要があります。その多くはよくあるパターンで、すでに解かれた問題なので、LLM が素早く複製できます。

    • 以前よりも、ライブラリを追加する前に AI が実装した小さな 内部ユーティリティ関数から始める可能性がずっと高くなりました。
      範囲が限定された問題にはかなり効果的で、自分で書くよりも完全で堅牢な実装を AI に作らせることができます。
      後でライブラリを追加することにしても、自然なカプセル化ができます。自分の関数の実装だけをライブラリを使うように変えればよく、利用しているすべての箇所を必ず触る必要はありません。時期が来れば、複数のライブラリを試すのも簡単です。
  • 皮肉なことに、かなり興味深いです。Armin は Python Web フレームワーク Flask の原作者です。同じような時期に Bottle という非常によく似たライブラリもありました。
    機能はほとんど同じでしたが、Flask が大きな人気を得た一方で、私はいつも Bottle を好んでいました。単一ファイルで依存関係がなく、プロジェクトにそのままコピーするのがとても簡単だったからです。
    改造するのも簡単で、最終的には全体を理解できるほどになりました。サーバーと WebSocket には Gevent を組み合わせましたが、そのやり方でかなり重いプロジェクトも作れました。
    今でも小さな Web プロジェクトには Bottle を使いたいという強い衝動があります。ただ、Python が何年もかけて導入してきた現代的な慣行の多くについていけていないため、今では少し古びて見えるのが残念です。

  • 自作するには有能なエンジニアリング能力が必要です。NPM や PyPI のようなエコシステムで常にライブラリだけを取ってきていたエンジニアしかいないなら、多くの問題の解法を自分たちで開発するのに苦労するでしょう。特に、長く持ち、必要な柔軟性を備えた解法でなければならない場合はなおさらです。
    「自分で行き止まりへプログラミングしない」ためには多くの練習が必要です。
    既存ライブラリより簡単に、よりうまくやれる場合もよく見ます。あるプロジェクトで、ファイル先頭にメタデータがある Markdown 変種のパーサーを実装しました。小さな文法を使い、画面 1 枚にも満たないコードでパーサーができました。
    ところが同じファイルをパースするフロントエンドライブラリが、予想よりはるかにひどいものでした。メタデータ識別子にハイフンがあると壊れ、調べてみるとメタデータ識別子をオブジェクトメンバー名として直接使っていました。単純な JSON オブジェクトを使う代わりに、名前の選択を人為的に制限し、something-something のようにハイフンがあると壊れるようにしていたわけです。
    結局、私のパーサーは捨てられました。理由は、人々がそれを「メンテナンス」しなければならないというものでした。しかしそれはただ動いていたし、文法変更にも簡単に適応できました。パーサーについて少しでも知っていれば、理解しにくいものでもありませんでした。
    信じがたいことですが、チームでパーサージェネレーターライブラリを使ってパーサーを書いたことがある人は、私以外に誰もいなかったようです。こうした例は他にもたくさんあります。

  • 1つの関数しか使わないのに数百個もコンパイルすることになるなら、警告ランプが点くべき。約1年前、サードパーティ依存関係を更新するプロジェクトを進めたことがある。
    そのうちの1つは、ありとあらゆる数学関数が入った充実した数学ライブラリだった。少し掘ってみると、私たちが使っていたのはリストの中央値を求めるたった1つのメソッドだけだった。
    担当エンジニアにWikipediaのページを示し、依存関係を取り除いたうえで、その数学演算を行う単一のメソッドを書くように言った。
    ただし本当の問題は、サードパーティ依存関係を使うこと自体ではなく、ライブラリの狭い一部分だけを取り込むという概念が必要だという点にある。巨大なライブラリのごく一部だけが必要なら、なぜ全体を持ってこなければならないのか? そのための方法として「マイクロフレームワーク」を提案したという話を聞いたことがある。

    • Rustでは、ライブラリが条件付きで有効・無効にできる**機能(features)**を定義できるため、実際に含まれる範囲を調整する組み込みの方法がある。Tokioは良い例で、Tokioに必要な直接依存関係は合計2つだけだと知ると驚くかもしれない。残りはすべて任意だ。 https://github.com/tokio-rs/tokio/blob/ee19b0ed7371b069112b9...
      残念ながら、人々が依存関係で使われるデフォルトの機能セットを入念に見て、積極的に削っているようには思えない。追加機能を取り込む構文は簡単だが、デフォルトで有効になっている任意機能を外すには no-default-features を指定し、それでも欲しいデフォルト機能を一つひとつ追加し直さなければならないので、より面倒になる。
      さらに悪いのは、ライブラリが自分の依存関係から不要な機能を切り落とせるようにするには、自前の機能を直接作って各依存関係の機能にマッピングしなければならないことだ。たとえば依存関係が5つあり、それぞれに必須依存関係1つと任意依存関係4つがある場合、ユーザーが推移的な機能を完全に制御できるようにするには、自分のライブラリにマッピング用の機能を20個作る必要がある。自分のコードについて下流のユーザーに配慮して作る機能は、ここには含まれてすらいない。
      だんだん、機能まわりの使い勝手が、特に不要な肥大化を減らす方向ではあまりにも悪いため、Rustのコンパイル時間問題の触媒になっているのだと見るようになった。こういう話題が出ても広く議論されてはいないようなので、現状が続くときに指し示せるよう、自分の強い考えをブログ記事に書くべきなのかもしれない。
    • そうすると、JSエコシステムのleftpadのようなことが起きる。
      信頼の仕組みが優れていれば、一方では問題ではないと思う。
      Shadcnのような方式、つまりコンポーネントが気に入ったら自分のライブラリにコピーするという考え方も、ある程度は気に入っている。だが脆弱性が出たとき、自分が影響を受けるのかどうか分からない。
      あるコードではそれでよいが、別のコードでは、できるだけ多くの目に見られているものに本当に依存すべきだ。
    • 知らない人向けに擬似コードで書くと、この程度だ。
      Median(list) { let len = length(list) if len % 2 == 0 { let x = floor(len/2) return (list[x] + list[x+1]) / 2 } return list[len/2] }
      ただし、リストはすでにソート済みだと仮定する。ここで is_odd を呼びたい誘惑はこらえた。