🐙

Re: Rails を主戦場としている自分が今後学ぶべき技術について

10 min read 3

この記事は、 Rails を主戦場としている自分が今後学ぶべき技術について(随筆) | うなすけとあれこれ についてのアンサー記事です。

うなすけ君が Ruby on Rails で育ってきたように、僕も JavaScript とともに育ってきたという自覚があります。なので、これについて書くことは、ポジショントークは避けられない、という感覚があります。

冷静に比較しようとも思いましたが、やっぱり開き直って思いっきりポジショントークをすることにしました。そっちのほうが面白いと思うので。

自分の基本的な主張は、こちらの記事にあるとおりです。

Frontend Study #1: 基調講演 - Frontend 領域を再定義する

自分と Ruby on Rails

僕は、キャリアとしては Rails の会社で JavaScript を書いてきたことが多かったです。学生の頃は socket.io でオンラインゲームを作っていましたが、2013 年の新卒のときの仕事(ゲーム業界)から JS on Rails でしたし、次の仕事も(教育)、その次も(Qiita)、フリーランスになってからも JS on Rails 案件を 3 つほど経験し、今思うとフリーランスやめてからの現職が、はじめての仕事としてのサーバーサイド Node.js です。

一番多かった業務内容が、Sprockets の Asset Pipeline を捨てながら commonjs/ESM のコードを追加することでした。悲しいですね。

じゃあ自分がどれぐらい Ruby と Rails を書けるかというと、日常の書き捨てのスクリプトをたまに Ruby で書いたりする程度で、 後は Rails Guide を読みながら一通り scaffold したり、 Rails Controller 上の ActiveRecord を触れる程度です。DSL の思想が強すぎる rspec が苦手です。

coffee script を書いていた 5 年以上前は、思想や文法が似ている ruby を参考にしていましたが、 TypeScript がメインになった今では全く参照しなくなりました。

自分は Ruby / Rails をどう認識しているか

2010 年代においては、 日本では Rails コミュニティこそが先進的な開発を行う組織でした。 GitHub の OSS フロー、PR のレビューの採用や、OSS エコシステム上でのオープンなコミュニティというものを先導してきたのは Rails コミュニティだったと思っています。自分を育ててくれた一部が Rails コミュニティだったという認識もあります。

また、Webpack の先祖は Rails 3 の Asset Pipeline だったと認識しています。アプリケーションと組み合わせて JS をビルドするという発想が、後々のフロントエンドパイプラインに強い影響を与えています。

Rails の 「設定より規約」という思想にはすごく賛同していて、自分がアプリケーションを設計するときでも実践したいと常々思っています。

とはいえ、 Rails の中で JS を書くのは、なかなかつらい経験でした。 Rails が高速に生成する負債を返却するのが、2010 年代の「フロントエンドエンジニア」の姿だったとも言えるでしょう。(そこでRailsに対するヘイトをためてしまってこんな記事を書いています)

そして、今では Ruby と Rails には未来を感じることが難しくなってきています。

その理由の一つは、型システムとコミュニティの趨勢です。型についてはあとで述べるとして、僕は GitHub の Trending Repository をみるのが好きなのですが、 Ruby は閑散としていて、みるのが辛いレベルです。

The State of the Octoverse | The State of the Octoverse explores a year of change with new deep dives into developer productivity, security, and how we build communities on GitHub.

GitHub のトレンドも右肩下がりです。

2010年前後のPerlを思い出す感じです。

流行っているからいい、悪いではないとも思いますが、OSS においてはコミュニティの趨勢というのがその言語の価値を決めるとも思っています。一時期、ActionScript の仕事をしたことがあるのですが、自分が触った期間で ActionScript の新しい OSS は一つも出てきませんでした。ベストプラクティスが定まっていない、トレンドの移り変わりが高い昨今の開発現場で、手持ちの弾で正しい設計を導けていないと感じながら、その言語で開発を続けるのは、とても苦しいものがありました。

