レシピIDリンク移行の話

こんにちは、2024年4月から、新卒でレシピ事業部プロダクト開発グループに所属している、張頌です。私が新卒入社して最初に取り組むこととなったのが、このブログで以前ご紹介したOne Experienceプロジェクトです。

このプロジェクトには、データ移行や多言語対応など、私が経験してこなかったタスクがたくさんありました。私が所属しているチームではスクラム開発によってプロジェクトを進めております。初めてこの体制で大きなプロジェクトに参加することは、大変でしたが面白い体験でした。私はこの中で、データ移行のレシピIDリンクの移行を担当いたしました。

私が最初にこのタスクを携わっていた時に、色々な理由でミスがありました。本稿はこのタスクの新人なりの振り返りをし、次にもし大きなプロジェクトに携わる時に、どう対処すれば問題を回避できるかという知見をまとめようと思います。

レシピIDリンクの移行について

日本版ではレシピに、決まったフォーマットでレシピのIDを書くと、レシピを表示する時にそのレシピIDを持つレシピページに飛ぶリンクが付けられました。以下の画像のように、例えばレシピの手順で「レシピID:12345のソース」と記法で書くと、「レシピID:12345」の部分がリンクになります。

事前調査によると、このようなレシピIDは日本版のレシピにおいて紹介文、材料、手順、コツ・ポイントなどのところにありました。

ところで今回、レシピのデータは日本版のデータベースからグローバル版のデータベースに移行しています。このときレシピのIDが変わるので、何もしないままだとリンク先の整合性が崩れてしまいます。このためレシピIDリンクについても修正が必要ですが、グローバル版のレシピのIDは移行してみないと決まりません。故に、先にレシピのデータを移行し、そのあとレシピIDリンクを置換することにしました。今回のタスクはリンク先となるレシピのIDを、グローバル版のIDに置換することです。

レシピIDリンク移行の振り返り

レシピIDリンク移行に取り組み始めた頃、私はまだ慣れていないことや知らないことが多く、あまり細かい調査はできていませんでした。その結果としていくつかの不具合を生んでしまいました。幸いOne Experienceのリリースの前に起こった不具合だったのでユーザーへの影響はなかったのですが、ここではそれらを振り返りたいと思います。

状態遷移の考慮漏れ

最初に取り組んだのは、日本版からグローバル版へ継続的にデータ移行している部分での置換です。データ移行を行っている部分のキャッチアップが難しかったので、コードを見ながら吸収しようと思いました。実際キャッチアップはすぐに終わり、タスクを始めた次の日にはpull requestを出しました。不安なところも色々ありましたが、コードを読みながら問題ないかを確認し、Approveをもらって、Deployしました。

そこで問題が発生しました。日本版でレシピを編集しても、グローバル版でレシピIDが変わらなかったんです。そこでデータ移行のテストファイルから、レシピ編集のシチュエーションをテストしている箇所を見つけて、シミュレートして試しました。Debugしてコードを追っていたら、レシピ編集の際には自分が実装を追加したところとは別の箇所のコードが動いていると気付きました。その後、丁寧にレシピ移行全体の状態遷移を考え、あらゆる状況を考慮して、テストでシミュレートしました。全遷移パターンで対応済みだと確信して、実装完了としました。

振り返ってみると、最初にコードをみた時に、レシピ編集についての実装があることには気付いていたことを思い出しました。このとき自分が実装したところだけで足りているのか不安で、周りの同僚には相談していましたが、実装に詳しい先輩には相談していませんでした。また、テストされている条件を網羅的に試して、できるだけ再現して漏れがないか確認した方が良かったと思います。

不可解なクエリ結果

データ移行の実装では、継続的なデータ移行とは別に、まとまったデータを一括でバッチ処理することでデータ移行を行う実装も存在しました。ここについてもレシピIDリンク移行を行う必要があったのですが、罠がありすぎて、難しかったです。いくつかご紹介します。

バッチ処理によって移行されてきたレシピに対して、後からレシピIDリンクの書き換えを行うことを考えます。どのレシピについて書き換えるべきか判断するための方法として、レシピの最終更新時刻を日本版とグローバル版で比較することで決めることとしました。ここで日本版の最終更新時刻はQueuery(きゅうり)という内製ツールでSQLクエリを実行して得ています。

以前にも似たような実装をした経験があったので、自信満々でpull requestを出して、早速Approveをもらって実行しました。検証環境で確かめて、何個か例を見ると書き変わったことを確認しました。

