3 ポイント 投稿者 GN⁺ 2023-07-03 | 1件のコメント | WhatsAppで共有
  • 10年目のJavaScript開発者がpackage.jsonを読む場面を苦痛の試練として描き、1つの設定ファイルにエコシステムの蓄積された複雑さがどれほど詰め込まれているかを示している
  • mainbrowsertype: moduleのようなフィールドは、Isomorphic JavaScript、ブラウザバンドル、CommonJSからESMへの移行における古いマイグレーションの傷跡を思い起こさせる
  • scriptsdependenciesdevDependenciesには、lintの派生形、watchman、gulp、left-pad、moment.js、resolutionsのように、古いツールとパッケージ管理の問題が複雑に絡み合っている
  • eslintpostcssjestといったビルド・テストツールは、厳格なルール、プラットフォームごとのインストールエラー、ESM・TypeScript設定の難しさを通じて、開発環境の維持コストを押し上げている
  • Nodeのenginesを見ながらもBunやDenoへ移行しない理由は、苦痛が消えるどころかさらに大きくなると確信しているからであり、開発者はファイルを閉じてひとまず生き延びる

package.jsonを読む試練

  • JavaScriptを10年間使ってきた開発者が、コンピュータの前でpackage.jsonファイルを開き、最後まで読まなければならない状況から話が始まる
  • このファイルは単なる設定ファイルではなく、JavaScriptエコシステムで経験した記憶と苦痛を一気によみがえらせる対象である
  • versionはまだ1.0に達しておらず、SemVerはずっと以前から無視する術を覚えた不快な記憶として残っている
  • nameは、すでに7年前に誰かが確保したまま更新していないパッケージのせいで、望む名前を使えない状況を示している

モジュールとバンドリングの古傷

  • mainbrowserフィールドが共存していることで、Isomorphic JavaScriptの痕跡が浮かび上がる
  • ブラウザバンドルでfsモジュールをrequireしようとした記憶と、それを動かすために使ったハックが不快な体験として残っている
  • type: moduleは、requireからimportへ移るマイグレーションと結びついている
  • CommonJSとESMモジュールを相互にimportしようとした経験は、大きな苦痛として残っている

スクリプトと依存関係の混乱

  • scriptsは、熱く苦しい混乱の中心である
    • lintlintalllintfastlintdiffのような派生形がある
    • 並列実行、不明瞭な引数、JSON形式の引数に対する二重エスケープが登場する
    • yarnやpnpmに移行した後でも、一部のサブコマンドは依然としてnpmを呼び出す
  • 一部のスクリプトは今でもwatchmanを使っている
    • symlinkをサポートしないため使うべきではないことを覚えておかなければならない
    • 関連イシューは2015年から開かれたままである
  • gulpベースのスクリプトも残っており、もっと現代的なものに置き換えたいが、誰も交換する勇気を出せない

パッケージ一覧が呼び起こすトラウマ

  • dependenciesには非常に多くのパッケージがあり、それぞれが独自のトラウマの記憶を呼び起こす
  • left-padは小さなパッケージだが、「インターネット全体を壊した」伝説的なパッケージとして残っている
  • 依存関係の一覧を修正するたびに、一部のパッケージはコンソールに寄付のお願いやbreaking changeの警告を長々と出力する
  • 開発者は、悪意あるパッケージが秘密を盗んだりコンピュータを壊したりしないことだけを願う状態になる
  • moment.jsは良いAPIを持つライブラリとして記憶されているが、インターネットはそれをmutableすぎて大きく、treeshakingも効かないと判断し、いまやdate-fnsへ移行しなければならない状況である
  • パッケージのバージョンは現在の基準から大きく遅れており、アップグレードが必要だが、過去のアップグレード体験のせいで大きな苦痛が予想される
  • resolutionsは、パッケージのアップグレードを避けるために選んだ、もう一つの苦痛として残っている

開発ツールが生んだもう一つの複雑さ

  • devDependenciesを見ながら、non-dev dependencyだけで済んでいた時代を思い出すのは難しい
  • eslintの設定は厳しすぎるほどになり、小さなミスでも赤い下線が現れ、CIはlintの問題をまるで世界の終わりのように扱う
    • チームのジュニアエンジニアには誤った安心感を与える
    • どのルールを有効にするかを巡って何度も議論があった
    • コードベース各所のeslint-ignoreの数だけ苦痛が増す
  • postcssは自分で直接使っているわけではないが、依存関係の依存関係が要求するパッケージとして存在している
    • 新しいプラットフォームでnpm installを試すたびに、不明瞭なC++コンパイルエラーを引き起こす
  • jestは高速なテストランナーとして始まったが、今では大きく重くなり、一部のBabelパッケージに依存している
    • アプリケーションの残りの部分はesbuildとswcの組み合わせでトランスパイルされる
    • ESMとTypeScriptに合わせて正しく設定する作業は、「PhDレベルの科学プロジェクト」のように難しい
  • コードベースには、TypeScript、esbuild、swc、babel、eslint、prettier、jest、webpack、rollup、terserなど、多くのツールとパーサーが関わっている
  • enginesにはnodeが明記されており、BunやDenoに移行しても苦痛は止まらず、むしろ悪化すると見ている
  • 最後の閉じ中括弧まで読み終えたあと、タブを閉じて息を整え、手と脳が無事なまま、ひとまず生き延びる

