「良くない」と思うコードに腹を立てても意味はない
藤村大介氏:既存Railsアプリに触る時の心構えと、新たに事業、プロダクト、コードなどをチームに受け入れる時、触れる時の心構えをお話ししようと思います。これは新しく会社に入る時や会社に新しいプロダクトがジョインする時に役に立つ話じゃないかなと思います。
1個目です。事業をやっていると、常にあらゆる状況でベストなコードは書けないんですよ。すごく上手な人がベストなコードをその時々で書いていても、事業の状況は絶対に変わるんですよね。状況が変わった時に過去ベストだったものが現在のベストじゃなくなることはメチャクチャあるんですよ。なのでまぁまぁ難しい。ずっとベストなコードは難しい。
その時でベストなものも、時間の都合で書けない場合があるんですね。スタートアップという、もう来月生きるか死ぬか、お金がなくなるかなくならないか、KPIを達成しないと調達ができない。でもするしかないみたいな、そういう世界ではこれは本当にそうなので、うまいプログラマーがいてもベストなコードが書けるとは限らないというのがあると思います。
なので、「事業をやっているとベストじゃないコードはどうしても生まれる」ということを理解する必要があると思います。これは何も悪くないし、避けられないことなので、僕もこういうのを今までガンガン積み上げてきています。なるべくベストは尽くしたほうがいいですが、それでもやはり生まれちゃうので避けられないというのは理解する必要があると思います。
それを踏まえた上で、良くないコードに腹を立てても意味がないというのは僕はけっこう重要かなと思っています。エモーショナルなものなのでなかなかそうもいかない場合もあると思いますが、これはちょっと話をさせてください。
先ほど言ったように実業界で出会うコード。仕事をしていてプロダクションコードはそれぞれにそれぞれの事情、現状があるんですよ。これはもうしょうがない。良いもの・悪いものもあるんだけど、とにかく僕が思うのは、生き延びてきた事業を支えているプロダクションコードというのはなんであれ尊いと思っているんですよ。
生き延びられる事業を作れるだけでマジですごいので、これは僕はすごい尊いものだなと思っています。生き延びた事業のプロダクションコードはそれそれで読むおもしろさがあると思いますが、なんにしろ僕は尊いと思っています。良かろうと、良くなかろうと、腹が立とうと、立てまいと、やれることは僕らは直すか・直さないかしかないんですね。
僕らは幸いプログラマーで直せるじゃないですか。なのでいろいろな気持ちはあるかもしれませんが、怒る前に直すという感じが正しいんじゃないかなと思っています。腹を立てても意味がないので直そうという感じですかね。
毎日少しずつ改善すればコードは必ず良くなる
あとは、毎日少しずつ改善をしていると、コードは必ず良くなると僕は思っています。事業存続に影響があるぐらい内部品質が悪い場合もたまにはあると思うんですね。そういう場合、作り直すというのはまぁある話だなと思っているし、実例としてもけっこうあると思います。だけど僕は、だいたいのプロダクションコードは漸進的に改善できると思っているんですね。
今までの経験もわりとそうでした。ハッピーな職場で働いてきた可能性もありますが、できると思う。結果的に今回もそうでした。自分の会社に新しい会社プロダクト、チームが入ってくるという状況で、そこからチームを拡大するところだったんですね。
拡大する前になんとかしてプロダクションコードがどんどん良くなっていくという事実を積み上げて、トラックレコードを作ることで良くないコードが直るという歴史を作り、改善を積み上げることで課題が少しずつ解決するという文化を作りたかったので、今回はすごく一生懸命に直しました。そういう副次的な効果もあると思います。なんにせよ、少しずつ改善すれば必ず良くなる。僕は強く信じています。
内部品質の改善に時間を使う「しゃがむ」判断も大事
直していくのは重要ですが、いったん「しゃがむ」判断も大事なことの1つかなと思います。先ほども言ったとおり、「プロダクト開発のアジリティを上げて、スムーズにお客さんに価値を提供し続ける」ためにいろいろ改善をしていたのですが、内部品質の改善はすごく重要だと思っています。
みなさん、コンセンサスかと思います。@t_wadaさんの「質とスピード」のスライドを見たことがある方はこの1枚をよく覚えていると思うんですが、内部品質が競争力を生んで内部品質のループが回るんですよ。しゃがむ必要がたまにあると思います。実際のところ僕らもそうでした。
しゃがんで内部品質を改善したほうがこのループが早く回る。事業としても中長期的に良くなるという判断ができるのであれば、その判断をして関係者の理解を取り付けて実際に内部品質の改善をするのがいいのかなと思います。「判断をする」とポンッと言ったんですが、意思決定をして実行するのはソフトウェアエンジニアしかできないんですよ。
ビジネスの人とかにはわからないので私たちが判断するしかない。判断して内部品質に時間を使うと決めてやるしかないですね。やるにしても周りのコンセンサスや理解は絶対に必要なので、関係者との強固な相互理解や信頼関係がすごく重要になるかなと思います。何をやったかというのはこのあとお話しします。
心構えのまとめです。新しく事業、プロダクト、コードに触れる時は、とにかく毎日少しずつ改善する。改善していく中で内部品質をもっと良くしたほうがいいという判断ができるのであれば決断をして、コンセンサスを付けてしゃがむ。それで良くする。関係者と強固な相互理解と信頼関係を築き改善しながら作っていくというのが重要かなと思います。
まずやったのは、開発環境の整備とデプロイプロセスの自動化
ということで概要の把握と心構えをお話ししたので、あとは実際に何をしたかを連打する時間に入ります。
最初に手をつけたのは開発環境の整備でした。これは新しく入ってくるメンバーのオンボーディングコストを減らしたいのと、開発環境を直すのが面倒くさかったので、ここに書いてあるrails db:seedsが通るようにするとか、docker-composeを完全にやるとか、いらない環境変数を消すとか、ドキュメントをリポジトリに移すとかをやりました。
あとはデプロイプロセスの自動化もしました。プロダクト改善速度の向上や障害対応コストの削減において、自動化してサイクルを短くするのがいいというのは多く知られているかなと思います。
もともとはmasterにマージされると本番にデプロイされるようになっていて、すごく安定していたんですね。だけど頻度は随時で、リリース用のプルリクエストを立てるという設計で、もう手動で作る感じだったんですよ。結果的にリリース単位がけっこう大きくなったので、プルリクエストをGitHub Actionsで自動で作るようにして、それを週2回勝手に朝に出すようにして定期リリースをしていくようにしました。
当初の目的もそこそこ達成できたかなというところがありつつ、リリースサイクルを短く頻繁にすることでチームのリズム感もとても良くなったんじゃないかなと思います。これは最初のほうにやったほうがいいかなと思います。
「内部品質改善に時間を使う」という意思決定をした
次は内部品質の改善に時間を使う、「しゃがむ」という意思決定です。先ほど言ったとおり、スムーズにお客さんに価値を提供し続けるために、プロダクト開発のアジリティを上げるというのが目的です。内部品質の改善で数ヶ月単位、向こう1ヶ月以降からプロダクト改善速度が大きく変わる感覚があったんですね。定量化するまでもなく明らかだったので、この意思決定をしました。
意思決定をする前に他業種のチームメイト、例えば事業を担当している人、プロダクトマネージャー、サポートのチームの人たち、2つの会社の経営メンバーなどと良好な信頼関係を築く必要があります。
「RubyistはNice」という言葉がありますが、それと一緒かなと思います。価値や内部品質の価値を理解してもらうと言いつつ、すでにご存じだったので何もいらなかった感じですね。「質とスピード」のスライドを読み直したりしていました。あとはみんなのコンセンサスを取って改善するというところに向かっていきました。
改善に向けて実際にやったこと
次からは突然具体的な話をひたすらします。feature specからsystem specへ移行をしました。開発では標準的な手法に変えたほうがメンテナンスコストが低いので、新しくしてベターな方向に変えていきました。spec/feature_helper.rbとかをひたすら理解して、system specも建立して、あとは一括置換でやりました。量が少なかったのでわりとすぐに終わりました。
ほかにも使っていないコードやGemをけっこうがんばって毎日のように削除しました。これは何が重要かというと、実装している時の認知負荷がすごいんですよ。「このコードは何をやっているんだろう」と読み進めていく中で「これは使っていないじゃん」となるのっていらない時間なんですよね。
これはすごくもったいないので減らしたほうがいいと思っています。毎回積み上がるのはすごく小さいけれどメチャクチャ細かく積み上がるコストだと思っていて、けっこう僕はこの生産性は事業にインパクトがあるものだと思っています。部屋が散らかっていると集中できないじゃないですか。それと同じです。僕の部屋はちょっと散らかっているのでなんなんだという感じですけど(笑)。
これのやったこととしては、単純に明らかなものからひたすら消していくだけですね。副業のチームメンバーに協力してもらいました。
あとはRSpecのfilter runを有効にする。これは僕が欲しかったのでやりました。あとFactoryBot.createは、別にFactoryBotという文字が書いてある要素が1ミリもないし、.createとか.buildのメソッドはSpec中になくグローバルに生やさないので、これはもう.createと.buildでいいんですよ。一括置換で全部変えました。
あとはtransactional testを有効にする。テストデータのクリーンアップがDatabase Cleanerのtruncationだったのでメチャクチャ遅かったんですよ。なのでtransactionに変えました。これはコードの実行中に書き換えるみたいなことをやりました。昨日ブログに書いたのでもしよかったら読んでみてください。
あとはRequest specで叩くエンドポイントはリテラルを使いました。実行中に書き換えてget post_pathではなく、get "/post/#{post.id}"みたいにしないと結局実際の値でテストをしなければいけないので、これを変えました。
あとはannotateをGitHub Actionsで自動化したり、開発環境をSSL化したりしました。これができていないと、重要なID連携で決済などを手元で動作確認できないので障害が起きるんですよね。なのでこれは先にやっておいたほうがいいです。
あとはrubocopの自動修正もやりました。これも前にブログを書いたので今回詳しくは紹介しませんが、やりました。
こんな感じで1年で1,500や2,000ぐらい減りました。あとはTrailing Whitespacesを消すとかしました。しょうもないんですが、これも認知コストがけっこう激しいのでやりました。
CTOが見ること・やること・考えることのまとめ
ということでいろいろやって1年かかってだいぶ良くなって、チームも無事に採用などうまくいって、先月僕の手を離れて1人立ちして非常に心強く、コードのクオリティもけっこう良くなったのでいい仕事ができたかなと思っていたりします。
あらためてまとめると、見ること・やることは資料を見てもらえればメソッドが並んでいるかと思います。心構えは先ほども言ったとおり毎日やる。あとは内部品質の改善に必要があれば決断をしてそれに時間を使う。そのために周りの人と信頼関係を作るというところかなと思います。
ということでご清聴ありがとうございました。