そこで、また問題が発生しました。データを調査したら、書き換わってないレシピIDが存在していると気付いたのです。そこでまた調査を始めまして、コードを読んで、色々バグが出そうなところにバグの仮説を立ててました。それでも結局見つからなかったので、ちょっと焦ったところもありまして、先輩とペアプロを始めました。

先輩と一緒に調査をしまして、そもそもQueueryから返ってくるレシピのリストに書き換わる対象レシピが入ってないのかもしれないと考え始めました。しかし、手元で同じSQLクエリを実行した結果には対象レシピが入っていたので、結果が違うことはありえないなと最初は思っていました。ところが、実装の方の中間変数をstep by stepに見たら、確かに対象レシピが入ってないことがありました。何故そうなっているのか全く分からず、同僚と共に調査を続けました。最終的に、Queueryの場合と手元の場合でSQLクエリの中でのエスケープの挙動が異なっているのが原因と分かりました。

ダブルチェックの重要さ

レシピIDリンクのバッチ処理を実際に動かす前に、継続的な移行のときの失敗を思い出し、全体的なデータ検証をした方がいいと思いました。そこでレシピIDに関連するバグが出そうな仮説を立て、検証することにしました。そういえば私はアイデアをたくさん出すことが得意なので、既存のデータの不具合を結構見つけました。実行する前に見つかって良かったと思いました。

その後もいくつかの問題を切り抜けながら、レシピIDリンクを移行することができました。最終的に無事に移行できて良かったです。最初にミスがあり焦ったけど、難しい仕様があり、罠みたいなバグが難しいのがしょうがなく、失敗しても焦らず、相談してチャレンジしていくことが大事だと非常にわかりました。これもだんだん出来るようになったポイントかなと思いました。

次に活かすポイント

今回の振り返りは、おそらく以下の3つのパターンに分けられると思います。

ポイント1:未知の存在を認識し、詳しい人や前例の知恵を吸収する

アクションをとる前に、何を追加で確認しないとダメかを知らないままに、今ままでの知識だけで対応して、ミスる場合です。新卒の時に、特にOne Experienceのような大きいプロジェクトの中で、全面的に細かく教えてもらえる人は少ないので、結局個人判断の時が多いです。ある程度のミスは起こるものであり、教訓にすべきだと思いますが、そのような状況でもできることはあると思います。

たとえば知らないタスクをもらったとき、課題の理解が足りなく解像度が低いままでタスクを進めないことです。自信はいいことだと思いますが、新卒として理解したのは、完全理解との距離があることを認識すべきでした。しかもレビュワーも知らないことがあるので、その時、知らないことがあるかを確認するためのリサーチや、気軽に他の人に聞いていくことで、改善するだろうと思います。

ポイント2:裁量範囲を模索し、適切に先輩とシンクする

アクションをとるとき、大量な意思決定があるけど、個人の裁量でできないことを決めちゃった場合です。状態遷移の考慮漏れを起こしたときのように、過信によって問題が起こりえます。

これについてはアクションをとる時に、先輩との報告と相談が必要です。それだけではなく、先輩から助言をもらった時にも、課題と話を完全に吸収してから、議論していくことが大事です。要するに、アクションをとる前に先輩の言っていることを理解してから、自分が何をするつもりか報告し、目標と認識がシンクして初めて、アクションをとるべきだと思います。

ポイント3:未然に防ぐ、考慮漏れ確認をルーチン化する

実際に、新卒に限らず、しょうがなく、人間はミスすることが必ずある生き物なので、そのミスした経験を持って、2度目をないようにするのがいいと思います。そのために、確認ポイントを書面化したり、今回のミスのフィードバックを吸収したりできます。特に重要そうなものを他の人と共有しても良いでしょう。

そして、次に似たようなことがあったら、網羅的な確認をきちんと怠けず行うべきです。自分の変更の影響力を理解しつつ、ユーザーのためにも、考慮漏れ確認をルーチン化した方がいいと思います。

最後に

今回は、One Experienceプロジェクトの環境でのレシピIDリンク移行の時の自分なりの振り返りをしました。私がこのタスクを始めて、想定外の問題がたくさんでました。難しい仕様をもつタスクなので、うまくできなかったところがありました。この振り返りと経験を次に似たようなタスクをやる時に、できるだけ参考にしたいと考えています。