技術的負債を増やさずに開発する

前島真一氏:では、始めたいと思います。お願いします。前島です。ginza.rbというコミュニティをやってます。あとはメドピアさんを始め、何社かで技術顧問として活動をしています。

主にやっていることは、負債を減らしたり、これから作るもので負債を増やさない、ということをがんばっています。ほかにもいろいろやっていますが、気になる人は資料があるので、そちらを見てください。

今日のテーマは「技術的負債」です。さきほども言いましたけど、「負債」と言ってもアプローチの仕方が2つあります。もうできている負債を減らしていくという話と、これからなにか作るときに負債を増やさないという2種類です。

今日はその2つのうち、「負債を増やさないで開発する」という話をしたいと思います。たぶん「負債を減らす」ほうは、これからあとの発表で話があるんじゃないかと思ってます。

では、負債を増やさないにはどうしたらいいでしょう? Railsでアプリケーションを開発しているのであれば、「Railsのレールに乗る」というのが1つの方法だと思います。ただみんな「レールに乗る」って言ってるけど、具体的に何ですか、となりますよね。

この発表ではその「レールに乗る」というのを、「Railsが提供している便利な機能を活用する」としたいと思います。

Railsって、便利じゃないですか。便利な機能たくさんありますよね。「こういう機能があったらいいな」って思って調べてみると、Rails側でもう既にメソッドが定義済みだった、みたいなことってよくあります。

ただ、あまりに数が多いので、すでにあることを知らないで自分で作って、その作った機能が微妙で「うーん……」みたいなことってありませんか?

そんな便利な機能の例を出したいんですが……ここにいる人たちは知っている人もけっこういるかなとも思いますが、僕がお手伝いしてる先では必ず1回は「こういうのあるんですよ」って言って「おぉ、なるほど」となります。なので、ちょっとそれを紹介したいなと思ってます。

コードで見る便利な機能

これは、「has_many」です。みなさん知っていますよね。ただ、ちょっと違うんですよ。「has_many」と定義するだけで使えるようになる機能って、たくさんあるんです。ドキュメントから抜粋するとこんな感じで、「has_many」だけでこれだけあります。

この「collection」というのは、「has_many :posts」だったら「posts」、複数形です。そして、スライドの下のほうにある「collection_singular」は、「post」。単数形で読み替えてください。いっぱいありますよね。これを全部知ってる人は意外と少ないんじゃないかと思っています。

今日、とくに紹介したいのはこれです。「collection=(objects)」と、「collection_singular_ids=(ids)」です。両方とも同じ機能で、引数が違うだけですね。機能としては、既存のcollectionをいい感じに置き換えてくれます。もし「has_many :through」で定義してた場合は、中間テーブルをいい感じで置き換えてくれます。

だいたいこのメソッドと合わせ技で使われるビューヘルパーがあって、「collection_check_boxes」です。これをスクラッチで書いてしまう人が、僕の周りでは多いです。

どう便利なのか、具体的なコードを例として出したいと思います。ビールの銘柄にタグ付けをするコードを考えます。ビールの名前があって、決められたタグの中から複数選択して、という感じの画面ですね。

これを作るには、こんな感じでbeerモデルがあって、tagモデルがあって、それと多対多で関連が貼ってある、みたいな感じです。

これをさきほどのものを使わずに書いてしまうと、ビューがこんな感じになります。

フォームのtagのところだけ抜き出してます。

ちょっと微妙な感じがしますよね。check_box_tagを使ってるのがそもそもイマイチで、その中でnameに独自のものを使っていて、さらにチェックされているのかどうかも、beer.tags.findみたいなところで独自にチェックしています。

コントローラーはこんな感じになります。

読めないですね(笑)。

(会場笑)

読めないということがわかれば大丈夫です。「なんかやってるな」というのがわかれば。具体的に何をやっているかと言うと、紐づいてるタグとパラメータで渡ってきたタグのidを見て、追加すべきものは追加して、削除するべきものは削除しています。ただ先ほど見てわかったように読みづらいですし、あれをメンテするのはイヤですよね。

先ほど紹介したメソッドを使うとビューはこんな感じに、コントローラーはこんな感じになります。

読めるようになりましたね。ふつうにscaffoldで書いたみたいな感じのコードになります。

内部的にはこうなっているはずで、beerモデルに「has_many :tags」とやったら、「tag_ids=」メソッドが生えます。それに対してtagのidの配列を渡してあげると、いい感じにその中間テーブルが追加されたり削除されたりします。便利ですね。

これ、「今日初めて知ったよ」という人、どれくらいいますか?

(会場挙手)

おぉ、よかった! ありがとうございます。いや、これみんな知ってたらどうしようかなと思いました。

(会場笑)

どうやって便利な機能を知るか?

では、こういったことをどうやって知っていったらいいかです。やはり銀の弾丸的なものはなくて、公式のドキュメントやRailsガイドが、こういう機能についていろいろ書いてあります。それを読んでいきましょう。あとはお近くにいる技術顧問とかそれっぽい人にレビューやペアプログラミングをしてもらうといいかなと思います。

ここまでの話を簡単にまとめると、「Railsが提供する便利な機能を使えば、負債を作らずに開発ができる!」……なんですが、そんな簡単な話ではないですよね(笑)。Railsが提供してるのは、いろんな便利な機能はあるんですけど、全部無条件に便利かと言うとそうではなくて。ちゃんと気をつけて使わないといけないものがあります。

