ゴメン!オレが悪かった!~技術的負債の懺悔~
ごきげんよう🙋♀️ツクリンクでエンジニアリングマネージャーをしているあっきー(@kuronekopunk)です。
この記事はツクリンク プロダクト部 Advent Calendar 2023 4日目の記事です。
前日はSRE泉田さんの「ECS スケジュールされたタスクが起動しなかったことを監視する」でした。
自社サービスのツクリンクは最初は自分がPHPで作っていましたが、エンジニアの参画と合わせて2014年からRuby on Railsにリプレースしています。
リプレースから10年弱経った今、とりあえずで作ったけどサービス成長で運用が辛く負債に感じる部分を紹介していきます。(2021年に書いたRails以降時のnote)
メール、通知の設計
管理者のアドレスをBCCに入れた
0→1のサービス開発当初、「ユーザーさんに送ったメールの内容を知りたい」という動機からユーザーさん宛のメールのBCCに管理者のメールアドレスを追加していました。
サービスが大きくなると管理者アドレスのメール数が増え重要なメールが埋もれる可能性が増えたり、システム的にはメール送信自体が増えるなどの問題が出てきました。
「ユーザーさんに送ったメールの内容を知りたい」という要望であれば適切なログ設計ができると良かったです。
メール経由のSlack通知で業務設計がされた
当時はSlackも無く、メンバー間の連絡はFacebookメッセンジャーを利用していました。そのため管理者宛の通知メールを用意しました。
例えば「問い合わせがありました」の様なメール。
そこから数年経ったあとで「Slackで見たい」という要望が出て、管理者アドレスで特定のメールを受信したらSlackチャンネルへ転送する設定を行いました。
具体的には以下の機能を使って実現されています。
※Gmail,Slack公式ドキュメントのリンク
・Gmail のメールを他のアカウントに自動転送する - Gmail ヘルプ
・Slack にメールを送信する
メールも見つつ、『Slackでも見れる』であれば良いのですが、そのSlack通知を元に業務フローが設計されました。
『システムからメール送信→メール受信→メールフィルターから転送→Slackに通知』となるので
システムから適切にメール送信されたか
Gmailで受信され、フィルターに適切にかかり転送されたか
Slackは転送を受信して表示してくれるか
といった自分たちでは確認しづらいポイントが増えてしまいました。
業務設計を考慮し直接のSlack通知に切り替えるか、CRMツールでのチケット管理にするなどの対応が必要でした。
クラス・DB設計
サービスクラス
複数モデルを跨いだ処理やモデルの責務でない処理をサービスクラスに切り出しました。
まだサービスクラスも増えていないので困ったことは出てきていませんが、すえなみさんも語られている『僕は単に CQS を実現したいだけだったのかもしれない』に同意です。
サービスという名前も役割が明確でない点が気になっているので適切に責務を分けていきたいです。
Kaigi on RailsでYOUTRUSTの寺井さんが話されていたCQRSアーキテクチャが一番イメージが近いです。
ログテーブルをアプリケーションから参照をした
ユーザーの行動ログを取得するGem impressionistを利用してログを取っていました。(他にも xxx_logs のようなテーブルも存在します)
アプリケーション側で「ユーザーが最後に◯◯した時間を利用したい」というユースケースが出た際に上記の行動ログから対象時間を取得して利用しました。別テーブルに値を保存していたので都度SQLが発行されるわけではないですがデータ量の増加に伴いバッチ処理でもスロークエリが出るようになりました。
またメール送信ログなども取っていますが、開封やクリックで対象レコードを更新する仕組みになっていました。( mail_logs.visited_at の様なイメージ)そのためログテーブルにUPDATEが走ってしまい処理を重くする原因にもなり得ます。
イミュータブルなデータ構造にすることや、分析に利用するものであればログに出して、BigQueryなど別で収集・分析を行うなどログの設計にも考慮が必要でした。
多対多の安直な中間テーブル名
Kaigi on Railsのmoroさんの登壇で話されていたイベントエンティティを見つけるといった話です。
conferencesとusersが多対多だった場合、 conference_users というような中間テーブル名をつけてしまっていました。
ドメインの分析・設計を行い、資料にもあるように参加登録→registrationsのような命名ができればよかったと思います。
その他設計
管理画面に集計画面を作った
デイリーの会員登録数やその他数値を集計した画面を作りました。
最初は簡単な集計だけでも、だんだん要望が増え複雑な集計をするクエリが増え始めました。そのため管理画面を開くと重い集計クエリが走る仕様。データ量の増加とともに問題が出てきました。
集計結果のキャッシュテーブルを作ったり、負荷を軽くしたものの、根本はLookerやRedash等のBIツールで外に切り出すのが適切でした。
※今はBIツールも利用していますが一部管理画面から消されていない集計画面が存在します
CSS設計を捨てられなかった、こだわり過ぎた
ツクリンクのCSS設計の変遷は以下の記事で紹介されています。
現在はFLOCSSベースのCSS設計で運用されています。FLOCSSに変更した際に過去のCSSを残した状態で新しいページのみ適用したため過去のCSSに引っ張られて影響が出やすい箇所や新しく入った方のキャッチアップにも時間がかかります。
キャッチアップに時間がかかるという点では、現在フロントバックエンドを分けずに開発しているためバックエンドメインでフロントエンドは軽く分かるくらいの方もいるためFLOCSSの思想理解や実際の命名に戸惑ったりキャッチアップに時間がかかるなどもあり、誰でも触れるようなCSS設計や運用設計、業務設計ができたら良かったと思います。
GTM、GAイベント設計をフロントに寄せすぎた
正解は無く間違っているとは言えないのですが運用がつらい点です。
GTM運用者とアプリケーションの開発者が異なる場合はコミュニケーション設計も含めて考える必要があります。コミュニケーションについてはa2iのセミナーでもお話させていただきました。
GTMだけで設定する場合、一般的にはCSSセレクタを指定して設定しますが、別コンポーネントの同じセレクタが出現したり、フロントの変更に伴いセレクタが変わったりと適切にイベント取得をするには難しい運用設計だと思っています。
上記を考慮し一時期は一意のセレクタを追加して `class="ga-contact-form-submit"` のような形でGTMにクラス名指定したトリガーとタグを作っていた時期もありましたが、GTM側でも都度設定が必要になるので止めています。
現状は `class="ga4-event"` といった汎用クラスを指定し、GTM側でdata属性の `data-ga4-event-name="contact-form-submit"` イベント名を拾っています。(※細かいAttributesなどの取得は説明を省いています)
GAイベントの取得、GTM管理の観点ではコストが低いのですがフロントにデータ取得のためのdata属性やクラス名が増えてしまい、フロント開発側にコストが寄っているように感じています。
このあたりはベストな運用の想像がついていないので誰か話させてください。
さいごに
ぱっと思いつくものを書いたけど、たぶんこれの何倍も負債を作ってきてしまっていると思います。ちゃんと責任をもって負債の借り換え・返済をしていくので力を貸してください。
明日はデザイナーのリヨタさんによるプロダクト開発と子育ての話です!お楽しみに!