🛣️

全ての道はRomeへ続くのか - これからのJavascript開発を考える

2021/11/24に公開

Romeとは

現代のJavascript開発には多くのツールチェーンが必要とされます。Babel,webpack,Jest,ESLint,Prettier,Typescriptなどを組み合わせて開発することが多く、さらにこれらの一部代替選としてesbuild,SWC,Viteなどのツールチェーンの選択肢が存在し、選択肢の多さやその組み合わせの複雑さに苦い思いをしたことがある方も少なくないのではないと思います。
こうした中で、新たに開発が進められているツールチェーン、Romeをご存知でしょうか?
https://rome.tools/

Romeは先に挙げたように複数のツールチェーンを役割ごとに組み合わせて使うのではなく、1つのツールチェーンでこれら全ての役割を担ってしまおうという壮大な計画を持つツールチェーンです。
Romeは2020/03にFacebookより発表されました。現在は法人化され、yarnやBabelの生みの親であるsebmckやTC39の一員の Jamie Kyleによって開発が進められています。

本稿ではRomeが解決しようとしている問題から現在の開発ステータス、そしてRomeを通じて見るJavascript開発の今後について考察してみようと思います。

現代のツールチェーンの問題点

冒頭でも少し触れましたが、まずRomeが考える現代のJavascriptツールチェーンの問題点についていくつか上げてみようと思います。

ツールチェーンの選択肢の多さ、役割の理解

現代のJavascript開発で必要とされるツールチェーンは大きく以下に分類されるかと思います。
()の中は代表的なツールチェーンを表しています。

  • トランスパイラ(Babel,esbuild,SWC)
  • バンドラ(webapck,Parcel,rollup)
  • 型チェック(Typescript,flow)
  • リンター(ESLint)
  • フォーマッター(Prettier,dprint)
  • テスト(Jest)

これらを組み合わせて1つのJavascript開発環境を作るわけですが、実際にはNext.jsなどのフレームワーク内部である程度よしなに設定してくれてることも多いと思います。それでもカスタムが必要だったり、自分でパッケージ管理をする必要はあるので完全に隠蔽されることは少ないため、実際の開発ではこれらのツールチェーンに関する知識を要求されることも少なくないかと思います。
各ツールチェーンの知識が要求されると言うのは、その分調査や実装工数につながってしまいます。

ツールチェーン同士の組み合わせの複雑さ

ツールチェーンを組み合わせる際に予期せぬバグや矛盾に当たってしまうことも大きな問題です。
例えばESLintの指定で;をなしで統一してるのに、Prettierでは必ず末尾に;を付けてしまう場合です。ESLintでPrettierの設定を取り込むようなプラグインなどもあるので、それらでうまく解消することはできますが、そもそも;に関する設定を2つのツールチェーンでうまくやらなければいけないというのも辛いものです。

また、どのツールチェーンに問題があるのかわかりにくくなってしまうこともあります。例えば「型チェックOKなのに実行時エラー」「実行時エラーを直すと型エラー」のような状態に陥ったとき、どのツールチェーンを疑えばいいでしょう?もちろん事象にもよりますが、それはwebpackかもしれないし、Typescript,Babelかもしれません。結局全部調べても原因が分からなかった場合どこにissueを立てればいいかも悩みどころになるかもしれません。
エラーがどのツールチェーンの問題なのかわからないというのは調査工数やバグ解消の工数が大きくなってしまうことに直結してしまい、実装者の疲弊・生産性の低下につながります。

ビルドの長時間化

ツールチェーンはそれぞれJavascript(Typescript)ファイルを解析し、AST(abstract syntax tree:抽象構文木)というJavacriptのコードを表現したObjectを生成します。
ASTの仕様はestreeが実質的な標準?のようでestreeに準拠してるものが多いようですが、各々のツールチェーンで必要とするASTの情報が異なるためパーサーは各々実装してたりします。
パーサーがそれぞれ異なるということはツールチェーンごとにパース処理が行われ、結果同じような処理が何度も走っておりアプリケーションのビルド時間で見ると長時間化を招いています。

「なら共通のASTパーサーがあればいんじゃないか」という声も上がりそうですが、そもそもどのツールチェーンでも使えるようなパーサーを実装するとツールチェーンによってはいらない情報まで付与されたリッチなASTパーサーになって単独で使うと処理が遅くなったり、さらに言えばesbuildやSWCはJavascriptで実装されてるわけじゃないのでパーサーの共通化は言語的に困難だったりもします。

余談「バベルの塔」

旧約聖書のバベルの塔の逸話は、高い技術力を持った人々が天にも届くような高い塔を作ろうとしたことで神の怒りを買い、神は一つだった言語を乱しバラバラにすることでその塔の建設をやめさせたという話です。
人々が何かをなし得るには、共通の言語がなくてはいけないということです。

