Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
96
Help us understand the problem. What is going on with this article?
@ysi

脱Rails初心者のためのリファクタリングガイド

概要

Railsのコードはそこそこ書けるけど、
いざコードの山を目の前にするとどこからリファクタリングしていいかわからない、という人(Rails歴 3年未満くらいの人)のためのガイド。

この記事ではフリーランスSEである私が過去に関わってきたいくつかのプロジェクトから、大体どのようなシステムにおいても使えそうなリファクタリングの手法と全体的な流れをまとめた。
なお、jsについてはフロントエンドに何のツールを使用するかで大きく変わるのでこの記事ではあまり深く書かない。

本題の前に

サービスクラスについては、ネット上には多くの著名人のリファクタリングの手法が掲載されているがそれを鵜呑みにしてそのまま実装すべきではない。
なぜなら、その手法の多くは熟練したエンジニアによるもので且つ、
チームの中でもリーダーをしているような裁量・自由度が高い人が書いたものが多いからだ。
脱Rails初心者向けレベルのリファクタリングはできるだけRailsのレールからはみ出さずシンプルに進めるべきだろう。

手順

すべてを同時進行でリファクタリングするのは骨が折れる。
特に優先してリファクタリングしたい箇所が決まっていなければ

  1. Fat Controllerの整理
  2. modelの整理 (+ serviceクラス)
  3. viewなど

の順ですると良い。
controllerがfatになっている場合、modelへ処理を移す可能性が高いのでよりスムーズに進められる。

Fat Controllerの整理

最初はあえてFat Modelを目指す

modelに処理を集めることでそのmodelの規模が把握でき、
serviceクラス用のツールを導入するかなど、今後の方針検討もしやすくなる。

全体の行数を減らす

  • publicメソッドはアクションのみに
  • 各メソッドの行数を減らす
    • ネストを減らす(if文のブロックならreturn if ~などで)
    • メソッド分割をする

処理の移動先は基本model。
modelへの移動とリファクタリングを同時にするのが厳しそうな場合は、「本来controllerにあるべき処理は…」などと考えず、アクションの中身を一旦全てmodelに映してから必要に応じてcontrollerに戻す、という手順を取るのも良い。

この、「必要最低限の処理だけがcontrollerにあり、残りほぼすべての処理がmodelにある状態」がまず第一に目指すべきところだと言えるだろう。

Modelの整理

  • controllerと同様に、メソッド分割をする・ネストを減らす
  • scope, association, 条件付きassociationを定義してデバッグしやすくする。
  • 値の検証はvalidate(s)に移動する
  • 機能ごとに処理を切り出す

機能ごとに処理を切り出す方法としては

  1. 同ファイル内でmodule化する
  2. 外部ファイルでmodule化する
  3. serviceクラスに切り出す

同ファイル内でmodule化する

機能のまとまりごとに、Module#concerning(参考)やmodule化&即includeの形でブロックにする。
ファイル全体の見通しが良くなる他、privateにして影響範囲を限定的にできる。
java出身の人などは特に、1ファイル1クラスでないことが非常に汚く見えるかもしれないが、実際にこのように記述されていることがよくあったしRailsライクであるようだ。
リファクタリング方針が決まらない内や、近い将来にさらなる改修が予定されている場合に一時的にこうしておくのも良い。

同ファイル内でmodule化するだけならmodelファイルの行数を減らすことにはならないのではないか?

この疑問に対して、実際にそれを私が調べたり人に聞いて回ったりして出した結論としては以下のようなものである。

  • ファイル自体が大きくともその中身さえ整理されていればあまり問題にはならない。 modelファイルが大きくなるのはそれだけ機能があるということなのである程度はしょうがない。
  • 可能であれば、テーブル自体をさらに細かく分割するか機能自体を減らすようにするのが良い

外部ファイルでmodule化する