とくに、開発者コミュニティが評価されている言語で、コミュニティから人がいなくなるのは致命的です。日本だとそこまで感じませんが、何か新しいことを調べる時に、海外コミュニティだと Ruby で書かれたコードはほとんど見かけなくなりました。観測範囲の問題もあるでしょうが…

Ruby が魅力的でないとうちのビジネスが困る――クックパッドが取り組む Ruby への貢献とエンジニア育成 (1/2):CodeZine(コードジン)

逆説的にこれが現実になりつつあると感じています。

これを書くと、私の観測範囲では〜というコメントが来るのは知っていますが、だとしてもデータと一緒に反論してほしい気持ちがあります。

Ruby と静的型について

そのコミュニティの趨勢を決定づけているものとして、自分は型アノテーションの有無があると思っています。

2000 年代に関数型言語周りの型推論の技術が発達し、2010 年代にそれが C#、TypeScript などに応用されていきました。タイプ数が多いから静的型はコード量が多くて不利、という言説は、今では覆りつつあります。それはほぼ古い Java に当てはまる特性です。

個人的には、もう型の静的解析がない言語を日常的に使いたいと思えません。自分が使う TypeScript の型は、ランタイムにおいて存在しない「偽物」かもしれませんが、ドキュメントとして機能して、開発を牽引するという役割は見事に果たしてくれています。

Ruby 3 にも型が導入されようとしていますが、TypeScript も実用できると思えるほど枯れるまでにとても時間が掛かりました。Ruby の型が枯れて実用的になるまでに、今の開発者コミュニティを維持できるかというと、自分は怪しいと思っています。要は 「Perl6 化」する可能性を懸念しています。 また matz はじめコミッターの型に対する姿勢にも疑問を持っています。

Ruby3 で導入される静的型チェッカーのしくみ まつもとゆきひろ氏が RubyKaigi 2019 で語ったこと - Part1 - ログミー Tech

追記: ここでいう型は、不正確な「型」の意味での静的型のことで、Rubyにもランタイムに型がある、というツッコミはあると思います

フロントエンドパフォーマンスと Rails Way

Rails と フロントエンドの View では、そもそも見ているものが違って、 Rails や他のフルスタックフレームワークは ActiveRecord の出口としての文字列、フロントエンドは動的な木構造の GUI コンポーネントとして捉えています。

自分の経験上、Rails とフロントエンドがいる組織で、結果として起こるのが、「誰が View を握るのか」という開発者同士の主導権の奪い合いです。Rails エンジニアは生産性の為に ORM とマッピングしたページネーションや Form を生成し、場合によっては多少の jQuery を追加し、リアルタイムなバリデーションやインタラクションを求められるフロントエンドエンジニアは、自分が担当になった瞬間に、それを捨てます。

React のような宣言的 UI のフロントエンドは、テンプレートの生成過程そのものを握る必要があります。Rails で生成した文字列にはその情報が残らないので、それを利用する限り jQuery 的な手続き的な差分処理になり、差分処理による状態遷移の複雑さが閾値を超えた段階で、設計として破綻します。

また、Rails がフロントエンドのパフォーマンスに関与しないので、次のような問題が起きがちです。

レシピサービスのフロントエンドを Next.js と GraphQL のシステムに置き換えている話 - クックパッド開発者ブログ

元の実装の FCP が遅かったのはシステムをリニューアルする前からわかっていた問題点のひとつで、巨大な CSS や defer できない JS が head で読まれていて、クリティカルレンダリングパスの最適化ができていないのが原因でした。なんとかしようにもどこで読まれているかわからない CSS が大量にあって消すのが難しい、haml(Rails の View)に埋めこまれた JS が head で読まれる JS に依存していて defer できない、などの理由で FCP の最適化が難しい状態でした。
ですので Rails が遅い、Next.js だと速い、というフレームワークの差ではありません。Rails でもスクラッチで書き直してチューニングすれば同程度のパフォーマンスはでます。

ブラウザからみたとき、個別のリクエストは単なる HTTP/S なので、それぞれが最適化されていれば、何がバックエンドだとしても理論上同じパフォーマンスは出ます。