現代のツールチェーンを取り巻く問題はBabelに限らずですし、全く違う話ではあるもののASTや実装言語の違いから共通化できないことで問題が生まれてると思うと何か因縁めいたものを感じるような気もします。
(まぁほとんどこじつけですね・・・w)

Romeの夢

閑話休題、Romeは前述の問題を1つの包括的ツールチェーンを作成することで解消しようとしました。1つのツールチェーンで1回のパースでリント、型チェック、トランスパイルなど多くのことを行えば体験は統一的でシンプルになり、高速になると考えたのです。
これはある種、言うはやすし行うは難しと言いますか、実際にやろうと思うと非常に多くの課題があります。ここ数年かけて多くのOSSで実装されてきたものをまとめて1つのツールチェーンで実装しようというのだから相当に壮大なものであることは想像に難しくないかと思います。

それでもこのRomeを構想しているのがyarnやBabelの生みの親であるsebmckや、TC39の一員の Jamie Kyleであると思うとこれも夢物語ではなくなってくるわけです。

当初の開発方針

Romeが実装しようとしているのは前述した現代のJavascript開発で求められるツールチェーン群の機能を含む、以下の機能群です。

  • Bundling
  • Compiling
  • Documentation Generation
  • Formatting
  • Linting
  • Minification
  • Testing
  • Type Checking
  • ...and more

「ドキュメンテーションの生成」が含まれているのが個人的には興味深いです。おそらくRustのドキュメンテーション生成のようなものを意識してるのでしょう。
Romeはこれらの機能をzero dependencyという他ライブラリへの依存を0でJavascriptで実装しようとしていました。これはこれまでの、基本的に他ライブラリへの依存があって当たり前で「最小ライブラリ構成」という多くのJSライブラリとは逆の方向でしたが、それによって安定性・速度などを達成しようという強い意志を感じられるものでした。
他にもAPIの最小化、強い型付けを基本とすることなどRomeはいくつかの独自コンセプトを打ち出しました。

開発方針の転換、JavascriptからRustへ

Romeを発表して1年前後でRomeの開発は進み、いくつかのLintができるまでになりました。

しかし現在Githubのリリースは止まっており、Romeを試しに使ってみようと思うとエラーで動きませんでした。
これはRomeがRustで再実装されることになったため、バグfixなど盛に行われていないからかもしれません。
https://rome.tools/blog/2021/09/21/rome-will-be-rewritten-in-rust

このブログの通りですが、Romeはいくつかの理由から大幅な方針変更を行いました。
簡単に概要を説明します。

Rustの採用

Romeの実装はJavascriptで開発が進められてきましたが、Romeチームでプロトタイピングした結果、Rustを採用した方が良いと判断しました。
パフォーマンス的な観点もありますが、最も大きな理由は生産性のようです。「JavascriptよりRustの方が絶対に生産性が高い」とかいう話ではなく、元はTypescriptではなく素の、しかもzero dependencyなのでBabelやJestもない状態でJavascriptでコンパイラを書いてるのだから現代の開発者体験に慣れてると相当辛そうですし、この辺はJavascriptとRustを比較するでもなく個人的には納得できるものでした。

zero dependencyの廃止

Rustを採用するもう一つの理由に、Rustならzero dependencyである必要がないということが挙げられています。
zero dependencyはRomeが他のJavascriptライブラリへ依存することで、パフォーマンスや安定性への懸念があったから採用された方針です。しかしRustの場合、Rustのライブラリではフットプリントが非常に小さかったり0コスト抽象化が可能だったりするためにこれらの懸念が非常に小さくなるので、zero dependencyである必要がないと結論付けられました。これも結果的にチームの生産性向上に大きな効果が認められる、Rust採用時の大きなメリットです。

コンパイラアーキテクチャの再設計

RomeチームはRustでのプロトタイピングを経て、コンパイラのアーキテクチャの再設計する機会を得ました。これにより有効なJavascript構文を元にパースすることを前提としたASTではなく、CST(Concrete Syntax Tree: 具象構文木)を採用し、さらにC#/Roslynコンパイラチームによって造られたRed-GreenTreeというアーキテクチャを元に、CSTとそのAPIの見直しがされました。

後者のCST採用はつまり、非estreeな独自仕様の採用になるものです(と、僕は解釈してますが間違ってたら指摘ください...)。これは「編集中の際などの、有効な構文でない状態でもツリーを生成でき、適切にエラーを与えられること」というのを大事にしての選択のようです。
おそらくIDEでの体験を相当念頭に置いているものだと思われます。

Romeの現在のステータス