例としては、user.rbの処理を切り出したいならusers/***_module.rbとして切り出す形を基本とする。
外部moduleだからといってconcerns/に置くのはおすすめしない。
concerns/に置くのはあくまで複数のmodel間で共通する処理のみとしておく。

serviceクラスに切り出す

serviceクラスの理想は明確な指針があること。
こういった感じのルールをチーム内で策定するか、公開されているossのgitリポジトリを共有し、このような形でやっていきたい、という方針を立てる。(例えばこれとか gitlabhq/gitlabhq)
逆に、メンバーそれぞれが勝手に俺流のserviceクラスを作り始めると地獄。
つまり、ルールを決めて継続的に運用していくことの難しさ・手間を考えると、チームの状況によっては下手にserviceクラスを開拓するよりはmodelの中に含めてしまった方が良いことも多いだろう。
サービスクラスを作るかどうかはあくまで選択肢の1つである。

Viewの整理

  • ロジックをhelperへ分離。
    • viewがごちゃごちゃしている割に忘れ去られているかのようにhelperが使われていない事がある。
      helperは使いにくい部分があるとは思うが、Decoratorなどを検討する前にhelperで事足りないか試してみると良い。
  • partial化
  • slim化
  • 単体のmodel更新のformは、できるだけform_tagではなくform_withを使う
  • jsの分離
    • viewに直接コードを記述せず、ページごとのjsファイルに分離する
    • そもそもサーバーに書ける処理はサーバー側に寄せる。
  • cssの整理
    • viewに直接styleを記述しない(せめてファイル内でクラス定義する)
    • js専用のclassはjs-プレフィックスをつけて区別する

補足、その他

リファクタリング時にspecも一緒に作る

リファクタリングは仕様がきっちり定まっていることが前提。
仕様がはっきりしていないのにそれに手を加えようとするのはおかしい。
specも同タイミングで作りリファクタリング前後で結果に変わりがないことを確認できると良い

specの書き方

この記事ではspecについてはあまり触れないが、
specはapp下のコード以上にレールがなくコーダーによって書き方がまちまちなため、コードレビューではあまり細かく(厳しく)指摘しないほうがベター(rubocopのrspec向けのルールがもう少し細かく設定できれば良いのだが)。
テストのレベルや記法についての要望がある場合は、方針をwikiなどにまとめて共有しておくと良い。

let記法やマッチャ、共通化のためのshared_examples_for、feature_specでよく使われるcapybaraなどはじめは書き方に癖が強くあると感じるものがたくさんあるので少しずつ調べていこう。

細かな記法より構造が整理されていることが大事

たった5行のメソッドでspecまでついていれば、細かい記法についてはそこまで気にならない。
逆に50行のごちゃっているメソッドなら、細かい部分についてもコードレビューで指摘したくなる。

リファクタリングしすぎない

使用頻度の低いところ、将来的に改修する予定のないところには時間をかけない。
また、きれいなコードを書くこと自体が目的とならないようにする。YAGNI.

コードレビューにおいても過剰なリファクタリング要求をしないように気をつけよう。

プルリクエスト単位を小さくする

リファクタリングでありがちなのが巨大な変更のPRになってしまい、自分が何を変えたのかが把握しづらくなること。コードレビュー者の負担も大きくなる。
1PRに詰め込まず、分割して作業していくことで自分が根が詰まる事も少なくなる。

まとめて作業したい場合は、commitの時にファイルを分けておけば後からgit cherry-pickを使って簡単にPR分割することもできる。git stash系コマンドも使えるようになっておくと便利

adminなら

ransackのgemを利用する

追記: adminについての記事書きました

Railsのadmin画面は本サイトとは切り離して今どきの構築に乗せないほうが爆アドだという話のポエム

コピペを減らしたいなら

新しいファイルを作るときは、コマンドからscaffoldやジェネレータを叩く形で適切なファイルが生成されるようにする。
コーディングにスニペットツールを利用する/してもらう。

Skinny Controllerの維持(rails console開発のすすめ)

これは本題から少しずれるが、
rails consoleを利用してコーディングしていくことで、自然とカプセル化されたmodelメソッドから作ることができ、実行結果が確認しやすくspecも書きやすくなる。
新機能を開発する時にcontrollerからコードを書き始めているのであればそれをやめてみましょう。

コードチェックツールの導入

rubocopやjs用linterなどのコードチェックツールをローカル環境に導入しよう。
自ずときれいなコードが書けるようになっていく。
また、導入する手順をチームに共有しよう

サードパーティツールを安易に導入しない

素晴らしいgemやjsライブラリは多いがやたらめったら入れればいいというわけではない。
Railsの標準から離れれば離れるほど運用ルール、メンテナンスコスト(アップデートリスク)、新規参入者の学習コストなどが増える。
導入はメリット・デメリットを把握して問題ないという判断の上で。

96
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ysi
Web,iPhoneアプリ好きプログラマ。 フリーランスで基本は企業でRailsを書いてます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
96
Help us understand the problem. What is going on with this article?