ですが、 ここに Rails Way というものが立ちふさがります。Rails Way はその文化として フロントエンドの最適化を内包していないので、フロントエンドの最適化は Rails Way からすると異常なやり方に見えることがあります。これが、 「Rails Way に従わないフロントエンドエンジニア」に見えることがあり、文化的な軋轢を生みます。その環境では、フロントエンドとしては、Rails Way に従わない合理的な理由を説明する必要に迫られます。

フロントエンドからすると、 Rails がフロントエンドの最適化に興味がないので、最適化のヒントが残っていません。なので自力でそれっぽい規約を導入するか、一切を諦めて個別のコンポーネントに徹するか、といった話なります。Rails の規約がある以上、そこに規約を足すとどうしても不自然になります。

Next.js が実践しているフロントエンドのベストプラクティスは、乱暴に要約すると「いかに静的に確定する部分を増やして、それを CDN に置くか」なんですが、Rails は全てのリソースが動的になりうる世界観なので、確定できるものが少ないです。

また、Rails はそのモノリシックさによって、段階的なリプレースも阻みがちです。今では API Mode もありますが…

Rails による API 専用アプリケーション - Rails ガイド

そもそも根本的な話なのですが、 Rails はサーバーサイドが主戦場なので SQL のレスポンスタイムの数百 ms のオーダーで考えますが、フロントエンドは GUI のインタラクションとして捉えるので 16ms のフレーム単位で捉える必要があります。このオーダー感覚が基本的なパフォーマンスチューニングの断絶の生んでいると、自分は考えています。

とはいえ、React で作ろうがパフォーマンスが悪化することはあります。それに対して知見をためていく必要はあります。

React 製の SPA のパフォーマンスチューニング実例 | リクルートテクノロジーズ メンバーズブログ

これは、 React や SPA 技術というものが、従来の Web の延長線上にある技術ではなく、どちらかというと GUI 開発の知見を元に開発しなければいけない技術だからです。伝統的には、フロントエンドの戦っている相手は、Rails ではなく、モバイルアプリの方でした。それが PWA や Core Web Vital などによって、Web フロントエンド全体と統合されてきた、という近年の歴史があります。どうせならパフォーマンスの伸びしろがあるものを使いたい、というのが、フロントエンド側の気持ちです。

Rails や他のフレームワークにとっての不幸は、フロントエンドの主流が、宣言的 UI でクラサバで同じテンプレート生成しつつ、JS のイベントハンドラも注入するというのが主流になったことです。JS のイベントハンドラを扱う以上、Node.js(または Deno のようなその他の JS 実装)以外の選択肢がなくなってしまいました。これは JS が好きな自分としても、多様性を損ねる状態だと思って健全だとは思っていません…

Rails の生産性は Rails だけのものなのだろうか?

Ruby on Rails の正体と向き合い方 / What is Ruby on Rails and how to deal with it? - Speaker Deck

Rails は大量のフォームを持った画面の生産速度にフィーチャーしています。これによって、少人数で生産性を担保できます。ですが、この規約を他の環境でも表現できれば、別に Rails を使う必要はないと感じています。

2000 年代では、Ruby の動的型によるダックタイピングという手法が、当時の Rails においては有効だったように思いますが、TypeScript の柔軟な表現力なら、 Rails を「食える」と思っています。

Next.js は View とルーティングに特化したフレームワークですが、この上に ORM のレイヤーを一つ重ねることで、Rails 相当になる期待感があります。

とはいえ、現状の Next.js のフルスタックフレームワークは全部実験段階です。この段階で Rails を倒せる、というのは、大言壮語であるという認識もあります。

2021 年 は Fullstack Next.js 元年なので、有望な Next.js 系フレームワークを全部試した

「雑に作っても速度が担保されるアーキテクチャ」こそが目指すべきで、フレームワークというものは、アーキテクチャを表現した実装だと思っています。アーキテクチャの伸びしろを求めると、そこをサポートしない Rails には表現力の限界があると考えるざるを得ません。