Romeの社内作業進捗は見えないですが、issueのweeklyを見る限りRomeは現在、RustでASTパーサー周りの作業を進めてるように思われます(ASTファサードと呼んでいるがCSTパーサーのこと・・・?この辺も詳細はよくわからず)。まぁRust採用の話が2月前なのでまだまだ作業中のようなので、ある程度動く形になるのはまだまだ先だと思われます。

ただRomeは法人なので動くのが何年も先になるかと言われると、1年先くらいにはある程度目に見える形が出てくるんじゃないかと勝手に想像してます。RomeはFacebookで始まったプロジェクトなので、Rome法人の出資元はおそらくFacebookだと思いますが、いくらFacebookといえどある程度速度感を持ってアウトプットしないといい顔しないと思うので・・・。 追記:会社設立時の投資家にFacebookいなかったんで今は関係ないのかもしれません。確認不足でした。。。

Romeが目指す開発者体験を想像する

一貫し統一された体験

Romeが目指すのはIDEまで含めた一貫した開発者体験です。

rome checkと打つだけでリント、フォーマットチェック、型チェック、デッドコードチェック、package.jsonの依存チェックやライセンスチェックまで行えるようにするようです。統合されてるのでそれぞれのチェックを分けるのではなく、「1つのチェックを通して問題を教えてくれる」というわかりやすい体験を目指しています。

このようにRomeの最大の特徴はやはり多くの機能が統合されていることでしょう。これにより現在のツールチェーンの組み合わせを考える・複数のツールチェーンの初期設定をしなければいけないという状況から、Romeをインストール・設定すれば良いだけという体験に変わります。

IDEで体験の改善

RomeはLSPを通してIDEで編集中にはチェックを、保存時にはオートフォーマットするような体験を目指しています。今でも同様のことはVSCやWebStormでもできますしIDE側の実装も絡むので一概には言えませんが、おそらくRomeでの体験はこれまでのものよりさらに早く安定的なものになるのではないかと期待できます。

また、Romeはrome check --reviewとすることで修正提案を行い、選択肢を選んで自動修正するような機能も実装予定です。これが他のツールチェーンと比べてどの程度強力なものになるかはまだわかりませんが、IDE上でこの機能を使えることを重視しているのでIDEからの修正提案がRomeを通じて強力になるような未来を考えているのでしょう。

コードの安全な変更

IDEのundo機能やGit履歴がなくとも、rome recoverというコマンドによって修正を任意の地点まで戻すことが可能です。これが実際どのように使われることを想定してるかはまだわかりませんが、想像するに「エラーがなかったころまで戻す」みたいな操作が容易になるのではないかと考えられます。

Romeは流行るのか

ここまで問題提起〜Romeの現状〜Romeが目指す未来の話をしてきました。
では実際Romeの開発がある程度形になってv1リリースもされたとして、Romeは流行るのでしょうか。

答えはもちろんわかりません。
最近の流れを見てるとviteがesbuildを採用したり、Next.jsやDenoがSWCを採用したりしてるのを見ると、正直Romeが流行るかはNext.jsやNuxt.js、最近出たところでいうとRemixといった何かしらの著名なフレームワークで採用されるなどしないと厳しいかもしれません。

ただNext.jsがRomeを採用するかはちょっと微妙な気がしています。というのもNext.jsを作ってるVecelには現在、SWCの作者のkdy1やwebpackの作者のsokraが在籍しています。Romeは統合ツールチェーンのため、これらのツールチェーンと競合になるので、Vercel的にRomeを採用するかどうかという議論になるとなんとなく彼らを雇っておきながら他のツールチェーンを採用するのは難しいのではないかと思ってしまいます。

あとめぼしいところで言うとDenoの動向は気になりますが、DenoがRomeを採用するかはまったく予測ができないです。解決しようとしてる課題感も近い気がしますし、Denoは内部的にSWC使ってたりもするのであり得なくはない気もするんですが、かといって今の段階でどうなるかは予想が難しいかと思います。

まとめ

Romeは絶賛開発中ですが、やろうとしてることの壮大さや実際にリリースされたら結構速度・安定性・簡単さの面でかなり強いんじゃないかと思ってます。ただ素晴らしいツールチェーンでも流行るかどうかは別だし、リリースされてもないので「これが次世代技術だ!」とも言いづらいです。

しかしRomeが提起した問題、特にツールチェーンの組み合わせの複雑さは僕も何度も苦しみましたし、今のフロントエンドの参入ハードルを高くしてる一因と感じています。
これを解決するのがRomeなのか他のツールチェーンなのか、はたまた他の何かなのかはわかりませんが、近い未来で何かしらの形で解消されることを願います。

Discussion