目次
■エンジニアの軌跡
2017.01.31 2024.03.28 約4分
さまざまな企業で働くエンジニアとリレー形式で対談を行うDevRelay。トレジャーデータの @kamipo こと上薗竜太氏に引き続きインタビューします。今回は、自身を追い込み続ける理由やモチベーション維持の方法を伺いました。この対談は、3部構成(vol.10・vol.11・vol.12)でお届けします。
idesaku:Rails の動向を毎朝チェックして、プルリクエストを投げ続ける、といった営みを日課として継続するモチベーションは、どこから生まれるんでしょうか。
kamipo:一番大きいのは「続いていることは続けたくなる」だと思います。
idesaku:たしかに、一度習慣になってしまったことは、断ちがたくなってきますよね。
kamipo:もともと「Active Record を直さなきゃ!」と思いながらやってきているのですが、ほかのことを考える必要がなくなるくらい、今は集中してやれていますね。余計な情報に関心が向かなくなったというか。
たとえば、以前だったら、起きて最初にやるのが「はてブチェック」だったりした時期もあるのですが、今は「そんな時間があったら、考えてプルリクしないと!」と思える状態になっています(笑)。
ひたすら考え続けていると、以前は気付けなかった問題に、一週間後にハッと気付くようなことがあるんですよ。
idesaku:それは「動かして気付くバグ」のような分かりやすいものではなく、別のレベルの問題に思い当たるということですか。
kamipo:たとえば、何年間も当たり前のように見ていた目の前のコードが、実は「どこからも呼ばれていない」という衝撃の事実に、ある日突然気付いたりします。
idesaku:それは衝撃です(笑)。
kamipo:コード量が膨大で、なおかつ「クソ」なところがいっぱいあると、個別の「クソ」なところにばかり目がいってしまって、他のところに意識が向かなくなってしまうんですね。なので、もう、そのあたりは全部直しちゃったほうがいい。
で、全部直して「ヤバい! 直すところがなくなってしまった!」というコードを、さらにひたすら見なおしていると、またヘンなところが見えてきたりするんですよ。
idesaku:「問題を直す」ことより「直すべき問題を見つけ出す」ことのほうが、むしろ重要になってくる。
kamipo:週末に10個とかプルリクを出すことがあるんですが、以前は「出すものがなくなる」ことを避けるために、若干手加減をして、ある程度手元にストックしたうえで小出しにするようなことをしていたんですよ。
でも、ある段階から「これではダメだ!」と気がつきまして。常に「出すものがない! どうしよう!」という状態に自分を追い込むことで、初めて進める領域というものがあるんです。
idesaku:さらなる「高み」が見えてくると。
kamipo:こういうことをずっと続けていると、「今、この境地にいるのは自分しかいない!」「このソフトウェアのことを、ここまで深く理解しているのは自分だけだ!」という独特の感慨がわいてきます。
idesaku:そこに楽しさを感じておられるんですね。以前は Active Record に関しては Aaron Patterson あたりが第一人者のようなイメージがあったんですが、kamipoさんが彼に代わってその座につく日も近いと。
kamipo:いやいや、まだまだですよ。Aaron のコミット数は何千という数です。私はまだ、398で20位くらいです。
idesaku:そうなんですか。Aaron といえば、「AdequateRecord」を入れてましたね。あの機能はどう見てますか?
kamipo:単純なクエリをキャッシュする仕組みですけれど、あれも実は STI あたりがちゃんと動いているか怪しいと思っていて、一度、きっちり調べてみるつもりでいます。
もし、調べてみてちゃんと動いているということが確認できれば自分の中で安心できますし、ちゃんと動いていなければ、バグとしてプルリクが投げられると(笑)。どちらにしても「深く考える」ことは無駄にならないんですよ。
idesaku:そういう生活を続ける中で、自分の能力が拡張されたために、それまで見えなかったものが見えてくる…ような感覚なのでしょうか。もう2年ぐらいやっていることになりますか。
kamipo:そうですね。2016年に入ってから、11月末までの間でのコミット数が250強くらいですね。この期間に、これだけコミットしているのは僕だけだと思います。
idesaku:「Active Record のダメなところをツブしていく」ということを日課にすることで身についた能力は、何か別のところにも生かされていますか。
kamipo:いろんなところに生きていると思いますよ。仕事でも、レビューなどの進め方がまったく変わってきます。スタックトレースを見ただけで、全体のイメージが浮かぶというか。…分かりますかね? この感じ。
idesaku:ちょっと分からないです(笑)。
kamipo:なんて言えばいいのかな。ファイル名と全体の行数、含まれるメソッド名を見れば「景色」が見えてくる感じです。この「景色」が見えていないと、レビューのときに diff だけ見ても、中にバグがあるかどうかって、分からないんですよ。全体の「景色」がイメージできていて、はじめて「あぁ、このコミット、バグってるな」という気配を感じとれるというか。
idesaku:ああ、それなら何となくですが、分かります。普通に使っていると見つからない、コーナーケースに気付くような能力は発達しそうですよね。
kamipo:まぁ、現時点で大量に直していくとなると、コーナーケースにあたっていくくらいしかないですしね。
kamipo:あと、これはコーナーケースではないのですけれど、仕事でたまたま踏んでしまって直したやつには「count、exists? メソッドが ORDER BY を消す」問題がありました。
idesaku:…なるほど、一見 ORDER BY が不要になるようには見えますね。どういう不都合が出るんですか。
kamipo:たいていのオプティマイザには「ORDER BY LIMIT Optimization」というのが実装されています。
たとえば、Twitter でツイートの内容を一覧表示する機能を思い浮かべてください。ここで「idesakuさんのツイート5万件のうち、最新の20件を表示したい」とします。
クエリプランによっては、全ユーザーのツイートを含んだテーブルから、まずidesakuさんのユーザーID で5万件まで絞り込み、それをソートした上で最近の20件を取り出すので、5万件全部をなめることになります。
インデックスを利かせて膨大なツイートの中から手際よく5万件まで絞り込めたわけですが、それでも全部なめるには多量すぎてパフォーマンスは良くありません。
特にソーシャル系のサービスなどの場合、こういう「インデックスが利いている状態でも、実際に必要な件数から考えると十分に絞り込めていない」ケースが結構あるんです。下手すると、インデックスが利いていても100万レコード以上なめなくてはいけないケースもありえると。
idesaku:たしかにそれは厳しい。
kamipo:そういうときのためにも「全部をなめない」クエリプランにしておくことがポイントで、その際に重要なのが「ORDER BY」と「LIMIT」なんです。さっきの例でいうと、ユーザーで絞り込んだ段階で、ツイートがソート済みになっているようなインデックスを用意しておくんです。そのうえで「ORDER BY」と「LIMIT」を入れると、そのインデックスの端から20件だけ返すように最適化されて、5万件全てをなめずにすむわけです。
idesaku:つまり、一見並び順が関係なさそうなクエリであっても、 ORDER BY が消されてしまうとクエリプランが変わってパフォーマンスが大きく低下するケースが出てくると。これ、Rails の側だけを見ている人たちを説得するのは大変そうですね。
kamipo:これは既に master ブランチに入っています。5.0.1 RC1 には入れられなかったんですけれど、今、バックポートの結果待ちです。
idesaku:お話を聞く限り、これ、かなり深刻な話ですよね。
kamipo:深刻ですよ。山ほどスロークエリが出ますから。これについては、仕事で別のスロークエリを潰すために実行計画を変えたところ、カウントの性能が下がってしまい、おかしいと思って調べていたら「ORDER BY」が消されていることに気がついたといった感じでしたね。
idesaku:Rails って、以前は MySQL との親和性が高かったような気もするんですけれどね。あまり、データベース側に詳しい人がコミュニティにいなくなってしまったんでしょうか。
kamipo:うーん、今はいないと思います。僕の立ち位置はDBA とソフトウェアエンジニアの中間ぐらいだと思っているんですが、本来DBA タイプの人は、Webフレームワークにコントリビュートしないですからね。
ただ、ORDER BY を消したくなる気持ちも分からなくはないんです。ORDER BY がついていると、PostgreSQL では通らなかったりしますので…。とはいえ、ORDER BY がないことでスロークエリが発生するユースケースは飛躍的に増えてしまいますからね。…まぁ、そういうのを見ながら仕事をしています。
idesaku:…で、ここであえてお伺いしたいんですが、そこまで Active Record がダメな状態なのであれば、kamipoさんは MySQL にもお詳しいわけですし、直接 SQL を書いてしまったほうが早いんじゃないかとも思うのですが。
kamipo:普通に書いていますよ。ActiveRecord::Base.connection.execute で。
idesaku:ですよね。エンジニア界隈では、わりと定期的に「O/Rマッパーダメ、SQL 最高」あるいは逆に「Rails で SQL 書くのは悪」みたいな言説が出てくるんですけれど、そのあたりについて、kamipoさんはどう考えているのかというのを知りたくて。
kamipo:うーん。それぞれが好きなようにすればいいんじゃないですかね。そのあたりは、利便性とシステム効率の「バランス」をどう取るかにかかってくると思います。
自分は MySQL について知っているんで、その上でうまく正規化して、このように作ったインデックスを、ユーザー側からこのように使わせたいといったことを念頭におき、効率の良い設計をすることを考えます。
一方で、同じものを Active Record のような O/Rマッパー側からも簡単に扱えるようにしておきたいという思いもあります。単純なクエリだったら、Active Record から find_byを一発で引き出せるような仕組みがあったほうが便利に感じる人も多いでしょうから。
その時々で、利便性と効率のどちらに重きを置くかというのは変わってきますよね。自分の中では、どちらかに寄りすぎないようにするというのを意識するようにしています。
idesaku:それができるようになるためには、MySQL と Rails の両方の世界を、きちんと知っていないと難しいですよね。kamipoさんの場合は、どちらもカバーしているからこそ、それができているという部分もありますよね。
とはいえ、O/Rマッパーのようにクエリを自動生成する仕組みの上で、最高に効率の良いクエリを作るのは難しそうだと思います。
kamipo:基本的には、クエリを先に考えていますね。クエリを Arel での構造に脳内変換して、自分がイメージしたクエリを作るようにコードを書くというスタイルですね。
idesaku:それって二度手間じゃないですか?
kamipo:完全に二度手間ですね。
idesaku:それでも、そのように書いた方がいいと判断しているのには、何か理由があるんでしょうか。一緒に仕事をしている開発メンバーが Rails の方だからとか。
kamipo:そうですね。ただ、僕が書いて、他の人から書き換えられないことが分かっているところなら find_by_sql を使いますけどね。セルフジョインしたりするクエリなどは、Active Record だと書きづらいので、そういうところは文字列でやってしまいます。
idesaku:では、他の方と一緒に仕事される場合に、事前にどちらかに方針を決めておくようなことは、あまりないのですか。
kamipo:そうですね。基本的に人のやることには口を出しません。「そっちの好きに書いてよ。こっちで必要なときには、好きに直すから」というスタンスです。(To Be Continued… 第3部へ続く)
次回は、一部で伝説となっている「金髪の神エンジニア」についての裏話を公開! そして kamipo さんがリレー相手としてご指名するエンジニアは…?
※本記事の内容は掲載当時の情報であり、現在の情報とは異なる可能性がございます。