【2024年】受託案件でRuby on Railsを使用した感想

5月から7月末の3ヶ月間に受託の案件をしており、その際にRuby on Railsで使用した。
選定の理由としては元々の依頼主がRailsに詳しかったり、別の先行しているプロジェクトで参考になるコードが多かった、つまり資産を流用できるのでリスクが低いと判断して採用した。

個人のRailsの経験で言うと合計で2年ぐらい過去に使用していたが、結構忘れてしまったところも多かった。ただ1ヶ月を過ぎたぐらいでだいぶ書き方を思い出したのでそこまで足を引っ張ったわけではなかった。

この記事の狙いとしては久しぶりにRailsを使った感触について書いていく。

実装の作業量はフロントの方が圧倒的に高い

最近のプロダクトの業務量としては圧倒的にフロント側の方が高くなってしまった。これは単純にユーザーが普段使用するtoCサービスにおいてUI/UXがかなり向上し慣れてしまったからだ。
その他にも純粋にサービスの機能で差を付けるのが難しく、また競合に真似もされるのでプロダクトにおいての違いの部分が使いやすさが重要になった。

そのためSPAのように画面遷移をなるべく挟まない、昔ながらMVCで十分という古いエンジニア多い意見に対して賛同できない。今はよっぽどPoCとかそういうプロジェクトでない限り純粋な機能差で圧倒できるビジネスモデルは難しい。(めちゃくちゃ使いにくいけど圧倒的な営業力で売上を上げているプロダクトはあるが、それはこんかいとは関係のない話)

以上の背景が前提にある。

Hotwireの感想

今回の案件では画面の動きをHotwireメインでサービスのコアとなる複雑なUI部分だけReactで実装した。Hotwireについては以下のブログ記事がとても参考になるし主張している箇所も納得できる。

Hotwireの良かった点、辛かった点、向いているケース、向いていないケース - 猫Rails

ただ上の記事でも書いてあるようにReactとHotwireの両方を利用するバットプラクティスを実践した。理由はコアの部分の仕様が複雑すぎてHotwireのみでは実現が難しかった。

Turbo Frames, Turbo Streams, Stimulusの使い分け

Turbo Frames, Turbo Streams, Stimulusの左から右への順番で複雑なUIに対応ができて、StimulusというのはJavaScriptも書きたい場合に使用する。要するに段階的に使えることがメリットらしいが自分は逆にデメリットに感じた。

そもそもそのUIが以上の3つうちどれからスタートすればいいか経験がないと判断が難しい。Turbo Framesからスタートしていけばという意見もあるが、どう考えてもその試行錯誤している時間は無駄だし、またUI変更とは頻繁あるのでその度に切り替えるのはコストがかかる。

Railsは選択のレールがしっかりしていて、迷いが少ないというのが売りだがここには迷いが生じているので個人的には微妙な点だった。

手続き型のJavaScript

Reactは関数型の傾向が強いが、Hotwire(Stimulus)は手続き型の記述なのでなんというか、昔のjQueryでの実装に近い。確かにJQuery使うのであればHotwireで良いと思う。 たぶんRails自体が関数型の書き方ではないから、それに合わせたと思う。

ただ手続き型で複雑なUIを実装していくには困難ということは歴史が証明しているので、今回コアの部分をReactする結果になった。

そのため機能によっては一部Reactなりで書かないといけないみたいなことが発生すると開発者がHotwireとReactの両方に精通する必要があるのでそこは問題がある。 しかし、上の記事にもあるようにView側は完全にReactでRailsAPIサーバーに徹する場合にコミュニケーションコストが確かに発生するので、小規模案件ではペイしないと主張も正しい。

TypeScriptを使わない

Stimulusは基本的にJavaScriptで記述していく(DHHがそのように方針を変更した)。そのためビルドが複雑になるのを回避するために、ReactもJavaScriptで記述するようにした。結果的にReactの部分が複雑になったのでケアレスミスが発生しまた補完が効かないので効率が悪かった。

確かにRuby動的言語だから統一したかった狙いはあるかもしれないが、個人的には開発者体験が悪かった。まあRuby書いている人がその延長でStimulusを書く流れだから分かるが、、フロントエンドエンジニアからは不評である。

以上のことを書いてて結局Reactが必要がないような仕様であれば100%意見は変わっていたと思う。ただし、ユーザー視点ではなくHotwireだとキツイので仕様変更しましょうというのも違う気がするし今回のように自社開発ではない場合に断るのは難しい。

Hotwire以外でRailsの良さを感じたこと

Hotwireに関しては厳しいことを書いたが改めて良さを感じたことも多かった。やはりActiveRecordは強い。ORMとしてのメリットも大きいがバリデーションが直感的かつ短い記述で書けるし、テストの周りもFactoryBotやFackerなど定番のライブラリを使って簡単に記述出来たこと。 以前Nest.jsで以上のバリデーションやテストコードを書いたがRailsほどに洗練も情報も十分ではないのでやはり大きく差がある。

またRails consoleの使いやすさやdebuggerも使いやすいので、ViewではなくAPIサーバーに徹したコードをかけばストレスはほぼ感じない。

ただ最大に良いところはRailsフレームワークの機能ではなく、アーキテクチャも提供している点だ。いわゆるRails wayというものである。Rails以外のフレームワークは最低限の置き場所は決まっているケースもあるが基本的にはプロジェクトごとにアーキテクチャが異なる。もちろんちゃんと分析し設計すれば良いのであるが、プロジェクト初期にそこまで分析できることは稀である。また初期にその設計が良かったとしても初期メンバーが入れ替わったり、サービスのピボットでベストが変わる可能性がある。つまり、Railsの設計方針はBestではないけどBetterであるし、世の中のほとんどのプロジェクトはそれで十分だったりする。

Hotwire以外でRailsRubyの残念なところ

RailsというよりもRuby動的言語であるためnilの判定を事前にビルドしてチェックできない。 これは上記で書いた利点が動的言語であるからのトレードオフになっている。RBSというLintでチェックもあるらしいがまだそこまで広がっていない。それにRBSを後から頑張って書くのであればGo言語で書き換えしてパーフォーマンスの向上も選択肢に入る。結局昔感じた大きなプロジェクトの場合に事前に型のチェックが出来ないことによる問題は当たり前に残っている。

大規模プロジェクトにRailsが使えない?GitHubやShopifyのような大きなサービスでも使われているじゃないかという反論もあるがそもそも比較することがナンセンスだと感じる。所属しているエンジニアの能力も給料も全然違うしRailsを保守していく費用も日本の一般サービスと全然違う。

ただ難しい問題で事前に型のチェックような入ると今のRubyRailsの良さの部分も大きく影響あるだろうし、たしかに静的言語の要素を積極的に取り入れてしまうと単純にRubyの魅力も同時になくなる。

まとめ

以前以下のような記事を書いた。
受託企業のプログラミング言語とフレームワークの選定 - cassette

色々と批判的なことを書いたが受託開発する上で実はかなり相性の良いフレームワークと感じた。受託開発だとスタート開始時点で十分な仕様が固まっていることも稀だし、またドメインエキスパートとなる人間と距離がある中でBestではないけどBetterであるアーキテクチャを提供するのは開発を進める上で心強い。