TECH PLAY

タイミー

タイミー の技術ブログ

264

Timee Advent Calendar 2024 6日目の記事です。 タイミーでスクラムマスター(以下、SM)/アジャイルコーチを担当している正義です! この記事では SMに求められるリーダーの性質は何か SMもプロジェクトマネジメントに関する知識を得ておくと良い というお話をします。 スクラムマスターに求められるリーダーの性質は色々ある SMはどのようなリーダーであるべきか、色々な観点で話されているのを見かけます。 スクラムガイドから読み取ろうとしたり、実践ベースで考えたり、学術的な観点などがあります。 (過去に弊社SMが登壇した際の資料もご紹介します!) https://speakerdeck.com/shinop/practical-scrum-master-vs-theoretical-scrum-researcher-d29104ff-15ed-4fcc-a0e9-9e0bef2a0d3a 私も数年間SMを担当してきたので、自分なりに考えていることをお話ししてみたいと思います。 スクラムマスターはどんなリーダーなのか? 「スクラムマスター」というロールはスクラムガイド(2013、2017、2020年版)で定義されているので、そこから見ていきましょう。 まずは 2013年版 から。 スクラムマスター スクラムマスターは、スクラムの理解と成立に責任を持つ。そのためにスクラムマスターは、スクラムチームにスクラムの理論・プラクティス・ルールを守ってもらうようにする。 スクラムマスターは、スクラムチームのサーバントリーダーである(訳注:メンバーが成果を上げるために支援や奉仕をするリーダーのこと)。 スクラムマスターは、スクラムチームとやり取りをするときに役に立つこと/立たないことをスクラムチームの外部の人たちに理解してもらう。スクラムマスターは、こうしたやり取りに変化をもたらすことで、スクラムチームの作る価値を最大化する。 このように、明確にリーダーのスタイルがサーバントリーダーとして説明されています。 そして責任についても、「スクラムの理解と成立に責任を持つ」とあります。そのために、理論・プラクティス・ルールをチームに落とし込み、チーム外の人々に対してスクラムチームを理解してもらう活動が中心に記載されています。 つまり、大切にすべき事柄をチームが会得し、実践していくための環境・システム作りに重きを置いていると捉えました。 続いて 2017年版 から。 スクラムマスター スクラムマスターは、スクラムガイドで定義されたスクラムの促進と支援に責任を持つ。スクラムマスターは、スクラムの理論・プラクティス・ルール・価値基準を全員に理解してもらえるように支援することで、その責任を果たす。 スクラムマスターは、スクラムチームのサーバントリーダーである(訳注:メンバーが成果を上げるために支援や奉仕をするリーダーのこと)。 スクラムマスターは、スクラムチームとやり取りをするときに役に立つこと/立たないことをスクラムチームの外部の人たちに理解してもらう。スクラムマスターは、こうしたやり取りに変化をもたらすことで、スクラムチームの作る価値を最大化する。 「スクラム」が「スクラムチーム」に変更されていますが、記載されている内容については大きな変更はありません。 責任については「SMは、スクラムガイドで定義されたスクラムの促進と支援に責任を持つ」とあります。「成立」ではなく「促進と支援」に変わったのは、スクラム成立はSMだけが責任を負うわけではなく、スクラムチームで進めていくことを意識付けているように感じました。 活動自体は、チームに落とし込む項目に「価値基準」が足されてはいますが、大枠では2013年からの変化はありません。引き続き2013年と同様の事柄に重きを置いていると解釈しました。 そして 2020年版 。 スクラムマスター スクラムマスターは、スクラムガイドで定義されたスクラムを確⽴させることの結果に責任を持つ。スクラムマスターは、スクラムチームと組織において、スクラムの理論とプラクティスを全員に理解してもらえるよう⽀援することで、その責任を果たす。 スクラムマスターは、スクラムチームの有効性に責任を持つ。スクラムマスターは、スクラムチームがスクラムフレームワーク内でプラクティスを改善できるようにすることで、その責任を果たす。 スクラムマスターは、スクラムチームと、より⼤きな組織に奉仕する真のリーダーである。 これまでとは異なる点が増えています。 「責任」に触れている部分(ほぼ全体)。 スクラムマスターは、スクラムガイドで定義されたスクラムを確⽴させることの結果に責任を持つ。 スクラムマスターは、スクラムチームと組織において、スクラムの理論とプラクティスを全員に理解してもらえるよう⽀援することで、その責任を果たす。 スクラムマスターは、スクラムチームの有効性に責任を持つ。 スクラムマスターは、スクラムチームがスクラムフレームワーク内でプラクティスを改善できるようにすることで、その責任を果たす。 責任の内容とその果たし方がセットで書かれています。 注目したい点の一つは「スクラムガイドで定義されたスクラムを確⽴させることの結果に責任を持つ」です。今までの「促進と支援」よりその先の変化、つまり結果にフォーカスしています。 また、「スクラムチームの有効性に責任を持つ」という点が一番大きい変化だと捉えています。スクラムチームの有効性、つまり結果に対し責任を持つことが明確に記載されました。 以前はスクラムの定着がSMの役割でしたが、スクラムが効果的に働き、有効である、結果を出すことがSMに求められるように変化しました。スクラムはあくまで方法であり、大前提となる目的を達成することとSMがそこに意識を向けるために責任が明文化されたと考えています。 そしてリーダーについて言及している部分は「スクラムマスターは、スクラムチームと、より⼤きな組織に奉仕する真のリーダーである」です。 これまでに色々な議論のきっかけになっている「真のリーダー」、これはとても抽象的なので様々な解釈があると思います。 継続して「奉仕する」が使用されているので、これまでのサーバントリーダーは引き継いでいるように見えます。しかし、サーバントリーダーが明示的に記載されなくなったのは、それ以外にも求められるリーダーとしての役割、リーダーシップが必要であることを示唆しているようです。 パス・ゴール理論の4つのリーダータイプではありませんが、 サーバントというスタイルで、チームの自律性を促し、支援するリーダー 自身が先導し、チームがゴールへ到達することを導くリーダー という形で、自分でチームを引っ張り、目的にコミットする形も必要だと考えられます。 (結果に対し責任を持つということは、こちらに繋がってきます) スクラムマスターが向き合う”結果”とは? 「結果」についての分解方法としては「直接的な結果」と「間接的な結果」があります。 直接的な結果:ある役割や行動が、最終的な成果やアウトプットに対して直接的に影響を与える場合を指す 間接的な結果:ある役割や行動が、最終的な成果やアウトプットに直接関与せず、それを実現するためのプロセスや環境を整備する役割を果たす場合を指す 開発においては、下記のようになります。 項目 直接的な結果 間接的な結果 対象 成果物などアウトプット プロセスや環境 影響の範囲 短期的で即時的 長期的で持続的 測定のしやすさ 測定可能(例:完成した機能の数) 測定が難しい(例:チームの生産性) 成果との距離 成果に直接結びつく 成果を間接的に支える SMはどちらの結果に向き合うべきでしょうか。 その答えはどちらか一方ではなく、アジャイルソフトウェア開発宣言のように「左記の結果を求めながら右記の結果に重きを置く」と考えています。 スクラムガイドでは、「スクラムマスターは、スクラムチームがスクラムフレームワーク内でプラクティスを改善できるようにすることで、その責任を果たす」とあります。これはスクラムチームに対して、間接的な結果および責任を果たすことに向き合うことを意味します。 しかし、SMは直接的な結果について完全に無関心でいることはできません。直接的な結果(品質、納期、成果物など)は、間接的な責任を果たす上での重要な指標であり、SMが仕事を効果的に遂行するためには、直接的な結果にどのように影響を与えるかを理解し、それを考慮する必要があります。 スクラムマスターがプロジェクトマネジメントを学ぶ必要性について SMはチームが自律的に活動していけるように、「皆さんはどうしたいですか?」を問う姿が多く見られます。 しかし、チームに問いかけ続けていれば全て問題なく進められるかというと、そうではありません。 チーム内で補える観点や知見に基づいてチーム内で考えることもできますが、それ以上の観点や知識はティーチングを主要なメタスキルとするSMが補う必要があります。 (ラーメンを構成する各材料の作り方をチームが知っていても、合わせて「美味しいラーメン」という成果物にする方法を知らなければ、想定よりもぬるかったり美味しくないラーメンができてしまうイメージ) SMがプロダクト開発に紐づく全てのことをティーチングできれば良いのですが、スクラムガイドやスクラム・アジャイルに関する知見のみでは難しいです。 スクラムガイドはスクラムを定義しており、スクラムは軽量級のフレームワークです。スクラムガイド自体も目次や用語集などを含めて全17ページ程度の少ないボリュームです。 スクラムにおける重要な情報が厳選され、抽象化して記載されていて、プロダクト開発をチームで進める上で必要な考え方や観点を全て網羅しているわけではありません。 そこで、比較的SMにとって親和性が高く、知っておくと活用しやすい知識は「プロジェクトマネジメント」だと考えています。 理由はシンプルに、「そのプロダクト開発は、いつ・どうなったら終わりなのか?」という説明責任をSMにも果たしてほしいからです。 チームが自己管理できるよう、チームの自律性を高めることを主軸にして意識していくSMだからこそ、上記の質問には答えられる必要があると考えています。 チームが自分たちで考えられる環境を求めていく一方で、チームに対してティーチングとコーチングを適切に行っていくためには、SM自身がプロダクト開発の完遂に対して必要な知見と考え方を自分で説明できるようになっていてほしいです。 そうでないと、プロダクト開発およびプロジェクトを前に進めるための適切なWhyの説明ができず、チームにとってひたすら自身の思い浮かぶプラクティスを推進しようとする人になってしまうのでしょう…(経験談) プロジェクトマネジメントから得られる観点を取り入れて知的創造を果たす 「そのプロダクト開発は、いつ・どうなったら終わりなのか?」という説明責任を果たすことは、一つのリーダーシップの体現です。 これはモノづくりをする上で行う2つの創造:知的創造と物的創造のうちの知的創造に該当します。 書籍: スティーブン・R.コヴィー『完訳 7つの習慣 人格主義の回復』(キングベアー出版) p.155 第二部「私的成功」第2の習慣「終わりを思い描くことから始める」より 簡単に説明すると、物的創造は定められた方向に向かって限られたリソースを活用し効率よく業務を遂行するための活動、つまりマネジメントと実行責任の完遂を意味します。 一方、知的創造は組織が向かう方向を定め、「終わりを思い描く」「ゴールを決める」活動となります。つまり、リーダーシップとそれに伴う説明責任の完遂です。 (ここでいう「終わり/ゴール」には抽象的なビジョンやミッションではなく、プロダクト開発のゴール、プロジェクトの終わりを当てはめています) プロダクト開発のSMが示す一つのリーダーシップであり、「自身が先導し、チームがゴールへ到達することを導くリーダー」に求められます。 では、どうやったらプロダクト開発における「終わりを思い描く」「ゴールを決める」という知的創造を果たすことができるのか? そのための一つとして、「プロジェクトマネジメントを学ぶ」を私はおすすめしているのです。 プロジェクトマネジメントのすべてをここで紹介するのは非常に大変なので、知っておくと良い点として一つ挙げるとPMBOK第6版に記載されている「10の知識エリア」があります。 注:PMBOKは2021年に第7版が出版されています。第6版では日本語版で約780ページあったものが、第7版では約370ページになっています。また、内容としてもプロジェクトマネジメントの手順をまとめたものから、プロジェクトの方針や考え方といった原理・原則をメインにした構成になっています。今回は、観点として具体的に分かりやすくリストアップされている第6版の内容を記載します。 10の知識エリア プロジェクト統合マネジメント プロジェクトやフェーズをどのように進めるのかを定義する知識エリア プロジェクト・スコープ・マネジメント プロジェクトやフェーズにおける作業範囲や、成果物の設定に関して定義する知識エリア プロジェクト・スケジュール・マネジメント 納期管理に関する知識エリア プロジェクト・コスト・マネジメント プロジェクトで承認された予算に関する知識エリア プロジェクト・品質・マネジメント 生成される成果物やプロジェクトの品質に関する知識エリア プロジェクト・資源・マネジメント メンバーなどの人的資源や、物的資源などの管理に関する知識エリア プロジェクト・コミュニケーション・マネジメント 会議予定を調整し、適切にコミュニケーション内容や方法を管理する知識エリア プロジェクト・リスク・マネジメント プロジェクトにおけるリスクの特定・分析・対応方法、対応策の実行、リスク監視に関する知識エリア プロジェクト・調達・マネジメント 契約終結やベンダーの管理に関する知識エリア プロジェクト・ステークホルダー・マネジメント ステークホルダーの関与度の定義や管理に関する知識エリア すべてを満遍なく検討してマネジメントしましょう、というわけではありません。 ただ、これらの項目について考えてみることで、プロダクト開発をスクラムで行おうとしている現場において、スクラムガイドに記載されていない観点を補うことができると考えます。 例えば、プロジェクト・ステークホルダー・マネジメント。スクラムガイドでは、「ステークホルダー」が誰なのかをはっきりと定義していません。それはプロダクト開発におけるステークホルダーは多く存在しますし、それぞれのステークホルダーに対しての期待や重要度は常に一定ではないからでしょう。 そのため個々のプロジェクトにおいては、チームが関わるステークホルダーは誰なのかを明確にし、ステークホルダーとの関わり方を定義しないと、チームの動き方や目指すことは容易にぶれてしまいます。 ステークホルダーマネジメントでよく見るのは、影響度合いと関心度合いを2次元で表現した次の図です。 ここに、チームが関わるプロダクト開発において関連するステークホルダーを配置します。 そうすることで、ステークホルダーの洗い出しと対応方針をチームで見える化し、透明性を高めることができます。 さらに、ステークホルダーに対しての具体的な関わり方を定義していくことで、プロジェクト・コミュニケーション・マネジメント(どのような場で何を議論すれば良いのかを定義し、チームにとって必要なコミュニケーションを適切に管理する知識エリア)の一部を満たすことができます。 また、チームの中からは意識しづらいが、じつは重要なステークホルダーも存在します(例えばCTOやCPOなど予算を握っている人やチーム)。そのようなステークホルダーに対しても意識を向ける視座をSMが獲得していて、チームに気づかせることができれば、必要な報告などを行うことができるようになり、急なプロジェクトの方向転換の回避などリスクマネジメントにつながる可能性もあります。 それは、SMの支援に記載されている項目の一つ、「スクラムチームの進捗を妨げる障害物を排除するように働きかける」という項目に該当します。 このように、プロジェクトマネジメントの要素を知り、観点として身につけていくことで「スクラムガイドには記載されていないが、プロダクト開発を行う上で知っておくべきこと」を補えるようになります。 そしてその知識を活用し、チームが向き合う開発が「いつ・どのように終わるのか?」をSMなりに考え、チームに適切なティーチングを行いつつ、プロダクト開発にとって必要なことをチームで考えていける環境を作ることがSMのリーダーシップの体現だと私は思います。 最後に プロダクト開発において、知っておかないといけないこと、チームで話した方がよいことはとても多いです。チームがそのすべてを足並み揃えて一つずつ学び、ディスカッションを行い意思決定できると、チームにとって学びを多く得られて良いのかもしれませんが、プロダクト開発という点においては、その動きが最適とは限らない状況もあります。 そのため、プロダクト開発を進める上で必要な様々なことに優先順位をつけて考え、その上でスクラムの理論とプラクティスを無理なくチームに適用し、徐々にチームの自律性を高めつつ、時には自分がプロダクト開発をリードしていけるSMでありたいです。 以上、約2年間で考えていた自分のSM像を少し言語化してみました! SMのあり方に正解はありませんが、一つのイメージとして誰かの参考になればいいなと思っています。 それでは、私の Timee Advent Calendar 2024 Day 6は終わりとなります。 明日は、SMのしほりんさんのターンですので、お楽しみに!
アバター
この記事は Timee Advent Calendar 2024 シリーズ 1 の5日目の記事です。 はじめに こんにちは。タイミーの DRE チームの chanyou です。2024年の3月に DRE チームにジョインして、社内のデータ基盤を作って運用しています。 DuckDB を使ってデータ基盤で扱うデータの品質を保証し始めたので、その内容をご紹介します。 データ品質と完全性 タイミーのデータ基盤で重視しているデータ品質 タイミーでは、DMBOK を参考に以下のデータ品質を重視して設計や日々の運用を行っています。  特性 意味 完全性 データが欠損していないか 適時性 必要なときにすぐにデータを参照できるか 一意性 データが重複していないか 一貫性 型・タイムゾーン・表記揺れなど、値の書式や意味が統一されているか 今回は完全性にフォーカスします。 完全性が損なわれるタイミング 上記の通り、完全性とは「どの程度データに欠損があるか」を意味します。 データの欠損は、主にデータ転送時に生じる場合が多いです。例えば、以下のようなケースが考えられます。 あるテーブルが転送対象から外れてしまっていた 転送元のシステムに追加されたカラムが対象に含まれていなかった パーティション分割された Parquet ファイルのうち、一部のファイルしか転送できていなかった 従来の完全性テストの実施方法 完全性を保証するということは、欠損がないことを保証することと同義です。欠損が生じやすい転送前後のデータを比較することで、欠損の有無を検知できます。 欠損を検知する仕組みのことを、タイミーでは「完全性テスト」と呼んでいます。 表形式のデータに対して厳密に完全性テストを実施するには、セル単位で比較を行う必要があります。 これまでは計算コストがかかるため、統計量を比較する手法を取っていました。 近似的に比較していたため、全レコード全カラムに対して欠損が全くないことを厳密に保証できない課題がありました。 詳細は 昨年のアドベントカレンダー記事 をご覧いただければ幸いです。 刷新した完全性テストの実施方法 今回のケース 刷新のきっかけとなったケースについて説明します。 S3 にある Parquet ファイルを BigQuery にロードする、非常にシンプルなケースでした。 S3 の Parquet ファイルと転送後の BigQuery テーブルのデータが完全に一致することを保証する必要がありました。 Parquet ファイルと BigQuery のデータ比較のためにスクリプトを実装しました。その内部のクエリエンジンとして DuckDB を採用して、セル単位の厳密なデータの比較に対応しました。 DuckDB を採用した理由 BigQuery 内のテーブル同士であれば BigQuery のクエリで完結しますが、データベースをまたいだ完全性テストは BigQuery の外側でデータの比較をする必要があります。 DuckDB は高いパフォーマンスを維持しながら、複数のデータソースに対して同様にクエリをかけることが可能で、今回のケースに非常にマッチしていました。 データの比較も EXCEPT 句が利用可能で、簡潔なクエリで表現可能でした。 他の選択肢として Pandas や Polers などの DataFrame インターフェイスのツールも候補に挙がりますが、依存モジュールのメンテナンスコストが一定かかるため、今回のケースではシングルバイナリでより手軽に実行環境を整備しやすい DuckDB に軍配が上がりました。 以上の理由で DuckDB を採用しました。 具体的な実装方針 S3 の Parquet ファイルの読み込みについては DuckDB が標準で対応しているため、DuckDB の read_parquet() 関数で簡単に読み込むことができます。 BigQuery に対するクエリは、後述の理由により BigQuery から GCS に Parquet ファイルとして出力を行い、 GCS の Parquet ファイルを DuckDB から読み込むことで対応しました。 DuckDB の EXCEPT 句を使って、片方のテーブルにしか存在しないレコードを抽出するクエリを実行します。以下がクエリの例です。 WITH source AS ( SELECT * FROM read_parquet(getenv( ' source_path ' )) ORDER BY id ), destination AS ( SELECT * FROM read_parquet(getenv( ' destination_path ' )) ORDER BY id ) SELECT ' source ' AS _location, *, FROM ( SELECT * FROM source EXCEPT SELECT * FROM destination ) UNION ALL SELECT ' destination ' AS _location, *, FROM ( SELECT * FROM destination EXCEPT SELECT * FROM source ); source だけあるレコードと destination だけにあるレコードを抽出して、連結して出力しています。 事前に DuckDB の Secret Manager で各クラウドリソースへの認証情報を設定する必要がありますが、それだけで上記のようなクエリでセル単位の厳密な完全性テストが可能となりました。 これらを実行するシェルスクリプトを実装して、Docker コンテナにまとめて実行環境に展開しました。 よかったところ 複数のデータソースに対するクエリが、非常に簡単に実行できた ローカル、S3、 GCS のどこにデータがあっても、 read_parquet() で読み込めるのは非常に体験がよかったです。 パフォーマンスが高く安定して実行できた 従来の完全性テストから実行環境が変わったため実行時間の比較ができないのですが、刷新後は 100GB 程度の Parquet ファイルの完全性テストが IO 含めて10分以内に実行できています。 デイリー程度の転送頻度であれば毎回実行しても差し支えない実行時間で、全く問題ありませんでした。 詰まったところや工夫したところ BigQuery Community Extension で読み取れないカラムがあった 当初 DuckDB の BigQuery Community Extension を使って、BigQuery に直接クエリを実行しようとしていました。 大半のデータには問題なく使えたのですが、一部の文字列型のフィールドで読み取れないカラムがありました。 エラーメッセージがなく、読み取れなかったカラムが ORDER BY で結果の順序を変えると読み取れることがあるなど、原因特定から難航しそうなので今回のケースでは Community Extension の使用は見送りました。 BigQuery 側のログではちゃんとクエリが走っていたので、 DuckDB での処理のどこかでコケてしまっていたようでした。時間があるときに内部実装を追って、修正できそうであれば PR を送りたいと思います。 jsonlines モードと jq の組み合わせが楽だった DuckDB には csv, json, html などの 出力形式 が多数あります。 今回はシェルスクリプトで DuckDB の結果を扱いたかったため、 jsonlines で出力したうえで jq で結果を処理するのが簡単でした。 柔軟に出力を切り替えられるので、あらゆるスクリプトで利用しやすいと思います。 まとめ 完全性テストを DuckDB を使って実施する内容をご紹介しました。DuckDB を使うことで、手軽にマルチクラウドな環境においても厳密な完全性テストを行えました。 DuckDB は非常に魅力的ですが、分析用途での DuckDB はガバナンスを効かせながら運用することが難しく、現状は社内で広く使ってもらうには様々なハードルがあるように思います。 一方で今回のデータテストのように、スクリプトの内部で利用するには統制を取りやすく、非常に相性がよいように感じました。分析用途の場合は DuckDB のステートを同期し続ける必要がありますが、テストの場合は同期が不要で揮発しても問題なく、カジュアルに DuckDB を使いやすかったです。 またシングルバイナリで環境整備も非常に簡単な点も運用しやすく、他のデータテストでも機会があれば利用を検討したいと思いました。 他にも dbt で CI 実行するときに、 DuckDB アダプタに切り替えることでコストを圧縮できそうです。 CI やスクリプト用途における DuckDB の活用の余地がまだまだありそうで、今後も模索したいと思いました。
アバター
こんにちは!タイミーでAndroidエンジニアとして働いている   @orerus  ことmurataです。今回は弊社のアプリ開発チームで経験した、Firebase Remote Config(以下 RemoteConfig)を使用したABテスト実装時のトラブルと、その再発防止策について共有いたします。 はじめに モバイルアプリ開発において、ABテストは機能改善の効果を測定する上で重要な手法の一つです。今回は、私たちが実装したABテストで発生した予期せぬ動作と、そこから学んだ教訓についてお話しします。 おことわり なお、今回の話はRemoteConfing自体に問題があるというものではなく、使用方法が適切でなかった為に発生した事象ですのでご留意ください! 発生した事象 実装内容 今回の事象のきっかけとなったのは、アプリ起動時にまず表示される「さがす画面」において、検索結果のソート順切り替え機能のABテストの実装でした。 リリース後は何事も問題なく動作していたのですが、しばらく経ったある特定のタイミングで同時に複数の不具合報告が挙がりました。 報告が挙がった不具合内容は「ソート順がおかしい」「検索結果が表示されない」といった事象でした。 以下は、実際に不具合が発生していた時の画面の例です。 アプリを開いた直後に表示される一番大事な検索結果の部分が空っぽになってしまっていますね・・・。 問題の具体に入る前に、ここでこの「さがす」画面の構成について簡単に説明します。 画面構成 こちらの画面の構成としては、画面上部のカレンダーを含む画面全体、および画面下部の検索結果が表示されている部分とで異なるFragmentが使用されています。(前者をCalendarFragment、後者をResultFragmentと仮称します) 検索結果を表示するResultFragmentは日付ごとに存在しており、ViewPagerにて管理しています。 カレンダーから日付が選択されると、その日付の検索結果を表示するためのResultFragmentが生成され表示されます。 ABテスト制御 ABテストの制御にはRemoteConfigを使用しており、CalendarFragment、ResultFragment、それぞれのFragmentが生成されるタイミングでRemoteConfigからソート機能のON/OFFのConfig値をそれぞれのFragment内に保持して使用しています。 不具合発生!!! 平穏に暮らしていた中で突如同時多発的に不具合報告が挙がったタイミング、それがABテストのロールアウトを行った時でした。 この時は全ユーザーに対してソート順切り替え機能をONにするロールアウトを行いました。 その結果、先述した「ソート順がおかしい」「検索結果が表示されない」といった不具合が複数のユーザーから報告されました。 もちろんすぐに原因調査を行いましたが、手元の環境では事象がなかなか再現せず困っていたところ、ABテストのロールアウトを行っていたことを思い出しRemoteConfigから取得したConfig値の利用箇所周りを重点的に調査した結果、その利用方法に問題があり先述のような不具合が発生する可能性があることが判明しました。 以下、その不具合発生に至った原因について解説します。 原因分析 1. RemoteConfigから取得したConfig値をFragment毎にキャッシュしていた 画面が破棄されるまでの間にConfig値が変化したとしても突然画面内で機能が変化しないように、Fragment生成時にRemoteConfigからConfig値を取得し、インスタンス変数にキャッシュしていました。 このキャッシュのやり方は画面内で1箇所のみでしか行われない場合には問題が生じませんが、画面内に複数のFragmentがある場合に不整合を生じさせる余地が発生してしまいます。 とはいえ、それだけなら不整合が発生する確率は低かったのですが、次の原因がその確率を大きく引き上げてしまいました。 2. Fragmentの生成タイミングの違い 先述した通り、この画面には複数のFragmentが存在しており、またそれぞれ生成タイミングが異なります。 CalendarFragment 「さがす画面」が表示されたタイミングで生成される ResultFragment 日付が選択されたタイミングで生成される この生成タイミングの違いにより、現実的に起こり得るケース(例えば「さがす画面」のままアプリが長時間バックグラウンドになっており、復帰後に日付が再選択されたケースなど)でCalendarFragmentおよび複数日付のResultFragmentの間でConfig値に不整合が発生してしまいました。 細かなロジックは省略しますが、この不整合が引き金となり冒頭で紹介したような不具合が発生していました。 なお、今回の事象が防げなかった原因がもう一つあります。 3. QAカバレッジの不足 今回のABテストについてももちろんQAを行っていたのですが、以下の観点が意識されておらずテストケースから見落とされてしまっていました。 アプリ起動中のRemoteConfigの値更新 Config値を複数箇所で保持することによるFragment再生成時の不整合発生の可能性 一度起きてしまえば「何故気づかなかったのだろう」と思えるようなシンプルな原因ではあるのですが、「画面が破棄されるまでは同じConfig値が使われる」という思い込みが気づきを遠ざけてしまっていました。 再発防止策 再発防止策として以下の取り組みを実施しました。 1. QAプロセスの改善 QAチェックリストのテンプレートに、RemoteConfigを用いたABテストやFeatureFlagの実装時はアプリを生存させたまま値を動的に更新するテストケースを実施する旨を追加しました。 2. RemoteConfigに関するデバッグ機能の拡充 RemoteConfigは内部でキャッシュされており、先述の動的なConfig値の更新のQAを行うことが困難であった為、デバッグ時にConfig値を容易に変更できる機能を実装しました。 3. ActivityやFragmentでのConfig値のキャッシュを止める こちらは今後の話になりますが、ActivityやFragmentでConfig値をキャッシュすると類似の問題が発生する可能性がある為、画面(または一連の機能)を構成する単位で必ず同一箇所のConfig値のキャッシュを参照するような構成への変更を検討しています。例えば、画面(または一連の機能)から参照される共通のViewModel内でのキャッシュを考えています。 なお、アプリの起動中は全てのConfig値が変化しないようにするという選択肢も考えられますが、FeatureFlag管理にもRemoteConfigを利用しており、Config値の変更は可能な限り速やかに行いたい(将来的にFirebase Realtime RemoteConfigへの置き換えも視野に入れている)為、そちらの選択肢は選択しませんでした。 まとめ 今回の経験から、以下の教訓を得ることができました。 画面または一連の機能(整合性を保ちたい単位)で必ず同一箇所のConfig値のキャッシュを参照するようにする 例えば共通のViewModel内でのキャッシュなど RemoteConfigから取得する値は変化するものという前提のうえでQAを行う RemoteConfigは非常に便利な機能ですが、適切な実装と十分なQAがないと思わぬ落とし穴に遭遇する可能性があります。今回の紹介で、自分と同じような体験をしてしまう方を少しでも減らすことができれば幸いです・・・! 皆様安心安全な状態でクリスマスや年末を迎えましょう!
アバター
はじめに この記事は Timee Advent Calendar エンジニアリングパート 3日目 担当は Android Chapter の tick-taku です。 来月でタイミーに入社して1年になります。Rails など新しいことにチャレンジしたり DroidKaigi や RubyKaigi など様々なカンファレンスに参加させてもらったりと濃い体験をさせてもらえて、この1年長かったような短かったようなという不思議な気持ちです。 1年間何やったかなと振り返ってみて Hilt やデザインシステムの導入など開発の基盤となることをメインにやっていたな〜と思ったので、この記事では入社直後からやっていた開発における自動化や仕組み作りの一環としてリリースワークフローを整えた話を実装ベースで紹介します。 リリース作業の自動化 タイミーの Android Chapter はこの1年でチームメンバーが3人から8人に増加しました。(嬉しい) メンバーが増加するにあたって追い風となる反面、手作業で行っていたタスクも多くメンバー間で迷いなくスピーディに実行できるよう効率化する「レール」を敷く必要があると感じ、仕組み作り・自動化に取り組みました。 タイミーではストリームアラインドチームを採用しており基本的に Android エンジニアは各チームに散らばっています。その中でも1スプリントを1週間としているチームも多く、スプリント終了後にリリースを行うルールを採用しています。リリースサイクルが短いためリリース作業も頻繁に発生しており、バイナリ作成など一部 Bitrise で自動化されているものもありましたが基本的には以下のような作業を毎週繰り返していました。 リリース用のブランチ作成、アプリバージョンの変更・コミットなどの事前準備 リリース作業を行う PullRequest (以下、PR) を作成 リリース PR 上でのバイナリの作成・ストアへのアップロードタスクの実行と動作検証 「Next Release」マイルストーンに紐づくPRを目視で確認し、リンクをリリースPRの description や GitHub Releases に記載 各実装 PR に対してマージするタイミングで「Next Release」のマイルストーンを手動でアサイン これらをその週のリリース担当者(ランダムで選出)が作業 そこでこれらを一部自動化する Workflow を作成し、リリース作業の効率化を図りました。その Workflow を紹介する前に、前提となる運用やスクリプトについて紹介します。 GitHub CLI 始めに頻出する GitHub CLI を紹介しておきます。 個人的には一番お世話になっているツールです。そもそも Git 操作を CLI で行うので、その延長で GitHub 上の様々な操作をコマンドで実行できるため非常に便利です。 GitHub CLI 基本的に PR の作成や CI のステータス確認などは GitHub CLI を利用しています。 コードレビューに関してだけはビジュアライズされている方が理解が速いので Web で確認していますが、それも GitHub CLI から見たい PR をブラウザで開くことができるので捗ります。 GitHub Actions においても基本的にはランナーにインストールされており token も secrets に用意されているので利用するハードルも低く相性もいいです。今回も GitHub 上の操作を自動化するために多用しています。 リリース PR の自動作成 まずリリース作業用の PR を自動で作成するスクリプトを用意しました。 #!/bin/bash new_version = " $1 " # リリースに含まれる PR をリストアップ # クローズされた PR のリストから次のリリースの対象となる Milestone にアサインされたものをフィルタリングします release_title = " Release ${new_version} " pr_numbers = $( gh pr list -s closed -L 100 --json " milestone,number,labels " -q " [.[] | select(.milestone.title == \\ " ${release_title} \\ " )] " ) updates = $( echo " $pr_numbers " | jq -r ' { feature_updates: [.[] | select(.labels[].name == "Update") | .number], bug_fixes: [.[] | select(.labels[].name == "BugFix") | .number], development_updates: [.[] | select(.labels[].name == "DevelopmentUpdate") | .number], others: [.[] | select(all(.labels[].name; . != "Update" and . != "BugFix" and . != "DevelopmentUpdate")) | .number], } ' ) updates_body = $( echo " $updates " | jq -r ' "## Updates", (.feature_updates | map("- #" + tostring) | join("\\n")), "## Bug Fix", (.bug_fixes | map("- #" + tostring) | join("\\n")), "## Development Updates", (.development_updates | map("- #" + tostring) | join("\\n")), "## Others", (.others | map("- #" + tostring) | join("\\n")) ' ) pr_body = """ # $release_title ## Release Note \\`\\` \\ ` $( cat releasenotes/whatsnew-ja-JP ) \\` \\ `\\` $updates_body """ gh pr create \\ -B master \\ -t " $release_title " \\ -m " $release_title " \\ -b " $pr_body " \\ -l " Release " ストアに申請する際のリリースノートも description 上で確認できるようにしています。 実装 PR の分類とラベルによる自動化 タイミーでは内部へのリリースお知らせなどのために、リリース作業時に各PRを FeatureUpdate や BugFix などに分類して description に記載しています。 以前はリリース担当者が手動で振り分け作業を行っていましたが、これが大きな負担となっていました。 そこで PR 作成時に実装者が Update などのラベルをつけることでどれがどの分類なのか自動で振り分けるようにしました。それが上記のスクリプトの updates_body 作成の部分です。 また、手動でラベルをアサインするのも手間なので feature/update/ なら Update ラベルを追加するなど branch 名で自動で付与されるようにもしています。 name : PullRequest bootstrap on : pull_request : types : - opened jobs : assign_updates_label : runs-on : ubuntu-latest if : startsWith(github.head_ref, 'feature' ) env : PR_NUMBER : ${{ github.event.pull_request.number }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} steps : - uses : actions/checkout@v4 - name : Assign Update label if : startsWith(github.head_ref, 'feature/update/' ) run : | gh pr edit $PR_NUMBER --add-label "Update" - name : Assign Update label if : startsWith(github.head_ref, 'feature/bugfix/' ) run : | gh pr edit $PR_NUMBER --add-label "BugFix" - name : Assign Update label if : startsWith(github.head_ref, 'feature/development/' ) || startsWith(github.head_ref, 'feature/development_update/' ) run : | gh pr edit $PR_NUMBER --add-label "DevelopmentUpdate" バージョン管理の改善とアップデートコミットの自動化 以前は Gradle の config 内で直接アプリバージョンを管理しており、リリースのたびに build.gradle に差分が生じていました。 gradle には他にも様々なアプリのコアとなる記述があり、頻繁に gradle を触るのも嫌ですがさらに自動で差分を更新しコミットするのもハードルが高そうです。 これを改善するため、入社時のオンボーディングタスクとしてちょうど VersionCatalog を導入したこともあり、バージョン情報を toml ファイルで管理しました。 [versions] versionMajor = "1" versionMinor = "0" versionPatch = "0" versionOffset = "0" def versionMajor = libs.versions.versionMajor.get(). toInteger () def versionMinor = libs.versions.versionMinor.get(). toInteger () def versionPatch = libs.versions.versionPatch.get(). toInteger () def versionOffset = libs.versions.versionOffset.get(). toInteger () versionCode = versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionOffset versionName = " $versionMajor.$versionMinor.$versionPatch " そして toml ファイル内の各バージョンを入力値に更新するスクリプトを用意します。 #!/bin/bash libs_file_path = " gradle/libs.versions.toml " version = " $1 " versions = (${version // . / }) sed -i -e " /versionMajor/s/.*/versionMajor = \\ " ${versions[ 0 ]} \\ "/g " $libs_file_path sed -i -e " /versionMinor/s/.*/versionMinor = \\" ${versions[ 1 ]} \\ "/g " $libs_file_path sed -i -e " /versionPatch/s/.*/versionPatch = \\" ${versions[ 2 ]} \\ "/g " $libs_file_path sed -i -e " /versionOffset/s/.*/versionOffset = \\" ${versions[ 3 ]} \\ "/g " $libs_file_path rm -rf " $libs_file_path -e " これを Workflow 内で実行することでアプリバージョンのアップデート作業を自動化しました。 sed でなんとでもなると昔から教わってきたので使いがちですが、余計なファイルが出来たりもするしあんまりイケてないのではと最近気付きはじめました... QA の運用について タイミーでは PR ごとに QA チェックリストを記載し動作検証を行っています。以前はチェックリストを PR の description に直接記載して PR 単位の動作検証を行っていました。 しかしリリース時の QA でもそれを見ながら検証していたので QA の度に該当の PR を見に行く必要がありました。 非常に手間がかかってしまうので、QA を Notion のデータベースで一元管理しページ内にチェックリストを記載する運用を導入しました。 Milestone が Release となっているものがリリース時の QA 作業対象となっており、リリース時には Notion を参照するだけでQA作業を進められるようになっています。イメージはこんな感じ。 ただし PR 作成時に Notion にわざわざ移動してページを作成するのも大変だったり忘れたりするので、PR が作成されると QA ページを自動で作成し URL を PR にコメントするようにしています。 Notion API によるクエリの実装は こちらを参考に させていただきました。 #!/bin/bash pr_number = " $1 " # 既に同じ number のページが存在する場合は処理を終わらせる page_id = $( curl -X POST ' <https://api.notion.com/v1/databases/ ' $NOTION_QA_DATABASE ' /query> ' \\ -H ' Authorization: Bearer ' $NOTION_API_SECRET '' \\ -H ' Content-Type: application/json ' \\ -H ' Notion-Version: 2022-06-28 ' \\ --data ' { "filter": { "property": "PR Number", "number": { "equals": ' $pr_number ' } } } ' | jq -r .results [ 0 ] .id ) if [ $page_id != ' null ' ]; then exit 0 fi pr = $( gh pr view $pr_number --json " title,milestone,url " ) # PR のメタデータを元に QA ページを作成 title = ' "title": [ { "text": { "content": " ' $( echo " $pr " | jq -r .title ) ' " } } ] ' data = ' { "parent": { "database_id": " ' $NOTION_QA_DATABASE ' " }, "properties": { "Title": { ' $title ' }, "PR Number": { "number": ' $pr_number ' }, "PR": { "url": " ' $( echo " $pr " | jq -r .url ) ' " } } } ' qa_url = $( curl -X POST ' <https://api.notion.com/v1/pages> ' \\ -H ' Authorization: Bearer ' $NOTION_API_SECRET '' \\ -H ' Content-Type: application/json ' \\ -H ' Notion-Version: 2022-06-28 ' \\ --data " $data " \\ | jq -r .url ) # 作成できた QA ページの URL を PR にコメント gh pr comment $pr_number -b """ ## Make QA :memo: $qa_url """ PR 作成時にスクリプトが走るよう Workflow を作成します。 タイミーでは branch の名前で自動化運用しているものもあり、特定の branch の場合は必要ないので走らせないようにしています。 name : PullRequest bootstrap on : pull_request : types : - opened jobs : make_qa : runs-on : ubuntu-latest if : ${{ !startsWith(github.head_ref , 'release' ) && !startsWith(github.head_ref , 'ladr' ) && github.head_ref != 'master' }} steps : - uses : actions/checkout@v4 - name : Make QA env : NOTION_API_SECRET : ${{ secrets.NOTION_API_SECRET }} NOTION_QA_DATABASE : ${{ secrets.NOTION_QA_DATABASE }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | ./.github/script/post_qa.sh ${{ github.event.pull_request.number }} 実装 PR がマージされたら Milestone をアサインする 上述しましたが、タイミーではリリースの差分を把握するために Milestone を利用しています。 なので PR を develop にマージしたら Next Release の Milestone をアサインする必要がありました。 が、これが結構忘れます。なのでリリース作業時に「あれ?これリリースの対象では...?」といった確認を慎重に行う必要があったりとりこぼしが発生したりと、精神的負荷が高い状態でした。 それを解決するために PR がマージされたらその PR に Milestone をアサインする Workflow を用意しました。同時に QA もリリースの対象として可視化されるように Milestone を変更します。すでに Milestone が付いている場合は実行しないようにしています。 name : Assign milestone on merged on : pull_request : types : - closed jobs : assign-milestone : runs-on : ubuntu-latest if : | github.event.pull_request.merged == true && !github.event.pull_request.milestone env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} PR_NUMBER : ${{ github.event.pull_request.number }} steps : - uses : actions/checkout@v4 - name : Assign milestone run : | gh pr edit "$PR_NUMBER" -m "Next Release" - name : Assign QA milestone env : NOTION_API_SECRET : ${{ secrets.NOTION_API_SECRET }} NOTION_QA_DATABASE : ${{ secrets.NOTION_QA_DATABASE }} run : | ./.github/script/update_qa_milestone.sh "$PR_NUMBER" #!/bin/bash pr_number = " $1 " page_id = $( curl -X POST ' <https://api.notion.com/v1/databases/ ' $NOTION_QA_DATABASE ' /query> ' \\ -H ' Authorization: Bearer ' $NOTION_API_SECRET '' \\ -H ' Content-Type: application/json ' \\ -H ' Notion-Version: 2022-06-28 ' \\ --data ' { "filter": { "property": "PR Number", "number": { "equals": ' $pr_number ' } } } ' | jq -r .results [ 0 ] .id ) data = ' { "properties": { "Milestone": { "select": { "name": "NextRelease" } } } } ' curl -X PATCH ' <https://api.notion.com/v1/pages/ ' $page_id ' > ' \\ -H ' Authorization: Bearer ' $NOTION_API_SECRET '' \\ -H ' Content-Type: application/json ' \\ -H ' Notion-Version: 2022-06-28 ' \\ --data " $data " リリース準備作業を実行する Workflow これらを含めリリース準備作業を実行する Workflow を用意します。 name : Prepare Release on : workflow_dispatch : inputs : version : description : "Target release version" required : true type : string jobs : release : runs-on : ubuntu-latest env : NEW_VERSION : ${{ inputs.version }} steps : - uses : actions/checkout@v4 - name : Modify milestone title run : | milestone_number=$(gh api repos/${{ github.repository }}/milestones -q ".[] | select(.title == \\ " Next Release\\") | .number") gh api repos/${{ github.repository }}/milestones/$milestone_number -X PATCH -F title="Release $NEW_VERSION" gh api repos/${{ github.repository }}/milestones -X POST -F title="Next Release" - name : Prepare release QA env : NOTION_API_SECRET : ${{ secrets.NOTION_API_SECRET }} NOTION_QA_DATABASE : ${{ secrets.NOTION_QA_DATABASE }} run : | ./.github/script/replace_qas_milestone.sh -t NextRelease -v Release - name : Make PullRequest uses : ./.github/actions/make_release_pull_request id : make_pr with : version : ${{ inputs.version }} github_token : ${{ secrets.GITHUB_TOKEN }} - name : Make release build uses : ./.github/actions/bitrise_upload_app with : pr_number : ${{ steps.make_pr.outputs.pr_number }} app_slug : ${{ secrets.APP_SLUG }} workflow_id : "upload-app-bundle-to-google-play-store" build_trigger_token : ${{ secrets.BUILD_TRIGGER_TOKEN }} github_token : ${{ secrets.GITHUB_TOKEN }} - name : Make app build uses : ./.github/actions/bitrise_upload_app with : pr_number : ${{ steps.make_pr.outputs.pr_number }} app_slug : ${{ secrets.APP_SLUG }} workflow_id : "upload-apk-firebase-app-distribution" build_trigger_token : ${{ secrets.BUILD_TRIGGER_TOKEN }} github_token : ${{ secrets.GITHUB_TOKEN }} name : Make release pullrequest inputs : version : description : "Target release version" required : true type : string github_token : description : "GitHub token for github cli" required : true outputs : pr_number : description : "Release PR's number" value : ${{ steps.make_pr.outputs.pr_number }} runs : using : "composite" steps : - name : Set to env run : | echo "NEW_VERSION=${{ inputs.version }}" >> $GITHUB_ENV echo "GITHUB_TOKEN=${{ inputs.github_token }}" >> $GITHUB_ENV shell : bash - name : Switch release branch run : | git switch -c "release/$NEW_VERSION" shell : bash - name : Increment version run : | ./.github/script/bump_version.sh "$NEW_VERSION" git config user.name "actions-user" git config user.email "action@github.com" git add . git commit -m "Bump version to $NEW_VERSION" git push origin $(git branch --show-current) shell : bash - name : Make Pull Request id : make_pr run : | ./.github/script/make_release_pr.sh "$NEW_VERSION" echo "pr_number=$(gh pr list -s open --json number,labels -q '[.[] | select(.labels.[].name == " Release")][ 0 ] | .number')" >> $GITHUB_OUTPUT shell : bash 簡単に各 step では、 Milestone のタイトルを Next Release から Release x.x.x に変更 し次のリリース用の Next Release Milestone を作成 Notion データベース上のリリース対象となる QA の Milestone を更新 してピックアップ リリース PR の作成 production のリリースバイナリを PlayStore の テストトラックにアップロード staging のデバッグバイナリを Firebase App Distribution にアップロード のようなことをやっています。 バイナリ作成のワークフローは既に Bitrise に CI が用意されておりそれを実行しています。 [!NOTE] Milestone を Next Release としているのは次のバージョンがいくつになるかリリース作業時に確定するためです。リリース作業中にバージョンを確定させ、gradle 内を更新し Milestone のタイトルを Release x.x.x のようなフォーマットに変更し次のリリース対象となる Next Release Milestone を作成します。 workflow_dispatch で次のバージョンを受け取るようにしており、GitHub Actions のタブから手動で実行することができます。 [!NOTE] 今回は長くなるので紹介していませんがリリース Workflow は通常用と hotfix でわけており、PR を作成するための step やアプリのビルドは共通で使い回すため Composite Action として切り分けています。 これで Workflow を実行すればリリース作業用の PR を勝手に作成してくれるようになり、今まで手動で時間をかけていた作業がボタンぽちーで終わるようになりました。 ちなみに GitHub CLI を利用していると以下のように実行できてとても便利です。 gh workflow run prepare_release.yml -f version=x.x.x お片づけ 最後にリリースした後についてです。 ストアにリリースし終えたら作業用 PR をマージします。その際に以下を実行する Workflow を用意しています。 GitHub Releases に該当バージョンのリリースを作成 master to develop の PR 作成 リリースしたマイルストーンのクローズ 検証した Notion の QA ページの Milestone を更新 name : Make Release on : pull_request : branches : - master types : [ closed ] jobs : release : if : github.event.pull_request.merged == true && contains(github.event.pull_request.title, 'Release' ) runs-on : ubuntu-latest env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} PR_NUMBER : "${{ github.event.number }}" steps : - uses : actions/checkout@v3 - name : Make Release run : | ./.github/script/make_release.sh "$PR_NUMBER" - name : Make Pull Request to develop run : | git switch master gh pr create -B develop -t "Master" -b "Merge master to develop" - name : Close milestone run : | milestone_number=$(gh pr view "$PR_NUMBER" --json "milestone" -q ".milestone.number" ) gh api repos/Taimee/taimee-android/milestones/$milestone_number -X PATCH -F state=closed - name : Update QA Milestone env : NOTION_API_SECRET : ${{ secrets.NOTION_API_SECRET }} NOTION_QA_DATABASE : ${{ secrets.NOTION_QA_DATABASE }} run : | version=$(gh pr view "$PR_NUMBER" --json "title" -q ".title" | awk 'match($0, /([0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9]+)?)/) {print substr($0, RSTART, RLENGTH)}' ) ./.github/script/replace_qas_milestone.sh -t Release -v $version [!NOTE] make_release のスクリプトは PR の description をそのまま GitHub Releases にコピペするだけですし、replace_qas_milestone は QA の Milestone をリリースしたバージョンのテキストに更新します。 最後に 以上がタイミー Android Chapter のリリース作業に利用している Workflow の紹介でした。 今まで手作業で30分〜1時間くらいかけて行っていたタスクが長くても10分以内には収まっていたり負担も減っていると感じています。 ただし、 - #PRNumber だけでタイトルを補完してくれるのは PR の中だけで GitHub Releases には番号しか見えてなかったりします。 またビルド関連は Bitrise で行っていて GitHub Actions から Bitrise の CI をトリガーする事が多いです。 Bitrise.io の QR からインストールできるのは非エンジニアがデモで触ってもらう際に非常に助かっているのですが、GitHub Actions の artifact で似たような事ができるなら費用面やパフォーマンス面を考慮して GitHub Actions に統一も検討できるといいかもしれません。 スクリプトがごり押しだったりまだまだ課題は残っていますし、もっと効率のいい運用がある気がしているのでメンバーのフィードバックを拾い上げて継続的に改善していきたいですね。 ぜひみなさんの オレの考えた最強のリリースワークフロー を教えてください! 明日は我らが Android Chapter のリーダー、murata-san です!お楽しみに
アバター
このエントリは「Timee Advent Calendar 2024」の12月2日分のエントリーです。 私は誰? 2024年5月入社した山田といいます。 ニックネームは「やまけん」とみんなから呼ばれています。 本名より浸透しているので、社員の中には本名を知らない方も一定いる(らしいです)。 productpr.timee.co.jp 前職では、オンライン商談システムを展開するベルフェイスでCREチームのマネージャーをやっておりました。 note.com タイミーは現在、累計ワーカー900万人にご利用いただいているサービスとなっています。 これからも多くの方々にいいサービスを提供し続けられるよう、タイミーでは顧客満足度を技術的アプローチで高めていくためにCREチームを立ち上げることとなりました。 今日はタイミーでのCREの立ち上げから現在、そしてこれからについてお話します。 CREのはじまり 本題へ入る前に、あまり聞き慣れないであろう「CRE」についてご説明します。 CREとは、 Customer Reliability Engineering(顧客信頼性エンジニアリング) の略称で、Googleが2016年に専門職種として立ち上げたことが始まりとされています。 GoogleがCREチームの重要性を初めて強調したのは、SRE(Site Reliability Engineering)が自社のインフラを安定させるための内部的な役割に特化していた一方で、顧客のシステムやアプリケーションにも同じレベルの信頼性が求められるようになったからです。 特に、クラウドサービスを提供する企業では、顧客側の環境でのダウンタイムやパフォーマンスの問題が、結果として自社のブランドイメージに影響を与える可能性が高いため、顧客と一緒に信頼性向上を目指すためにCREが生まれたとされています。 cloudplatform-jp.googleblog.com CREの特徴 CREは、従来のサポートチームやカスタマーサクセスチームとは異なり、技術的な専門知識を持つエンジニアを主体として構成されます。 CREでは、技術的な信頼性やパフォーマンスにフォーカスし、エンジニアリングの観点からシステムの安定性を保証します。 例えば、企業内部でのSite Reliability Engineering(SRE)の原則と手法を、顧客の環境に適用することを目的とし、顧客のシステムやクラウドインフラの信頼性を保証しています。 タイミーでのCREの役割 ここからが本題になります。 前述したGoogleでのCREは、主に開発者向けにプラットフォームやAPIなどを提供する事業の中でSREから派生した役割でした。 一方で、タイミーのようなエンドユーザに機能と体験を提供する事業会社では、Googleが提唱したような役割すべてにあてはめることはできないため、以下の役割を持つこととしました。 顧客満足度向上を推進するために小〜中規模程度の開発及び改善を高速に実行する CREが向き合うべき課題は、自社既存顧客のVoCの中から顧客満足度改善に帰結する小〜中程度の課題を取り扱います。 ここでいう小〜中程度の課題とは、1ヶ月以内で開発〜リリースのできるもの、または調査・分析、開発〜リリースまでのリードタイムが最大でもクォーター(3ヶ月)以内で対応を完結できる課題を取り扱うこととしています。 タイミーCREの提供価値 タイミーのCREのアウトカムは大きく2つあります。 1つは顧客が使っているスマホアプリや事業者向けシステムの機能改善を推し進めることで顧客満足度を高めていくことです。 もう1つは、顧客と接点のある部署(営業、カスタマーサポート etc…)が顧客に向けたサポート力を高めることです。 こちらは社内の管理システム等の機能改善を推し進め、営業やカスタマーサポートといった、顧客と接点を持つ人たちのサポートを充実化させていくことが該当します。 CREの今とこれから 現在、タイミーのCREでは社員が情報を安心安全に活用できるように今夏から情報セキュリティ強化施策を中心に、社員が利用する管理システムの利便性改善に取り組んでいます。 プロジェクトは順調に進行しており、セキュリティ強化施策も終盤に差し掛かってきたため、来年からプロダクトの顧客満足度改善の本格始動へ向けて着々と準備を進めています。 サービスが急成長している中でも、安心、安全に利用できるプロダクトを目指して、CREは顧客に寄り添う開発に取り組んでいこうと思います。 顧客の体験を良くしていくためのプロダクト開発に興味ある方は、是非一度お話しましょう!
アバター
タイミーのsyam( @arus4869 )です。アドベントカレンダーの初日を担当します! 2024年も残りわずか。今年中にやりたかったことの一つが、6月に参加した「アトラクタの認定スクラムマスター研修」の振り返りをまとめることです。この研修では、スクラムの理論を実践しながら学び、多くの気づきと学びを得られました。 私はスクラムマスターではありませんが、チームと共にスクラムを実践する立場です。研修を通して得た知見を、開発者目線で共有したいと思います。同じようにスクラムを学んでいる方々の参考になれば幸いです。 研修の内容と学び 研修中の様子 6月に参加した「アトラクタの認定スクラムマスター研修」は、実践を通じてスクラムの考え方やイベントの進め方を学べる宿泊型の研修でした。 詳しい研修内容についてはネタバレを避けるためここでは触れませんが、興味がある方は公式サイトや同じ研修に参加された櫻井さんのレポートをご覧ください。 認定スクラムマスター研修 | アトラクタ 櫻井さんの研修レポート 研修で印象的だった学びを3つのポイントにまとめてお伝えします。 1. バックログ管理の新しい視点 スプリント計画を模擬的に体験する中で、「バックログは優先度ではなく縦の順番で管理する」という考え方に触れました。このアプローチは非常に新鮮で、「今、一番解決したい課題」に集中する仕組みを整えられると感じました。 従来は優先度が高いものから進める方針が多かったため、割り込みや変更による並列作業が増えることが課題でした。 しかし、この研修での「縦に並べたバックログを順番に進めることで効率的に進行できる」というアプローチでは、順番に1つずつ進めることができるので、スイッチングコストが大幅に減り、1つの課題に集中しやすくなります。割り込み依頼が発生した場合も、既存のバックログに新しいタスクを適切に並べ直すだけで対応可能です。このシンプルな管理方法で、優先順位の変更にも柔軟に対応でき、チーム全員が次に進むべきタスクを迷わず把握できるようになります。 また、「バックログアイテムは具体的で詳細であるべき」という原則も再確認しました。特に、外部要因が絡むタスクや準備が整っていないタスクは「Readyではない」と判断し、進めないことが重要です。このルールを守ることで、不確実なタスクに時間を割くことなく、チーム全体の進捗を安定して保つことができるため、重要な考え方だと感じました。 2. チームの柔軟性を高める工夫 「スプリントゴールが変われば、バックログの順番も変わる可能性がある」という考え方はとても印象的でした。この仕組みによって、「チームが今何に集中すべきか」が自然と明確になり、計画そのものがチームの方向性を示すツールとして機能することが分かりました。特に、ゴールが具体的であるほど、チーム全体が一つの目標に向かって足並みを揃えやすくなる効果を実感しました。 さらに、「スプリントゴールは何を基準に決めるべきか」という議論では、明確な正解を求めるのではなく、その時点での最善を試す柔軟な姿勢が重要だと気づきました。このように、状況に応じてアプローチを調整できる点こそが、スクラムの持つ大きな強みだと感じました。 3. イベントごとの目的を見直す スクラムイベントには、それぞれ明確な目的を持つことが重要だと学びました。 例えば、スプリントレビューでは、進捗を確認するだけで終わらせず、「次にどう進むか」を全員で議論する場にすることの大切さを実感しました。このように未来志向の議論を行うことで、チーム全体が次の課題に向けた共通認識を持つことができます。 さらに、模擬スプリントレビューでは、デモにサンプルデータではなくリアルなデータを使うことの効果を学びました。実際に使うデータを用いることで、ユーザー視点での具体的なフィードバックを得やすくなり、プロダクトの質を高める大きな助けになると感じました。 また、デイリースクラムでは「早期に問題の芽を摘む」という考え方に触れました。短時間であっても、全員で現状を共有することで、予期せぬ問題への迅速な対応力が身につくことを実感しました。 研修を経ての現在地 研修後、すぐに全てが変わったわけではありませんが、少しずつ改善を進めています。最近では、チーム内での見積もり方法を見直しました。 以前は「Tシャツサイズ」を使った見積もりを行っていましたが、「Mサイズが曖昧すぎる」という課題に直面していました。小さなMサイズも大きなMサイズも同じ扱いとなり、多くのタスクがMサイズに分類され、タスクの規模感がぼやけてしまうことが問題でした。 この課題を解決するため、現在は「フィボナッチ数列を使ったストーリーポイント」に切り替え、それぞれのポイントに具体的な基準を設ける方法を採用しています。これにより、リファイメントの際に基準を参照しやすくなり、チーム全体で見積もりの共通認識を持つことができるようになりました。 ストーリーポイント チームにとって必要な日数 作業例 基準となるPBI 1 0.5日以内 簡単なfunction追加、エラーハンドリングなど - 2 1日程度 クラスの作成、簡単なadmin機能の作成など。簡単な既存コードのリファクタリング - 3 2日程度 簡単なAPI作成、既存コードのリファクタリング、テストコード作成、デザインの作成、バックエンドの軽微な機能追加など。 - 5 3〜4日程度 フロントとバックエンドが絡んだ単一機能の開発 - 8 5日程度 フロントとバックエンドが絡んだ複数機能を持つ1画面程度の機能開発 - 13 10日以上(上限なし)※分割しなければならない 複数のプラットフォームが絡んだ2画面以上の機能開発、インフラの構築を含む新規アーキテクチャ構築など - まだストーリーポイントで見積もりする手法を実験中のため、これからの振り返りや次のスプリント計画にどのように役立つかを検証し、改善していきたいと考えています。新しい方法を試す中で、チーム全体の共通理解を深めながら、少しずつ改善を重ねています。 締めくくり 研修会場の中庭 スクラムを実践していく中で得られた一番の気づきは、「小さな改善の積み重ねが、チーム全体の成長に繋がる」ということです。今回受けた「アトラクタの認定スクラムマスター研修」は、理論を学ぶだけでなく、実際に手を動かしながら多くの気づきを得られる貴重な機会となりました。 ストーリーポイントの導入や見積もり基準の設定といった取り組みが、これからどのような成果に繋がるのか、さらに試行錯誤を続けていきたいと考えています! この記事は自分自身にとっての振り返りの一環となりました。 スクラムを学び始めた方や、すでに実践している方の参考になれば幸いです! それでは、みんなでより良いチーム作りを目指してがんばりましょう!
アバター
こんにちは、タイミーのデータアナリティクス部でデータアナリストをしているmihashiです。普段は主にタイミーのプロダクトに関する分析業務に従事しています。 今回は前期に社内で取り組んだデータ活用推進に関する事例を共有できればと思います。 前提 タイミーでは、全社のデータ利活用ツールとしてLookerを利用しています。これまでもより多くのメンバーにLookerを活用してもらうために、定期的に講習会を開催してきました(以下の記事も参照してください!) 社内向けLooker講習会のご紹介 少人数制でLookerの講習会をやってみた話 なぜLookerでデータ活用を推進する話になったのか 1点目は、データアナリストだけでは増加するデータ関連ニーズへの対応が困難になってきたためです。タイミーでは社員数が1,000名を超え、データ活用スピードの向上が急務となっています。そのため、データアナリストだけでなく、各組織内でもデータ抽出が行える体制を構築する必要性が生じています。 2点目は、既存の取り組みだけではデータ活用の潜在能力を引き出しきれない点が課題として浮上してきたためです。各組織内での解くべき課題の難易度が増し、より高度なデータ活用が求められる中で、従来のLooker講習会だけでは対応しきれないケースが増えており、講習内容の見直しが必要なフェーズに差し掛かっていました。 そこで、社内でLookerに関するヒアリングを実施しました。その結果、「各組織にLookerを使える人材を最低1名配置することで、全社的なデータ活用を効果的に推進できるのではないか」という仮説が浮かび上がりました。この仮説に基づき、「全員がLookerの基本操作を習得するのではなく、各組織においてデータ活用の推進を担う人材がLookerを使いこなせる状態」を目指すべきという結論に至りました。 利用普及のために取り組んだこと スキル水準の定義 社内ヒアリングの結果をもとに、タイミー独自のLookerスキル水準を策定しました。またこのスキル水準をもとに、期中でLevel2到達者を対象の3割程度を目標にして、データ活用推進の活動を実施することになりました。 スキルチェックの実施 スキル水準で定義した内容を軸としたスキルチェックの問題を作成し、対象者に受験をしてもらいました。また運営側も採点が実施しやすいよう、明確な採点基準の作成も行いました。 Looker講習会 約3ヶ月で、基礎編・応用編の2種類の講習会をオンライン・オフライン合わせて5回ずつ実施してきました。構成としてはインプット+実践で実施し、実践中はアナリストが不明点をフォローするようなハンズオン形式を採択しました。 Lookerクイズ スキルチェックで誤答の多かった箇所を中心に、クイズ形式で解説を配信し、対象者の理解促進を実施しました。 ポータルサイトの整備 Lookerの使い方をまとめたポータルサイトを従来のものから大幅にリニューアルし、上記の講習会資料やLooker式の一覧などを掲載しました。今後もLookerを使いこなせるようになるためのさまざまなコンテンツを準備していく予定です。 やってみた振り返り 期末に再度スキルチェックを実施した結果、目標としていたLevel2到達割合には届かずでしたが、Level1到達割合は大幅に増加し、社内のLookerレベルの底上げに貢献できました。 一方で、実務でLookerを使うまで到達しにくいといった課題も新たに浮き彫りとなり、今後は実務での活用をサポートする仕組みを作ることも検討する余地がありそうです。 また副次的ではありますが、本取り組みを通じて自分自身もLookerに関する知識が向上し、業務を進める上での糧となりました。 We’re Hiring! 私たちは、ともに働くメンバーを募集しています!! データアナリストのポジション カジュアル面談 も行っていますので、少しでも興味がありましたら、気軽にご連絡ください。
アバター
こんにちは。今回は LeSS' Yoaké(レスの夜明け) Asia 2024 というカンファレンスに参加したメンバーの内、3名で記憶に残ったセッションやトークについてざっくばらんに会話をしてみたので、その内容を一部編集したうえでブログにまとめさせていただきます。 登場人物の紹介 raz プロダクトエンジニア。昨年まではスクラムマスターをしていました。 maho バイブスキング。昨年からスクラムマスターをはじめました。 shihorin スクラムマスター。2024年5月タイミーにジョイン。 印象に残ったセッションやトークはなんですか? Last Day午前中のOST(オープン・スペース・テクノロジー)の様子。 shihorinが参加したテーブルでは「偉くないけど組織を変えたい」というテーマでディスカッションしていました。現場から組織へアプローチするにはどうすればいいのか、どんな課題がありそうなのかを話してみたかったので、このテーブルに参加。実績や感謝を積み重ねていった先で、上層部からの信頼を得て話せる機会を作っていくことが大事そう、といった内容でディスカッションをしていました。 shihorin: OSTの「偉くないけど組織を変えたい」というテーマが印象に残っています。現場は忍耐が必要だ、コツコツやろうと思っていても、それを組織の上層部と握れていないと結局覆されてしまうことがあるという話を中心にしていました。上層部が求めているのは、一般的に見えやすい数字であることが多く、実際にうまくいっているのかどうかは現場に近づかないとわかりません。だから、数字に表れるような成果をほしがってしまう一方、現場としては「数字だけじゃないんだよ」という声があって難しいという会話がありましたね。 raz: なるほどね。そのテーマ、確かにOSTでありましたね。Slackで見た記憶があります。偉いほうがやりやすいのはそうですけど、それは「トップダウンでやれ」と言っているのと同じです。深く考えずに言いますけど、アジャイルとかそういう最近の考え方で言うと、ズレている感じはしますね。 maho: 場合によっては、トップダウンがあってもいいとは思うんですけどね。 raz: 両方大事ですよね。 maho: うんうん。ただ、トップダウンだけだとコミットメントするのが難しいときはある気がしますね。 raz: すべての人が納得しないとうまく進められないから、推進する人が偉いかどうかはあんまり大事じゃないのかなって。もし推進する人が偉い人と会話も不可能みたいな立場だと、相当根気が必要かなとは感じます。 maho: 推進する人が持っていたほうがいいもので言うと、逆に何があるといいんですかね。私は一つ、バイブスはあるのかなと思っています。 raz: おお、最近のホットトピックね。 maho: 人に感情とかコミットメントする気持ちみたいなものを伝えていくうえでは、やっぱりバイブスの力って強いんですよね。その場にいるからこそ共有できるものだと思っているので、もちろんバイブスがすべてを解決するとは思っていないんですけど、大きな影響力があると感じます。推進する人にバイブスを作り出して伝えていく力があると、広がりやすく共感も得られやすいのかなと。そこからコミットメントが生まれやすい感覚を持っています。 raz: mahoさんにはぜひ、バイブスをテーマにしたブログを書いてもらって。 maho: BasのセッションでもLeSSの導入の話があったじゃないですか。あのときに同一拠点でチームメンバーが可能な限り直接会える環境にできたほうがいいって言っていたと思うんですよ。直接会えるほうがバイブスは伝わりやすいので、その場にいるっていうことが強く影響する。仕組みでそれを起こしやすくするのはオフラインなのかもなと思いました。 shihorin: 確かに、オンライン上の切り取られた情報だけだとバイブスが伝わりづらいのかもしれないですね。 maho: 不可能ではないとは思うんですけどね。オンライン上でもバイブスってあると思うんですけど、なかなか作り出すのが難しい。少なくとも情報は限られてしまうので、ライブ感はその場にいるほうが強いのかなと思います。 shihorin: 今の推進する人の話を聞いて、私とmahoさんが座っていたテーブルにいたLINEヤフーの方たちとの会話を思い出しました。LINEヤフーではLeSSのチームの中にスクラムマスターはいない代わりに、スクラムを推進するチームがあるそうなんです。推進チームでは、開発者やPMの中から有志でスクラムを推進したい人たちを集めて、スクラムマスターの役割を共同でやっている。話をした方たちは、まずスクラムを一緒に盛り上げてくれそうな素直な人をその推進チームに引っ張ってきて、挑戦してもらっていると言っていました。最初から自分で手を上げて推進していきたいと言えると一番いいとは思うけど、いい意味で巻き込まれてみるというのも選択肢としてあるのかもしれないなと感じました。誘ってくれたので乗っかってみるみたいな。それもバイブスなのかもしれないですね。 raz: そうですね。やっぱりオンラインだと他のチームの様子がわからないじゃないですか。巻き込まれたいなとか、あっちに行きたいなっていう気持ちをあまり持てない感じはあります。 maho: そもそもわからなかったら、そういう気持ちにもなれないですよね。 raz: そういうムーブメントを起こすのがオンラインだと難しそう。オンラインだと空間的な距離が遠くて見えないから。自分から見ようとしなければ見えないので、推進をするのが難しいんだろうなと思いますね。 maho: オンラインだと「ちょっと向こうで何かやっているな」「気になるな」とかもないですもんね。 raz: たとえば、チームのSlackチャンネルを全部見て追っているとか、そこまでしている人は一定知っているかもしれない。でも、今のタイミーの規模感だと、全部見るのは逆に情報量が多すぎてノイズになるから、チャンネルを抜けている人もいると思います。僕も抜けちゃってますけど……。結果、全部の情報が遮断されるから「別のチームが何をやっているのかよくわからない」みたいな人が増えるような気がします。 maho:今のrazさんの話を聞いて思ったんですけど、オンラインだとそういうのも“情報”になりがちかなって。LeSSの夜明け初日のグループワークで、五感で学ぶ話があって、五感が使われにくいのはオンラインの弱点かもと思いましたね。他のチームが何をやっているのかは、Slackだと単に文字情報になりがちですけど、同じオフィスで隣合って仕事をしていたら会話が聞こえてきて、楽しそうな様子を感じるとかがありますよね。より五感が使われやすい。その辺は人の心を動かしやすいという意味で、やっぱり違いなのかもしれないなと思います。 続いては、初日のCraig Larman氏によるKeynote「AI & HR: Evolving Organizations in an AI World」から、AIとの向き合い方について考えさせられたことを話し合いました。 raz: 僕は最初のKeynoteの「AIエージェントを使って生産性が爆上がりしていった結果エンジニアの形が変わる」という話が印象に残っていて、価値観を変えるタイミングの一つなのかなと思いました。今後は元気にデジタルデバイスを扱える人からすると「AIを使いこなせない」ことを驚かれる時代になっていくんだろうなと。きちんと「価値観のアップデートをしていかないとね」と伝えていく必要があるのではと思います。 あと、XでEbackyさんが「LeSSだから大規模で」という考え方じゃなくて、その時代や状況に合わせてちゃんと変化していくことが大事なんじゃないかなと話されていたのに共感しました。僕らはアジャイルな人間である限り、価値観のアップデートを常にしていかないといけないよなと思いました。AIで生産性が十倍百倍千倍と上がっていくのはすごいけどね。個人的には、そうなった結果として、自分たちの価値観のアップデートが必要になる時代が来ると考えています。ただのツールじゃなくて、考え方や価値観が変わるものなんじゃないかな。使いこなすことが前提みたいな感じですかね。 shihorin: そのセッションの最後で、“ヒューマンスキル”は人間だけが持っているものじゃなく、AIのスキルでもあるという話がされていました。たとえば心理カウンセリングは、人間にカウンセリングされるよりも、AIにカウンセリングされるほうを好んでいる人が多いというデータがあるそうで。ヒューマンスキルではなくなっているというのが衝撃的だったかも。 raz: バイブスはね、ヒューマンスキルですよ。きっと。 maho: AIでもできるようになるかもしれないけど、バイブスはそうであってほしいですよね。価値観のアップデートの話はとても納得したんですけど、その時代に適応して価値観をアップデートしていくうえでは、やっぱり見たいものしか見ないという姿勢はダメなんだろうなという気持ちになりましたね。別に自分に都合のいいものだけ見えているわけではないと思うけど、見たいものしか見ずに固執してしまうと、自分の頭の中で見えていると思っている世界と、現実の世界にギャップが生まれることになる。適応しているつもりでも適応できていないみたいなことが起きていくのかもなと。オープンマインドでいきたいですね。 raz: AIによって人間の仕事がなくなるとかは、どうなるかわからないですからね。なくなると言われている仕事もなんだかんだ残り続けているじゃないですか。逃げ切りマインドで「自分はこのメンタルで最後まで生き抜いて死ぬんだ」というのも考え方の一つとしてはありだと思いますね。たとえば、一生ガラケーを使っている人が悪いわけではない。変えないのが悪いわけではないですけど、選択肢が狭まる可能性はあると思いますね。 maho: 確かに。選択肢が狭まっていないかどうかは気にしたい。 raz: 「ガラケーでも孫とLINEできないけどいいか」となるのも適応の一つだとは思うんですけどね。 shihorin: LINEができないという悩みを解決したいから「スマートフォンに変えよう」となるのは、内発的動機づけで変えようとしているんですよね。携帯会社から「ガラケーのサービスが終わるので変えてください」と言われても内発的動機づけじゃない。 raz: サービスが終わるなんて知らんってなるんですよね。 maho: 使い続けられるなら変えないという選択肢もありますもんね。 raz: そもそも新しい携帯を使いこなす気がないならね。買ってみた結果いいじゃんってなる可能性も、もちろんありますけどね。 脱線しましたが、LeSSだから大規模じゃなきゃいけないみたいなのも違くて、固まった考え方から脱することが大事かなとは思いましたね。 maho: 確かに、枠を広げるっていう意味ですよね。 参加して気づいたことや学んだことはなんですか? maho: 全体を通じて思ったことはコツコツやることの大切さです。意外と他の参加者が悩んでいることも、そのコツコツができていないからの悩みなのかなと。一撃でどうにかしようとして、うまくできなくて悩んでいそう。セッションとかを聞いても、成功している人の例は地道にやるべきことを積み上げているパターンな気がするんですよね。それが結局一番の近道なんだろうなと思いました。 shihorin: それだけ長く育んでいたのに壊されちゃったという話もありました。プロフェッショナルな人でもそれぐらいの年数がかかるのだと印象に残りましたね。自分は半年スクラムマスターをしてるけど「うまくいかん」と言っているのは、おこがましいのかもしれません。 maho: 時間的な遅れがあるフィードバックもありますからね。やってすぐフィードバックを得られるものでもないけれど、人ってすぐに結果を求めがちじゃないですか。 shihorin: そう、ほしくなっちゃう。 raz: でも、そこに根気強く付き合ってくれる会社というのもなかなか難しいんじゃないかな。 maho: 結果を出す、実績みたいなのは大事そう。 raz: プロダクトがちゃんと作られていれば、一定大丈夫だとは思うんですけどね。物ができていれば続けられるとは思う。 shihorin: 私はスクラムのカンファレンスに初めて参加して、その場だけの学びじゃなく、今こうして話しているように、後で会話をしたときに気づく学びもあるので、学びは点じゃなくて線なんだなって気づきました。 raz: 大事ですよね。アフタートークじゃないですけど、セッションについて話して理解を深めること。まず、話すだけでもアウトプットになって理解を深めることになりますし、他の人からフィードバックをもらえる。自分はこう考えたなどの話をシェアしてもらえれば、より学びが強化されますからね。そういうのがオフラインカンファレンスのいいところ。オンラインでも、ちゃんと設計すればできるんでしょうけど。オフラインカンファレンスに参加してよかったですか? shihorin: そうですね、私は参加してよかったなという気持ちになりました。 読者に伝えたいこと shihorin: 参加してみたらええやん。受け身で教えてもらうんじゃなくて、誰かと喋ったり、OSTなどのコンテンツに積極的に参加したりするのが大事!新米スクラムマスターの方にもおすすめです。 maho: 参加して何を感じたか、学んだかを話して整理する。具体的なアクションをする。続けていくことが大事だと思います。アクションに対するフィードバックを得て自分のふるまいを改善していくことが、アジャイルやスクラム、LeSSに向き合っていくことかなと。継続した活動をサボることなく、甘えずにやっていきたいです。一人では対話できないし、継続もなかなか難しいので一緒にやっていくことが大切だと思います。「私がバイブスキングだ」という気持ちでバイブスを大事にしていきたいです。 raz: セッション全部を通して、オーガナイザーはこれが正解だよという言い方はしていませんでした。自分たちで対話して考えることが多かった、変わったカンファレンスだったので、人によっては「全然何も教えてくれない」と思うかもしれません。でも、それがかえって現実的というか……。実際に人の成功体験から学べることはあまり多くないし、失敗やうまくいっていないこと、今目の前で起こっていることに向き合っている時間が物事を進めていくうえでは大事です。レスの夜明けは、そこに焦点を当てたいいイベントだと思います。あのカンファレンスを受け入れられない人は、LeSSの導入が難しいかもしれないので、価値観、考え方をアップデートしていかなきゃと気づいてもらえるといいのかも。こういうイベントは、つい受け身になりがちですが、臆せず参加してみて友だちを作ったらいいと思います。 今回3名でアフタートークをして得られた学び・気づきを、今後の仕事で生かしていきたいです!
アバター
タイミーの新谷、亀井、甲斐、須貝です。 Kaigi on Rails 2024 が10月25日、26日の2日間で開催されました。タイミーは昨年に引き続きKaigi on Railsのスポンサーをさせていただきました。 また、タイミーには世界中で開催されているすべての技術カンファレンスに無制限で参加できる「Kaigi Pass」という制度があります。詳しくは以下をご覧ください。 productpr.timee.co.jp この制度を使ってオフラインでは6名のエンジニアが参加しました。 ライブ感ある集合写真 参加して聞いたセッションのうち印象に残ったいくつかをピックアップしてご紹介します。 Keynote: Rails Way or the highway https://kaigionrails.org/2024/talks/palkan/ https://evilmartians.com/events/keynote-rails-way-or-the-highway-kaigi-on-rails-2024 test-prof の作者であり Evil Martians の principal backend engineer である @palkan さんによる Rails way とは何なのか、Rails を拡張する際に考えていることについての発表でした。 Railsのレールを伸ばすときの考え方として、「カスタムアプリケーション開発者の考え方ではなく、フレームワーク作者の考え方を持とう」というメッセージは個人的に強く刺さりました。一方、Railsアプリケーション開発者にとってフレームワーク作者の考えを持つのは簡単なことではないと思います。rails/rails のコードを気軽に読むのはもちろん、main branch にあるコードだけでフレームワーク作者の意図を読み取るのには限界があるため Issue や Pull Request の議論のキャッチアップも必要だと感じます。 個人としてこの能力を持つための努力をしていきたいと思いつつ、組織としてこの能力を持ち続けるためには技術顧問の採用によってこの部分を補完するのも一つの手としてありそうだなと思いながら聞いていました。 ( @euglena1215 ) speakerdeck.com RailsのPull requestsのレビューの時に私が考えていること https://kaigionrails.org/2024/talks/yahonda/ Rails コミッターをされている @yahonda さんが Rails に Issue / Pull Request を出すときに押さえておいてほしいポイントと @yahonda さんがコメント・レビューするかの判断基準についての発表でした。 1つ前の基調講演で「フレームワーク作者の考えを持とう」という @palkan さんの発表から実際に Rails コミッターはどんなことを考えながらコメント・レビューをしているのかという流れで繋がりを感じながら聞いていました。 ユースケースがあるか、という観点においては 自身も Real world use case? と聞かれてうまく答えられなかったことがある ため Pull Request の例が出てきたときはヒヤヒヤしながら聞いていました。「誰のどの問題を解決したいのか」「その問題はこの解決方法で解決するのが適切なのか」という観点は普段のプロダクト開発においても重要な観点であり、OSS活動においてもプロダクト開発においても意識し続けたいと思います。 ( @euglena1215 ) speakerdeck.com Identifying User Identity https://kaigionrails.org/2024/talks/moro/ @moroさんによる登壇です。昨年は Simplicity on Rails という話を Kaigi on Rails でされています。私の理解では昨年の登壇で地に足のついた Rails way とは?という話をされていたと思っており、そこからの流れで今年、基調講演も含めて Rails way についての言及が多く、大変にっこりしております。 そのうえで、今回の @moro さんのお話は User Identity にスコープを絞ったお話です。最初の方で「大雑多に『ユーザー』と呼びがち」という話をしてカラフルな名前について言及していたので、てっきりモデリングの文脈で言及される「よく『ユーザー』って名前をつけがちだけれど場面によってそれぞれの『ユーザー』の振る舞いが変わるんだからもう少し適切にモデリングしようよ」という話かと思ったのですが違いました。今回は User Identity です。おそらく、 Identity の話をしたかったが、「ユーザー」という表現にも納得感がない。とはいえ、伝えたい内容が Identity だから泣く泣く「ユーザー」という表現にしたかったので冒頭でエクスキューズされたのかな、と勝手に思っております。 架空のユーザー管理機能をつくるぞ、ということで、 Gem などを使わずにどうすれば適切に identify したい単位としてのユーザーをコードで表現すればいいのか?ということについて説明されておりました。ユーザーが「いる」こと = identity という説明をして、 users テーブルにはほぼ id だけあればいい、というお話です。これにはものすごく納得感があります。たしかに、ユーザーの属性としてメールアドレスだとか名前だとかをつけがちですが、システムとしてそれらが必要な場面はほとんどなく、大体の場合 association としての id (存在していること)さえわかれば十分です。そして、昨今の個人情報の扱いという点でも id とメールアドレスなどのユーザー属性のライフサイクルは異なります。そういう点でもこの主張にはとても納得感があり、かつ登壇内容も明快でわかりやすくとても参考になりました。 ユーザーの登録に関しての説明も良かったですね。「登録しようとしているコト」を表す UserRegistration モデルを用意すると Rails っぽくユーザー登録をハンドリングできる、というのは昨年の登壇を聞いているととても納得感のあるものです。個人的に面白いと思ったのは UserRegistration モデルに belongs_to :user, optional: true で UserRegistration モデルと User の association がオプショナルになりうる、ということです。これは、ユーザー登録途中で離脱した状態を表現でき、なかなかのアハ体験でした。 少しだけ懇親会でお話を伺い、こんな質問をしてみました。「ユーザー登録を表現する UserRegistration のようなイベントモデルを見つけるにはやはりチームメンバーにそれなりのスキルが必要でしょうか?」と。「いやーそうなんですよねー」という回答をいただきまして、このあたりのスキルは「一朝一夕では身につかないんだなー」と思い、今後もさらに興味を持って Rails と向き合っていきたいなと思いました。 ( @yykamei ) speakerdeck.com Hotwire or React? 〜Reactの録画機能をHotwireに置き換えて得られた知見〜 https://kaigionrails.org/2024/talks/haruna-tsujita/ 私はHotwireを触ったことがなく、個人的価値観としてはそこまで重きを置いていないのですが、だからこそ、このような場で実際に向き合っている方々の知見を得られるのは有用な機会であると思い、本発表を聞かせていただきました。 登壇者の@haruna-tsujitaさんが所属される株式会社キャタルは英語4技能塾を運営されており、前々職でガッツリ関わった分野なので非常に懐かしく感じています。そして、前々職と同様の少数精鋭だからこそ、バックエンドエンジニア2人が片手間でReactの面倒見るのがしんどいというペインがあり、Reactを捨ててHotwireに至るという意思決定がされたことがわかりました。 この事情は非常に理解できて、私の前職は技術者が数人規模のスタートアップで、Railsエンジニアが片手間でVue JS2を触っていましたが非常に開発量が多くしんどい思いをしていました。また、モダンなJSはプロジェクトの基幹部分の整備が非常に重たく、私は仕組みが出来上がったVueやReactのコンポーネントつくるくらいなら書けますが、まっさらな状態からモダンJSの実行基盤をつくる、という能力を持っていないことが結構なコンプレックスになっています。そして、これは私の個人的感情だけでなくそこを触れる人材を確保し続ける、というのがスタートアップにとっては重荷であるということを経験しています。会場の挙手の反応などを見てもHotwireを利用している企業は一定数存在するらしい、という空気があり、「Railsエンジニアが片手間でフロントを見る」という選択肢においては利用できるのかもしれないという学びを得ました。幸いにして弊社においては専業でReactを書いてくれるフロントエンドエンジニアの面々がいるおかげでこのような心配をせずに済んでいるのですが…。 話を戻しますが、HotwireはSPAとはアプリケーションを構成する考え方が違う、というnay3さんの話を孫引きして解説してくれているのがまた面白い話で、SPAのコンポーネント単位の考え方から離れて、違うURLで部品の差分を表現する「Web紙芝居」と呼ばれるちょっと昔に逆行した仕組みを敢えて用いることで設計をコントロールしているのは興味深いです。 ある程度ちゃんとした組織なら私はSPAを推すでしょう。ですが、数人規模の会社でよりにもよって私がテックリードをしなければならないような危機的状況に陥った場合、もしかするとHotwireは助けとなるかもしれない。そういう示唆を得られた登壇でした。 (甲斐 宏味) speakerdeck.com Railsの仕組みを理解してモデルを上手に育てる - モデルを見つける、モデルを分割する良いタイミング - https://kaigionrails.org/2024/talks/igaiga/ igaigaさんによるRailsらしいモデル設計とその分割についての発表でした。ご自身もおっしゃっていたようにpalkanさんのオープニングKeynoteの内容とも重なるところがあり、自分はこの手の話が好物なのでとても楽しく聞かせてもらいました。 モデリングをする際にイベント型モデルを見いだせるとRailsのレールにぴったりハマることが多いというのは自分の実感にもよく当てはまるところで、Railsアプリケーション開発者としてこのあたりのコツを掴んでいるかどうかは大きな差になると思います。イベント型モデルを発見できるとControllerはそのイベント型モデルに対するCRUDになるので基本のアクション(create, show, update, deleteなど)で表現できるようになり(いわゆる「 DHH流ルーティング 」)、このレールに乗せていく感覚を得られると開発が楽しくなってきます。 このあたりの話はigaigaさんが参考資料に載せられていたtexta.fmというポッドキャストでもよく議論されているので、このセッションが面白いと思った方にはぜひ聞いてほしいです。特に ep3の「Low-Code Development」 ではイミュータブルデータモデリングにも触れており、論理モデリング段階でupdateとdeleteをしないという思考の制約を入れるとイベントを発見しやすくなる、といったテクニックをt-wadaさんがされていて非常に勉強になります。 ( @sugaishun ) speakerdeck.com     今回の Kaigi on Rails は Conceptual な発表が多かったように思います。そのおかげで Railsway とは何なのかをより一層理解できました。 Kaigi on Rails を運営してくださったオーガナイザーのみなさん、発表されたスピーカーのみなさん、スポンサーとして Kaigi on Rails を盛り上げてくださった各企業のみなさんありがとうございました! 今年も一昨年もスポンサーのブース抽選に外れブース出展できなかったのですが、来年こそはブース出展できることを楽しみにしています。来年はブースで会いましょう!   また、Kaigi on Rails の後夜祭として LT イベントを TOKIUM さんと永和システムマネジメントさんと 11/12(火)で共同開催します。Kaigi on Rails ネタや Kaigi on Rails によらない Ruby/Rails ネタなどいろいろなことをお話しする会になるかと思うので、興味があればぜひご参加ください! tokium.connpass.com
アバター
こんにちは!タイミーでデータアナリストをしているkantaと申します。 普段はマーケティングの皆様とCM施策やCRM関連の分析を担当したり、他部署向けの講習会を企画したりしております。11月から半年ほど育休を取得予定のため、育休前最後の仕事(?)として当ブログの執筆を担当いたします。 さて、今回は 「BigQuery + Looker Studioでt検定した話」 と題しまして、その方法をご紹介できればと思います。 なぜ BigQuery + Looker Studioでt検定をしたいのか? BigQuery + Looker Studio でt検定する手順 1. UDFの作成 1-1. JavaScriptライブラリ「jStat」のダウンロード 1-2. 任意のGCSバケットにjstat.jsをアップロード 1-3. p値を算出するUDFの作成 1-4. t検定を実施するUDFの作成 2. クエリの作成 3. Looker Studioで可視化 3-1. Looker StudioのデータソースでBigQueryを選択 3-2. 先ほど作成したカスタムクエリを記述 3-3. 見た目を整えて完成! BigQuery + Looker Studioでt検定した感想 We’re Hiring! なぜ BigQuery + Looker Studioでt検定をしたいのか? タイミーでは、BIツールとしてLooker Studioを使っていますが、分析の中でいくつか機能の不足を感じる点があります。その一つが、今回のタイトルにもなっている t検定 です。t検定はその施策の効果が有意であったか、有意でなかったかを推定するために非常に重要な要素です。PythonやRなどのプログラミング言語だけでなく、Excel、スプレッドシートおよびLookerでも実施できます。 しかしながら、Looker Studioではt検定がサポートされておらず、他のツールで出力した検定結果をインポートする必要があります。(2024年10月現在) そうなると、一つの検定を実施するために以下の三つを管理しなければなりません。 これらの管理工数を少しでも削減するために、以下のような構成でLooker Studio上にt検定結果を表示したいと思います。 BigQuery + Looker Studio でt検定する手順 それでは、その手順についてご紹介します。 UDFの実装につきましては、以下のブログを参考にさせていただきました。 BigQueryで始めるt検定 - Re:ゼロから始めるML生活 1. UDFの作成 BigQueryには ユーザー定義関数(UDF) という機能があり、SQLまたはJavaScriptで関数を作成できます。この機能を利用して、JavaScriptでt検定のUDFを作成します。 1-1. JavaScriptライブラリ「jStat」のダウンロード GitHubリポジトリから jstat.js をダウンロードします。 https://github.com/jstat/jstat/blob/1.9.6/dist/jstat.js 1-2. 任意のGCSバケットにjstat.jsをアップロード GCSバケットにアップロードし、バケット名とファイルパスを控えてください。 ここでは、 YOUR_BUCKET 配下に libraries/jstat.js として作成したものを記載します。 1-3. p値を算出するUDFの作成 以下のクエリをBigQueryで実行します。 YOUR_PROJECT ・ YOUR_DATASET は事前に作成しておいてください。 CREATE OR REPLACE FUNCTION `YOUR_PROJECT.YOUR_DATASET.studentt_cdf`(t FLOAT64, dof FLOAT64) RETURNS FLOAT64 LANGUAGE js OPTIONS ( library= " gs://YOUR_BUCKET/libraries/jstat.js " ) AS """ // スチューデントのT分布を用いて、与えられたt統計量と自由度に対する双方向のp値を計算 return jStat.studentt.cdf(-Math.abs(t), dof) *2 """ 1-4. t検定を実施するUDFの作成 1-3で作成したUDFを利用し、t検定を実施するUDFも作成します。 同様に以下のクエリを実行してください。 CREATE OR REPLACE FUNCTION `YOUR_PROJECT.YOUR_DATASET.ttest_ind`(data ARRAY<FLOAT64>, data2 ARRAY<FLOAT64>) AS (( WITH dataset1 AS ( SELECT d AS A FROM UNNEST(data) as d ) ,dataset2 AS ( SELECT d AS B FROM UNNEST(data2) as d ) , mean AS ( SELECT AVG (A) AS ma, AVG (B) AS mb FROM dataset1, dataset2 ) , lena AS ( SELECT COUNT (A) AS len_a FROM dataset1 ) , lenb AS ( SELECT COUNT (B) AS len_b FROM dataset2 ) , Ama AS ( SELECT A, ma, A - ma AS A_ma, FROM dataset1, mean ) , bmb AS ( SELECT B, mb, B - mb AS B_mb, FROM dataset2, mean ) , pow_Ama AS ( SELECT SUM (A_ma * A_ma) AS A_ma_2 FROM Ama ) , pow_Bmb AS ( SELECT SUM (B_mb * B_mb) AS B_mb_2 FROM bmb ) , S2 AS ( SELECT (A_ma_2 + B_mb_2) / (len_a + len_b - 2 ) AS s_2 FROM pow_Ama, pow_Bmb, lena, lenb ) , t AS ( SELECT len_a, len_b, (ma - mb) / SQRT ((s_2/len_a) + (s_2/len_b)) AS t_value FROM mean, S2, lena, lenb ) SELECT `YOUR_PROJECT.YOUR_DATASET.studentt_cdf`(t_value, len_a + len_b -2 ) AS p_value FROM t )) 2. クエリの作成 以下のクエリを実行し、p値を得ることができます。 実際に使用するときは各グループの数値を ARRAY_AGG で配列にして扱うことが多いです。 WITH test_data AS ( SELECT [ 0.0 , 5.0 , 29.0 , 3.0 , 4.0 , 32.5 , 46.3 ] AS A, [ 9.0 , 4.0 , 5.0 , 6.0 , 4.0 , 2.0 , 3.0 , 1.0 , 2.0 , 4.0 ] AS B ) SELECT `YOUR_PROJECT.YOUR_DATASET.ttest_ind`(A, B) AS p_value FROM test_data 3. Looker Studioで可視化 3-1. Looker StudioのデータソースでBigQueryを選択 3-2. 先ほど作成したカスタムクエリを記述 定義したUDFはLooker Studioからも実行できます。 3-3. 見た目を整えて完成! 詳細は 公式ドキュメント をご確認ください。 手順は以上となります。 BigQuery + Looker Studioでt検定した感想 ここまでお付き合いいただきありがとうございました。今回の最大のメリットは、すでに述べたように、管理すべきツールを減らせる点にあります。 その一方で、実際のデータだとクエリが冗長になり、レビューやメンテナンスがしづらくなってしまう場合もあります。 また、そもそも検定とBIツールによる可視化は分けて行うべきという考え方もあると思い、実装してみたものの用途は限られるという印象です。 あくまで選択肢の一つとして、分析の要件や環境に合わせた選択が必要ですね。 We’re Hiring! 私たちは、ともに働くメンバーを募集しています!! カジュアル面談 も行っていますので、少しでも興味がありましたら、気軽にご連絡ください。
アバター
自己紹介 こんにちは。タイミーのデータエンジニアリング部 データサイエンスグループ所属の吉川です。 1年前にタイミーに入社し、データサイエンティストとして日々業務に取り組んでいます。 前職では大手ゲーム会社で全社のCRMや離脱予測のモデル構築を担当していましたが、縁あってタイミーに入社することになりました。 まず入社後のミッションは「データを活用したプロジェクトの立ち上げ」でした。 立ち上げにあたり、何に取り組むか、社内の問題を探索し、下記のようなビジネス面とデータ活用面の2軸で取り組む領域やテーマの選定を行いました。 ビジネス面: ポテンシャルがあり、ビジネスインパクトが大きいか データ活用面: データ量が多く予測モデルの活用などデータ駆動型のアプローチが取りやすいか 具体的な取り組みとして、顧客のスコアリングモデルを構築し、そのスコアを基に顧客フォローを実施するなど、仕組みの構築を行っています。 この記事の目的 ちょうど1年前にタイミーに入社したのですが、応募や内定承諾の意思決定をする時にこんな情報があったら欲しかったと思ったことを2つ、この記事で書きたいと思います。同じようにタイミーのデータサイエンティスト職への応募や内定承諾を迷われている方がいらっしゃれば、その方の一助になれば幸いです。 1. フルリモートで働けるのか? 入社時はフルリモートでしたが、あくまで一時的な状態で、そのうち出社にならないかという不安がありました。 私の転職理由は、前職は素晴らしい職場だったのですが、フル出社体制で片道1時間30分の通勤を強いられていたことが転職理由の一つでした。朝、子どもがまだ寝ている時間に出社しなければならず、子どもとの時間を確保するために転職を決意しました。そのため、新しい職場でもフル出社になれば、同じ問題の繰り返しになってしまうという懸念がありました。 実際に入社して1年経った今でも、入社直後と変わらずフルリモートで勤務できています。データサイエンスグループでは地方にお住まいの方もいらっしゃるので、フル出社になるということは、よほどの大きな経営方針がない限り今のところはなさそうです。 また、タイミーのビジョン、ミッションと照らし合わせても、この柔軟な働き方は今後も続いていくと考えています。 2. 転職をして、人間関係の資産がゼロの中、リモートで業務を進めるのは大変なのでは? 対面での関係性がある状態でのリモートワーク経験しかなかったため、上記のような不安を持ちました。 入社して1年経ちますが、データサイエンスグループをはじめ協働している他部門の心理的安全性が高く、入社前に抱いていた不安は杞憂に終わりました。 プロジェクトの実例 特に他部門との関係性がない中で多くの関係者と合意形成を図りながら業務を進めるのは、リモートだと大変なのではと考えていました。しかし、タイミーの組織はフラットで階層が少なく、コミュニケーションコストが低いため、リモートであってもプロジェクトマネジメントに大きな困難はありませんでした。 例えば、現在他部門と進めているスコアリングのプロジェクトでは、調整に必要な関係者も多くなく、MTGの場で意思決定できるため、ストレスなくプロジェクトを進行できます。 前々職で同じようなプロジェクトを経験しましたが、階層型組織にありがちな「MTGで方針決定後、さらに上位レイヤーに確認する」ということを何度も繰り返しており、意思決定から実行までにかなりの時間がかかっていました。その時との体感差でいえば、同じことが1/3くらいの期間でできたのではないかと思います。 リモート前提の組織設計 また、タイミーはリモート前提で組織が円滑に回るように仕組み化されています。従来の対面を基本とした働き方前提でリモート環境を組み込むと、情報共有の不足やコミュニケーションの断絶など、さまざまな無理が生じがちです。しかし、タイミーは最初からリモート前提での業務遂行ができるようにさまざまな制度設計がされているので、そのような問題を最小限に抑えられているように感じています。 具体的には、MTGはオンライン・オフラインどちらでも参加が可能だったり、ドキュメントを残して情報共有したりする文化が徹底されています。これにより、物理的な距離に関係なく、チーム全体で一貫性のある業務遂行が可能となっています。 データ基盤の整備 さらに、データ部門に限っていえば、タイミーではデータ基盤が整備されているため、BigQueryを叩けば必要なデータを即時に取得でき、これにより、データ利活用におけるコミュニケーションコストが抑えられていると感じています。 自律的な働き方と組織の成長 こうした環境下なので、自発的に課題を見つけ、自律的に動ける人にとってはとてもやりがいのある職場だと思います。階層が少ないことで意思決定が迅速に行われ、社員一人ひとりの意見やアイデアが直接反映されやすい。だからこそ、データサイエンティストは自身の専門知識と創造力を最大限に活かし、新しい取り組みを通じて組織全体の成長に寄与することができます。 加えて、タイミーでは最新のテクノロジーの活用にも積極的で、生成AIなどの取り組みも始まっています。社内GPTなども利用可能で、そうしたものを活用しつつ、業務効率化を進めています。実際、この記事の作成においても、誤字脱字のチェックに社内の生成AIツールを活用しました。 タイミーはデータサイエンティストにとって技術的な挑戦が可能で、組織的な柔軟性もある非常に魅力的な環境だと思います。 以上、1年前の入社時に、当時知りたかった2つのことを記事にまとめてみました。 今、タイミーへの応募や入社を検討中の方にとって、少しでも参考になれば幸いです。 We’re Hiring! タイミーのデータエンジニアリング部・データアナリティクス部では、ともに働くメンバーを募集しています!! 現在募集中のポジションは こちら です! 「話を聞きたい」と思われた方は、是非一度 カジュアル面談 でお話ししましょう!
アバター
スクラムマスターの吉野です! 株式会社タイミーは LeSS Yoake ASIA 2024 のスポンサーとして参加し、私も招待チケットを利用して現地に行ってきました! 今回の記事はDay5のレポートです! Day1のレポートは こちら セッション : 企業変革の現実から考える「組織レベルのアジャイル」への道のり (ABeam Cunsulting) 1つのチームから複数チームへ拡大するパターン 組織改革の形で、大規模な組織にアジャイルを適応するパターン 2軸にて、組織へアジャイルを広めていくストーリーが聞けました! 前者については、スクラムが成功したチームがあっても、2つ目以降のチームに同じ形で導入しようとしても、簡単にうまくいくわけではない、というお話でした。 ふりかえりの質が悪い、Doneの定義が形骸化している、頻発するメンバーの入れ替えに伴い開発が安定しない、というスクラムチーム立ち上げ期に経験する問題。スクラムマスターがチームの「自分ゴト化」をリードし、その後、見守る形へ移行していく流れで各問題の解決に向けて進められていました。 個人的な感想となりますが、かなり共感できたのは「スクラムマスターが背中を見せる」という点です。 スクラムマスターがリーダーシップを発揮するところがスタート地点だと考えています。なぜなら、まずはスクラムマスターがリードし、チームを成功に導き、その後適切にデリゲーションを行なっていく流れが大切だと考えています。 いきなり「xxした方がいいよ」「xxはやめた方がいい」とアドバイス中心になっても、チームおよび個人は受け止めることに心理的なハードルを感じてしまう可能性があります。 そのため、まずはリーダーシップを発揮することが、序盤においては大切だと考えています。 2つ目のパターンについては、大規模アジャイルの導入を成功させたい企業に対し、支援および現場に入ってリードしたお話でした。 経営層もどうすれば良いのかわからない、もしくは、経営層が考える実行計画がある状態からどうやって成功させるかを考えます。 実際にできる人はいるのか?試しに作ってみたら、できる可能性は見出せるのか?という形で、想像と小さく実験することにより、アジャイルの必要性を徐々に知ってもらうとのことでした。 ポイントをいくつか説明する中で、印象的だったのは“アジャイルという言葉を使わない”です。 アジャイルという言葉にはさまざまな印象が紐づいてしまっていたり、言葉に引っ張られてしまっったりで、本当に考えるべきことにフォーカスが当たらない可能性があるとのこと。これは私が好きな考え方でした!私もチームへスクラムを適応していく際には、「気がついたらスクラムをやっている」というパターンを選んだりします(必ずその形というわけではありませんが)。スクラムガイドを読み込み、チームがスクラムを形式から体現するのは大切だと思うのですが、「スクラムをガイド通りに行う」が目的になってしまうケースをいくつか経験してきました。アジャイルやスクラムはあくまで考え方や実行する上での選択肢であり、本当にやるべきことは顧客に届ける価値をいかに高められるか、そして事業をいかに成功させるかにフォーカスすることを忘れないため、あえて初期段階ではスクラムを意識させない形でリーダーシップを見せていくことも行っています。 クロージングキーノート : How to adopt LeSS (Bas氏) LeSSを導入する前からLeSS導入後について、 どのような準備が必要か 最序盤の動きは何が必要か どのような改善点があるのか を、Bas氏の経験を交えて話されていました。 意思決定はとても重要とのことです。 いくつかをピックアップすると、 私たちのプロダクトの定義はなにか? 内部的なことや小さなプロダクトを含めて大きく定義する必要がある。それは、顧客が思っているよりも小さく定義してしまうと顧客との対話が難しくなる プロダクトオーナーはだれか? 真のプロダクトオーナーは、プロダクトにまつわるお金を辿っていくとわかる。プロダクトの予算の決定権を持っている人はだれなのか 同一拠点で、リアルで顔を合わせながら仕事をする プロダクトの開発が複数拠点を跨いでいたり、チームメンバー同士が顔を合わせることがなく仕事をすると、協働がしづらくさまざまなデメリットの要因となる というお話が印象的でした。 LeSSだからLeSSのやり方があるわけではなく、他の形式や概念を採り入れた開発をしていても重要とすべきことは同じだと感じました。 自分たちのプロダクトを自分たちが一番理解し、必要な意思決定者を巻き込み、チームがコラボレーションしやすい環境でアウトカム最大化のためにアウトプットを最大化させられる働き方をする大切さの再確認ができました! Bas氏の苦悩が伝わったエピソードとしては、「マネージャーや経営層が変わった場合、そこまで積み重ねたものがすべてひっくり返ってしまう可能性がある。決して短くない数年間が、経営者の一声ですべて無くなってしまった経験もした。しかし、私はその事象に対して、どうすれば良いのかの答えをまだ持てていない」というお話でした。絶対的な力に抗えない状況は、苦しいですね...。もちろん私も答えを持っていませんが(笑)、一度LeSSを実施できた組織は横断的にノウハウを獲得できていると思うので、再挑戦に向かえるのではないか、と思っています。 以上がDay5のレポートとなります! 直接お話をしたことがなかったCraigさん、Basさんと直接お話ができ、私にとっては貴重な体験ができたカンファレンスでした。 LeSS Yoake ASIA 2024は初回ということもあり、次回以降はさまざまなところでカイゼンが行われると思います!それも、次の楽しみになりそうな印象でした! カンファレンスを運営されたみなさま、貴重なお時間をありがとうございました! (お弁当は好きなプレートをチョイスできる形式だったのですが、炭水化物を2つ選んでしまい、お腹いっぱいになりました)
アバター
こんにちは、タイミーでデータサイエンティストとして働いている小栗です。 今回は、機械学習モデルの予測の不確実性を定量化する手法であるConformal Predictionについてご紹介します。 Conformal Predictionとは 機械学習モデルの予測値がどの程度信頼できるか知りたい場面は多いと思います。 医療診断のように誤った予測が重大な問題につながる状況でモデルを使用する場合、予測の不確実性を定量化してそれを元に判断できると嬉しいです。 Conformal Prediction(以下CP)はUncertainty Quantification(不確実性の定量化。以下UQ)のパラダイムの1つであり、モデルの予測値の集合/区間を統計的に厳密に作成します。 Conformal Predictionで生成される予測集合の例。出典: Angelopoulos, Bates (2022) CPの利点 UQ手法には、分類モデルが出力する確信度を用いる方法、複数のモデルから算出した予測値の分散を用いる方法、学習サンプルと予測サンプル間の距離を用いる手法など、さまざまなものがあります。 これらのUQ手法に対するCPの優位性として、以下の点があります。 予測集合の中に真の結果を含むことをユーザーが指定したエラー率で保証できる モデルに依存しない手法であり、かつ計算コストが比較的低い 予測誤差の分布に関する仮定は要求されず、入力Xと出力Yのペアが独立同一分布(i.i.d.)であることのみを仮定する 他のUQ手法で算出した不確実性は1について保証できない場合があるため、1がCPの最も優れた特徴かと思います。 2に関しても、事前学習モデルの有効性が明らかになってきた昨今の状況を考慮に入れると大変ありがたい性質です。 また3についても、現実世界では予測誤差が正規分布でないケースがありますし、i.i.d.は依然として強い仮定ではあるものの機械学習では標準的な仮定であるため、CPの実用性につながっていると感じます。 予測の不確実性を算出する手順 CPにて予測の不確実性を算出するワークフローを簡単にご紹介します。 Conformal Predictionの手順(分類での例)。出典: Angelopoulos, Bates (2022) 以下の手順でキャリブレーションデータのConformal scoreを計算する データセットを学習データ、キャリブレーションデータ、テストデータに分ける 学習データを使ってモデルを訓練する ユーザーが許容できるエラー率αを設定する(例えば5%) キャリブレーションデータの各サンプルに対してConformal scoreを計算する。Conformal scoreは値が大きいほど予測が外れていることを表現できるものにする。scoreはユーザーがカスタマイズ可能。上図の例ではモデルの予測値であるsoftmax出力値を1から引いた値が利用されている キャリブレーションデータのscoreが全体の1-αとなる分位点を求める テストデータの各サンプルに対し、分位点より大きい予測値を持つクラスをピックアップし、予測集合とする Conformal score自体も不確実性を表現したスコアですが、その尺度をさらに厳密に較正するためにキャリブレーションデータを用いる、というのがざっくりとしたイメージです。 結果として、ユーザーが定めたエラー率が厳密に保証された予測集合が得られます。 CPの利用方法とコツ CPを利用したい場合、scikit-learn-contribプロジェクトとして開発されているPythonライブラリ” mapie ”を使えば、scikit-learn likeにサクッと実装できます。 回帰の場合 from mapie.regression import MapieRegressor mapie_regressor = MapieRegressor(regressor) mapie_regressor.fit(X_train, y_train) # エラー率αを設定(複数設定することもできる) alpha = [ 0.05 , 0.32 ] # 予測値と予測区間の上限/下限値を計算 y_pred, y_pis = mapie_regressor.predict(X_test, alpha=alpha) # 可視化に関するコードは省略 # ↓α=0.05の予測区間は点線、α=0.32の予測区間は色付きの帯で示されている 出典: https://mapie.readthedocs.io/en/latest/quick_start.html 分類の場合 from mapie.classification import MapieClassifier mapie_classifier = MapieClassifier(estimator=classifier, method= 'score' , cv= 5 ) mapie_classifier = mapie_classifier.fit(X_train, y_train) # 予測値と予測集合を計算 y_pred, y_pis = mapie_classifier.predict(X_test, alpha= 0.05 ) # 可視化に関するコードは省略 # ↓α=0.05での各クラスの領域が示されている 出典: https://mapie.readthedocs.io/en/latest/quick_start.html CPを行う際に意識したいこととして、Conformal scoreとアルゴリズムの選択があります。 CPではエラー率の保証は常に成り立ちますが、得られた予測集合が有益かどうかは主にConformal scoreによって決まります。 scoreによって予測誤差の大きさに応じて入力Xを正しくランクづけできる場合、予測が簡単な入力には小さな予測集合を、難しい入力には大きな予測集合が得られます。 逆に、ランクづけを正確に反映できないscoreを選択した場合、すべてのクラスが予測集合に含まれるなど、意味のない予測集合が得られてしまうことに注意が必要です。 アルゴリズムについては、データを学習データとキャリブレーションデータに分割するSplit conformal predictionをはじめとし、すべてのデータをモデルの訓練とキャリブレーションの両方に使用するCV+やJackknife+といったものがあります。 基本的なConformal scoreやアルゴリズムはmapieで実装されているため、気になる方は 公式ドキュメント をご参照ください。 CPは有用な手法である反面、前述のとおりi.i.d.の仮定が満たされない状況で出力された結果は信頼できないことに注意が必要です。 学習・評価時のデータと推論時のデータの分布が異なる状況は現実世界でしばしば存在するため、そのようなケースで得た予測結果を過信しないよう気をつける必要があります。 おわりに 本記事ではConformal Predictionの概要と利用方法についてご紹介しました。 CPのアイデアは1990年代にすでに提唱されていましたが、特にここ数年で機械学習コミュニティで人気が出てきたようです。 必要な仮定が少ない手法であり、mapieなどのライブラリも充実してきたことから、非常に実用的なUQ手法だと感じています。 すでに英語・日本語でいくつか有益な文献・記事が存在していますので、気になる方はそちらもご参照ください *1 *2 *3 *4 We’re Hiring! タイミーのデータエンジニアリング部・データアナリティクス部では、ともに働くメンバーを募集しています!! 現在募集中のポジションは こちら です! 「話を聞きたい」と思われた方は、是非一度 カジュアル面談 でお話ししましょう! *1 : "A Gentle Introduction to Conformal Prediction and Distribution-Free Uncertainty Quantification" *2 : "Conformal Prediction" *3 : 『直感的に理解するConformal Prediction』 *4 : 『モデル予測に自信がないときに、素直にわからないと答えConformal Predictionに助けてもらおう』
アバター
こんにちは! スクラムマスターを担当している吉野です! 株式会社タイミーは LeSS Yoake ASIA 2024 のスポンサーとして参加し、私も招待チケットを利用して現地に行ってきました! 本記事ではDay1のコンテンツのいくつかをレポートします! Opening Session オープニングキーノートは、Craig Larmanさんによる "AI & HR: Evolving Organizations in an AI World" というタイトルで、AIが私たちにもたらしてくれる変化を、AIができることの紹介、AIの変化に合わせて個人の役割の変わり方からチームのあり方の変化、と、徐々にスコープを広げて話されていました。 特に印象的だったのは、一部の専門性をAIが担ってくれる未来はそう遠くないというお話でした。 現在は各スペシャリストで構成されています。 それは、各メンバーがバリューを個々の専門性の領域で発揮しているからとなります。 チームメンバーの専門性が特別必要な領域や、ルーティンワークを必要とする業務は、AIにお任せすることで、個人の時間に余裕を持たせることができます。余裕ができた時間で何をするのか? それは、学習です。それも自身の専門性を深める学習ではなく、他の領域についても学ぶことが大切とのことでした。 そうすることで、個々が専門性を高めるチームではなく、様々なことに関わることのできるメンバーでチーム内の協働を促進し、よりチームの透明性を高められるということでした。 クロスファンクショナルなチームを作ることに苦労されているお話をよくお聞きしますが、このようにAIを使うことで、チーム組成を進めることができるとのこと。 (GLAD Developer = Generative-AI & LLM-Assisted Development) そして、将来は一人の開発者と複数のAI Agentsの可能性も… AIが専門性を担ってくれる可能性と、チームの形の可能性を一つ知れたセッションでした。 スポンサーセッション(Red Hat) Red Hatさんによるスポンサーセッションでは、いくつかのチームでの議論を通しつつ、チームのスクラム/LeSSをより促進させるためには、組織変革へ目を向ける必要があることをメインに話されていました。 また、その組織変革に必要な要素を表す「スターモデル」を通して戦略/構造/プロセス/リワード/人材の考え方について、具体的な例を通しての説明がありました。 実際に使用し、方針を現場で立てるには、各項目のつながりとWhyとなる軸は説明できる必要があると思いますが、観点が簡潔にまとまっているモデルで、とっかかりに使えそうな印象を受けました。 また、その組織を変えることができる人物は「スクラムマスター」とのことです。 現在のタイミーでは「Agile Practice Team」というスクラムマスターで構成される、組織に向き合うチームがあります。 私も所属しているチームで、この発表はチームの存在意義を言語化してくれている!という気持ちになりました。 その他にも、ディスカッションの時間もあり、チームで多くの意見を出し合いました。 1時間とは思えない密度のセッションでした! The LeSS Company CEOによるトークセッション フルスタックCEOというパワフルな自己紹介をされていた、The LeSS Company CEOのBastiaan氏によるセッションでした。 LeSSの広がりやどのような場で活用されているのか、世界レベルでのお話を聞くことができました。 また、本セッションテーマについて、次のセッション時間に直接お話を伺いしました。 Bastiaanさん、Basさん、Akiさんとの廊下トーク 本来であれば、LeSS Morning 主催のワークショップの時間だったのですが、偶然にも廊下で3人のお姿を見つけて、話しかけてみました。 お話しした内容のうち、いくつかを以下に記載します。 最適なスケールフレームワークを選択するには? A. やりたいことをマネージャーなどに伝えるのではなく、まずは経営層の困っていることなどに寄り添うこと。システムモデリング(思考)などを使用し、困っていることを明確にする。 その中で、いくつかのフレームワークを当てこんだ時にうまくいくのか、うまくいかないのか、どのようなデメリットがあるのかを明確にする。 まずは、経営層の困っていることやプロダクトの課題を見える化することが大切。 特に、昨今はチームの認知負荷を減らすことが重要視されている傾向がある。多くの経営層の悩みにつながっており、あなたの場でも関係する可能性があるので見てみると良い。 Stage of Agileのデータから、LeSSの割合が少なくなっていて、SaFEの割合が多くなっている。これはなぜなのか? A. ガードナーに伺ったところ、実際のLeSS利用率は増えている。ガードナーは7000人にヒアリングしている。State of Agileの方は1000人しかデータを取っていないので、どのような人に向けてのアンケートかで、内容が変わってしまう。また、その人の意思とは反するデータになっているケースもある。例えば、ボスが変わってしまったことで、利用しているフレームワークがガラッと変わってしまうなど。なので、データの信憑性としてはそこまで高いものではないため、ある程度参考値ぐらいに考えた方が良い。 (名刺もいただきまして、「困ったらいつでも連絡して欲しい!」とオープンに接してくれたのがめちゃくちゃ嬉しかったです!) スポンサーセッション : LINEとヤフーそれぞれの組織でのLeSS実践奮闘記 LINEヤフーより、nakoさん( @nako418 )の登壇でした。 LeSSに挑戦し、難しい点に当たりつつもLeSSの実践を進めていったお話でした。 印象的だったのは、常に何ができるのかという選択肢を考え、一番最適な形を選択し続ける動きをされていたことです。 例えば… フィーチャーチームとコンポーネントチームのどちらが良いのか?(理想と現時点) オーバーオールイベントの参加者はどのような人を集めるのか? など、要所要所の考える必要があるポイントでしっかりと選択肢を洗い出し、都度最適と思えるものを理由と合わせて選択して実践していくことは、とても参考になりました。 また、困ったお話についても、バックログの整理方法など多くの人が向き合うことになりそうな問題をピックアップされていて、すごく共感が多かったセッションでした! (整理は、ひとまずエピックレベルで並び替え、必要になる上位のものに一旦フォーカスしていくとのことでした) Q&A Craigさん / Basさんに質問Time 一度業務で抜けたため終盤のみの参加でしたが、それでも濃い質問と鋭い返答が多い印象でした。 例えば… 質問 : マネージャーがいない組織における、責任の所在は?生産性の可視化ができていない、となった場合の問題は誰が責任を負うのか?マネージャーがいない組織において、どのように解決していくのか。 回答 : チームが責任を持つことが大切 スクラムマスターがそれを促す 人数の少ないマネージャーができるのであれば、人数の多いチームメンバーもできるのではないか? “マネージャーでないとできない” という制約を個人の中に持ってしまっていたことに気がついた瞬間でした。大切なのは、“なぜチームが気にしないのか” ということを考え、システムとシステム構造を変えることが大切と話されていました。 ディナー (写真を撮っていなくて残念…) 8種類ぐらいのビールと美味しそうな夕食が並んでいて、ビュッフェスタイルの懇親会でした。 ビールを何回もおかわりしつつ、多くの方とお話しして回りました。 そのため、夕食にはほとんど手をつけず… イベント全体で時間が押していたため、1時間と短めの懇親会でした。 (もっと多くの方と交流したかった…!!) 終わりに 以上でDay1のレポートとなります! 運営の方にお話を伺ったところ、初めての大規模開催とのことでした。 機材トラブルや時間が押してしまった等、いくつか大変そうなシーンもありましたがあの規模のカンファレンスを開催されていたのはめちゃくちゃすごかったです! (同じ規模のイベントをいつか私も開催したい) まだDay5がありますので、引き続き楽しみつつ学んでいきたいと思います!
アバター
こんにちは! データアナリストの@takahideです。 タイミーではプロダクト向き合いの分析業務を行なっています。 先日、東京都立大学の経済経営学部にてデータアナリストの講義を行わせて頂きました。 データアナリストの魅力やアカデミックな知の重要性に改めて向き合うことができたので、ご紹介できたらと思います。 内容は以下になります。 講義の背景 講義内容 事業と組織に関して データアナリストの業務内容 アカデミックな知の活用 講義の御礼 最後に We’re Hiring! 講義の背景 講義に至った背景は、知り合いでもある 米田泰隆准教授 とお話しした際に、データアナリストの業務内容が学生の皆さんの興味と合致するのでは、と意気投合したことでした。 今回お伺いしたのはゼミ形式の講義で、計量経済学から最新のデータサイエンスまで幅広い知見に基づいたデータ分析の授業が行われていて、業務内容との整合性も高いと感じました。 講義内容 講義は大きく下記テーマを扱いました。本ブログでは、1、3、4を中心にお話します。 1.タイミーの事業と組織に関して 2.データアナリストになるまで 3.データアナリストの業務 4.アカデミックな知との繋がり 事業と組織に関して まずは、タイミーの事業説明を行いました。 タイミーはワーカーと企業の双方が出会うツー・サイド・プラットフォームであり、分析対象となるデータも「ワーカー」「企業」「マッチング」に関するものが中心となります。 ビジネスモデルは一見単純に見えますが、ワーカーと企業の複雑な意思決定を読み解く必要があり、データアナリストとして非常にやりがいがあります。 次に、タイミーのデータアナリストの組織に関してお話ししました。 大きくは「プロダクトアナリティクス」と「ビジネス&マーケティングアナリティクス」という2つのグループに分かれていて、各グループが、担当領域に対して分析で得られたインサイトを提供しています。 例えば、プロダクトアナリティクスグループであれば、顧客に提供する価値体験ごとにチームが組成され、各チームにデータアナリストがアサイン、チームの責任者と協働して分析業務に携わります。 また、チーム単位の分析とは別に、会社横断での分析も行っていて、こちらはプロダクト✖︎マーケティングのようにグループ横断のものも多いです。 データアナリストの業務内容 こちらのパートでは、データアナリストの業務内容を説明しました。 タイミーのデータアナリストは、プロダクト、マーケティング、ビジネスのメンバーと伴走しながら、意思決定をサポートしています。 データアナリストは、データを用いた分析をもとに、全社戦略や事業に貢献することが期待されています。自分が行った分析がきっかけで、顧客に価値を届けられた瞬間は非常に大きな達成感を得られます。 では、「データを用いた分析」を進める上で必要になるものとは何になるのか? 「データを用いた分析」なので複雑な分析スキルが重要と思われがちですが、個人的には「仮説構築」が最も大切だと感じています(もちろん高度な分析スキルが不要というわけでは全くないです)。 「分析」を上流工程から下流工程へのプロセスと考えると、上流工程で作られた仮説に基づいて様々な定量・定性分析が行われます。そのため、仮説が間違っていると、定量・定性分析自体も間違った結論を出すことになってしまいます。 仮説の精度を上げる手法は色々ありますが、重要なのが「言語化」だと考えています。 仮説構築を「何気ない問いを、定義を確認し、抽象化して解像度を上げる一連の流れ」と考えると、「個人的な問い」をメンバー間で納得できる「共通言語」に仕立てていくことが大切になります。 アカデミックな知の活用 最後は、データアナリストとアカデミックな知の繋がりに関してお話ししました。 アカデミックな知は先ほどの「言語化」を考える上で非常に重要だと考えています。 個人的に、データアナリストの業務を長く続けるにつれて、アカデミックな知の価値を強く感じることが多くなってきました。 向き合う課題が複雑であるほど、それを解くためのアイディアも多様な切り口が求められますが、アカデミックな知はそうした切り口の宝庫になっていると感じます。 具体例をいくつか挙げてみたいと思います。 経済学 - 経済学の一分野であるマーケットデザインはマッチングの最適化を考える上で参考になります。 統計学 - プロダクト開発では、施策の効果判断を行う際にランダム化比較試験を行います。この背景には統計学の知見があります。 人類学 - ユーザーの声を知ることはプロダクト開発において非常に重要ですが、定性調査の各種手法の背景には人類学や社会学の知見があります。 論理学 - 仮説を構築する上で、演繹的推論、帰納的推論といった古典的な推論が必要になります。また、仮説的推論は創造的な思考を生み出すヒントになります。 これらの知見は、必要な時に使える便利なツールではないですが、様々な観点でものごとを捉えられるほど「言語化」が捗り、結果として仮説構築など分析の重要なプロセスを推進しやすくなります。 講義の御礼 改めて、今回データアナリストの講義を行わせて頂きありがとうございました! データに対する理解度が高い方が多く、また、ご自身で起業されていて分析の勘所を備えているなど、お話しして非常に大きな刺激を受けました。 大学で学べることを羨ましく思いつつ、アカデミックな知と業務との繋がりをこれからも作っていきたいと思いました。 最後に 今回は、データアナリストの外部活動として、大学での講義に関してお話ししましたが、きっかけは「大学で講義を行いたい」という提案でした。 タイミーでは、メンバーの意見を真摯に聞いて、サポートしてくれる文化があります。 年次に関わらず分析プロジェクトの提案ができ、新しいことにチャレンジしたい方にとって非常に魅力的な環境だと思います。 We’re Hiring! 私たちは、ともに働くメンバーを募集しています!! カジュアル面談 も行っていますので、少しでも興味がありましたら気軽にご連絡ください。 hrmos.co
アバター
エンジニアリング本部 プラットフォームエンジニアリングチームの徳富です。我々のチームでは、CIパイプラインの効率化と開発体験の向上を目指し、CircleCIからGitHub Actionsへの移行を進めてきました。移行によってテスト・静的解析(以降CIと記載する)の実行時間をp95で9分短縮しましたが、この移行にはいくつかの課題もありました。今回は移行の背景や移行時の苦労について紹介します。 本記事のまとめ CircleCIからGitHub Actionsに移行 し、CI実行時間をp95で 9分短縮 並列実行やテスト結果の連携 における課題を解決し、効率化を実現 テスト結果のPR通知や分割テスト の仕組みを工夫し、開発体験を向上 CircleCIからGitHub Actionsへ移行した背景 以前利用していたCircleCIのPerformanceプランでは、並列実行できるジョブ数が80に制限されていました。1回のCI実行に40の並列ジョブを使用していたため、同時にCIを実行できる開発者は最大2名まで。この状況では、今後開発者が増加するにつれて、 CIの待ち時間が長くなるリスクを抱えていました。 プランのアップグレードも検討しましたが、以下の理由から GitHub Actionsへの移行 を決断しました。 理由1: 学習コストの削減と効率化 弊社では基本的にGitHub Actionsを利用していますが、RSpecテストと静的解析のみにCircleCIを使用していました。これをGitHub Actionsに統合することで、 CircleCIとGitHub Actionsの両方を学ぶ必要がなくなり 、開発者の 学習コストが削減 されます。また、以前はCircleCIでのCI完了後に、デプロイをGitHub Actionsで行うワークフローでした。CIの実行基盤をGitHub Actionsに移行することで、テストもデプロイもGitHub Actionsに統一できるようになります。これにより、デプロイフローがよりシンプルになり、メンテナンス性も向上しています。 理由2: セキュリティリスクと管理の効率化 CIプロバイダーをGitHub Actionsに一本化することで、複数のプロバイダーを管理する必要がなくなり、 情報流出のリスクが低減 し、一貫したセキュリティ対策を実施できます。さらに、管理するツールが減ることで 管理業務が簡素化 され、運用が効率的になります。 移行する上での課題となっていたポイント テスト実行時間ベースでテスト分割する仕組みがGitHub Actionsにない テストの失敗した情報をわかりやすく参照できるUIがない matrix strategy を使って並列実行をすると 後続のjobに結果を送れない Flakyテストを一覧で参照できない 1. テスト実行時間ベースでテスト分割する仕組みがGitHub Actionsにない CircleCIには、 CircleCI CLI を使用してテストファイルを実行時間ベースで分割する機能 がありましたが、GitHub Actionsには同様の機能がありませんでした。 そこで、代替として mtsmfm/split-test ライブラリを導入し、 RSpecテストを均等に分割 する仕組みを整えました。このライブラリは、過去のテスト実行時間をもとに、実行時間が均一になるようにテストファイルを分割します。そのためデフォルトブランチのテスト結果をS3に保存し、その結果を利用してテストを効率的に分割・実行するフローを構築しました。 2. テストの失敗した情報をわかりやすく参照できるUIがない CircleCIには、 テスト結果をわかりやすく表示する機能 が標準で備わっており、開発者はテストの成否や詳細を簡単に確認できました。しかし、GitHub Actionsにはそのような標準機能がなく、開発者がテスト結果を確認するのに手間がかかってしまうという課題がありました。 そこで、 SonicGarden / rspec-report-action にパッチを適用し、 テスト結果をPRにコメントする仕組み を導入しました。この改善により、CircleCIにログインせずにPR内で直接テスト結果を確認できるようになり、作業効率が向上しました。 3. matrix strategy を使って並列実行をすると 後続のjobに結果を送れない GitHub Actionsでは、 matrix strategy を使うことで、並列実行ができます。しかし、この並列実行には問題があり、 後続のジョブにテスト結果を渡せない という課題がありました。 最初は、各マトリックスで生成されたテスト結果をアーティファクトとしてアップロードし、後続のジョブでダウンロード・集計する方針を検討しました。しかし、25並列で実行していたため、アーティファクトのアップロード頻度が増加し、すぐに GitHub Actionsのレート制限 に達してしまいました。 この問題に対処するため、テスト結果をGitHub Actionsのアーティファクトではなく、 S3にアップロード する方法に切り替えました。これにより、レート制限の問題を回避でき、後続のジョブでテストの結果をPRに投稿する仕組みが無事に機能するようになりました。 4. Flakyテストを一覧で参照できない CircleCIではテストインサイトを使ってFlakyテストを確認できましたが、GitHub Actionsにはそのような機能がありませんでした。当初は CodecovのTest Analytics を使ってFlakyテストを確認していましたが、絞り込み機能が不十分で、ページの表示に時間がかかるという問題がありました。 そこで、現在は Datadog Test Visibility を利用してFlakyテストを確認しています。Datadogを使用することで、テストのTrace情報などの詳細なデータを収集できるだけでなく、不安定なテストが検出された際にアラートを設定することも可能になりました。 完成したワークフローの一例 ここで、最終的に完成したワークフローの一部をご紹介します。GitHub Actionsへの移行を検討している方にとって、少しでも参考になれば幸いです。 name : ci on : push : branches : - '**' tags-ignore : - '*' concurrency : group : ${{ github.workflow }}-${{ github.ref }} cancel-in-progress : true permissions : id-token : write contents : read pull-requests : write jobs : generate-matrix : runs-on : ubuntu-latest timeout-minutes : 1 outputs : parallelism : ${{ steps.set-matrix.outputs.parallelism }} ids : ${{ steps.set-matrix.outputs.ids }} steps : - id : set-matrix run : | parallelism=25 # テストの並列数 ids=$(seq 0 $((parallelism - 1)) | jq -s | jq -c) echo "parallelism=[$parallelism]" echo "ids=$ids" echo "parallelism=[$parallelism]" >> "$GITHUB_OUTPUT" echo "ids=$ids" >> "$GITHUB_OUTPUT" rspec : needs : [ generate-matrix ] runs-on : ubuntu-latest timeout-minutes : 20 env : DD_CIVISIBILITY_AGENTLESS_ENABLED : true # Datadog Test Visibility用 DD_API_KEY : ${{ secrets.DATADOG_API_KEY }} # Datadog Test Visibility用 DD_ENV : ci # Datadog Test Visibility用 strategy : fail-fast : false matrix : parallelism : ${{ fromJson(needs.generate-matrix.outputs.parallelism) }} id : ${{ fromJson(needs.generate-matrix.outputs.ids) }} services : # rspec実行に必要なコンテナを起動 steps : - name : Generate token id : generate_token uses : actions/create-github-app-token@v1 with : app-id : app idを指定 private-key : キーを指定 - name : Checkout uses : actions/checkout@v4 with : token : ${{ steps.generate_token.outputs.token }} - name : Setup Ruby with caching uses : ruby/setup-ruby@v1 with : ruby-version : 3.3 bundler-cache : true - name : Configure AWS credentials uses : aws-actions/configure-aws-credentials@v4 with : role-to-assume : # S3にアクセスできる権限をもつロールを指定 aws-region : # リージョン名 # NOTE : デフォルトブランチで記録された各RSpecテストの実行時間データをダウンロード - name : Download rspec junit run : aws s3 cp s3://デフォルトブランチでのテスト結果(JUnit XML)が保存されているS3を指定 tmp/rspec_junit --recursive # NOTE : テストファイルを分割するためのツールをダウンロードし、後続ステップでテストファイルをダウンロードして分割する。 # これにより、マトリックスを使用して並列で実行されるRSpecテストが均等に分割され、 # すべてのテストジョブがほぼ同時に完了するようにする。 - name : Split test file run : | curl -L --output split-test https://github.com/mtsmfm/split-test/releases/download/v1.0.0/split-test-x86_64-unknown-linux-gnu chmod +x split-test - name : RSpec run : | # テストファイルを分割しテストを実行する bundle exec rails db:create db:schema:load ./split-test --junit-xml-report-dir tmp/rspec_junit \ --node-index ${{ matrix.id }} \ --node-total ${{ matrix.parallelism }} \ --tests-glob "spec/**/*_spec.rb" \ --tests-glob "packs/*/spec/**/*_spec.rb" | xargs bundle exec rspec \ --format progress \ --format RspecJunitFormatter \ --out report/rspec_junit/${{ matrix.id }}.xml \ -f j -o report/results/${{ matrix.id }}.json \ -f p # テストが完了したら、テスト結果をS3にアップロードし、後続のジョブで利用できるようにする - name : Upload test report if : always() run : | aws s3 cp report s3://テスト結果が保存されているS3を指定 --recursive rspec-status : runs-on : ubuntu-latest timeout-minutes : 1 needs : [ generate-matrix, rspec ] if : ${{ !cancelled() && github.event_name == 'push' }} steps : - name : Check previous job status run : | if [ "${{ needs.rspec.result }}" == "success" ] ; then echo "テスト成功" else echo "テスト失敗" exit 1 fi upload-rspec-junit : runs-on : ubuntu-latest timeout-minutes : 2 needs : [ generate-matrix, rspec ] if : github.ref == 'refs/heads/main' # デフォルトブランチを指定 steps : - name : Configure AWS credentials uses : aws-actions/configure-aws-credentials@v4 with : role-to-assume : # S3にアクセスできる権限をもつロールを指定 aws-region : # リージョン名 - name : Download rspec junit run : aws s3 cp s3://各マトリックスでのテスト結果が保存されているS3を指定 rspec_junit --recursive - name : Upload rspec junit run : aws s3 cp rspec_junit s3://各テストのJUnit XMLをアップロードするS3を指定 --recursive pr-comment : needs : [ generate-matrix, rspec ] if : ${{ !cancelled() }} runs-on : ubuntu-latest timeout-minutes : 2 steps : - name : Generate token id : generate_token uses : actions/create-github-app-token@v1 with : app-id : app idを指定 private-key : キーを指定 - name : Checkout uses : actions/checkout@v4 with : token : ${{ steps.generate_token.outputs.token }} - name : Configure AWS credentials uses : aws-actions/configure-aws-credentials@v4 with : role-to-assume : # S3にアクセスできる権限をもつロールを指定 aws-region : # リージョン名 - name : Download RSpec reports run : aws s3 cp s3://各マトリックスでのテスト結果が保存されているS3を指定 report --recursive - name : RSpec Report if : steps.find_pr.outcome == 'success' uses : SonicGarden/rspec-report-actionアクションに一部パッチを適用したアクションを指定 with : token : ${{ steps.generate_token.outputs.token }} json-path : report/results/*.json comment : "${{ github.event_name == 'push' }}" # PRにコメントするかどうか pull-request-id : ${{ fromJson(steps.find_pr.outputs.pr_json).number }} hideFooterLink : true 得られた成果 テスト時間の短縮による開発効率の向上 GitHub Actionsへの移行により、 p95でテスト実行時間が9分短縮 されました。並列実行の制限がなくなったことで、CI/CDのパフォーマンスが大幅に向上しました。この改善により、開発者の作業効率が向上し、より早くフィードバックを得られる環境が整いました。 CircleCIの時はp95で15分 GitHub Actionsに移行後はp95でテストが6分程度で終わるようになっているになっている 移行のまとめと今後の課題 CircleCIからGitHub Actionsへの移行により、 テスト実行時間の大幅な短縮 という大きな成果を得られました。しかし、移行に伴う課題も少なくなく、とくに テスト結果の連携 や テストの分割 には工夫が必要でした。 今後は、GitHub Actionsの新機能やさらなる最適化手法を活用し、テストやデプロイのより一層の効率化を目指していきます。本記事が、同様の課題に直面している方々の参考になれば幸いです。
アバター
こんにちは。okodoooonです🌵 ラスベガスに来てから、同行しているメンバーと手分けをしてセッションを聞いて、知見をまとめる作業に追われており、なかなかハードな日々を過ごしておりました。 今回は初日に聞いた5つのセッションがどれも知見に溢れるものだったので、そちら紹介していきたいと思います💪 スライドが公開されたり他メンバー担当分の記事が上がり次第リンクなど追って貼っていきます。(2日目以降のやつも頑張って書いております) 目次 Data alone is not enough(訳:データだけでは不十分である) 概要 セッション内容の紹介 感想 Breaking the mold: A smarter approach to data testing(型破り:データテストに対するより賢明なアプローチ) 概要 セッション内容の紹介 感想 How dbt transformed FinOps cost analysis at Workday(訳:dbtがWorkday社のFinOpsコスト分析をどのように変革したか) 概要 感想 Surfing the LLM wave: We can't opt out and neither can you(訳:LLMの波に乗る:私たちも、あなたも避けることはできない) 概要 セッション内容の紹介 感想 Semantic layers: The next data revolution or just overrated hype? (訳:セマンティックレイヤー:次のデータ革命か、それとも過大評価された流行か?) 概要 セッション内容の紹介 感想 Data alone is not enough(訳:データだけでは不十分である) 発表者:Preston Wong (Analytics Engineer @ Settle) 概要 データを提供できる組織や環境を作るのではなく、洞察を提供できる組織や環境を作るべきで、 理想の組織に改善されるために、Settleが取り組んでいるJTBDというフレームワークの紹介をしていました セッション内容の紹介 以下のような画像と こちらブログ の引用ともに レポーティング業務はデータ組織にとってゲートウェイドラッグのようなものだ 。 これらのレポートは本当に意味のあるものなのか?変化を促しているのか?実際に使用されているのか?誰かがそれを見て、2秒で終わるために7時間も費やすのはなぜだろう? 最近のデータイニシアチブは、情報へのアクセスを民主化することに重点を置きすぎており、ビジネスへの影響を促進することに十分に注力されていない。どんなに多くの情報があっても、それだけでは組織の変革を促すことはできない。 という強烈なメッセージを発信していました。 とても耳が痛いですね。 「 ビジネスインパクトをいかに生み出すか 」という観点なしに、ただデータを出力し続けるチームになってしまうことへの懸念を紹介してくれました。 それらの懸念を紹介した後に「データチームがどのようにしてビジネスのデータが持つ潜在能力を最大限に引き出せるのかを考える必要がある」と続き、 レポーティング業務から抜け出せない理由はコンテキストを持っていないことが多いからと説明していました。 良い例として、Q3の新規アクティブ顧客数が安定して増加しており、最近の月では20%増加したことが挙げられます。データ担当者がこれに気づいたとしても、直接的な文脈を持っているとは限りません。このデータには、最近の企業買収があったことや、その顧客基盤を取得したことが含まれておらず、それが20%増加の理由です。この説明はデータ自体では行えず、他の誰かが説明する必要があります。つまり、なぜそうなったのかを答えるのは難しいのです。 Settleではデータチームを適切にステップアップさせていくためにデータ組織の JTBD(JobToBeDone) を設定しており、その運用によってレポーティング組織から適切に脱却しつつあると紹介しています。 JTBD of Modern Data Team データの活用 メトリクス管理 プロアクティブなインサイト 実験のドライブ データとのインターフェース 紹介されていた各要素ごとの課題例のproblemとimpactがこちらです(ちょっと文量が多いので畳んでいます)。 データの活用(クリックすると展開) データの活用 オペレーショナルデータを必要とするチームに提供すること ex. マーケティング部門からのリクエストで「ハブスポットで解約した顧客に自動メールを送信し、タッチポイントとアウトリーチを行いたい」 解決したHow 多様なソースからのデータを、主に内部データを含めて、dbtモデルに統合。このデータマーケットは、ビジネスの包括的な表現として機能し、意思決定に不可欠な業務属性、将来のフラグ、メトリクスおよびSSoTを提供。 リバースETLと組み合わせることで、各チームの好みや主要なSaaSツール(SalesforceやHubSpot、Intercomなど)にシームレスに同期されたSSoTを持つことが可能となり、効率的なデータ活用とリアルタイムのインサイトが実現可能。 インパクト このワークフローを合理化し、データを取得して作業を行うのに何時間も費やす代わりに、数分で済むようになった。 このアプローチにより、彼らはツールセットを簡素化し、結果として効率的なセルフサービスモデルを実現。 マーケティングチームは最新の関連データに直接アクセスでき、タイムリーかつ正確に実行できるようになった。 メトリクス管理(クリックすると展開) メトリクス管理 共有された定義と重要な事実の基準を作成すること ex. もしファイナンスのステークホルダーに尋ねれば「アクティブカスタマー」とは、自分の銀行口座を接続した人だと言うでしょう。マーケティングに聞けば「アクティブカスタマー」は、完全なオンボーディングフローを完了した顧客だと言います。しかし、プロダクトに尋ねると「アクティブカスタマー」は、最近プラットフォーム上で活動があった顧客だと言います。では、どれが本当に正しいのでしょうか? 解決したHow セマンティックレイヤーを活用することで、これらのメトリクスを明確に定義し、文書化。 インパクト Hexのようなツールや、ExcelやGoogle Sheetsを好むステークホルダーがアドホックレポートを作成する際にも使用可能に。 指標に関する煩わしい問い合わせ対応が大幅に減少。 プロアクティブなインサイト(クリックすると展開) プロアクティブなインサイト プロアクティブ(=先手を取れる)ようなデータの提供 (= 先行指標を提供) ex. 顧客の解約が若干増えているが、どのデータが私たちが顧客を維持するためによりプロアクティブになる手助けをしてくれるか? 解決したHow 戦略的リーダーを特定、ビジネスユニットと密接に連携 「アシュアランススコア」を開発 顧客がサービスを解約する可能性を定量化し、チームが迅速に行動を起こすためのアラートを受け取れるようにする インパクト 。このデータ駆動型のアプローチにより、リスクのある顧客を早期に検出し、ターゲットを絞った介入措置を講じることが可能に。 顧客のライフタイムバリューを向上させ、会社の長期的な成功と持続可能な成長において重要な役割を果たすことに成功。 実験のドライブ(クリックすると展開) 実験のドライブ プロアクティブなインサイトの発見と連携しながら、ビジネスに対して測定可能な影響を与えるための実験を推進すること ex. 現在使用している機能に問題が発生しました。リスクを高めたり、ビジネスへの影響を評価したりするために利用できるデータポイントはあるか? 解決したHow 組織内のステークホルダーと協力して、実験を展開 インパクト 全体のスコアを向上させることができ、非常に洞察に富んだ重要な結果を得ることに成功 データのインターフェース(クリックすると展開) データのインターフェース ビジネス全体にわたるチームメンバーに必要な情報や結論を提供し、彼らがそれを活用できるようにすること ex. この収益はどこから来ているのか?モデルでどのように定義されているのか?この数字を週次レポートに使用しても良いのか? 解決したHow 彼らが自分自身で質問を探求し、答えを見つけるために必要なツールを提供できるようにする description生成をAIに実行してもらいその内容をチェックするプロセスを実行する dbtとmetabaseを接合して、metabase上でdbt exploreと同様のメタデータを閲覧可能とする インパクト 最終的にデータチームのレポーティング作業負担を軽減 JTBDのマトリクスは最終的にこのような形になります。 Settle社による所見 生のデータ出力だけでは不十分であり、JTBDフレームワークは、ステークホルダーのコンテキスト、動機、および具体的なニーズを理解することが、データを実行可能なインサイトに変換し、ビジネスを推進するために重要である。 私たちのステークホルダーを支援するために、データの活用を推進し、より効率的なセルフサービスモデルを実現するべきである。これにより、ステークホルダーは自身の専門分野で最も得意なことに集中でき、データの整理に関する煩わしさから解放される。 感想 事業会社のデータ部署に勤めている僕としては、めちゃくちゃ刺さりました。 JTBDを弊社なりに構築しつつ、もっとアナリストやDSと協働して、意思決定に刺さる指標がビジネスに素早く届けられるような状態を構築しなきゃなあ。 なぜ早くデータを出せるようにするのか、なぜメタデータを拡充するのか、なぜこのようなインターフェースを構築するのか、などをしっかりとビジネスインパクトと紐づけた上で、今後意思決定をしていけるようになれる気がしています! Breaking the mold: A smarter approach to data testing(型破り:データテストに対するより賢明なアプローチ) 発表者:Anton Heikinheimo (Senior Data Engineer @ Aiven) , Emiel Verkade (Senior Analytics Engineer @ Aiven) 概要 僕たちはdbt testを書きまくっているけれど、デリバリーが遅くなることの方がデータ品質において深刻なケースが多いよね。 warningの利用やWHERE句による代替で不要なテストを消してデータ品質を向上させていこう セッション内容の紹介 朝にSlackを開いて、dbtパイプラインのfailedの通知を確認して、トリアージを決めて、ステークホルダーと合意形成して、解決策を実装・テストしてデプロイする。そんな朝がたくさんあるのはおかしい。dbtのデータテストのベストプラクティスに従っているはずなのに そんな言葉から始まった本セッションでは、以下のようなMEMEでテストを書くこととデリバリー品質の向上が一致しないことに対する課題感を説明していました。 現状のdbtテストを単純に実装していくと、以下のような課題点があると説明しています。 テストが落ちるとその下流のビルドがすべて落ちるので、データの品質が落ちる。 特定のモデルが更新される一方で、他のモデルが更新されないことがありロジックによっては致命的である。 buildコマンドではテストは落ちるが、run → testの順番なのでrunによって不正なテーブルが出力される。 データテストが何であるか、そして典型的なデータセットアップがどのようなものか を以下の図を用いて説明していました。 この図における右と左のアサーションテスト(正常な場合に予測されるものと一致していることを確認するテスト)の役割を整理すると、以下のように記述できます。 左側のアサーションテスト ソースデータに関する仮定をテストするのに役立ちます。 このデータは、外部および内部のソースから来る可能性があり、データコントラクトやQAフレームワークが組み込まれていないこともあります。 右側のアサーションテスト 私たちが適用したロジックに関する仮定をテストするためのもの。 すでにソースデータをテストし、それが期待に合致していることを確認していると仮定しているもの。 入力データが検証された後、出力データをテストすることで、私たちはロジックの整合性を確認できる。つまり、dbtプロジェクトで行ったすべての変換が、期待した結果をもたらすことを確実にするためのテスト。 例えば左側のアサーションテストで落ちた場合は図中の Data と記載されたテーブルに悪いデータが留まり、左のアサーションテストが解決されるまで更新されない ショートサーキット が発生します。 ただし、テストの重要性が不明瞭で、モデルのテストに対して厳しい要件を持つことでプロセスを標準化しようとしたり、テスターなどのメトリクスを持つことで対処しようとしたりする場合、テストが品質に与える影響を無視しているとも言えます。 上流のプロダクトやSaasの変化や変更は加速しており、テストは 「そのテスト作成時点のアサーションでしかない」 という主張です。なのでよく落ちるテストに対しては削除することが合理的な場合があり、落ちるケースに対しては以下のような対応をしてしまうとのことでした! <= before | after => このようにフィルターとして扱ってしまうことで、テストによるデータ鮮度の品質を落とさず、そもそも間違ったデータが渡らないようにしてしまおうという発想のものです。 テストはデータ基盤のしなやかさを構築するためのものでもあり、そのためにSettleのチームではテストのデフォルトの 重要度を「警告」に変更 したとのことでした。確かにこのようにすれば、上流の変化に“しなやかに”対応しつつデータ鮮度の品質は落とさずにいられますね。 また、このテストではなくフィルターを記載する取り組みをスケールさせるために、マクロを使用していると話していました。 <= before | after => ↑こんな感じでマクロを呼び出せるようにして、外部キー制約やaccepted_valueなどをフィルターとして表現可能にしているとのことです! 最終的に以下のようにテストを減らしつつ、しなやかな構成に変更できたと締めくくられています! 感想 弊社もdbt + Lookerで構築されたセマンティックレイヤーがかなりのオペレーションに活用されており、こちらのパイプラインの停止がビジネスの停滞に繋がりうるため、とても参考になる発表でした! 外部に公開する数字とか請求金額とか絶対に間違えちゃいけないもの以外は、実は多少ずれていても意思決定に大きく影響しないものもありますもんね! 主キー制約とかまでフィルターでやっちゃうのは執念が感じられて笑っちゃったのと、弊社だとSalesforceみたいなデータソースの自由入力や意図しないデータ入力にパイプライン全体が影響を受けていたりするので、すぐにでも実践を検討したいです! How dbt transformed FinOps cost analysis at Workday(訳:dbtがWorkday社のFinOpsコスト分析をどのように変革したか) 発表者:Eric PuSpeaker (Senior Software Engineer @ Workday), Pattabhi Nanduri ( FinOps Data Engineer @ Workday ) 概要 DBTとJinjaマクロを活用してAWSやGCPを中心としたプラットフォーム上でのコスト要因を把握 DBTマクロやテンプレートを用いることで、ユーザーの負担を軽減し、データモデルの構築から実装までを自動化 モデル定義を通じて、ドキュメンテーションの自動生成やテストの自動実行 Workday社でのビジネス上の課題 コストとコストドライバーの理解: AWSやGCPから発生するコストやその要因を把握。 コスト最適化の機会の特定: コスト削減のための最適化ポイントを特定。 コストの割り当て:アプリケーションサービス、顧客、テナントごとにコストを適切に割り当て。 コストの予測:未来のコストを見積もり、予測する。 レポートとセルフサービス機能: 自己サービス型のレポート作成、チーム開発、行レベルアクセスやオブジェクトレベルアクセスなど、SOXコンプライアンスに対応したアクセス制御。 作成したダッシュボード(写真撮り忘れました) 今日の作業全体が表示され、コンピューティング、ストレージ、データベースなど、さまざまな支出の詳細が表示されているもの。同様に、月ごとのコンピューティングトレンドも表示されているもの。 コンピュータに関連する支出の詳細が表示されているもの。現在の四半期での使用状況を見ることができ、プロセッサやインターネット利用料などがわかるもの。 ダッシュボードのDimension 階層型の製品Dimension 最初のレベルでAWS製品とGCP製品といった大分類で区切り、7つのレベルにブレークダウンする形で分割しているもの。 階層型のプロジェクトDimension マーケットや人、組織に関する情報が含まれる。人と組織の情報は、プラットフォームの所有者や責任者に関連している。 全体のデータ構造はこんな感じです。 ユーザーが早い段階で次元カラムとファクトカラムを明確に定義するようにして、カラムのタイプを一度だけ定義すれば、その情報がデータマートの最終段階まで引き継がれるようにしているとのことです。 モデルの概要を宣言するだけで、残りのコードが自動的に生成され、モデルが正しく効率的に動作するように設計されています。 こちら紹介されていたサンプルクエリ。 lightdashを活用しているので、その辺りのアクセス制御もマクロを活用して行っているとのこと。 動的なユーザーフィルター sql_filter: {account_project_id in [select account_project_id from cpus.flops.access_control_bridge where login_user=${lightdash.attributes.user_name}]} ユーザーアクセスコントロール - name : effective_discount_column data_type : numeric meta : dimension : required_attributes : is_opus_super_user : "true" - name : aws_cur_table meta : required_attributes : is_opus_super_user : "true" このような構成にしたメリット ユーザーに多くの教育を必要としない。 パーティショニングが自動で切られてテストも自動で追加されるため気にしなくても良くなる。 統一された生成方法なのでドキュメンテーションが自動的に生成される仕組みを導入できた。 情報をリネージグラフと組み合わせて、実行計画を作成し、それをAirflowに送信、実行計画に基づいて、Airflowはクラスタの動的スケーリングを行い、各ステージのワークロードに応じた最適なリソースの割り当てが可能。 感想 クラウド料金を探索できるデータモデリングをする際のDimensionの切り方や、主要なコストはそれ単体をFactとして切り出して名前をつけてしまっているところなんかが地味に参考になりました! マクロ化には賛否色々ありそうですが、テストが自動で設定されるようになっていたり、DBTが生成するドキュメントに自動的に反映するような仕組みになっていたりして、ここまでやりきってしまえるならメリットが大きそうだなあと思いました! Surfing the LLM wave: We can't opt out and neither can you(訳:LLMの波に乗る:私たちも、あなたも避けることはできない) 発表者:Amanda Fioritto(Senior Analytics Engineer @ Hex), Erika Pullum(Analytics Engineer @ Hex Technologies) 概要 データ分析&BI統合ツールのHexにできたmagicと呼ばれるLLMによるサポート機能をHex社自身が使い倒して LLMによるクエリ生成の精度をどうしたらどのくらい向上させることができるか試行錯誤したレポートです セッション内容の紹介 Hex社にはドッグフーディングの文化があるので、リリースした magic というLLMによるクエリサポート機能を自社でどこまで活用できるか常に使用してきたそうで、社内のmagic機能に「パトリック」という名前をつけているそうです! パトリックの精度は論文やLLMモデルの公式発表などによると90%であるとのこと。 パトリックをオンボーディングする時の仮説 ⇒ 人間の同僚とそれほど違わないかもしれない。 ⇒ 人間のステークホルダーにとってデータが役立つように工夫することが、パトリックが新しい役割で成功する手助けにもなるかもしれない。 ⇒ データ組織の役割はステークホルダーにとって使いやすいデータ資産を作ることであり、現在ステークホルダーにはパトリックも含めている。 パトリックの評価の比較対象は spider というtext-to-sqlのオープンソースとのことです。 パトリックの評価を比較可能にするために「前四半期に予約された会議の数はいくつですか?」という質問を投げることにしたが dim_dates というテーブルがJOINできていなかった。 会議の日を特定するカラムを指定できていない。 など人間のステークホルダーからもよく寄せられる質問のようなミスをした。 「MMセグメントにいる顧客数は何人ですか?」という質問にパトリックが答えられるためには「顧客」とは何か、「セグメント」とは何を意味するのか、「MM」が何を指すのかを理解する必要がある。 パトリックのチューニング方法 「エンフォースメント」というツールを使ってDWH内のアクセス範囲を拡げる、または狭める。 ドキュメンテーションへのアクセスを許可してカンニングペーパーありの状態にする。 アクセス範囲のチューニングの実験結果 アクセス範囲を全snowflakeテーブルに拡大 ⇒ 正答率10% アクセス範囲をHex社内のテーブルに限定 ⇒ 正答率38% アクセス範囲をHex社内のテーブルに限定して、よく使われるテーブルにフラグをつける ⇒ 正答率46% ドキュメンテーションの実験をする際に、ドキュメントの品質にも差をつけた。 低品質: segment_type は Enterprise や Mid-Market などの値を含むことができる。 高品質: 低品質なものに加えて同義語も提供します。例えば、「Small Midsize(中小企業)」は「SMB」とも呼ばれるし、「Mid-Market」は別の呼び方もある。 ドキュメントの品質によるチューニングの実験結果 低品質のドキュメント ⇒ 正答率33% 高品質のドキュメント ⇒ 正答率51% セマンティックレイヤーを使った場合の実験結果 正答率75~88% 実験結果を受けてHex社の見解 MMをMidMarketと変換できるような人なら、パトリックのサポートをうまく活用し、自分たちの作業を進められるかもしれません。 データチームにとって、これらのツールがどのように、そしてなぜ機能するのか、またそれが質問に答えようとする人たちにとって役立っているのかを理解することが重要です。 データウェアハウスの整理やモデルのドキュメント化やコンテキストの整理は必要です。 重要なテーブルをマーキングする作業と高品質のドキュメント整備がそこまで差異がなかったのは驚異的でした。 LLMをうまくクエリビルダーとしてワークさせたいならセマンティックレイヤーの導入が必要そう。 感想 LLMをクエリビルダーとして使う想像は誰もがしたと思いますが、(僕も去年試行錯誤しました https://speakerdeck.com/okodoon/slackkarazi-you-yan-yu-deshe-nei-zhi-biao-wowen-ihe-waserarerubotwozuo-ritaka-tuta )実際にこれをすると何%改善するのかというところまで定量的に示してくれている実例は滅多にないので素晴らしい発表だと思いました! セマンティックレイヤーを作っていくことで、社内のクエリコスト低下だけでなくLLM活用まで見据えられそうなことが明確に示唆されたので、セマンティックレイヤー整備を引き続き頑張ります! LLMをステークホルダーと捉えてマシンリーダブルなデータ基盤に寄せていくって発想は今後のデータ基盤にきっと求められていく要素なんだろうなあと思いました! Semantic layers: The next data revolution or just overrated hype? (訳:セマンティックレイヤー:次のデータ革命か、それとも過大評価された流行か?) 発表者:Katie Hindson(Head of Product and Data @ Lightdash) 概要 dbtによってトランスフォーメーションが容易になったが課題が多く、数字の不整合の解消、指標の統一、LLMの活用といった課題を解決するためにセマンティックレイヤーが有用であると思われる。 ユニバーサルなセマンティックレイヤーを参照できるツール(Lightdash)がセマンティックレイヤーの活用方針としては望ましい セッション内容の紹介 セマンティックレイヤー不在における課題点を以下のようなスライドで紹介しています。 クエリごとに同じ指標を出しているはずなのにバラバラになってしまう。 指標の定義が社内で噛み合わない(アクティブユーザーのアクティブって何?「使用」の定義は?) LLMをデータ基盤で活用するためには同じ質問に対して同じ答えが得られることが重要。 そして、今日のセマンティックレイヤーは以下のようにスタンドアロン型かバンドル型かに分類されるとの主張をしていました。 スタンドアロン型 ex. ATSCALE, Minerva, cube, dbt Semantic Layer インテグレーションが不足しており、ビジネスユースケース足り得ない。 バンドル型 ex. Lightdash, MicroStrategy, Looker, SAP BIツールに統合されたセマンティックレイヤー。他のモダンデータスタックとのインテグレーションが不足している。 セマンティックレイヤーの使用体験として必要なもの メトリクスファーストの探索(テーブルを意識しないで指標名だけで思考が完結するような体験)。 データカタログでメトリクスの意味が確認できること。 データカタログから、簡単にメトリクスの探索ができること。 これらの体験を満たしているセマンティックレイヤーとして、Airbnb社が内製しているMinervaがとても優れていると述べていました。( Minervaに関するAirbnb社の記事はこちら ) セマンティックレイヤーのユニバーサル性として必要なもの スタック全体に適用できるほど汎用的 必要なすべてのツールと統合可能 今後セマンティックレイヤーがデータ基盤の中心となるため、あらゆるデータスタックと接続可能な状態を構築する必要があるとのことでした! 感想 結構ポジショントークみが強く「ユニバーサルって言葉をLightdashにだけ使うのは言葉として強すぎるのでは?」という指摘をQAで受けていて少し面白かったです。 Looker上にセマンティックレイヤーを構築している弊社としては、データカタログからの滑らかな探索体験みたいなところは実現が難しそうで、Lightdashも全然候補になってくるなと思いました! Gemini on Lookerを超えるセマンティックレイヤー✖️LLMの体験を創出できるのか、これからもWatchし続けていきたいです。
アバター
イベント概要 2024年9月18日に「GENBA #4 〜データサイエンティストの現場〜」と題してタイミー、ビットキー、AbemaTVの3社でデータサイエンスに関する合同勉強会を開催しました。 今回はそちらの勉強会からタイミーのデータサイエンティストである小関さん(@ozeshun_)の発表をイベントレポート形式でお伝えします。 自己紹介 まず、自己紹介をさせていただきます。私は2022年にタイミーにデータサイエンティストとして入社し、現在は機械学習モデルの改善、機械学習パイプラインの構築、そして推薦API基盤の運用を担当しています。最近では、検索機能にも携わっており、Elasticsearchのキャッチアップしながら頑張っています。趣味は野球で、ロッテやレンジャースの試合を観るのが好きです。 本日は、以下のような内容でお話ししようと思います。まず最初に、タイミーのサービス紹介を簡単に行います。その後、私が担当しているレコメンド機能について、A/Bテストをどのように運用しているのか、具体的なアーキテクチャやプロセスを説明します。また、A/Bテストを実施する際の指標のモニタリング方法についても触れ、その結果をどのように管理しているのかを順を追ってご紹介していきます。 サービス紹介 前提となる部分なんですが、タイミーはなんぞやというと働きたい時間と働いてほしい時間をマッチングする「スキマバイト」サービスです。 アプリを開くと仕事のリストが表示され、ワーカーは気軽に仕事を申し込むことができます。一方、クライアント側も求人情報を簡単に掲載でき、効率的に働き手を見つけることが可能です。 レコメンド機能におけるABテスト レコメンド機能におけるA/Bテストについて説明しますが、まずはタイミーのアプリ上でこのレコメンド機能がどのように実装されているのか、そしてどこを注視すべきかをお話しします。 タイミーのレコメンド機能は、アプリを開いた際に、トップ画面の「探す」タブや「お気に入り」タブに表示される「あなたのおすすめの仕事」バナーをタップすると、パーソナライズされた仕事のリストが表示される仕組みです。たとえば、探すタブの真ん中あたりにあるバナーや、お気に入りタブの上部に表示されるバナーをタップすると、あなたにおすすめの仕事が一覧として表示されます。このように、ユーザーごとにパーソナライズされた結果として仕事が表示される形で実装されています。 ユーザーが「あなたのおすすめのお仕事」画面でお気に入り登録を行うと、推薦がうまく機能していると判断でき、その後の申し込みに繋がれば、さらに効果的だったと見なせます。このように、推薦機能がどの程度成功しているかを確認するために、まずはお気に入り登録、その次に申し込みの動向を追っています。 レコメンド機能におけるA/Bテストについては、非常にシンプルな方法を取っています。基本的に、既存のアルゴリズムと新しいアルゴリズムを対決させる形でテストを行っています。導入初期では、レコメンド機能を表示するグループと非表示のグループで比較した結果、表示グループのKPIが圧倒的に良かったため、レコメンド機能の導入を決定しました。 現在では、既存アルゴリズムと新規アルゴリズムを日々比較し、どちらがより効果的かを評価しながら運用しています。 タイミーのレコメンド機能では、オフラインでの検証からオンラインにデプロイするまでのプロセスが非常に迅速である点が特徴です。このスピード感のある運用についても、具体的にどのように実施しているのか、説明していきたいと思います。 最後に、A/Bテストで計測している指標についてお話しします。先ほども少し触れましたが、まず重要なのは「お気に入り数」です。レコメンド機能を通じてお気に入りに登録される数が増えると、アルゴリズムが正しく機能していると判断できます。これが先行指標として、アルゴリズムの改善を測る基準になります。 次に遅行指標として、申し込み数も重要です。レコメンド画面から直接申し込む場合もありますし、レコメンド経由でお気に入りに追加した後に通知を受け取って申し込むパターンもあります。このように、申し込み数が遅れて増えることを確認できると、さらに効果的な結果となります。 また、全体の指標も見ていく必要があります。お気に入り数については、レコメンド経由のお気に入り数とそれ以外のお気に入り数の合計で構成されています。ただし、レコメンド経由のお気に入り数が増えたとしても、それ以外の部分が変わらないというわけではなく、時には減少することもあります。特に申し込みに関しては、ワーカーの時間が限られており、タイミーのアプリでは1日に1回しか働けないため、レコメンド経由での申し込みが増えると、それ以外の申し込みが減少するというトレードオフが発生することがあります。こうした点を注意深くモニタリングしています。 なお、他にもマッチングスピードなども指標として確認していたりなど、さまざまな指標を計測しながら運用を続けています。 ABテストの設定方法 A/Bテストの設定方法についてですが、タイミーでは非常に高速にデプロイできる仕組みが整っていますので、そのアーキテクチャについて説明します。 レコメンドシステムのアーキテクチャを簡単にご紹介します。左側がアプリ本体の基盤がある場所で、これはAWS上に構築されています。一方で、私が所属しているデータエンジニアリング部が主に操作するデータ基盤は、Google Cloud側にあります。 つまり、アプリの基盤はAWS、データの基盤はGoogle Cloudといった形で役割分担がされています。私たちが管理する部分は基本的にGoogle Cloud側が多いため、特に高速なデプロイが必要なときには、Google Cloud上にサービスがあると非常に効率的です。この点を念頭に置いた設計を採用しています。 次に、AWS上にあるRails APIがタイミー本体のAPIとなります。これに対して、右側のCloud Run上に構築したアイテム推薦APIがリクエストを受け取り、「推薦アイテムを返して」とリクエストが来ると、ロードバランサーを経由してその結果を返すといった構成になっています。 A/Bテストの振り分けについては、先ほども述べた通り、Google Cloud側で管理しています。Google Cloud上のアイテム推薦API内でA/Bテストの振り分けがすぐに設定できるような設計になっており、柔軟で迅速に対応可能なシステムとなっています。 実際にどのようにA/Bテストを設定しているかというと、とてもシンプルで、すべてYAMLファイルで管理しています。 具体的には、YAMLファイルの中にA/Bテストの設定を書き込んでいます。たとえば、アルゴリズムAとBを5対5で割り振るようなA/Bテストを設定する場合、durationをstart_atとend_atで指定し、A/Bテストに使用するキー名も設定します。その下には、variantsとしてアルゴリズム名を指定し、アルゴリズムAに対してウェイトを0.5、アルゴリズムBにも同様に0.5を割り振ります。また、表示するグループ(display)と非表示グループ(hidden)に対する割り振りもここで指定します。たとえば、表示群と非表示群に5対5で割り振るように設定することができます。 このYAMLファイルを記述するだけで、あとは推薦結果を正しく出力できるMLパイプラインが整備されていれば、すぐにA/Bテストが開始できる状態になります。 ABテストで計測する指標のモニタリング A/Bテストで計測する指標のモニタリングについてですが、弊社ではGoogle Cloud上のデータ基盤を活用し、Looker Studioを使ってさまざまな指標を可視化しています。具体的には、毎日モニタリングが必要な指標をグラフとして可視化し、日々確認しています。たとえば、テスト群とコントロール群で「お気に入りクリック数」を比較する場合、グラフ上で緑の線(テスト群)が上にくると良い結果と判断できます。 こういった重要なグラフは、Slackで毎日通知されるように設定しており、A/Bテストの結果をリアルタイムで確認できる体制を整えています。また、週に2回ほど、可視化された指標をもとに同期的にレビューを行い、モデルが意図通りに機能しているか、次にどのようなアクションを取るべきかなどを議論しています。 DWH(データウェアハウス)やデータマートのモニタリングについても、かなりしっかりと構築しています。まず、データレイク層にはアプリの生ログやレコメンドAPI側のログが保存されています。これらのログを直接スキャンすると、スキャン量が非常に大きくなってしまいます。皆さんもご存じの通り、これを毎回BIツールで呼び出すのは非常にコストがかかるため、データマートとデータウェアハウスを効率的に設計しています。 私が所属するデータサイエンスグループでは、これらのシステムのバッチ実行部分もしっかりと構築しています。毎日、ビジネスKPIやランキングメトリクスといった指標を集計し、それをデータマートに格納することで、BIツールで簡単に参照できるような仕組みを作っています。 現在、バッチツールとしてはCloud Composerを使用していますが、今後はdbtへの移行も検討しています。このように、効率的なデータ処理とモニタリング環境を日々改善しています。 ABテストの仮説と分析結果の管理方法 A/Bテストの仮説と分析結果の管理について説明します。私たちのチームでは、A/Bテストの仮説と結果をすべてNotionのデータベースで一元管理しています。アナリストチームがプロダクト全体で利用できるデータベースを構築しており、それを活用しています。 ドキュメントにはまず簡単にテスト内容をまとめ、その後「確かめたいこと」を記載します。これにより、テスト後に検証すべき内容が明確になり、スムーズに分析を進められます。次に「仕様」も詳細に記載し、他のメンバーが振り返りやすいようにしています。 A/Bテストの設計では、比較手法としてA/Bテストを行うか、因果推論的手法を使うかを事前に決めます。サンプルサイズも事前にシミュレーションし、実験期間を見積もりますが、期間が長すぎる場合はA/Bテストをやめて全ユーザーに公開し、因果推論的手法を使うこともあります。 メトリクスでは「お気に入り数」など重要な指標を設定し、ガードレールメトリクスも活用します。また、テストの条件とアクションを事前に設定することで、実験の目的を明確にしています。 テスト終了時にはドキュメントに結果と考察を書き込み、次の疑問や検証すべき内容を整理して次のステップに繋げるようにしています。 最後に、仮説の立て方についての具体例をお伝えしておきます。私たちは、確認したいポイントに対して「こう考えているので、この特徴量を加える」といった形で仮説を立てています。そして、その仮説に基づいて、意図通りに動いているかを分析します。また、仕様の部分には、これまでのアルゴリズムが抱えていた課題や、新しいアルゴリズムで行った変更を記載しておき、仮説に対応させることで、後からの検証がスムーズに進むようにしています。これらは、仮説検証を効率よく行うための例として紹介させていただきました。 まとめ 本日はタイミーのレコメンド機能におけるA/Bテストの運用についてお話しさせていただきました。まず初めにA/Bテストの概要をご説明し、次に、A/Bテストを素早く開始できる仕組みや、モニタリング体制についてお話ししました。最後に、A/Bテストの分析結果の管理方法や活用事例を紹介させていただきました。 本日はありがとうございました! お知らせ 現在、タイミーでは、データサイエンスやエンジニアリングの分野で、共に成長し、革新を推し進めてくれる新たなチームメンバーを積極的に探しています! また、気軽な雰囲気での カジュアル面談 も随時行っておりますので、ぜひお気軽にエントリーしてください。↓ product-recruit.timee.co.jp hrmos.co hrmos.co
アバター
こんにちは、タイミーのデータエンジニアリング部 データサイエンス(以下DS)グループ所属のYukitomoです。 DSグループではMLパイプラインとしてVertex AI Pipelinesを 利用 しており、その開発環境の継続的な効率化を進めていますが、今回はここ最近の変更点を紹介したいと思います! Vertex AI PipelinesについてはGoogle Cloudの 公式ページ や、 前回の記事 を参照ください。 モノレポ環境に移行 当初はパイプライン毎にレポジトリを用意していました。しかしながら新規でパイプラインを起こす度にレポジトリの作成から行うのは、 ちょっとした“作業”ではあるのですが気軽に行うには少し腰が重い cookie cutter を使って初期状態を揃えたり 前回の記事 のような標準化を行っても、異なるレポジトリを異なる開発者によりメンテナンスを行うと、それぞれが独自の進化をしたり属人化したりしがち といった問題を抱えていました。そこで、モノレポ化して1箇所に集積し、 CI/CDや、パイプラインビルド等の付随スクリプトを共用化、付随スクリプトのような非本質的な機能開発を効率化 フォルダ構成やファイルの命名規則などを統一、“隣のプロジェクト”を参照しやすくすることで、ベストプラクティスを共有化、パイプラインの機能そのものの開発を効率化 することを目指しました。同一レポジトリ傘下に収めることで従来よりも敷居が下がり、共通知化を進められていると感じています。 コンテナイメージの共通化 パイプラインを1箇所のレポジトリに集めた段階でパイプラインのコンポーネントは200個以上あり、パイプラインのコンポーネントそれぞれが独自にDockerfileやpyproject.tomlを持っていました。脆弱性対応や機能追加のための依存モジュールのアップデートはそれぞれのpyproject.tomlを更新することになりますが、ファイルの数が多いと更新に手間がかかってしまいます。そこで、同一パイプラインのコンポーネント間ではコンテナイメージを共用できるような形にアーキテクチャを改めました。 おおまかな方針は以下のとおりです。 コンポーネントの入出力を定義するyamlファイル(component.yaml)はそれぞれ名前を変え、1つのフォルダにまとめる。 コンポーネントの中のロジックを記述するpython コードも1箇所にまとめ、全体をコンテナイメージに複製。 単一コンテナだけでは処理しきれない場合を考慮し、複数のコンテナイメージを格納できるようコンテナイメージ用のフォルダは階層化。 従来のアーキテクチャ % tree PIPELINE_X ./PIPELINE_X ├── components │   ├── component_A # コンポーネント毎にフォルダを用意し、 │   │   ├── Dockerfile # Dockerfile/pyproject.tomlはそれぞれ独立に配置 │   │   ├── component.yaml │   │   ├── pyproject.toml │   │   └── src │   │      └── ... │   ├── component_B │   │   ├── Dockerfile │   │   ├── component.yaml │   │   ├── pyproject.toml │   │   ├── src │   │      └── ... │   └── component_C │   ├── Dockerfile │   ├── component.yaml │   ├── pyproject.toml │   ├── src │      └── ... └── pipelines    └── main.py 新しいアーキテクチャ % tree ./PIPELINE_X ./PIPELINE_X ├── components │   ├── definitions # 1. component.yamlは1箇所に集約 │   │   ├── component_a.yaml │   │   ├── component_b.yaml │   │   └── component_c.yaml │   └── src # 2. src以下全てをコンテナイメージに複製. │      ├── component_a.py # component_*.yamlの設定で動作するpython fileを指定 │      ├── component_b.py # DockerfileのCMDを指定するイメージ │      └── component_c.py │ ├── containers # 3. 単一コンテナでは難しい場合に備え │   └── main # 複数のコンテナを利用できるようフォルダを階層化 │   ├── Dockerfile │   └── pyproject.toml └── pipelines    └── main.py コンテナ数を減らすことで、 dependabot の運用は格段に楽になりました。コンテナが減ることで警告の数も下がり、警告の数が下がることで更新の初動も取りやすくなるという好循環のおかげで、2024年10月現在、dependapotからの警告は画面のスクロールが不要な範囲にはおさまるようになってきました。 また、コンテナを集約する段階で気がついたのですが、いくつかのDockerfileの中で利用するpoetryのバージョンが古いままだったり、マルチステージのビルドが正しく行われていなかったりするものも少なからずありました。Dockerfileに限らずコードライティング全般に言えることですが、記述量は可能な限り少なくする方が、このような小さな不具合の発生は抑制でき、安定したコードを供給できます。 CDにおけるビルドキャッシュの利用 タイミーDSグループにおいて、CI/CD環境は GitHub Actions 、クラウド環境は Google Cloud を利用しています。指定されたトリガー条件が発生した時にコンテナイメージをビルドするのですが、GitHub Actionsの場合、ジョブ単位でVMが異なるため、連続するGitHub Actionsの実行の場合でもdockerのビルドは一からやり直しになってしまいます。 そこで、 こちら や こちら の内容を参考に、ビルド結果をVMの外部、Artifact Registryにキャッシュ、次回実行時に再利用することでCI/CD の処理を高速化させました。なお、以下の設定ではbuildcacheは通常のコンテナ用Artifact Registry(下のコードで言うと${IMAGE_NAME})とは異なるRegistry( ${IMAGE_NAME}-buildcache )に保存しています。 GitHub Actions内での記述より抜粋 docker buildx create --name container --driver=docker-container docker buildx build \ --builder=container \ : --cache-from=type=registry,ref=${IMAGE_NAME}-buildcache \ --cache-to=type=registry,ref=${IMAGE_NAME}-buildcache,mode=max \ -t ${IMAGE_NAME}:${IMAGE_TAG} \ --load . docker push ${IMAGE_NAME}:${IMAGE_TAG} # -cache-from/-cache-toに指定するrefの値にsuffix '-buildcache'を付加し # 本来のイメージとキャッシュイメージの置き場所を分離しています。 パイプライン命名規則の工夫 MLパイプラインを開発していると、 あるパイプラインを少しだけ変えたパイプラインを実現したい Gitの別ブランチで管理すればいいんだけれど、比較しながらコード書きたい といったケースはよくあると思います。簡単にこれを実現しようと cp -r でパイプラインを丸ごとコピーしたとしても、従来のアーキテクチャでは様々な設定(パイプラインの名前、参照するコンテナイメージの名前、バージョン情報)を書き換える必要があります。そのため、煩わしい作業が発生していました。また、それらの設定方法も統一が取れておらず、“微妙に”パイプライン毎に異なっていました。そこで、それらのバージョン情報以外の情報を全てパイプラインが保存されているフォルダのパス情報から取得するよう統一し、 cp -r だけですぐにパイプラインの亜種が作成できるようにしました。 従来のアーキテクチャ # 1. パイプラインの名前はパイプラインのpyproject.toml内のname属性や環境変数(dotenv)を利用 # 2. コンテナイメージの名前は コンテナイメージのpyproject.toml内のname属性を利用 # 3. Version情報: # パイプライン -> パイプラインのpyproject.toml内のversionを利用 # コンテナイメージ -> コンテナイメージのpyproject.toml内のversionを利用 . PIPELINE_X ├── components : │ ├── containers │   └── main │   ├── Dockerfile │   └── pyproject.toml # 2 [tool.poetry].name -> パイプラインの名前 │ # 3 [tool.poetry].version -> パイプラインのversion ├── pipelines │   └── main.py ├── .env.* (prod/stg/dev..) # 1 パイプラインの名前は .envからやpyproject.tomlなど └── pyproject.toml # 各種のやり方が存在。 # 3 [tool.poetry].version -> コンテナイメージのversion # Compile されたpipeline config抜粋 { : "deploymentSpec" : { "executors" : { "exec-comp_a" : { "container" : { : "image" : "${GAR_REGISTRY_PREFIX}/blahblahblah:vX.Y.Z" # 2. コンテナイメージの名前"blahblahblah"はコンテナイメージのpyproject.tomlより # 3. コンテナイメージのVersion "vX.Y.Z"はコンテナイメージのpyproject.tomlより }..}..}..}, : "pipelineInfo" : { "name" : "arbitrary_string" # 1. パイプラインの名前は }, # 注: ${GAR_REGISTRY_PREFIX} は Artifact Registry のアドレス 新しいアーキテクチャ # 1. パイプラインの名前はRepository内のフォルダ位置を利用 (= ${SERVICE}-${PIPELINE} ) # 2. コンテナイメージの名前は "${パイプラインの名前}"-"${コンテナのフォルダ名}" # 3. Version情報: # パイプライン -> パイプラインのpyproject.toml内のversionを利用 # コンテナイメージ -> パイプラインのversionを利用 SERVICE/**/vertex_ai_pipelines/PIPELINE_X # 1  ├── components : │ ├── containers │   └── main # 2 │   ├── Dockerfile │   └── pyproject.toml ├── pipelines │   └── main.py ├── pyproject.toml └── .env.* (prod/stg/dev..) # Compile されたpipeline configの一部を抜粋 { : "deploymentSpec" : { "executors" : { "exec-comp_a" : { "container" : { : "image" : "${GAR_REGISTRY_PREFIX}/${SERVICE}-${PIPELINE_X}-main:vX.Y.Z" }..}..}..}, : "pipelineInfo" : { "name" : "${SERVICE}-${PIPELINE_X}" }, : # 注: ${GAR_REGISTRY_PREFIX} は Artifact Registry のアドレス ちょっとした変更ではあるのですが、新しいパイプラインを構築する際の初動を早くすること、また簡単にできることにより、新しい方式を試そうという心理的な敷居を下げることができていると考えています。 今回紹介した取り組み以外にも、Vertex AI Pipelinesに限らず効率化するための具体的なアイデアはいくつかあるのですが、プロダクションを動かしながら変更しており、障害の発生を抑えるためにも、一度に大きな変更は与えずステップを踏みながらMLOps基盤を理想の姿に近づける活動を続けています。 We’re Hiring! タイミーのデータエンジニアリング部・データアナリティクス部では、ともに働くメンバーを募集しています!! 現在募集中のポジションは こちら です! 「話を聞きたい」と思われた方は、是非一度 カジュアル面談 でお話ししましょう!
アバター
みなさんこんにちは。タイミーのデータエンジニアリング部 データサイエンスグループ所属の菊地と小関です。 2024年9月27日(金)、28日(土)に開催された PyCon JP 2024 に参加してきました。今回はPyCon JP 2024の雰囲気と、特に興味深かった&勉強になったトークセッションをいくつかピックアップしてお届けしようと思います! PyCon JPとは PyConJPは1年に1度開催されていて、今年はTOC有明コンベンションホールにて9月27日(金)、28日(土)の2日間にわたって開催されました。 概要については、 PyCon JP 2024 の「What is PyCon JP」をそのまま引用させていただきます。 PyCon JP は、Python ユーザーが集まり、Python や Python を使ったソフトウェアについて情報交換、交流をするためのカンファレンスです。 PyCon JP の開催を通じて、Python の使い手が一堂に集まり、Python にまつわる様々な分野の知識や情報を交換し、新たな友達やコミュニティとのつながり、仕事やビジネスチャンスを増やせる場所とすることが目標です。 当日のタイムテーブルは こちら を参照いただければと思います。 PyCon JPの雰囲気 今年の参加者数は600名超で、日本国内のみならず、海外からの参加者も多く、非常に活気がありました。昼食についても様々な文化圏の方に配慮して用意されていた印象です。 また、企業ブースも多数出展しており、トークセッションで気になった企業様の発表内容を更に詳しく伺うことができました。スタンプラリーや書籍販売、Pythonに関する求人が掲載されている等、ビジネスチャンスを増やせるよう、非常に配慮された設計だったと感じています。 個人(菊地)的には、企業ブースにいたGrooveX社の「 LOVOT 」がとてもかわいかったです(写真を撮っていないのが心残りです…)。 特に興味深かった、勉強になったトークセッション まず、小関が興味深かった&勉強になったトークセッションを紹介します。 FastAPIでのasync defとdefの使い分け speakerdeck.com このセッションでは、タイトル通りのFastAPIにおける実装方法の使い分けを、Pythonにおける並列処理・並行処理・非同期処理の性質を説明した上で解説しており、非常にわかりやすかったです。 要点は以下の通りでした。 マルチスレッド、非同期処理ともにI/Oバウンドな処理の場合に高速化が期待できるが、マルチスレッドはスレッドが増えるとスレッド切り替え分だけ処理速度が低下してしまう つまり、スレッドが多くなるような処理だと非同期処理の方が処理速度が速くなる場合がある Fast APIにおける def と async def の違いと使い分け def 並行処理 (マルチスレッド) 同期処理を行いたい場合はこちらを使う async def 非同期処理 マルチスレッドだとスレッドの切り替え時間がかさむぐらいのリクエスト量をさばきたい時に有効 低コストで実現する社内文書RAG機能を搭載したAIチャットボット開発 speakerdeck.com このセッションでは、RAG機能を用いたAIチャットボットの開発について、主に開発前の実装スコープの決め方や実装方法の詳細の観点からお話しされていました。 開発前にpros & consを言語化して、目的にあったHowとして今回のRAG + Chat botを選択した過程がわかりやすくまとまっていたり、システムのアーキテクチャ自体も丁寧に説明されていたりしたので、今すぐにでも社内で試せそうな感じでした。 弊社だとNotionのドキュメント量が膨大なので、indexingするドキュメントの選定基準をどう決めたのかが特に気になりました。Notionのページ階層が綺麗にまとまっている組織だとindexingする対象を選択する時の手助けになるのかなとも思いました。 LlamaIndex は未履修なので、近々個人で触ってみようと思います! 次に、菊地が興味深かった&勉強になったトークセッションをピックアップして紹介します。 PandasAI:生成AIがデータ分析業務にもたらすパラダイムシフト speakerdeck.com このセッションでは、機械学習・生成AI・データ分析の基礎の基礎を説明した後に、 PandasAI について紹介しています。 PandasAI とは、自然言語でデータ分析をしたり可視化を行ったりできるデータ分析のためのAIエージェントで、OSSとして公開されています。OpenAIのAPIキーがあれば使用できるとのことです。 また、SaaSとしても提供しており、登録・課金を行えばAPIキーがなくとも使用できるそうです。 データコネクタとしては、CSVやParquet, XLSX等はもちろん、PostgreSQL, MySQL, BigQuery, Databricks, Snowflakeといった各種クラウドサービスのデータソースへ接続できます。 内部の処理としては、自然言語をPythonコードやクエリに変換してデータを操作し、結果をユーザーへ返却しており、悪意のあるコードの実行に対する制御もできるそうです(ホワイトリストとして特定のモジュールのみ実行可能にする等)。 OSSとして提供されているので一度試してみたいなと思っているのですが、弊社でも導入しているLookerやその他BIツールに搭載されているAI機能とどのように差別化していくのかは気になりました。 データサイエンスのフルサイクル開発を実現する機械学習パイプライン speakerdeck.com このセッションでは、まずCARTA MARKETING FIRM社におけるデータサイエンティスト像「フルサイクルデータサイエンティスト」について紹介し、類似概念「フルスタック」との違いを説明しています。 その上で、理想状態とのギャップを課題として整理し、データサイエンティストがより本質的な価値創出ができる状態に向けて、これまでのデータ分析基盤の歴史(luigi → Amazon SageMaker → Prefect)が紹介されています。 タイミーのデータサイエンティストも「フルサイクルデータサイエンティスト」に近い働き方をしている点や、ML基盤の歴史が似通っていたり、チームの規模感や構成が近いなど、多くの共通点がありました。「わかる〜」と心の中でうなずきながら、セッションを聴かせていただいておりました。 あえてApache AirflowではなくPrefectを選定した経緯などは、機会があればぜひ伺ってみたいなと思いました。 終わりに いかがでしたか?PyCon JP 2024の雰囲気が少しでも伝わっていますと幸いです。次回は広島開催とのことで、ぜひ来年も参加させていただきたいなと思っています。 ちなみに、タイミーには「 TDE10 」という、プロダクト開発やデータ職種メンバーを対象とした成長支援制度があります。今回は「世界中で開催されているすべての技術カンファレンスに無制限で参加できる『 KaigiPass 』」を利用してPyConJPに参加しました。 今後もこのような機会があれば、積極的に技術カンファレンスに参加していきたいと考えておりますし、外部登壇も積極的に行っていきたいです。 We’re hiring!!! 現在、タイミーでは、データサイエンスやエンジニアリングの分野で、共に成長し、革新を推し進めてくれる新たなチームメンバーを積極的に探しています! また、気軽な雰囲気での カジュアル面談 も随時行っておりますので、ぜひお気軽にエントリーしてください。↓ product-recruit.timee.co.jp hrmos.co hrmos.co hrmos.co hrmos.co
アバター