1件のコメント

 
GN⁺ 2023-07-03
Hacker News の意見
  • かなり笑える。18年ほど JavaScript を書いて、3年くらい前にやめたけど、このリストにある悩みの大半はこの10年くらいのものだ。
    2013年に書かれていたなら必ず入っていたはずの ポリフィル が抜けているのが特に目立つ。2013年の JavaScript の悪夢で、今ではほとんど消えたものは他に何があるだろう?もちろん、この笑えるリストに置き換わったわけだけど。
    一番気に入ったのは、コードベースで動いているツールやパーサーを数えながら TypeScript、esbuild、swc、babel、eslint、prettier、jest、webpack、rollup、terser を思い浮かべ、他にも抜けがあるかどうかすら知りたくなくなるほどつらい、という部分。

    • Grunt、Gulp、CoffeeScript、IIFE、上書きされた undefinedwithプライベートフィールドのまねのためのクロージャ、変更されたプロトタイプ、デフォルトのグローバル変数、呼び出し位置によって変わる this、比較時の暗黙の型変換……
      そう、JavaScript の10年以上はこんなふうに過ぎてきた。
    • コールバックの話には同意する。でも10年前の自分の JavaScript を見ると、最大の苦痛はすべて SomeFunction.prototype をいじって作った 偽クラス だった。
      それを即時実行の無名関数で包んでオブジェクトとして返し、SomeFunction がクラスであるかのように new SomeFunction()SomeFunction.actualFunction() を呼ばせていた。アロー関数がなかったので、イベントをスコープに結び付けるために Function.prototype 自体をいじって .apply() を使ったり、イベントコールバックごとに終わりのない .bind(this) 地獄に落ちたりしたのも大きい。
    • Firebug を使うために Firefox で開発しつつ、ie6、ie7、ie8 をそれぞれ個別にサポートしなければならなかった。
    • ユーザーがインストールしているフォントに制限されないように、ヘッダーを小さな Flash アプレット に置き換えて、好きなフォントを使っていた時代もあった。
    • コールバック地獄 と、全般的な型の欠如が大きかった。
      今日も、まだ Promise も宣言ファイルもなしに素の JavaScript を使っている会社のテスト課題を書くという不運に遭ったけど、2012年に初めて Node を触ったころに戻った感じだった。JavaScript を使わなければならないという要件は JSDoc で回避したが、記憶よりずっとひどかった。
  • 新しい JavaScript、正確には TypeScript プロジェクト を始めるたびに、いつも同じ手順を踏む。
    たいていフロントエンドなので React を選ぶ。Svelte も試してみたい気持ちはある。バンドラーは以前なら create-react-app や Webpack のラッパーだったが、今は設定が少なく主要フレームワークをすべてサポートする Vite が最善だ。
    TypeScript コンパイラをインストールして設定するが、たいていテンプレートからコピペする。最新の安定版 JavaScript と ESM モジュールをターゲットにし、eslint と prettier をそろえて設定し、TypeScript や React などのプラグインも追加する。コミット前に Prettier と eslint を走らせるフックも入れる。
    IDE 拡張もインストールし、.devcontainer や推奨拡張機能の一覧を作っておく。スタイル自体には興味がなく、コードの一貫性とミスの検出が重要なので、eslint はたいてい既成の設定を使う。
    その次に jest や vitest のような単体テストを設定し、TypeScript 関連の設定と eslint ルールがまた付いてくる。CSS フレームワークは tailwind が好きなので styled-components と twin.macro までインストールして React とうまく合うようにし、Google Fonts プラグインやフォントライブラリも入れる。
    こうして丸一日くらい経ってようやくプログラミングを始められる。なぜこんなにやることが多いのか? Vite + TypeScript + React/Vue/Svelte + eslint/prettier + tailwind/bootstrap + jest/vitest の組み合わせを使う人はこれほど多いのに、90% のユースケースにそのまま合う 設定不要ライブラリ はなぜないのだろう?

    • これを全部知っている人でも1日かかる。バックエンドと運用寄りの開発者として、何度かこういうスタックに入ってみようとしたが、正直多くの場合は普通に失敗した。
      ツールチェーンをどうにか立ち上げても、自分がコントロールしている感じがまったくなく、ごく小さな揺らぎでも崩れそうで、直すこともできなさそうだった。
      最近は、この JavaScript スタックの大半を自分がやりたいことの 障害物 と見ることにして、昔のように Flask と Jinja2 ベースのサーバーサイドレンダリングアプリを作った。最新風にきらびやかではなく、CSS もないので今はかなり不格好だが、素のモダン CSS で行ってみるといいと助言された。それでも、そのスタックで機能をどれほど速く作れるかには驚くほどだ。
    • リンターが2つある。これだけでも狂っている。残りはもっとひどいが、JavaScript エコシステムにおけるツール選択の最小の断片でさえ 苦痛 に満ちている。
    • TypeScript のスターターディレクトリをコピーして、package.json の名前だけ変える。もちろん自分はバックエンドだけだけど、フロントエンドでもそういう 個人用テンプレート を作れるのでは?
    • npm create がやってくれないの?
      npm create vue を実行すると、設定の大半がすでに済んだ、かなり良くて動作する Vue 構成が出てくる。ボイラープレートや細々した遊びが多いことには同意するが、今われわれが Web で作るアプリも昔よりずっと大きくなっている。ESModules だけでツールなしに JavaScript を使うこともできるが、それらのツールが実際に何かをしてくれるから、あまりそうしないのだ。最もきれいな方法ではないが、とにかく動く。
    • 上で好みの構成を全部入れた スタブプロジェクト を作って GitHub に置いておけばよさそう。
      新しいプロジェクトを始めるときにクローンして、何か所か名前だけ変えれば準備完了になるはず。
  • これは、人々が JavaScript と言っているものの、実際には「Node エコシステムのごちゃごちゃ」を意味している、また別の例です。
    最初から package.json ファイルを開いて読む必要があると言っていて、その後もずっとその流れです。言及されているもののうち、JavaScript 自体に関係するものはほとんどありません。

    • JavaScript 開発者のうち、NodeJS を使わない割合はどれくらいでしょうか?
      JavaScript と NodeJS が同じではないのはその通りですが、ほとんどの人は筆者が何を意味していたか理解したはずです。もっと正確に書ければよかったとは思いますが、完全に間違っているとも言いにくいです。
    • なるほど、エコシステムは言語批判の有効な対象ではない、ということですね。
    • package.json を使うバンドラーなしで、一度本当に JavaScript を書いてみてください。ECMA が公式仕様にパッケージマネージャーやバンドラーを入れるまでは、package.json は事実上 JavaScript の一部です。
    • それがそんなに気になるなら、誰かが JavaScript と言うたびに、実際には ECMAscript のことを指しているのだと思えばいいです。
    • その通り。これは JavaScript エコシステムへの批判です。
  • ふざけた調子がいいですね。皮膚と神経終末が何層も焼け落ちた後では、こういう痛点を思い出すのは有益です。
    逆に言えば、やる気のあるジュニア開発者に新しい MacBook を渡して深い水に放り込んでも、1日以内に生産性を出せます。コンテナ、.NET、nginx、よく使うコマンドラインツール100個を知らなくてもよく、問題が起きたらただ検索すればいいのです。

    • その通りです。皮肉なのは、その妥協のせいで、ブラウザで動くものを20年作ってきたシニア開発者を放り込んでも、1日以内には準備が整わないことです。
      直接手で触れさえすれば数日で船全体を作れると分かっているのに、渡された粗悪な道具でボトルシップを直すために、不必要な複雑性の層と戦わされる。JavaScript、HTML、CSS だけを使う個人プロジェクトを素早く進めていたところから、現代のフロントエンド業務に投入されると、まさにそんな感じです。
    • そうすると、1日から3日かけて何かをぐちゃっと貼り合わせて作り、その後10年後悔して、こういう文章を書くことになります。循環は続きます。
    • 私の知っているジュニアたちは「この複雑さを処理するフレームワークを作ろう」と言います。
      それを作るのに何カ月もかかり、問題の一部がフレームワーク過多であるため翌日には時代遅れになる、という事実を素朴に分かっていません。
    • 今では検索せずに ChatGPT に聞きます。
  • 「説明どおりにやったら、grunt がインストールされていないと言われた。私はもう年寄りなので grunt が何なのかよく分からず、自分の grunt ファイルも何か合っていなかった。そこで少し検索して、grunt が何なのか分かった。Grunt とは……今でも実は何なのかよく分からない。」
    --Joe Armstrong, https://www.youtube.com/watch?v=lKXe3HUG2l4
    数日前に gem install wayback_machine_downloader を実行しました。Ruby という言語を、そのエコシステムに入るほど好きになったことはありませんが、外から見る限り、そのエコシステムは驚くほど安定して維持されているように見えます。gem は今でも使われているツールで、Rails もまだあります。
    複雑なフロントエンドが必要で、JavaScript が0〜30行を超えるような新しいプロジェクトを待っています。そのときは、最近いちばん人気の巨大フレームワークよりも HTMX のほうが幸せになれそうです。

  • これは JavaScript ではなく npm についての記事です。
    こういうごちゃごちゃのせいで npm が嫌いで、できるだけ避けています。個人的には Deno を使い、職場では npm を不要にしてくれる monorepo ベースの bazel 風のものを使っています。
    ただ、JavaScript/TypeScript はプログラミングしやすい言語だと感じていて、何を作るにしても最初に選ぶ言語です。人々が「JavaScript はひどい」と言うとき、実際には npm で React アプリをビルドするのがひどい、という意味であることがあって悲しくなります。現代の JavaScript/TypeScript のコーディングは本当に楽しいです。

    • 同意します。ビデオゲームを作っているのですが、依存関係を一切使わないことにしたら、コーディングが本当に楽しくなりました。
  • JavaScript とそのエコシステムが私たち全員に PTSD を残したせいで、この記事のコメディを楽しめなくしているように感じます。
    面白い文章をめぐって「真面目な」議論が多すぎます。好きなだけ解剖してもいいですが、本当の意図はユーモアだということを覚えておくとよいでしょう。

    • ユーモアだからといって真面目ではない、というわけではありません。
  • 正直、最初に触れたエコシステムが Node で、後から学んだものにもそれぞれ十分に狂った問題があったと思っています。
    Haskell は複数のマシンで使うのが非常に難しく、2019年に MacOS にインストールするのも苦痛で、エコシステム管理も大変でした。マクロと拡張はかなり混乱を招き、言語の後方互換性もよくありません。一方で、2010年の JavaScript はたいてい2023年でもそのまま動くと期待できます。
    Docker イメージや codespace/gitpod のような環境をきちんと作ることさえ難しく、GitHub が提供するデフォルトイメージもいまいちで問題があります。
    C はほとんどのプラットフォームでおおむね問題ありませんが、言語自体は正直複雑で、システム全般に関する膨大な知識が必要です。その結果、一般的なプログラミングの多くの用途には合わない、移植性の低いコードになりがちです。
    もっと言うこともできますが、複数の言語を使ったことがある人なら、どのエコシステムにも複数の欠陥があり、私たちは皆ずっと苦しみ続けていることを知っています。PHP ではプロジェクト設定を数日以内に終えられたことがなく、Elm は苦痛が少ないものの、善意の独裁者状態と開発速度という独自の問題があります。

    • だから、ほとんどすべてのことに Go を使おうと努めています。最適な選択でなくても、雑務が最小限です。
    • C が複雑ですか?使用頻度に関係なく、頭の中に入れておけるほとんど唯一の言語です。現代の JavaScript 構文の一部は毎日使っているのに、記憶から簡単に蒸発します。
  • 複数のプログラミングパラダイムや言語、フロントエンドとバックエンドのどちらにも慣れていると思っている多言語開発者
    package.json が20行を超えているのを見るたびに、ほぼ正確にこういう反応になる

    • 今開発中の新しい Angular フロントエンドをにらみつけているところだが、フロントエンドの構成要素が4つあり、それぞれの package.json が200行を超えている
      悪魔を追い払おうとするかのように声に出して言う。「恐れてはならない。恐怖は精神を殺すものだ。恐怖は完全な消滅をもたらす小さな死だ。私は自分の恐怖と向き合う。それを私の中を通り過ぎさせる。通り過ぎた後には、心の目でその道筋を見る。恐怖が通り過ぎた場所には何もないだろう。ただ私だけが残る」
      泣いている
    • 自分も同じ。ただ、話題から外れるけれど、プロフィールのリンクが uBlock Origin にブロックされて、怪しいWebサイトの迷宮へつながっている。hackernewsers を使ったことがないので、これが正常な動作なのかは分からない
  • 昔ながらの JavaScript 開発者が記事を読みながら PTSD とフラッシュバックに苦しんでいる場面
    恐れてはならない。恐怖は精神を殺すものだ。恐怖は完全な消滅をもたらす小さな死だ
    私は自分の恐怖と向き合う。それを私の中を通り過ぎさせる。通り過ぎた後には、心の目でその道筋を見る
    恐怖が通り過ぎた場所には何もないだろう。ただ私だけが残る
    記事を読み終えて生き延びたことに気づくが、すでに汗だくになっている