2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
歴史あるプロジェクトのとある技術的負債を隙間プロジェクトの 210 PullRequestsで倒しきった話(全1記事)
リンクをコピー
記事をブックマーク
川原万季氏:「歴史あるプロジェクトのとある技術的負債を隙間プロジェクトの210PullRequestsで倒しきった話」というタイトルでお話をさせていただきます。よろしくお願いします。
先ほど「Twitter」に資料を上げたので、手元でご覧になりたい方は、そちらをご覧ください。
自己紹介をします。「makicamel」という名前で活動をしています。Rubyとビールとお酒が大好きで、好きなVRゲームは「Beat Saber」です。株式会社アンドパッドというところで働いていて、今日はこの話をします。
さて、みなさんのプロジェクトに技術的負債はありますか? ある方が多いんじゃないかなと思うんですけれども、返していくのはなかなか難しいですよね。
というのも、小さな負債はカッとなってシュッと返せばいいのですが、大きな負債になるとなかなかそうもいかず、1日、2日ではなかなか終わらないので、計画を立てて戦略的に倒していくのがセオリーかなと思います。
とはいえ、なかなか優先度が上がらず後回しにされて、放置されて、利息が膨らんでしまいがちだなと感じています。
始めるの自体はそんなに難しくないんですよね。ですが、それを継続して最後までやりきる難易度が高いなと感じています。でも、やりたい。だって、楽しくコードを書きたいから。
というわけで、今日のお話です。まず、背景の説明からしていきたいと思います。アンドパッドの中で一番古い歴史あるプロジェクトですね。7年間で大きく育ったRailsアプリで、毎日数十人が触るようなプロジェクトになっています。
7年も育ってきたら、技術的負債もたくさんあるわけですね。複雑な設計・実装、神テーブル、神モデルとか、とにかく一つひとつもなかなかつらい負債なんですが、これらが組み合わさることで、より複雑なつらい負債となっています。
今日の対象は、「CrudController」というモジュールです。これは開発初期を支えたオレオレフレームワークです。最初の、コミットの2つ目にCrudControllerが出てくるくらい、開発初期から支えてくれたものなんですが、アプリの成長に合わせたメンテナンスが行われず、最近では負債化してしまっていました。
「ANDPAD」のつらみを話す時には、必ず名前が出てくるような存在になっていて、何年も前から課題として紹介されていたり、撤廃の手順書が書かれるんだけれども、解消がなかなか進まないという状態になってしまっていました。
そんなCrudControllerがどんなものかというと、こんなふうにコントローラで2行書くだけで、(スライドを示して)こんな感じでめちゃめちゃいっぱいのメソッドが生えたようにいい感じに振る舞ってくれるというものです。
つまり、CrudControllerというモジュールをincludeすると、クラスメソッド「crud_controller」が生えるんですね。それでアクションメソッドを生やしてくれたり、サブのメソッドをいろいろ生やしてくれて、いい感じに振る舞ってくれます。
さらに、そのいい感じのものをカスタマイズしたい場合は、メソッドをオーバーライドして、レスポンスをカスタマイズできるというものになっていました。「すごく簡単で便利で、タイプ数も激減するし、めっちゃ良さそう」という感じはするのですが、まぁ、やはりそれだけでもないわけですね。
例えば、暗黙的にメソッドを定義してくれるんですが、暗黙的なので、動的なメタプロ(メタプログラミング)がされるんですね。表側には一切出てこないので、「メソッド定義、newもcreateもないけど、なんで動くの?」みたいな感じになったり、コールバックも暗黙的に追加してくれるので、便利なんですが、「ここの@userはいったいどこから来たの?」みたいな感じになってしまう。
ほかにも、ANDPADのアンチパターンとして、超ファットコントローラなんですね。それをさらにモジュールに分割してあるので、これらを全部読まないとどんな挙動になるとかまったくわからない。
また、先ほどもお伝えしたように、メタプロが使われているので、メタプロが苦手な人にはちょっと読みにくいなど、複雑な状態になっていました。
バグを生みやすいし、学習コストも高い。オンボーディングの方もそうですよね。あと、認知コストが高く大変な状態になっていました。また、利用しているコントローラが300ぐらいあって、1日2日でどうにかなる量じゃないという感じです。
そこで、倒しきるために(スライドを示して)以下の4つのことをやったりとか、心掛けたりしていました。
まず1つ目、スクリプト化ですね。手作業でやることは考えていなかったんですね。時間と根気と集中力が必要だし、あくまでも本業が別にあって、その隙間時間の取り組みとしてやっていたので、これは選択肢には入っていませんでした。
逆に、部分的にではありますが、撤廃の手順書が書かれていたので、書かれているんだったらスクリプト化できるんじゃないかなと思って、書いてみました。
ちょっと長いんですけど、書けたんですね。長めなんですが、やっていることはそんなに難しくないので、順を追って見ていきたいと思います。
まずは1つ目ですね。「crud_controller .*アクション名」というのを含むファイルを抽出します。Rubyは、スクリプトの中でバッククォートで囲んだものをコマンド実行できるので、まず「git grep -l 'crud_controller .*new' -- 'app/controllers'
」しますね。
そうすると、対象となるcrud_controllerを含んでいるファイルが抽出されるので、そのコントローラのクラス名を取得して、specがあるものから最初はやろうと思っていたので、そのspecがあるコントローラを抽出します。
その対象がわかれば、コントローラファイルを文字列として読み込んで、File.readすることで、文字列として扱えます。文字列として扱ってしまえば、もうこっちのもんですよね。
正規表現を使ったりsubを使ったりして、ふだんと同じように文字列を切り貼りします。最大でここが20個ぐらい生えるんですよ。24個ぐらいメソッドが生えるので、ここがちょっと長いんですが、これで変更した文字列をコントロールファイルに書き戻します。File.writeで書き戻して、「RuboCop」をかけて、コミットを積む、までをやっていました。
今回は、文字列としてファイルを整形したり、ファイルをいじったりしたのですが、カスタムCopを作ってauto-correctするのも良さそうだなと思っています。
スクリプトを書くと何がいいかというと、継続的に負債を返却できるようになるんですね。というのも、コマンドを打つだけで、diffどころかコミットの作成まで完了するので、隙間時間で行えるようになります。例えばミーティング前の10分とか、空かないですか?
そういう時に10分どころか1分もあればdiffが作れます。書いたとおりに動くし集中力不要でできるので、継続的にやりやすくなります。
また、この時は手元でコマンドを打っていたのですが、「GitHub Actions」とかでプルリクの作成まで自動化するのも良さそうだなと思います。
スクリプトにするともう1個いいことがあって、作業手順を忘れられるんですね。ということは、いつでもやめられるし、いつでも再開できる。実際、本業が忙しくなった時に、途中で「ちょっと今は無理だな」と思って1年弱放置したことがあります。その間に、このCrudControllerの振る舞いを知らないことで障害が起きてしまったんですが、「あぁ、あれはやはりやったほうがいいよな」と思って再開した時もハードルが低く、再開ができました。
2つ目ですね。変更リスクを減らすのを心掛けていました。ボーイスカウトルールというものがあります。『コードに汚い部分を残したままにすることは、誰から見ても「してはならないこと」なのです』というのがあるんですが、これをあえて守らないようにしていました。
どういうことかというと、変更の関心事を少なく保つようにしていたんですね。例えば、「crud_controller User, [:update]」と呼ぶと、search_oneというメソッドが生えます。さっきのメタプロで生やされるやつですね。
ただこれ、@userというインスタント変数にセットするのは、before_actionの中ではなくて、update内でやったほうが、良さそうだなと思って、そういうふうにしたんですが、そうすると、別のコールバックで呼ばれていた@userがnilになってエラーになるということがありました。
コード品質に関心がある人、ボーイスカウトルールを守る人ほど、ついでに直してしまいがちだと思うんですね。でもそうすると、関心事が増えてバグの混入に気がつきにくくなるので、それはそれで別として、CrudControllerの撤廃は撤廃として、さらなるリファクタリングは、また次の変更で直そうというようにしていました。
また、(スライドを示して)こんなプルリクがあります。勘のいい方はお気づきかもしれませんが、Commitsが43、Files changedが33、diffが「+584-207」で、けっこう大きな変更になっています。これが障害を起こしました。
確かに変更量は多かったんだけど、テストがあったのになんで気がつかなかったんだろうと思って、よくよく見てみたら、テストの中にexpectがなくて検知できなかったんですね。なにもテストしていない、いわゆる偽陰性のテストになっていました。
つまり、テストはあるけどテストをしていない状態だったんですね。これはさすがに二度と起こしたくないので、expectationのないitを検知するカスタムCopを作って導入しました。
ちなみにこれは、@r7kamuraさんが「rubocop-rspec」の本体にパッチを送ってくれて、今ではrubocop-rspecで利用できるようになっています。
また、ちょっと話が戻りますが、変更量を少なく保つというのもやっています。基本的にやっている方も多いんじゃないかなと思うんですが、機械的な変更もプルリクを分割すべきかというと、そこは議論の余地があるなと思っていて。
というのも、RuboCopのauto-correctとかは、何千、何万行という感じになったとしても、そんなに分けないと思うんですよ。そこは、安全性とコストのバランスだなと思うんですが、今回は安全性を取って、小さくプルリクを分割するようになりました。
というのも、さっきお伝えしたとおり、最初はテストがあるところからやっていたのですが、テストがないところもあって、追加しながらやっていたりとか、手作業でちょっと仕上げをしたりというところもあったので、今回は安全性を取って、小さくプルリクを分割しました。
これは1年と200プルリクエストぐらいかかったんですけど、これがなかったら、正直もうちょっと少ない日数とか、もうちょっと少ない、100行かないくらいのプルリクエストで完了できたんじゃないかなと思います。でももし障害が起こったら、めちゃめちゃモチベーションが下がるんですよね。そうすると継続できないので、今回は安全性を取って、こういうふうにしていました。
変更リスクを減らすというのは、以下のようなことですね。プロジェクトを進めていると、安全な変更を阻害する要因がいくつか出てくるんですね。それをただ、そのまま場当たり的にするというよりも、具体化して、それを地道に排除するということをやっていました。
3つ目はチーム戦にする、ですね。私たちはチーム開発をしているので、レビューを依頼するわけですが、最初の頃は、コード品質に関心が高い人にレビューを依頼していました。でもそうすると負債返却がコード品質に関心が高い人の関心事になってしまって、みんなの関心事じゃなくなるんですよね。
そこに分断が発生してしまうのはすごく良くないなと思って、途中からコードのオーナーっぽいチームにレビュー依頼をするようにしました。これで負債をみんなの関心事にするようにしました。
また、最初スクリプトを手元に置いてやっていたのですが、それをrakeタスクにして、masterマージして、誰でも取り組めるようにしました。
それからもう少しして、メンバーが手を挙げてくれたので、2人で取り組むようになりました。これ、すごくよかったです。レビューが爆速で回るんですよ。お互いにプルリクを作って、レビューし合えるようになるので、レビューがすごくスムーズに回るようになってありがたかったです。ほかにも、進め方の改善を提案してくれて、倍速で撤廃が進むようになりました。ありがたかったですね。
これがすごくよかったんですけど、なんといっても1人でやっていると、飽きるんですよ。さすがに1年間も同じことをやっていると、飽きる。
ですが、人と一緒にやるとコミュニケーションが発生するので、すごく楽しいんですね。なので、チーム戦、隙間プロジェクトであっても2人以上でやるというのは、すごくおすすめです。
最後ですね、モチベーションを保つということなんですが、先ほどもちょっとお話が出たんですが、飽きるんですね。社内でCrudControllerに一番詳しい人というのをやっていたので、「もうみんな完全に理解してしまって、困っている人はいないんじゃないかな」と思い始めるんですが、やはりそんなことはないわけなんですよ。
けれども、自信がなくなってくるので、モチベーションを保つために、まず進捗を見える化するというのが、1つおすすめです。
数字ですね、何パーセント終わったかとか、あと何ファイル残っているんだろうというのをやることで、進んでいることが目に見えてわかるので、すごくうれしくなります。
あとは、私はけっこう長い期間1人でやっていたので、手元でコマンドを打って確認していたのですが、チームでやる場合、複数人でやる場合は、例えば「Slack」に通知とかしていると、みんなの目に見えてよいかなと思います。
また、褒めてもらうというのもすごく大事だなと思っています。timesで「なになにが終わった」とつぶやくと、みんな優しいので、リアクションをすごいつけてくれるわけですよ。
そうすると、「あっ、これはちゃんとやっていて意義があることなんだな」と自分でも認識できるので、褒めてもらうというのもすごくよかったなと思っています。
また、「開発本部自慢大会」というのを会社で月1でやっているんですが、この自慢大会で、CrudController撤廃完了を自慢した時も、みんなが「すごい、すごい!」とすごく言ってくれて、「あぁ、興味関心を持ってくれてたんだな」というのがわかって、これでまた次の負債の返却もがんばろうと思うことができました。
ということで、CrudControllerの撤廃が完了しました。
最後に、まとめです。隙間プロジェクトでこういう巨大な負債に立ち向かっていく時に一番大事なのは、継続性だなと感じました。
というのも、隙間プロジェクトでこういうことをやるのは、けっこうもう、善意とかやっていきとかで駆動されることが多いんですよね。なので、継続できないと終わらないんですが、逆に言えば、継続できると終わるんですよね。
なので、この、安全に楽しくできる方法を探すと良いのかなと思っています。あとはみなさん、先ほどの浦嶌さん(浦嶌啓太氏)の発表を聞かれてました? すごくよかったですよね。私も創意工夫とパワープレイをもってやっていきたいなと思いました。
では、楽しくコードを書いていきましょう。以上です。ご清聴ありがとうございました。
関連タグ:
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05