みんなよく言っている、検索すると出てくるワードがありますよね。「callback つらい」とか、「default_scope is evel」とか。いろいろありますよね。あとはgemも同じような感じで、「devise つらい」とか。そういう話があります。

これをどうやって取り扱ったらいいとかというと、負債を増やさないためにはちゃんと調査する時間があるかないかで違うかなと思っています。もし調査する時間がない場合は、単に禁止する。そのほうがいいかなと思っています。

というのは、1回入れちゃって「あ、これつらい」と思うと、「じゃあやめよう」ってなると思います。ですが、一度入れてしまうとやめるのがすごく大変です。なので、とりあえずやめておくというのも1つの手ではあります。

ただ、できれば今のプロジェクトの状態を見て、適応できるかできないかをジャッジして止めたいです。ただ「みんながダメと言っているからやめる」というのは、あまりよくないかなと思ってます。

要注意な機能の例

例えばwebpackerって最近よくdisられている気がしますが、簡単にes6を使う環境を整えられて便利なケースもよくあります。書いてあるとおり、フロントエンド専任がいなくて「とりあえずサッと使いたい」みたいなときは便利ですね。

あとはdeviseも昔からよくdisられているんですが、これはなぜかというと、deviseが提供している認証機能をカスタマイズしていくのがすごく大変だというところから来ています。なので、カスタマイズしなくていいような要件がもしあれば、別に入れても問題ない……となることがあります。

単純に「ダメだ」と言われているやつをやめるのではなく、なぜそれがダメだと言われてるのか、やめるとしたらどうやって回避していくのか。あとはどんなときなら使ってもいいのか、ということを把握しておくといいかなと思っています。

要注意なものの例として、ちょっと小さい例なんですけど……。

「has_many」で定義した場合に「<<」メソッドが入りますが、それはあまり使わないほうがいいと僕は思っています。Userが「has_many :posts」だったら、「user.posts << (……)」みたいに書けるんですが、これはイマイチです。

なぜかと言うと、この「<<」メソッドはレシーバーのオブジェクトの状態によって挙動が異なるからです。つまりUserが保存済みかどうか。保存済みだったら即クエリが発行されるんですが、未保存だったらすぐには発行されません。Userが保存されたときに発行される。みたいな挙動がぱっと見わからないんですよね。

ですので「has_many」で定義していると「build」や「create」というメソッドも生えていて、こちらのほうが同じことをよりわかりやすくできるので、なるべくならそちらを使うといいかなと思います。

あとはどんな時なら使ってもいいかなんですが、この例のように関連先のインスタンスができあがっていて、かつこの「<<」の挙動をみんなわかっているというケースなら使ってもいいかな、と思っています。

この例でもcreateが使えるので、あまり使いどころがないような気がしますね。

このように注意したほうがいいものについては、なぜ注意が必要なのか、どう避けるのか、どんなときなら使ってもいいのかを把握して。かつそれをチーム内で共有する必要があります。

負債を作らないためには、今言ったような気をつけなきゃいけないものについて、どうやって対応したらいいのかみんなで覚えて、対応していく必要があります。「みんなで」と言いましたが、最低限マスターブランチに入らないように、レビューの段階で止められるくらいの感じになっている必要があります。多数の人が負債になりそうなものを知っている必要があるんです。つまり、「負債を作らない」ということは、「社内教育をがんばりましょう」ってことなんですよ。

ちなみにメドピアの社内では、社内読書会やったりペアプロやったり。あとは1週間で得た知見を共有する、ふりかえり会をやっています。

負債を作らないために「便利な機能」「要注意な機能」を知る

話を戻しますが、先ほどの「collection<<」の例はちょっと簡単すぎましたね。実際あんなに簡単にいくケースって多くないんです。

例えばじゃあコールバックをどうやって回避するといいかと言うと、ググるといろんなやり方が出てきます。

フォームオブジェクトを使ったりサービスクラスを使ったり、ほかにもいろいろあります。それをどう選ぶのか……うーん。悩ましいとこですよね。

さらに、「じゃあフォームオブジェクトにしましょう」となったときに、そのフォームオブジェクトをどう実装するのかというところも決めないと、片手落ちみたいな感じになります。

かと言って「フォームオブジェクトいきましょう」だけで、ふわっとした感じで済ませておくと、それぞれフォームオブジェクトを実装する人の数だけのフォームオブジェクトの種類ができてしまって、それが負債につながっていくわけです。

ではどれを選んだらいいのか、難しいですよね。そういうことを相談するコミュニティがあります。clean-rails.orgです。

これは可読性の高いRailsのコードを議論するコミュニティです。今のところ、オンラインでのみやってます。

今のコールバックの例のように、どうやって実装したらいいのか悩むときに相談できる場所ですので。みなさまからの投稿をお待ちしております。

まとめると、負債を作らないためには「何が便利なのか」「何が要注意なのか」というのを知りましょう。さらに要注意なものについては、どうやって避けたらいいのか、みたいなものをまとめて、みんなが知る必要があります。つまり、勉強と教育をがんばりましょう。

以上になります。ご清聴ありがとうございました。

(会場拍手)