そしてその限界が、近年の UX やパフォーマンス指向、Core WebVital による SEO への影響とかち合って来ています。フロントエンド的には、新規に Rails を採用する理由は、既存のエンジニアリソース以外、ほとんどないと言わざるを得ません。

パフォーマンスは贅沢品から、ビジネスを勝ち抜くための必須要件になりつつあります。これについて書くと長くなるので、また今度にしますが、もっというと、 Next.js ではパフォーマンスと生産性は両立します。Rails はそこでパフォーマンスを欠いています。

ActiveRecord について

Rails において ActiveRecord がキラーコンテンツなのは間違いありません。僕も、Ruby と SQL がおざなりな状態でアプリケーション開発に参加できたのは、ActiveRecord の抽象度が高いおかげだと思いました。

うなすけ君のブログの反応を見ると、 ActiveRecord がある限りはRails は安泰という反応が多いですが、自分が思うに、ActiveRecord が使われ続けているのは、Rails の競争力が担保されているからだという風にも見えます。その Rails の競争力に疑問を感じているので、今後も ActiveRecord が安泰だと思ってはいません。本当にそうだとしたら、 Rails ではなく同じく ActiveRecord を使う Hanami がもっと使われているはずだからです。

Hanami | The web, with simplicity

Ruby と TypeScript の決定的な差は、コミュニティの規模の他に、型表現の柔軟性だと思っています。大規模開発においては、型の有無は採用の決定的な判断基準になります。

自分が prisma に感動したのは、既存の型システムでは難しい SQL に対する型のマッピングを、TypeScript の表現力で行えている点です。また、宣言的マイグレーションという他にない強力な機能があります。

Prisma - Next-generation ORM for Node.js and TypeScript

現時点で、 prisma migrate は一部の機能が実装されておらず --experimental ですが、このフラグが外れたときに Prisma は Node.js 界での ActiveRecord として君臨する,という予感があります。というかすでにそうなりつつあります。

ただ、 prisma 自身は現時点で javascript-client しかないのですが、仕組み的には他の言語からの Adapter を書く余地が残されています。この辺りでエコシステムの広がりがあるかもしれません。ただ SQL に対する型のマッピングは TS 以外で表現できるかは微妙だと思っています。

ActiveRecord の長所の一つはパフォーマンスですが、 Prisma の API 体系は SQL の写像のようなコードを書けるので、この点でもパフォーマンス上の伸びしろを感じています。

言語的一貫性

API に徹するんだからそこは Ruby でいいじゃん、のような反応に自分が思うのは、言語の興味の方向性が、アーキテクチャ上の伸びしろに決定的に関与する、ということです。

自分が 2010 年代中盤の Altjs を観測していたときに感じたのが、「JS に興味がない言語のコミュニティのフロントエンドツールは、関心の少なさからフロントエンドの抽象に失敗する」という点でした。

翻って、Ruby コミュニティ発のフロントエンドツールは、 rjs, pjax, turbolinks, webpacker、最近では stimulus などがありますが、どれもフロントエンド的に成功したとは言えません。

stimulusjs/stimulus: A modest JavaScript framework for the HTML you already have

Dr.Axel 曰く、ドッグフーディングが大事だから、JavaScript にまつわるツールは JavaScript に書くほうがいいのでは?という話もあります。これは Rails の Asset Pipeline の発展が途絶えた点からも、納得感があります。

Writing JavaScript tools in other languages – a new trend?

というわけで、Web に関するものは一旦 Node.js に集約して、その上で Web Assembly の時代がきたら、そこでまた多様性を獲得すればいい、というのが自分の意見です。

まとめ

  • Rails のフロントエンド周りは限界
  • Rails は文化的にパフォーマンスに対する指向を持たない
  • ActiveRecord の賞味期限は Rails と同じ
  • 型による大規模化への伸びしろの有無が決定的な差になってきた

前にも書きましたが、フレームワークや言語は死んでも、そこで培われた思想は残るので…

この記事に贈られたバッジ