Webアプリケーションエンジニアとしての1年目を振り返る

1. はじめに

明けましておめでとうございます。エンジニアのミツモトです。 年末は皆さんいかがお過ごしでしたでしょうか?

年末、私は家でゆっくりしながら、リーダブルコードを読んでいました。 リーダブルコード

この本は私がスタメンに入ってから最初に勧められて読んだ本です。

スタメンがリーダブルコードを推奨している理由 スタメンはチームでの開発が前提になるため、誰もが読みやすいようにコーディングすることを大事にしています。そのため、人によって読みやすさの基準に差がでないよう、リーダブルコードを参考にしています。 あわせてRubocopも採用し、コーディングスタイルを統一しています。

リーダブルコードを読みながら、Webアプリケーションエンジニアとしての1年目を振り返ったので、今回は、この1年でコードの書き方がどう変わったかを紹介します。

2. 読みやすいコードを書く

変数・メソッドの命名に気を付ける

変数・メソッド名で処理の中身をわかりやすくするため、次のことをしています。

  • 品詞の扱いを気をつける
    • メソッド名で何が起きるのか/できるのかをわかりやすくするため
  • 他でどういう命名がされてるか調べる
    • プロダクト/チーム全体で用語を統一する
    • Facebook など類似のプロダクトのURLやCSSの変数を参考にする

最初の頃は品詞を意識していなかったため、名詞・形容詞が入り混じっていました。 (最近でもどの単語にするか迷う)

昨年、社内で話題になったのは ghkw というコマンドラインツールです。 github 上のキーワードを検索し、利用件数を知ることができます。複数のキーワードを同時に検索できるため、比較するのに便利です。

実装の長いメソッドを分ける

メソッドが長くなると、コードが読みづらくなり、テストも複雑になります。ユニットテストが書きやすい粒度にコード量を保つことで、可読性が増え、不具合も減ります。

メソッドを分けるため、具体的には次のことをしています。

  1. どういう処理を行っているか箇条書きにする。
  2. 箇条書きにした処理の中で、別メソッドとして切り出せるものは分ける。
  3. 別メソッドにすることが難しければ、メソッドの中で段落に分ける。

こうすることで、1つのメソッドの読みやすさが向上します。

それでも分かりづらい場合はコメントをつけます。本来は「コードのみで処理がわかる」状態が一番良いですが、他の人や未来の自分からすると、「一体何をしてるんだ?」となり得るものはコメントを付けるようにしています。

コメントを付ける際は、背景やメンテナンス時の注意事項など、あとで読む人に伝えたいことをシンプルに書くように気をつけています。

条件式がネストしていたら早めに return する

条件式がネストしていると、コードが読みづらくなります。

if post.present?
  if post.image?
    puts 'hoge1'
  else
    puts 'hoge2'
  end
else
  puts 'hoge3'
end

階層の深い条件式はスタックが多くなり、「最初の条件って何だっけ?」となります。 他の人にとっても読みづらくなるので、早めにreturnするのはオススメです。

return puts 'hoge3' if post.blank?

if post.image?
  puts 'hoge1'
else
  puts 'hoge2'
end

3. Rails での設計の考え方

Model に対しての ロジックを View と Controller に持たせない

スタメンはバックエンドに Ruby on Rails を採用しており、MVCアーキテクチャがベースになります。

昔のコードを見返すと Model にあるべきロジックが Controller や View に出ていました。 Modelにロジックが定義されていないと、 Controller や View でコードが重複する可能性があり、DRY ( Don't repeat yourselfの略。コードの重複を防ぐプログラミングの原則 )から遠ざかります。

View でよくあるのが Model の状態に関連した条件式です。

if @post.present? && @post.image? && (@post.image.width > 1024 || @post.image.height > 1024)
  # サムネイル表示する
end

こういう場合は Model のインスタンスメソッドにします。View はスマホ版とPC版、メールなど、同じような内容で複数のテンプレートを実装することも多く、Modelにメソッドとして実装することでDRYになります。

class Post
  def has_large_image?
    image.present? && (image.width > 1024 || image.height > 1024)
  end
end

Controller でよくあるのが、 scope を使っていないパターンです。

@posts = Post.where(user: current_user, type: %i(image video))

というような where 句を 昔は Controller に直書きしていました。

こういう時は Model に scopeを定義することで、他のController や View でも呼び出すことができます。

View と Controller は、 Model に定義したメソッドを最低限呼び出すだけにしましょう。(ただし、最近 FatModel になりつつあるので、 Model 内の整理も必要)

private メソッドでカプセル化する

昔は深く考えずに全て public メソッドで定義していたのですが、外部から参照してほしくないメソッドは private メソッドとして定義するようになりました。これによって外部から呼ばれないことが保証され、リファクタリングがしやすくなります。

private メソッドは、それを用いる public メソッドのテストで動作が確認されるため、private にすることでテストを省くことができます。

validation でエラーを返す

インスタンスとして無効なのであれば、インスタンスメソッド内でエラーハンドリングするのではなく、 validation としてエラーを返します。エラーメッセージも扱いやすいし、 validation をかけるタイミングも設定できます。

ActiveModel::Validator を継承した custom validator を使えば、 複数のモデルに共通した validation を DRY に実装することができます。

4. メンテナンス性を高める

YAGNI だけど、拡張しやすく

YAGNI とは、"You ain't gonna need it"の略で、「機能は実際に必要となるまでは追加しないのがよい」というエクストリーム・プログラミングにおける原則です。

新しい機能を実装するとき、「こういう機能も付加した方が良いのでは?」という話が出ます。要望に沿って付加機能を実装したとしても、使われなければ意味がありません。また、gemや他サービスでそれを補うものが出てくる可能性もあります。(実際に google drive の gem を扱ったとき、作り過ぎなくてよかった!ということがありました。)

作り過ぎは禁物ですが、予見できる要望を後で作りやすくするために設計段階に考慮しておくのは大事です。 DB のテーブル構成を考える時など、あとで修正する時にコストが高くつくものは拡張性を意識します。

コードの共通化

既存の機能と似たものを作るとき、そのままコピーして部分的に変更するだけで、実装できるものがあります。ですが、これをするとロジックの修正が必要になった際に修正箇所が多くなり、メンテナンス性は大きく下がります。

スタメンに入ってからも何度かそういう場面がありました。最初の頃はコピーして実装することが多かったですが(スタメンの皆、すみません...。)、レビュー等で指摘を受け昨年の終わり頃から共通化を意識するようになりました。

Model と Controller は共通部分を Module に切り出すことで、次に機能を追加する時に楽になります。 View についても template を共通化します。

その瞬間は面倒だと思いますが、後で全部つくり直すより今やった方が良いです。

5. おわりに

エンジニアになったばかりの人の参考になればと思い、今回の記事を書きました。

こうやって書き並べると、まだ十分にできてない部分があるため、今後もより良いコードを書けるよう頑張ろうと思います。2019年は Rails の基礎を固めつつ、フロントエンドにも幅を広げていきます。

スタメンではエンジニアを募集しています。もし興味がありましたらこちらからご連絡ください。

オフィスで、話ができるのを楽しみにお待ちしています。