✊
Only My Rails Way
これは何
「Rails Wayに沿って〜」とはReview欄などでよく言われるが、定義が人によってぶれている気がするので俺のRails Wayを示した記事です。
もはや本来のモノとは別物かも知れませんが、俺はこういう観点でRailsをみて、コードを書いているよ、ということを知ってもらう意味でもこの記事を公開することにしました。
前提として、「数人以上のチームでプロダクトを実際に開発して運用する」場合の自分のスタンスを示したものです。(私も仕事では独自DSLは書きませんが自由研究用途なら自分も独自DSLを書いたりします。)
それでは、いってみましょう。
Model層
- データベースの操作およびビジネスロジックを記述する。
- テーブルの属性は原則NOT NULLにするべき。どうしても要件上NULLを許容しなければならない場合のみNULLを許容する。
- Controllerからparamsを無思考で渡さない。 使用する属性のみController側で取り出してからModelに渡すのが良い。
- 外部通信(特にメール送信)が起こるロジックに注意。テストを書くときに正しくスタブ化しておかないとCIで外部通信が無限に飛んでしまう場合がある。yieldを使って外部からメール送信ロジックを差し込むようなメソッド設計にするのも検討。
Service層の可否
- 基本的に採用しない。 ビジネスロジックはModelに詰め込む。
- Service層の濫用はドメイン貧血症を引き起こす弊害があるため。
- Service層の採用についてはよく論争になるがそれでいいと思っている。
- Service層に限らず、Railsデフォルトにないpackageを切り出そうとするときは立ち止まってチーム内で議論をしたほうが良い
- Modelはでっかくなっちゃってて良いし、単に「コードの行数を減らす」ためだけにファイルを切り出すのは責務が曖昧になりがちで余計可読性を損なう。コードの行数で可読性を判断しない。
- 新しく入った現場の既存コードにServiceがあると身体中を光の速さで確かな悪寒が駆け巡る。
Repository層の可否
- 原則採用しない
- データベース以外からのデータの取り出し(例えばAPIコールなど)は、Model層にActiveModel::Modelを利用して書く。
- Modelが、DBと通信しているのか他のどこかと通信しているのかを意識せずに書けるようになるとGood
View層
SSRするなら
- erbが安定択
- slimやhamlは見かけの記述量は減らせるが、erbはrubymineで補完しやすいので実質のタイピング量がそこまで減らせるわけではない。あとslimやhaml使うとReactやVueに移行するときに書き直しが大変。
- erbは遅くない(ruby2.5でかなり高速化された)
- そもそもviewのレンダリングエンジンの早い遅いを議論する前にN+1が起きていないかを調べるのが先。
- フロント側でのjQuery#htmlでのレンダリングを前提にしたjs.erbダメ絶対。絡まった運命も変えていける力が欲しい。
- draperも最近はあんま必要だと思ったことがない。helperでいいんじゃない?
- Form Objectも基本的に使用しない
フロントにHTMLの描画に任せ、JSON APIに徹するなら
- 標準のAPIモードを使い続けるのが一番いい
- grapeはあまり必要だと思ったことがない
Controller層
- Controllerの責務は「入力値のバリデーション」「入力値を適切なビジネスロジックへ委譲」「適切な出力形式を選択してoutput rendererに処理を委譲」の3つ
- Controllerは薄い方が良いが、薄ければ良いというわけではない。
- とりわけ速度を重視するフェーズではある程度Controllerにロジックめいたものが入ってしまうのは仕方ないが、リファクタリングチケットは残しておく。
- 横断的関心事(ex.ロギング)以外をbefore_actionやafter_actionに書かない。 ビジネスロジックが中途半端に書かれていたり、onlyやexceptが跋扈するcontrollerに出会うと人はどこまで立ち向かえるのか不安になる。
- before_actionやafter_actionでDRYしようとしない。
- Model層でも述べたが、Modelに渡すのに必要なパラメータを選定するのはControllerの役割であるという認識
テスト
- mainはいつもCI Green. 当たり前のようだが、これが守られている現場は観測範囲では50%以下。当たり前のことを当たり前にやっていきたい。ちゃんとしよう。
- RSpecに自分は慣れているが、別にminitestでも良い
- FactoryBotはやはりつよい
- TDD原理主義者ではないが、不具合の修正であったり、設計が固まっていたりする場合はTDDするとサクッと書けるケースが多いのでよくやる
- 実装書いてからテスト書くのも別にそれはそれでいいと思う
- テストをどこまで緻密に書くかは必ず納期と相談。 カバレッジ100%は死ぬほど暇な現場でないと現実的ではないので、達成できなくても気にしない。
- テストを書く優先順位は以下
- 正常系
- 異常系のうち(発生確率)×(被害)が大きいと見積もられるもの
- 異常系のうち(発生確率)×(被害)が小さいと見積もられるもの
デプロイ
- 最速目指すならheroku
- チームにインフラエンジニア不在の状況ならPaaS、SaaSに思いっきり依存する。いちアプリケーションエンジニアがIaaSで頑張らない。
DRYに関するポリシー
- たまたま似通っているだけのコードを共通化しない。
- 責務が同一であってはじめて共通化して良い。
- Rubocopをだますために責務を考えずに5行ごとにメソッドに切り出したりするのは最悪。 それなら書き下したほうが良い。
- 「一見ダサいコード」は以外と読みやすい。こじれたプライドを捨てて書き下そう。
パフォーマンスに関するポリシー
- 最初の実装時にあれこれ考えてひねた実装にするのではなく愚直に書いてまずは計測
- 最初の記述と矛盾するようだが基本的にRubyは遅くないしRailsも遅くない。遅いのは99%自分が書いたIOかSQL。
- パフォーマンス向上のために Ruby -> Go とか Ruby -> Javaのフルリプレースプロジェクトを立ち上げるのは予算と時間の無駄。遅いのはいつも言語じゃなくててめえのコード。
継承か委譲か
- 継承でないとうまく動かないライブラリを利用しない限り、基本的に委譲。
- 「横のクラスの機能を使いたいんだよ〜」で継承しない。「機能の横恋慕」というアンチパターン。
独自DSL
- 書かない。
ライブラリ管理
- 利用するライブラリは少ない方が良い。ライブラリが多ければ多いほどメンテされないライブラリを抱え込んでしまうリスクが高まる。
- ライブラリが多いとアップデート時のエンバグリスクも増える。そのままバージョンを上げられなくなるRailsを何度も見てきた。そっから先は一方通行。
- 「コードが(例えば独自DSLとかで)短く書ける」だけがメリットのライブラリは利用しない。ライブラリを増やすくらいならコードの文字数が増えるほうが良い。
- 標準構成で特別な設定もせずにCoCでガンガン書き進められるって最高ですよね。
- 新規採用するライブラリはstar数やメンテの頻度をチェックする。
認証
- auth0(SaaS)の利用か、あるいはDevise。
- Deviseつらいとよく言われるが他のライブラリはもっとつらかった。悲しみって多分明日へ向かう原動力。
決済
- Stripeが最高に体験がいい
データベース
- RDBド安定。検索にElasticSearchくらい
- どちらも本番ではマネージドサービスを利用したい
- 特殊な要求・要件があってはじめて他のDBを検討する。開発者の使いたいありきのエゴでピーキーなDBを採用しない。
モノリスかマイクロサービスか
こういう記事を書いておいてアレだが、
- マイクロサービスにしたってプロダクト同士が依存関係を持ってしまったら開発速度はスケールアウトしない。
- ぶっちゃけモノリスでマザーズなりM&AなりEXITまでいけると思うし、まずはそれを目指すべきと思っている
- 「IPOはさらなる資金調達の場であって株主の利益確定の場ではない」「M&Aはさらなる成長のための手段であって創業者の利益確定の手段ではない」とはよく言われるが、ITベンチャー経営者の多くが「EXITで株を売り抜けて数億円〜数十億円のキャッシュを得たい」と本音では考えている。プロダクトの出口戦略(売り抜け)を考えるのは悪いことではない。エンジニアの役割はプロダクトに殉死することではない。
マインド
- コードゴルフに興味はない
- まず動くモノを世に出すのが何よりも優先される。そしてそれでお金を生み出すことができれば最高だ。
- 動いて儲かってるきれいなコード>動いて儲かってる糞コード>動いて儲からないきれいなコード>>>>>動かないきれいなコード=動かない糞コード
- PL毀損してJカーブを狙う戦略はあまり好きではない。初月から黒字をコツコツと。できれば複利で増やしたい。
- フレームワークの本質は「自由をあえて束縛することで速度を上げる」ことにある。割り切りは重要。フレームワークに叛逆しない。
Discussion
SMBなサービス開発の現場はこんなかんじですね〜