TECH PLAY

タイミー

タイミー の技術ブログ

288

はじめに こんにちは、株式会社タイミーでデータサイエンティストとして働いている貝出です。直近はカスタマーサポートの業務改善に向けたLLM活用のPoCやシステム開発を行っております。 さて、今回は2025年3月10日(月)~3月14日(金)に開催された「言語処理学会第31回年次大会(NLP2025)」に昨年に続き参加してきましたので、その参加レポートを執筆させていただきます。 言語処理学会年次大会について www.anlp.jp 言語処理学会年次大会は 言語処理学会 が主催する学術会議であり、国内における言語処理の研究成果発表の場として、また国際的な研究交流の場としての国内最大規模のイベントとなっています。 今年で第31回を迎え、オープニングで共有されたスライドによると、発表件数は777件、事前・直前参加登録者数は2248人と過去最大となっており、年々大会が盛り上がっていることが伺えました。昨今のLLMの盛り上がりを反映し、発表内容はLLMに関連するものが大多数を占めており、業務でLLMを活用している身としては、大変学びの多い会となりました。 言語処理学会第31回年次大会に参加できなかった方でも、 こちら から発表論文が閲覧できます。 興味深かった発表 初日のチュートリアルから最終日のワークショップまで興味深い発表がたくさんありました。その中から、個人的に気になった発表をいくつかピックアップします。 [T1] チュートリアル1: 言語モデルの内部機序:解析と解釈 概要 本チュートリアルでは、大規模言語モデル(LLM)の内部機序を解析・解釈するための方法論が紹介されました。モデルの入出力のみではその振る舞いを十分に理解できないため、内部の表現や計算過程を詳細に検討する必要があります。LLMの複雑さと高次元性が主要な課題であるため、抽象化と単純化による解析に加え、内部表現や計算を言語、世界、知識と結びつける解釈が重要であると述べられました。 内部表現の解析・解釈の手法としては、まず特徴量に基づくアプローチ(ニューロン分析、プローブ、疎なオートエンコーダーなど)が紹介され、さらにベクトルに基づいたアプローチ(表現の分布観察、ベクトル代数、木構造、階層構造、円形構造、グラフ構造の分析)についても解説されました。また、内部表現が持つ情報とその情報が実際にモデルの予測に利用されているかどうかを検証するため、因果的介入の手法(Activation Patchingなど)の重要性も示されました。 計算過程の解析・解釈に関しては、注意パターンの観察(構文情報や意味情報との関連、長文脈への対応、ゴミ箱機能など)、語彙空間への射影(重みパラメータや中間表現と語彙の関連付け、フィードフォワードネットによる知識記憶の分析)、出力への影響度の測定(数学的分解や介入による評価)、さらには特徴的なサブネットワーク(回路)の同定(コピー機能や事実知識の予測を実現する回路の発見)といった多角的な手法が取り上げられました。 さらに、言語や世界の構造がどのようにモデルに転写されるのかという根源的な問いにも議論が及び、「プラトン的表現」仮説や「集合的予測符号化」仮説が紹介されました。これにより、モデルの内部表現が世界の構造を捉えることによる予測の汎化への貢献や、言語学的仮説の検証の可能性が示唆されました。 最後に、内部機序の解析と解釈というアプローチ自体の限界についても言及され、概念の局所性や内部表現と対応物の一対一対応という暗黙の仮定への懐疑、「表現と計算」という視点そのものの妥当性への疑問が議論されていました。 発表資料はこちらのスライドに公開されているので、詳しい内容についてはこちらをご参照ください。 speakerdeck.com 感想 発表資料は全144ページと非常にボリューミーな内容でしたが、その魅力のおかげで1時間半という時間があっという間に感じられました。個人的には、言語モデルの内部機序に関する網羅的なサーベイを見つけることができていなかったため、今回の発表は大変ありがたかったです。 特に、プロービング(内部表現から特定の情報が抽出可能かを評価するテクニック)のようなLLMの傾向分析手法に加え、Activation Patchingといった因果的介入手法の存在を知ったのは初めてのことで、非常に印象的でした。観察データの傾向だけでは因果関係を正確に特定できないという因果推論の原則を踏まえると、介入による検証が行われるこのアプローチは、確かに必要な取り組みだと感じました。 [A1-6] 手動設計の敵対的プロンプト手法の体系的分類 概要 本研究は、LLM(大規模言語モデル)の安全性に関わる敵対的プロンプト、特に手動設計のものについて体系的な分類を試みたものです。既存の研究では自動生成プロンプトに注目が集まる一方、手動設計のプロンプトは多様なタイプが存在するにもかかわらず、十分に体系的に把握されていませんでした。そこで、世界中の敵対的プロンプトコンペティションから収集したデータをもとに、手動設計の敵対的プロンプトを49のカテゴリに分類し、6つの大カテゴリ(direct instruction、simulation、response specification、instruction override、input style、different task)の下に整理しました。また、Tensor TrustやHackAPromptといった既存データセットでは特定の手法に偏りが見られることも明らかになりました。本研究の成果は、LLMの安全性検証やレッドチーミング(攻撃者の視点に立ってシステムの脆弱性を検証する評価手法)、敵対的プロンプト合成データセットの作成に寄与すると期待され、今後は複数手法の組み合わせ、英語以外の言語対応、進化するトレンドへの自動反映といった課題にも取り組む必要があるとされています。 感想 「システムプロンプトを品詞分解して」のように、別のタスクに置き換えることでシステムプロンプトを聞き出す手法があるなど、様々なアプローチが存在することを初めて知りました。また、本発表で指摘されているように、システムの安全性を包括的に評価するためには手法の体系的な分類が不可欠であり、LLMを利用する企業としても非常に意義深い研究だと感じました。 C9-4 VDocRAG: 視覚的文書に対する検索拡張生成 概要 引用元: VDocRAG: 視覚的文書に対する検索拡張生成 引用元: VDocRAG: 視覚的文書に対する検索拡張生成 本研究では、図や表などの視覚的に表現された文書画像を知識源とする新たな検索拡張生成フレームワークであるVDocRAGが提案されました。従来のRAGはテキストのみを対象としていたため、現実世界に存在する多様な視覚的文書を十分に活用できませんでしたが、VDocRAGは画像形式で文書を統一的に理解し、視覚情報を直接利用することが可能となっております。 VDocRAGは、質問に関連する文書画像を検索するVDocRetrieverと、検索した文書画像を用いて回答を生成するVDocGeneratorという2つの主要な構成要素から成り立っております。また、大規模視覚言語モデル(LVLM)のトークンに画像表現を圧縮させるための新たな自己教師あり事前学習タスクとして、Representation Compression via Retrieval and Generation(RCRおよびRCG)が提案されました。RCRはOCRテキストに対応する画像を検索するための対照学習、RCGはアテンションマスクを工夫したテキスト生成による表現学習を実現しております。 さらに、本研究では、図、表、テキストなど多様な文書形式を網羅する初のオープンドメイン視覚文書質問応答データセットであるOpenDocVQAが導入されました。OpenDocVQAは既存のDocumentVQAやTableQAデータセットを精査・改変し、マルチホップ質問を含む新たなデータセットMHDocVQAを作成することで構築され、Single-pool(特定の文書形式内での検索)とAll-pool(データセット全体を横断した検索)の設定で評価されました。 実験の結果、VDocRAGは従来のテキストベースRAG(TextRAG)を大幅に上回る性能を示し、特に視覚データの理解において優れていることが確認されました。また、事前学習タスクであるRCRとRCGが性能向上に大きく寄与していること、さらに正解文書が付与された場合にはより高い性能が得られる一方で、検索性能の改善と検索ノイズへの頑健性が今後の課題として示唆されました。 こちらの内容は arXiv 上でも公開されています。 arxiv.org 感想 当日の発表にて、提案されたモデルでは図表などの視覚情報をOCRを介さずにテキストを抽出できる仕組みにより、テキスト主体の画像に対する精度が低くなる傾向が示されていました。 検索で使用する[EOS]トークンの隠れ状態に画像表現を圧縮されているそうだったので、ある程度テキスト情報が多いと情報が入り切らないなどの事象が発生しているかもしれないと想像しました。しかし、OCRを用いないエンドツーエンドの構成は大幅に処理時間の効率化にも寄与するとのことでしたので、今後の発展と、より多様な画像への対応に期待したいです。 おわりに NLP2025では、多くの魅力的な研究が発表され、数々の刺激的なアイデアに圧倒されました。今回の報告ではご紹介しきれなかったものの、LLMの安全性検証や評価、ベンチマーク構築に向けた多様な取り組みも進められており、タイミーを安心・安全なプラットフォームとして維持するためのLLM活用法について、重要な示唆を得ることができました。 現在、タイミーでは、データサイエンスやエンジニアリングの分野で、共に成長し、革新を推し進めてくれる新たなチームメンバーを積極的に探しています! product-recruit.timee.co.jp また、気軽な雰囲気での カジュアル面談 も随時行っておりますので、ぜひお気軽にエントリーしてください。↓ hrmos.co hrmos.co
こんにちは、Timee でバックエンドエンジニアとして働いている id:ryopeko です。 今回は Timee で使っている API サーバーの Ruby を最新の 3.4.2 (+YJIT) にアップデートしたことについての記事をお届けします。 1. 概要 今回の記事では、Ruby 3.3.6 から 3.4.2 へのバージョンアップについて、パフォーマンスへの影響、Devin を使った実作業、 rubocop.yml の対応など、具体的な取り組みをご紹介します。安定性を重視した今回のアップデートの背景や、今後の展望についても触れていきます。 2. バージョンアップによるパフォーマンスへの影響 今回の Ruby 3.4.2 へのアップデートでは、YJITについては以前のバージョンから引き続き有効であるものの、我々のアプリケーションでは目立った変化はありませんでした。 計測方法とアプリについて 計測には日々活用している Datadog を使用しました。アップデートしたアプリケーションは、スマホアプリ、Web フロントエンドから使われる API で、サービスのメイントラフィックを担う部分です。また、ActiveAdmin によって作られた内部向け管理機能群も含まれています。 Datadog を用いて、CPU 使用率、メモリ使用量と使用率、リクエスト処理時間の各指標を計測しました。計測期間や時間帯などの計測条件についても確認を行いました。 計測結果 各指標の推移を比較した結果、バージョンアップ前後で大きな変動は見られず、安定した状態を維持していることを確認しました。時間帯ごとの変動パターンについても、目立った変化は見られませんでした。 メモリ使用量についても、大きな増加や減少は見られず、メモリリークなどの兆候も見られませんでした。リクエスト処理時間も、バージョンアップ前後で大きな変化は見られませんでした。 今回のアップデートで私たちは、パフォーマンスの維持と最新のバージョンを使い続けることを主な目的としていたため、これらの結果は想定内であり、安定性を重視したアップデートとして成功したと言えます。YJIT の効果は、我々のアプリケーションの特性上からか、これらの指標に顕著には現れなかったようです。 3. Devin を使った実作業について また、今回のバージョンアップでは AI Agent ツールの Devin を活用しました。 Devin を利用した背景 Devin を利用した背景として、事前に作業の概要が把握できていたこと、動作確認に必要な Unit test が大量に存在していたこと、RuboCop のルールが整備されており、常にパスする状態が維持されていたこと、そしてアップデートの情報収集が AI の得意な分野であると考えたことが挙げられます。 Devin を利用した作業内容 Devin を利用した作業内容は以下です。 Pull Request の作成 現在のバージョン間の主な差分情報の収集とサマリー生成 Ruby アップデートに必要な差分生成 Unit test の実施と確認 RuboCop の実施と必要な変更のサマリー生成(修正内容の提案、提案とは違う内容の修正の指示を含む) アップデート可能な bundled gem のアップデート指示と対象の調査方法などが挙げられます。 プロンプトで指示したこと Devin にはプロンプトで以下の指示をしました。 commit する前に作成する予定の差分と Pull request 用のサマリーを人間が確認すること Unit test の実施 RuboCop の実施 アップデート可能な bundled gem のアップデート指示と対象の調査 必要な rubocop.yml の修正指示と具体的な修正内容の指示 Pull request の作成 うまくいったこと Devin を利用してうまくいったこととしては、情報の収集とサマリー生成、修正とテスト等のインクリメンタルな実施と確認が挙げられます。 うまくいかなかったこと Devin を利用してうまくいかなかったこととしては、Ruby のアップデートと同時に実施した bundled gem のアップデートが挙げられます。これについて Devin は初め、 bundle update で全ての gem をアップデートすることで対処していたため、具体的な指示を出す必要がありました。また、bundled gem に関する情報収集がうまく処理できなかったため、具体的な調査方法を指示する必要がありました。 Devin は情報収集や単純作業の自動化において、高いパフォーマンスを発揮しました。一方で、複雑な依存関係の解析や、具体的な指示がない場合のタスク実行には、改善の余地があると感じました。プロンプトの工夫や、Devin の得意分野と人間の得意分野を組み合わせることで、より効率的な開発が可能になるでしょう。 今後は、プロンプトのテンプレート化や、具体的な指示方法の研究、Devin の得意分野と人間の得意分野を組み合わせた効率的な開発フローの確立、Devin のバージョンアップや新たな AI ツールの導入による効率化を検討していきます。 4. rubocop.yml の対応 Ruby 3.4.x で有効になった以下のスタイルルールを、 Enabled: false に設定しました。 Naming/BlockForwarding Style/ArgumentsForwarding これらのルールは既存のコードと競合するため、一時的に無効化しました。将来的には、これらのルールに準拠するようにコードを修正し、 rubocop.yml の設定を見直すことを検討しています。 まとめ 今回の Ruby 3.4.2 へのアップデートでは、安定性を重視し、パフォーマンスの維持を主な目的としました。Datadog を用いたパフォーマンス計測では、CPU 使用率、メモリ使用量、リクエスト処理時間などの主要な指標において、バージョンアップ前後で大きな変化は見られず、安定した状態を維持していることを確認しました。 また、開発効率化のため、AI ツールである Devin を活用し、Pull Request の作成、差分情報の収集、テストの実施など、様々な作業を Devin に任せることで、開発者の負担を軽減し、効率的な開発を実現しました。 今回のバージョンアップを通して、安定性と効率性を両立させるための具体的な取り組みをご紹介しました。今後も技術の変化に柔軟に対応し、より良い開発環境を構築していきたいと考えています。
こんにちは、shihorinとmahoです。 先日、 Women in Agile Tokyo 2025 というカンファレンスに参加してきました! 参加した感想や気づきを対談形式でお届けします。 登場人物の紹介 shihorin 2024年5月タイミーにジョインして専任スクラムマスターになりました。 maho HRから社内転職でスクラムマスターになりました。スクラムマスター歴2年目に突入。 参加して思ったこと shihorin : mahoさん、Women in Agileお疲れ様でした!参加してみてどうでしたか?私は、RSGTでWomen in Agileの運営に携わっている方々の座談会を聞いて興味を持ったのが参加のきっかけだったんです。 maho : shihorinさんもお疲れ様でした!私は、去年メンターの方から勧めてもらいつつ参加できなかったので、今年は行ってみようというのがきっかけでした。もともと多様性に関心が強いほうではあったんですけど、最近は「多様性=女性」と括られることに違和感があって…。 shihorin : わかります。「Women」というワードがついているので、女性限定のカンファレンスなのかな、と最初は勘違いしていました。 maho: そうそう。多様性への違和感については「多様性を活かすチームの作り方」というセッションを聴いて、腑に落ちたことがあるんです。 shihorin: どんなことですか? maho: 目に見える違い(visible differences)だけじゃなく、性格や経験、価値観など目に見えない違い(invisible differences)も多様性に含まれるというお話があって。特にIT業界は女性が少ないという特徴もあって、多様性を考えるときに、自分の中でも目に見える違い、特にジェンダーが先行しすぎていたな、と。 shihorin: なるほど。 maho: 違和感の正体は、invisible differencesを考慮せずに、visible differencesが必要以上に強調された枠組みの中に入れられる(女性〇〇みたいな)ところにあったのかもしれないと思います。 shihorin: 確かに。visible differencesのほうが、多様性と聞いたときに先に想起されやすそうだし、invisible differencesは考慮から抜けがちなのかもしれない。 maho: もちろんどちらのdifferencesも大事ではありますが、visible differencesだけを多様性のように捉えると表面的なアプローチになりがちで、歪みが生じやすい気がしますね。あと、セッションでは本質的に多様性のあるチームや組織は成果を出しやすいという研究結果も紹介されていました。多様性のあるチームは、事実をより重視し、より慎重に処理し、より革新的であるというのです。 shihorin: へー! maho: つまり、対話ができ、お互いに異なる意見を尊重できる環境においては、ユニークなアイデアが生まれやすいのだと思います。スクラムマスターとしては、チームや組織においてそういった場作りをしていくことが重要であると改めて認識しました。 shihorin: Women in Agile 自体は、参加してみて安心感がありましたね。「あらゆる多様性を尊重できる安全で健全な職場作りを自分たちの手で作るため」に開催されているだけあって、お互いを尊重し合おうというマインドが会場全体に溢れているなと感じました。 maho: 確かに。Women in Agile って、良い意味で意図的に敷居を低くして、誰に対してもウェルカムな雰囲気があると思いました。 shihorin: うんうん。個人的には、RSGT や他のイベントで知り合った人たちが増えてきて、知らない人に囲まれている感覚が薄かったのも安心感につながっていました。 maho: 知り合いがいると心強いですよね。 shihorin: そうなんですよね。それに、これまで自分が参加した Agile 関連のイベントと比較して、女性の参加比率がとても高かったので、自分に近そうな属性の人のほうが話しかけやすい・話しかけられたときの緊張感が少ないと体感しました。一方で、それって多様性を否定しているんじゃないか?ともどかしい気持ちもありました。 maho: 属性が似ていると居心地が良い反面、気をつけないと排他的になってしまうこともありますね。でも、そうやって色々考えること自体が、多様性を受け入れるうえで大切なプロセスなのかも。 shihorin: そうですね。Women in Agile に参加して、改めて多様性について深く考えることができました。 印象に残ったセッション maho: 今回のカンファレンスで、特に印象に残ったセッションは「アジャイルのない地域でアジャイルを根付かせる〜三島物語〜」でした。 shihorin: ああ、あのセッションですね! maho: このセッションでは、他者を巻き込んで文化を生み出していった実体験についてのお話がありましたよね。 shihorin: まさに体当たりでアジャイルを広めていくお話、面白かったです。 maho: 自分が良いと思っているものをそのまま伝えても、なかなか相手に伝わらない。他者を巻き込んで文化を生み出していく、良いと思ってもらうだけでなく実際に行動に移してもらう。これはとても難しいことですが、相手の文化に寄り添い、その世界観に合った伝え方をすることが、興味関心を持ってもらうための第一歩だと感じました。 shihorin: 押し付けではなく、相手に寄り添うことが大事ですね。 maho: そうなんです。とても基本的なことではあるんですけど。あとは、何かアクションを起こすときには一緒に楽しむ気持ちも、文化を根付かせていくうえでは無視できない要素な気がします。 shihorin: 私は「Art of Hostingから学ぶ 〜askとofferで現れる自分自身も尊重される運営〜」が印象に残りました。このセッションを聞いて、Art of Hostingという概念を初めて知りました。 maho: 私もです。 shihorin: 自分の心の内側が整っていないと、話し合いの中に不要な複雑さ・煩雑さを持ち込んでしまう、あることないこと言ってしまう、という話に共感しました。 maho: 焦って余計なことを言ってしまうことはよくある気がします。 shihorin: そうなんです。対話の中で本当はモヤモヤしているのに言葉に落とし込めなくて、モヤモヤを飲み込んだまま相手の話に同意してしまう、といった経験はたまにありますね。「まず自分自身をホストしよう」という話の中で、自分の気持ちに気づく、大切にするというワードが出てきましたが、具体的にどういうことなのかもっと詳しく聞いてみたいと思いました。 shihorin: Closing Keynote も印象的でしたね。 maho: WAKE Career を運営している bgrass 株式会社の代表、だむはさんの話、良かったですよね。 shihorin: 地道に一歩ずつ模索しながら、時には失敗も経験しながらリーダーになっていった話を聞いて、共感できました。若くして起業したカリスマ、自分とは遠い存在のスーパーマンみたいなイメージを勝手に持っていたので、自分が共感できたことに驚きました。 maho: だむはさんが乗り越えてきた壁についてのエピソードを聞いてみたら、すごく人間味があって、親近感が湧きました。今までのマッチョなリーダー像に必ずしも寄せていく必要性はない。それに代わる新しいリーダー像をそれぞれが作っていってもいいのではないかという視点には、とても考えさせられるものがありました。 shihorin: そうですよね。誰かが言っている・作ったリーダー像が絶対的な正というわけではなく、自分なりのリーダー像を目指したいと私も思いました。 maho: 私も同じ気持ちです! shihorin: もう一つ良いなと思ったポイントがあります。ジェンダーギャップを解消したい、という軸が最初から一貫してぶれていなかったことに加えて、自分の中だけでなく周囲に伝わっていたことです。やっぱり熱量を持って伝えることって大事ですね。 maho: Closing Keynoteのお話からも熱量の高さを感じました。 shihorin: 頑張っている人をみて自分も頑張ろうって思えるのが、カンファレンスにいく一つの目的だなと特に思いました。今までも「カンファレンスに参加すると登壇者や他の参加者から熱量を受け取れるよ」といった話を周囲から聞いてきましたが、徐々にわかり始めた気がしました。 OSTに参加した感想 maho: OST はどうでした? shihorin: 1回目は「対話の練習場」がテーマのテーブルに参加しました。「Art of Hostingから学ぶ 〜askとofferで現れる自分自身も尊重される運営〜」で登壇されたガオリュウさんがこのテーマを挙げられていて、対話の練習をしたいなと純粋に考えたためです。 maho: 私も同じテーマに参加しました! 対話や関係性について持論を話した際に、自分が思っていた以上に共感や、気づきにつながりましたというリアクションをもらえたのが嬉しかったです。 shihorin: 反応があると嬉しいですよね。 maho: また、この OST が終わった後にも、常に向き合っているからこそ言語化できているとお話いただいて、自分の意見や考えに対して色々なフィードバックがあるという機会が、これまでの私にとっては多くなかったので、とても刺激になりました。 shihorin: OST が終わった後もフィードバックがあったんですね! maho: shihorinさんはどうでした? shihorin: 私は「誰かの経験談を聞くのって結局n=1の話だし、直接的に参考にはならないんじゃないか」と今まで悩んでいましたが、経験談を抽象化することで「あるあるだよね」「自分も昔似たような経験をした」というように共通点が見つかって、n=2、n=3……になっていく。その中から、自分の仕事に活かせる気づきやヒントを得られそうだと今回の OST で感じました。抽象化って大事なんだなという気持ちになりました。 最後に maho: 今回のカンファレンスに参加して、誰かの話から新しい発見を求める純粋なゲスト感覚でいるだけではなく、自分の経験や考えを発信してフィードバックをもらい、またあわよくば少しでもコミュニティにインスピレーションを与えられるようチャレンジをしていくフェーズに移っていくべきかもと思えたのは良かったです。 shihorin: いいですね! 確かに、今まで誰かの話を聞いて「勉強になったな」で終わることが多かったかもしれない。 maho: そうなんですよね。せっかく貴重な経験をしてきたのに、それを発信しないのはもったいないなと思って。 shihorin: 自分の経験や考えを発信することで、誰かの役に立つかもしれないし、コミュニティ全体の活性化にもつながりますもんね。 maho: だから、これからはもっと積極的に発信していきたいと思っています。 shihorin: 私も自分の経験や考えの発信にチャレンジしてみたいです。まずは OST の参加テーマ選びのときに「このテーマなら自分の経験や考えが他の参加者の役に立ちそう」という観点を持ってみようと思いました。 maho: 良さそう! ぜひ、試してみてください。 shihorin: 今までは「このテーマ私も相談したい、私も悩んでいる」という観点でテーマを選んでいたので、発信するという観点がありませんでした。 maho: どうしても自分の悩みを相談したいって気持ちが優先されがちですよね。 shihorin: ですね。これからは発信する側にもなってみたいです。
はじめに こんにちは! タイミーでPlatform Engineerをしている @MoneyForest です。 今回は、弊社のDatadogにおけるAWSメトリクス収集を、従来のCloudWatch GetMetric APIからCloudWatch Metric Streams方式に移行することで高速化した取り組みについて紹介します。 背景 タイミーのワーカー様向けアプリケーションは、ピーク時に1分あたり十数万リクエストを処理するような規模で運用されています。そのため、システムの異常を素早く検知し、対応することが求められます。 主にシステムの異常検知は、メトリクス、ログ、APMをソースとしてエラーレートやレイテンシーの異常を判断し、DatadogのモニタリングアラートでSlackに通知することにより行われます。 DatadogによるAWSメトリクス収集の仕組み Datadogでは、AWSのメトリクスを収集する方式として2つの方法があります。 CloudWatch GetMetric API方式 CloudWatchのAPIを定期的にポーリングしてメトリクスを収集 メトリクスの遅延が15-20分程度発生する可能性がある CloudWatch側の遅延(5-10分) Datadogのポーリング間隔(10分) APIレート制限による追加遅延(最大5分) CloudWatch Metric Streams方式 ストリーミングしたメトリクスをAmazon Data Firehoseを介してDatadogに送信 aws.s3.bucket_size_bytes や aws.billing.estimated_charges のような2時間以上遅れてレポートされるメトリクスは取れないので、GetMetric APIも設定する必要がある 2-3分程度の遅延 それぞれの方式をポンチ絵で書くと以下のようになります。 GetMetric APIはまとめて取ってくるPull型、Metric Streamsは継続的に送信するPush型というイメージです。 タイミーではGetMetric API方式のみでAWSメトリクスの連携を行っていましたが、最悪のケースでは20分近い遅延が発生する可能性があり、問題の検知が大幅に遅れる可能性がありました(実績ベースではALBのメトリクスなどが10-12分程度遅延している状態でした)。 タイミーのトラフィック規模では、メトリクス送信の遅延が大きすぎるとして、CloudWatch Metric Streams方式の検討を始めました。 CloudWatch Metric Streams方式への移行 検証環境を活用しながら実装、コスト、切り替えの流れを検討し、移行を進めました。 実装面の考慮 タイミーではIaCとしてTerraformを採用しています。 一方で、CloudWatch Metric Streams方式を実装する手段として、AWSのIntegration画面からCloudFormationテンプレートが提供されています。 CloudFormationテンプレートの内容を全てTerraformに書き換えるのはコストが高いため、 aws_cloudformation_stack リソースでCloudFormationスタックのApplyを行うことにしました。 # Datadog CloudWatch Metics Streams の設定 # CloudFormationのテンプレートがDatadog側で提供されているため、そのまま利用する resource "aws_cloudformation_stack" "datadog" { name = "datadog" template_url = "https://datadog-cloudformation-stream-template.s3.amazonaws.com/aws/streams_main.yaml" # テンプレート内部で名前指定をしたIAM Roleを作成するのでオプション指定が必要 capabilities = [ "CAPABILITY_NAMED_IAM" ] parameters = { ApiKey = data.aws_ssm_parameter.datadog_api_key.value Regions = join ( "," , [ "us-east-1" , data.aws_region.current.name ] ) } lifecycle { ignore_changes = [ parameters [ "ApiKey" ] , # ApiKeyの変更を無視 ] } } コスト面の考慮 CloudWatch Metric Streamsの料金体系は、 AWSのPricingページ のExample 21に以下のように書かれています。 アプリケーションが 30 日間休まず毎日 24 時間稼働し、毎分 10,000 メトリクスを更新し、CloudWatch メトリクスストリームが、米国東部の Kinesis Data Firehose 配信ストリームを経由してパートナーの HTTP エンドポイントにデータを送信する場合、月額料金は次のようになります。 CloudWatch Metric Streams メトリクス更新の合計数 = 10,000 メトリクス更新/分 x 43,200 分/月 = 432,000,000 メトリクス更新/月 432,000,000 メトリクス更新 (1,000 メトリクス更新あたり 0.003 USD) = 1,296 USD/月 CloudWatch の月額料金 =1,296 USD/月 許容できないほど高くなることはなさそうなため、検証環境に数日反映して実績ベースで確認することにしました。 結果として、日次で$20ほどかかっていた CW:GMD-Metrics がなくなり、代わりに$50ほど CW:MetricStreamUsage にかかるようになりました。 タイミーでは本番環境より検証環境の方がAWSリソースが多いため、このコスト増を最大値と見込み、許容範囲と判断しました。 切り替え時の考慮 切り替えに関しては、CloudWatch Metric Streamsを有効化することによるメトリクスの変化を検証環境で確認しました。 結果として以下2点の問題が明らかになり、それぞれ対応を行いました。 CloudFormationスタックの適応中(8分程度)はダッシュボードで一部のメトリクスが重複して計上されてしまっている Datadogのドキュメント には以下の記載があり、特別なケアは必要ないものの、キレイに切り替わるわけではなさそうなことがわかりました。 API ポーリングメソッドを通じて特定の CloudWatch ネームスペースのメトリクスを既に受け取っている場合、Datadog は自動的にこれを検出し、ストリーミングを開始するとそのネームスペースのメトリクスポーリングを停止します。 こちらは反映が完了すると元通りになるため、許容範囲と判断し、念のため開発者へリリースタイミングを周知するにとどまりました。 aws.applicationelb.httpcode_elb_5xx など一部のメトリクスにおいて、CloudWatch Metricsのデータポイントがなかった場合はNO DATAになる GetMetric APIの場合は、CloudWatch Metricsにデータポイントがなかった場合、Datadog側には0のデータポイントが入りましたが、CloudWatch Metric Streamsの場合は、0のデータポイントが入らないという仕様差異がありました。 こちらはモニタリングアラートがRecoverしなくなるなど明確な問題があったため、事前に該当するモニタリングアラートに default_zero をつけて回る修正を行いました。 まとめ CloudWatch Metric Streams方式への移行により、メトリクスの収集が高速化され、MTTDを約10分ほど短縮できました(まだリリースしたばかりなので、本当のところはMTTDが短縮される見込み、です)。 これからも高トラフィックなアプリケーションで信頼性を担保するための地道な改善を続けていきたいと思います!またね〜
みんなでパシャリ タイミーの新谷、神山です。 東京Ruby会議12 が1月18日に開催されました。タイミーは Gold Sponsor として協賛をさせていたただき、ブースを出展していました。ブースに来ていただいたみなさんありがとうございます! 盛況なブースの様子 タイミーからは @ryopeko が「functionalなアプローチで動的要素を排除する」というタイトルで登壇しました。 speakerdeck.com また、タイミーには世界中で開催されているすべての技術カンファレンスに無制限で参加できる「Kaigi Pass」という制度があります。 productpr.timee.co.jp この制度を使って2名のエンジニアが参加しました。 参加して聞いたセッションのうち印象に残ったいくつかをピックアップしてご紹介します。 Keynote "Scaling Ruby @ GitHub" GitHub 社の John Hawthorn さんによる GitHub というプロダクトがどのようにして規模を拡大し、高可用性とパフォーマンスを維持しながら、アプリケーションを効率的に運用しているかについて紹介するセッションでした。 セッションの中ではたくさんのノウハウの紹介がありました。 大量の Pull Request のデプロイを効率的に管理するためのマージキュー 問題が発生した際に素早く切り戻すための Feature Flag flipper gem パフォーマンスチューニングを行う際の性能比較を行いやすくするためのツール scientist gem データベースレプリケーションを最適化するためのツール freno gem DB再接続の機構と過度に再接続を行わないようにするための Circuit Breaker の仕組み 発表の中で GitHub はモノリスな Rails で運用されていて、コード行数は380万行弱、テストコードは200万行弱という話がありました。単純な引き算をするとテストを抜いた実装に関するコードは180万行弱ということになります。これは現在のタイミーのモノリスの10倍以上ものコード行数です。 モノリスであること自体がスケールのボトルネックになるわけではなく、モノリスを取り巻く周辺環境の整備さえできればこれだけ組織がスケールするという点は大きな励みになりました。 また、発表の中での「GitHub 社に入社後、GitHub が普通の Rails アプリケーションだったことに驚いた」という話も印象的でした。飛び道具を使うわけではなく、実直に目の前の課題に対処することへの重要さを再認識しました。 発表の中でいくつかの GitHub 社のテックブログが引用されていたと思うので、こちらを参考にさせていただこうと思います。 github.blog 心のどこかで遠い存在のように感じていた GitHub が我々の開発の延長線の先にいるように感じられた、そんな発表でした。 (@euglena1215) 新谷が書いているように、GitHub が弊社の開発の延長線上にいるように感じました。 特に scientist の gem は変更に対して堅牢さをもたらしてくれるなと感じているので、結構気になっています。 スライドが公開されたら見返したい。 (@dak2) Writing PDFs in Ruby DSL Cookpad Inc. の Hiromi Ogawa さんによる Ruby DSL で PDF 書こうぜというセッションでした。 github.com 私は前職で thinreports で PDF に出力する項目を修正していたことがありました。 github.com そのため、個人的にセッションの内容が気になっていました。 PDF の構造は知らなかったので、自分にとっては知見だなあと思いつつ自分だったらどう書くんだろうかと思いながら楽しく拝見しました。 業務で使っているツールなどをハックして再実装するなどの試みは、そのツールとの機能差分が比較できてより理解が深まりますよね。 何か Ruby で再実装できないかなあと意識する良いきっかけになりました。 セッションのスライドの PDF 自体も Ruby DSL で自作されていたという最後の伏線回収まで綺麗でお見事だなと思いました(笑)。 (@dak2) Simple組み合わせ村から大都会Railsにやってきた俺は これまで軽量なライブラリの組み合わせによって Web サービスを開発してきた(≒ シンプル組み合わせ村に住んでいた)ところから、フルスタックな Rails を書くようになった(≒ 大都会Railsに移り住んできた) moznion さんによるシンプル組み合わせと大都会Railsの比較を行うセッションでした。 speakerdeck.com 私は業務でちゃんと使ったことがあるのが Rails のみでSimple組み合わせ村に住んだことがないシティーボーイだったということもあり、興味深く拝見しました。 Simple組み合わせ村も大都会も「どちらも正解だよね」と結論だったのですが、Simple組み合わせ村に住んだことがない自分としては適材適所の具体例を一歩踏み込んで聞けると尚良かったなと感じました。 また、これは発表と直接関係ないのですが、Simple組み合わせ村が発達している言語においてはDI(Dependency Injection)も同様に発達しているような印象があり、それは組み合わせの差し替えを容易にするためだったりするんだろうか…と聞きながら考えていました。 (@euglena1215) 私も production 利用のコードベースで触ったことがあるのは Rails のみな都会っ子です。 セッション内容を聞きながら、20年経っても Rails が当初の価値観を崩さず、Rails でいつづけられるのは、Easy であるからこそなのではないかなと思っていました。 Simple 組み合わせ村は「俺の考えた最強のxxxx」が乱立するんだろうなと思いますし、すべてのプロジェクトでそれが通用するかと言われると微妙なところがあると思っています。 Rails は Rails Way という言葉があるようにレールに乗った開発を推奨しているので、同じコンテキストの情報が Web にたくさん落ちていますし、そういった面からもクイックにやりたいことを実現できる素養があって、ここまで使われているんじゃないかなあと考えていました。 (@dak2) Regional.rb and the Tokyo Metropolis 広義の “Tokyo” の地域.rbのオーガナイザーが一堂に会し、色々なことに対してガヤガヤと話す RubyKaigi の Ruby Committers and the World 的な会でした。 東京近辺にこんなに地域.rbがあるのかという驚きが第一印象でした。他の言語のコミュニティにあまり参加したことがないのですが、ここまで地域コミュニティが発展している言語はあまり多くないのではと予想します。Asakusa.rb のようなOSS活動などでアウトプットすることを目的とした地域.rbもあれば、しんめ.rb のような初学者向けの地域.rbがあったりと裾野が広さを改めて感じることができました。 たくさんの地域.rb オーガナイザーたち 私は普段 omotesando.rb に参加することが多いのですが、地域.rbがあることで普段の業務や趣味で取り組んでいることを対外的に発表する機会が生まれ、登壇資料を作る中で自分の中でも理解の整理が進み、発表した内容を元に懇親会で興味ある人とざっくばらんに話すという一粒で三度美味しい経験をしているので、地域.rbのオーガナイザーには感謝してもしきれません。本当にありがとうございます。 (@euglena1215) 三浦半島.rb が立ち上がったのを聞いて Kaigi Effect を感じました! 私は自宅近くの地域.rbにしか参加していなかったので、これを機に他の地域.rbにも顔を出してみようかなあと思いました。私なりの Kaigi Effect です(笑) (@dak2) Keynote “Ruby と Rust と私” Cookpad Inc. の Suzuki Kohei さんによる Ruby と Rust の実装を比較しながら感想を述べていくセッションでした。個人的には最近趣味で Rust を触ってみているので、どういう違いがあるのだろうかと気になっていました。 speakerdeck.com 並行処理の文脈で「Ruby だと工夫が必要なところが、Rust では普通に書くだけで達成できる」という言葉が印象に残っています。メンテなども考えると、この間の距離って強い気持ちがないと埋めづらいなと聞きながら思っていました。 Result 型の question operator によるエラーハンドリング便利ですよねとか、SQLx で DB の row をデータ型にマッピングできるのかとか共感や発見が得られて面白かったです。 余談ですが @euglena1215 が Suzuki さんと ISUCON の Ruby 実装に型を付けたいという話をされたようで、ISUCON の Ruby 実装にも型がつくかもしれません。 (@dak2) 今回のコンセプトは「Ruby と Class」ではなく「Rubyと暮らす」でした。 発表としても業務で Ruby を使っている、というよりももう一歩生活の中に Ruby が溶け込んでいるような印象を受ける発表が多かったような気がします。 RubyKaigi とも Kaigi on Rails とも異なる、アットホームな味のある楽しいカンファレンスでした。東京Ruby会議12のオーガナイザー、ヘルパーのみなさんありがとうございました!
「エンドユーザーのためのデータ品質向上への取り組みと展望」 というタイトルでファインディさんとクローズドな合同勉強会を実施しましたので、タイミー目線でのレポート記事をお届けします。 きっかけ 以前同じ企業に所属していた両社の社員の間で合同勉強会の話が持ち上がったのがきっかけです。 両社共にデータ品質向上のための取り組みを進めており、参考になることが多いのではということで合同勉強会を開催する運びとなりました。 勉強会当日の流れ 勉強会はファインディさんのオフィスにて開催いただき、タイミーからはデータアナリスト、アナリティクスエンジニア、データエンジニア等のメンバーでお邪魔しました。 各社でLTを行った後、データに関連する話題を中心とした懇親会を行うという流れです。 LTの発表内容 各社からの発表のタイトルと概要についてご紹介します。 ファインディ マルチプロダクトのデータ基盤にデータメッシュを採用した話 / ファインディ ひらきさん データ基盤チームの発足から、データメッシュを採用するに至った経緯や運用についてお話いただきました。特にマルチプロダクトにおけるデータ基盤の運用の難しさや、データメッシュにおけるデータプロダクトをどのように定義するかについて、興味深く拝聴しました。 PII保護のためのDLP運用 / ファインディ tagashiraさん データ権限の管理方針や、DLP(Cloud Data Loss Prevention)を利用した個人情報保護に関する取り組みについて共有していただきました。DLPを使い込んでいないと出てこない課題感が多く含まれており、大変勉強になりました。 タイミー 各発表者からコメントを貰いましたので、合わせてご紹介します。 ユースケースに合わせたデータ品質 / タイミー atshsy タイミーでは、2024年3月にデータ品質の向上を目的として、RDBからデータ基盤へのパイプラインを刷新しました。刷新からしばらく経ち、データ品質について改めて評価したところ、完全性の観点で新たな課題が見えてきました。この発表では、改善に向けて取り組んでいる内容と、ユースケースごとに求められるデータ品質の違いについて共有いたしました。 ユーザーニーズに合わせたデータ鮮度の提供 / タイミー okodoon アナリティクスエンジニアのokodoonです。 複数レイヤーを保持しているデータ基盤の更新を、ユーザーの求める更新頻度で更新されるように全体の更新戦略を構築していく話をしました。レイヤーごとに求められる鮮度の概念が異なることを説明しつつ、dbtコマンドを用いて「どのように最適な更新頻度を実現するのか」現時点で弊社が考えているデザインを発表した形です。 基盤の信頼性を活かした 全社データ活用推進の取り組み / タイミー kuritama データアナリストのkuritamaです。 私からは、データ基盤の信頼性を活かして、全社のデータ活用推進に取り組んでいる話をしました。タイミーでは ディメンショナルモデリング を運用し安定したDWH・データマートを提供しており、そのデータマートへのアクセス手段として全社的にlookerを導入しています。データアナリストは分析業務と並行してlookerの全社活用推進を担っており、勉強会ではその活動内容の一部を紹介いたしました。 懇親会の様子 ファインディさんに食事をご用意いただき、大変美味しくいただきました! 各所で小さなグループが自然発生的に出来上がり、LTの内容に限らずデータに関する課題についての議論が行われていたのが印象的でした。 振り返って 初の取り組みということもあって緊張の面持ちでお伺いしましたが、蓋を開けてみると終了時間を過ぎても話題が尽きず、和やかな勉強会となりました。ファインディの皆様が暖かく迎えてくださったお陰と感じています。 開催をリードしてくださったひらきさん、ファインディの皆様、本当にありがとうございました! 勉強会の内容については、事前に想像していたよりも両社が似た課題を感じていることに驚きました。データ品質に関する課題について、他社がどのように捉え対応しているのかを知る機会は多くありませんので、とても貴重な機会となりました。 他のタイミーのメンバーからは、オープンな勉強会と比較して話しやすかったという感想がありました。共通の課題について率直に意見交換できるのは、クローズドな勉強会ならではの良さがあったように感じています。 終わりに タイミーでは、今後もこうした形式で他社のデータ関係者と交流を図っていければと考えています。ご興味がありましたら、気軽にお声がけください!
はい、亀井です。 yykamei という名前でインターネット上ではやらせてもらっています。所属はタイミーです。 今回、 Kaigi Pass という制度を利用して Regional Scrum Gathering Tokyo 2025 (RSGT 2025) にボランティアスタッフとして参加させていただきました。 ShinoP さんと takakazu さんに「RSGT 2025 でボランティアスタッフをやるのですが、スタッフでも Kaigi Pass 使えます?」と相談したところ「いいね!」というお声がけをもらい、晴れて Kaigi Pass を利用して参加することができました。お二人に感謝。 Day 0 RSGT への参加自体は今回が2回目です。2回目の今回はスタッフとしての参加で「なんもわからん」という状態で若干の不安を抱えながら Day 0 を迎えました。 この Day 0 というのは、つまり「前日」の意味ですね。 RSGT 自体は 2025 年 1 月 8 日に始まりますが、その前日からいろいろと活動をするわけです。 Day 0 の日に会場についたところ、誰かから「ここはふわっと始まるんで」と言われ、たしかに、「ふわっと」作業が始まりました。到着時点ですでになんらかの作業をしていたメンバーが数人いたのですが、やることがわからないので観察するしかありません。観察していると、だんだんと「なるほど、これをここに持っていくのね」「ブースの荷物を持っていくのね」などがわかってきますし、「誰か◯◯をやってー」という感じのヘルプ(指示ではない)が発生するので慣れないながらもそういう作業をします。 とはいえ、今回のボランティアスタッフはそれなりの人数がいらっしゃったようで、すぐに誰かがなにかしらの作業をしてくれるので、やはり観察する時間が長かったです。 Day 0 のメイン作業といえばノベルティーの準備でしょうか。参加した方はわかるかもしれませんが、トートバッグみたいなものがそれぞれに配られます。 その中にステッカーだったりペンだったりが含まれていますが、そうした内容物をトートバッグに入れる、という単純な作業を行なっていました。これ、それなりに大変でして、現地参加者の数が結構いらっしゃいますので単純に量が多くて大変です。 ただ、そこはさすがというべきか、スタッフがこの単純作業の中で自分の役割を見出し、途中から流れるようにノベルティーが完成していきました。まさしく自己組織的な何かを垣間見たような気がします。 ノベルティー準備の様子 この単純作業の最中にトラブルもありまして、作業がいい感じにスムーズに進み始めたあとに「これもトートバッグに入れる必要がでましたー」ということになり、その時点まで完成させていたノベルティーをもう一度点検してやり直す羽目になりました。こういうスクラム関連のワークショップ、ありますよね。もう擦られ続けた VUCA というワードですが、こんなところにもその片鱗が見えました。 Day 0 では、夕方以降に Speakers Dinner というものが開催されました。こちらは登壇者とスポンサーの方々、そしてスタッフが和気藹々と話す場です。やはりここも「ふわっと」始まります。ただし、会場を借りている時間の関係もあるので終了時間はきっちり守ります。それが終わったら各々二次会などに行っているようでした。 Day 1 そして、いよいよ Day 1 を迎えます。ここからが本番ですね。 Day 1 での主な私の役割は Room B での部屋付きです。 The way through data to quality “Measuring Quality” と Untangle your team with a new approach. (プロポーザルの段階では "A new innovative way to manage the complexity of a team.” でしたが、タイトルを変更したようです))という二つの登壇を担当しました。 これらの登壇は海外から来てくれたスピーカーがやってくれたので、メイン言語は英語でそれを日本語に通訳してくれます。質疑応答についても日本語で質問をすれば英語に通訳され、英語で質問されれば日本語に通訳される、という感じで通訳の方々が大活躍する現場です。 また、通訳の皆さんは別の部屋で行うので、もしスピーカーや質問者がマイクを使わずに話してしまうと通訳の方々にオリジナルの音声を届けられず、部屋付きは意外と気を抜けません。当初は「のんびり登壇を聞いていようか」ぐらいに思っていたのですが、部屋付きに入ると「これは参加者や登壇者の満足度に影響しそうだ」ということに気づきました。 そのため、マイクに音が入っているか確認しながら会場の様子に気を配る、ということをしたので、登壇内容はほとんど理解できませんでした。あとで録画を見ようと思います。 Untangle your team with a new approach. という登壇をしてくれた Teemu に同じ部屋付きだった松下さんが「Last name はどうやって発音するの?」と聞いていてさすがだな、と思いました。その問いに対して Teemu は「そうなんだよ。みんな、このスペルからは発音の仕方がわかる人はなかなかいないよね」的なことを回答していました。部屋付きだと登壇者とこういう会話ができていいですよね。 Day 2 Day 2 も引き続き部屋付きでした。この日の担当は Cowtopia: Designing Agility Through Playful Innovation で、こちらはワークショップになります。 1 時間 40 分の時間を使ったワークショップでクネビンフレームワークを体験してチームを改善していくためにはどうすればいいのか?というような内容だったように思えます。部屋付きで見ていると参加者同士で交流をされていてとてもよかったですね。登壇を聞くのもいいですが、ワークショップに行くと半ば強制的に人と交流することになってそれはそれで Gathering ができてよいのではないでしょうか。 Day 2 にもなってくるとスタッフ業もだんだん慣れてきてようやく廊下を楽しむ、ということができるようになってきました。ただ、人と話している時にもトランシーバーの音声に注意していたので 100% 会話を楽しめたか?というとそこは課題がありそうです。 次回、チャンスがあればいかにうまくトランシーバーを扱っていくか?ということを研究したいと思います。そういえば、 Day 2 で正義さんに「30 秒ピッチやりたいんですけどどこに行けばいいですか?」と質問され「え!なにそれ!?」という感じで Confengine を見たところたしかに “Lunch Time (with 30 seconds pitch from anyone @ Terrace Room)” というのがあり、あわてて実行委員の永瀬さんに聞きに行きました。 そしたら永瀬さんも「え!なにそれ!?」という感じで急遽連携を取り合ってなんとか 30 秒ピッチを開催することになりました。スタッフの新さんがきっちり 30 秒はかって、肉声でドラを叩くということをやってくれて急ごしらえの割には面白いコンテンツになったのかもしれません。 Day 3 Day 3 はメインホールで Open Space Technology (OST))を行い、事前に募集している人たちは別の部屋でワークショップに参加する感じです。この日は特に私に役割があったわけではないのですが、とりあえず、 OST の部屋で入り口付近にみなさん座りがちだったので、奥のほうに誘導していました。 OSTが始まるときの様子 — 円形に並んで座ります OST は始まるタイミングでは全員が円形に並んで座ってインストラクションを聞いて、各自持ち寄りたいトピックを書いていくのですが、その円形の並びがどうしても手前に偏ってしまったんですね。その偏りの修正をやっていました。 OST が始まる前に OST の寸劇があったのですが、私は廊下にいたので見られませんでした。あとで録画を見て楽しもうと思います。 OST では相変わらず熱量を持った参加者がテーマを持ち寄り、いたるところで活発な議論が行われていたようです。熱量が凄すぎてその日は寒かったはずですが、会場内は熱気が凄かったですね。私はなんとなくフラフラしていたのですが、途中から furoshiki.fm のテーマについてのテーブルにお邪魔しました。実はリスナーなのですが、ここぞとばかりに「お世話になっております」というフィードバックを出させていただきました。これが役に立つのかわかりませんがこれからもファンでいさせていただこうと思います。 Day 3 の OST とワークショップが終わるといよいよクロージングキーノートです。本間さんのお話です。最初の導入部で、現在、本間さんがされている子供たちとのお仕事の話をします。「ホンダの話じゃなかったっけ?」と思ったのですが、この導入部がないと実はホンダでのワイガヤの話がすっと入ってこないんです。ある意味焦らすような感じなのですが、全体を通して振り返ってみると「めちゃくちゃいい話だな!」という感想になります。さすがです。 ホンダの話になってくると City の開発の裏話?的なお話がされますが、とにかく「ワイガヤ」というのがキーワードですね。そして、本間さんは最後のほうで RSGT の OST に「ワイガヤ」を見出していただいたようで、ちょっと嬉しいですよね。そういえば、先日 Slack の Huddles を使ったプラクティスとその背後にある考え という記事を書きましたが、このワイガヤにも通じるな、と一人でにっこりしておりました。 まとめ ということで、つらつらとボランティアスタッフとしての RSGT 2025 を書いてみました。ボランティアスタッフだとあまり RSGT を楽しめないかも?などと思っていたのですが終わってみるともう一度やりたいな、という気持ちになっています。 スタッフとしての仕事に慣れてくるとだんだんカンファレンス全体を俯瞰して見られるようになりわずか 4 日ながら自分の成長を実感しました。こんな感じで正統的周辺参加をしてコミュニティーに関わっていくのか、と実感しております。 オーガナイザーとスタッフの皆さんで最後に記念撮影
こんにちは。タイミーのデータアナリティクス部でデータアナリストをしている亀山です。担当業務としては、主に営業部門のデータ分析を行っています。 今日はA/Bテストと※DIDの効果検証がより信用できるように仕組みづくりをした話を紹介したいと思います。 ※DID(Difference in Differences、差分の差分法)は、ある施策の実施前後で、影響を受けたグループ(比較群)と影響を受けなかったグループ(対照群)の変化を比較することで、介入の効果を推定する手法です。 取り組んだ背景 以前同じ部署の夏目さんがブログで取り上げている通り 、データアナリティクス部では過去に、効果検証の事前設計と結果を管理できるような仕組みを作成しました。その後、効果検証のテンプレートは多く使用されるようになり、知見も貯まってきましたが、残課題としては以下がありました。 汎用性の高いシンプルなテンプレートを作成したため、A/Bテストなど特定の手法を駆使した効果検証では入力項目に不足がある 特定の効果検証の手法も積極的に使っていきたいが、手法に関する知識が人によって差があるため、効果検証のクオリティにばらつきが出てきてしまう やったこと 今回は、これらの課題を解消するために、よく使われる効果検証の方法論のドキュメント化とその手法に特化したテンプレートの作成を行いました。 よく使われる効果検証の方法論のドキュメント化 社内でよく使われる効果検証としてA/BテストとDIDがあげられたため、それらの方法論のドキュメント化を行いました。 ドキュメントは以下のようにNotion上にデータベースを作成して、A/BテストとDIDが一覧ですぐに参照できる形式にしました。 A/BテストとDIDの方法論のデータベースの一部 これらのドキュメント整備で工夫した点は2点あります。一つ目は概論や設計方法だけでなく、社内事例を参照した解説まで記載したことです。ただの効果検証手法の説明であれば、ネット検索をすれば参照できますが、社内事例も含めることで、タイミーで行う効果検証だからこそ、考慮するポイントなどを記載しました。 二つ目は社内事例の解説の箇所で、Pythonなどのサンプルコードも記載したことです。説明だけでなくサンプルコードも載せることで、コードの転用につなげ、作業効率の向上を図りました。 特定の手法に特化したテンプレートの作成 A/BテストとDIDのそれぞれの手法に特化したテンプレートも作成しました。背景でもお話しした通り、それまでのテンプレートは汎用性が高くシンプルな形式でしたが、今回作成したテンプレートはそれぞれの手法ならではの入力項目を追加しました。 例えば以下の画像のように、A/Bテストならば「Randomizeの単位」や「割り当てタイミング」などA/Bテストで考慮すべきポイントをテンプレに含ませるようにしました。 まとめ 新しいテンプレートを作成してから2ヶ月弱経ちましたが、従来のテンプレートに加えて、今回作成したテンプレートも使ってもらえているようです。今後の展望としては、他の効果検証の方法の追加や既存のドキュメントのブラッシュアップも行っていき、より信用できる効果検証を行える仕組みを作っていきたいです。 また、個人的な感想ですが、私はDIDを担当することで、今までの知識の整理ができてとても良かったです。知識は入れるだけでなく、外に出すことも必要だなあと思います。今後もインプット・アウトプットを繰り返すことで、自身の分析スキルも引き上げていきたいです! We’re Hiring! タイミーでは、一緒に働くメンバーを募集しています。 https://hrmos.co/pages/timee/jobs カジュアル面談も実施していますので、少しでも興味を持っていただけましたら気軽にお申し込みください!
タイミーのプロダクトマネージャー(以下、PM)の 柿谷( @_kacky )、 高石( @tktktks10 )、吉池、大歳 です。 今回は、タイミーの「 Kaigi Pass 」制度を利用し、12/6にオンサイト開催された プロダクトマネージャーカンファレンス (以下pmconf)のDAY2に参加してきました。 この制度を通じて、非常に有意義な学びを得ることができましたので、その内容を共有します。 2024.pmconf.jp productpr.timee.co.jp プログラム概要 パネルディスカッション テーマ:「プロダクトマネージャーと仮説/戦略」 登壇者:FoundX 馬田さん、Zen and Company 宮田さん OST(Open Space Technology) 参加者持ち込みテーマでのディスカッション 懇親会 他社のPMと交流するネットワーキングの場 パネルディスカッションからの学び 「プロダクトマネージャーと仮説/戦略」というテーマで行われたセッションでは、戦略の現状や仮説構築におけるポイントが深く掘り下げられました。 戦略のコモディティ化 宮田さんが指摘した「戦略のコモディティ化」という現状に対し、差別化の重要性が議論されました。タイミーにおいては、プロダクトマネジメントや戦略を担う立場でもあるため、プロダクトマネージャーがどこにエッジを立てるべきかを再考するきっかけとなりました。 実はSaaSって、国内だと取れる戦略少なくて、4パターンぐらいに集約されるんじゃないか。 ・会計、人事や契約レビューや契約書管理などは垣根を超えて、Multi-Horizontal化 ・Verticalは参入障壁高くて、じっくりAll-in-Oneを作り込む… — Yoshitaka Miyata / 宮田善孝 (@zenkou_1211) 2024年7月22日 仮説構築の重要性 宮田さんは、仮説は地道なインプットを重ねた結果として生まれるものであり、見栄えの良さを求めるべきではないと強調していました。また、日本のプロダクトマネージャーが海外事例を十分に収集せず、国内のプラクティスに偏っていることや、議論テーマが数年間変化していない点に警鐘を鳴らしていました。 近年は、ChatGPTのようなツールやX(Twitter)を活用して、海外の著名なPMの投稿や事例を簡単に収集できる環境が整っています。 私自身も、海外事例を意識的に取り入れる姿勢を持ちたいと感じました。 AI経済の構造改革 馬田さんからは、書籍 『AI経済の勝者』 のフレームワークを用い、AIを活用した3つのソリューションレイヤー(ポイントソリューション、アプリケーションソリューション、システムソリューション)についての説明がありました。特に、リスクを取りながらシステムソリューションレイヤーに挑戦し、構造的な変革を進めることの重要性が強調されていました。 AIを前提とした新しいルールや戦略については、まだ理解が浅い部分も多いため、該当の書籍を読み、知識を深めたいと思います。 productpr.timee.co.jp OSTでのディスカッション OSTセッションの開始に先立ち、pmconfスタッフから進行方法の説明がありました。その後、会場から話したいテーマを募り、16の分科会に分かれて合計3回のディスカッションが行われました。 それでは、実際に、我々が参加したOSTで取り上げられたトピックに触れたいと思います。 プロダクトマネジメントに関する海外事例や書籍について知りたい このテーマでは、パネルディスカッションで取り上げられた海外事例をもとに議論が行われました。プロダクトマネジメントにおけるプロセスや役割の定義など、海外事例をどのように参考にし、実践しているのかが話題に上りました。しかし、参加者の環境や事業フェーズ、提供するサービス内容が異なるため、議論は各自の事例紹介にとどまりました。 さらに、プロダクトの海外展開時におけるPMの役割や、プロダクト開発体制、国内と海外のカルチャーの違いへの対応についても議論が広がり、非常に興味深い内容となりました。 LLMの活用事例 「LLMの活用事例」では、以下の点について議論が行われました。 AI投資の方向性 : トップダウンで進める企業が多い一方、エンジニア主導でボトムアップに進めるケースも見られました。特にアーリーフェーズでは投資家の影響が強く、AIへの投資は手段が目的化しているように見える場合もありました。 ROIの課題 : ROIの可視化が難しい中でも、AIへの投資は不可欠であるという意見が多く出ました。 個人的には、どんなユースケースで活用できるのか考える営みは、プロダクトマネージャーとして最低限要求されるべきなのかなと思いました。改めて、自社のプロダクトマネージャーや戦略部門のメンバーとも議論してみたいなと思いました。 リテンション施策の効果検証 「リテンション施策の効果検証がしづらい」というテーマでは、多くのPMが同じ課題を抱えていることが分かりました。他社のPMからは、データアナリストが充実している弊社の環境について羨ましいという声をいただき、自社の強みを改めて実感しました。 一方で、効果が測れない施策については、測定の努力を続けながらも「やるべきことを決める仕組み」が重要だと考えています。 BtoBtoCのプロダクトはCにどのように向き合うか 多くのBtoBtoCプロダクトにおいては、キャッシュポイントがBにあるためにCに対する体験改善がどうしても後回しになってしまう課題感について各社の意見交換が行われました。 各社の知見を寄せ集める中で「Cの声を現場までインタビューしにいく」ようなすぐできるアプローチから「Cの体験が良くなることでKPIが達成され、売上があがるようなビジネスモデルに変更する」といった根源的なアプローチまで、さまざまなアイデアが生まれました。 個人的には、顧客が満足するポイントとキャッシュポイントの距離の近さは、優秀なビジネスモデルを構築する上で重要な要素の一つだと考えています。今後自分がプライシングに関わることがあれば、意識したいですね。 メンバーへのスケジュール意識を高める PMとしてはスピード感を持ってプロジェクトを進めていきたいと考えているが、その温度感・危機感のようなものがどうもチームメンバーに伝わらない….。そんな課題について掘り下げるグループでした。 会話の中では「そもそもどうしてスケジュール感が必要なのか」といった課題の発端について意見交換が行われ、 経営陣がプロジェクトの進捗に不透明さを感じており状況を把握したいため 顧客の不便を早期に解消したいため 確定申告など、1年に1度しか訪れない外部イベントに間に合わせる必要があるため など、多種多様なきっかけがあることが分かりました。 基本的なアプローチとしては、PMが情報を包み隠さずチーム全体で情報の非対称性を減らしていくことが大事そうです。しかし、元の課題によっては単にチームの計画をオープンにしたり、開発する機能のスコープを削ったりと、スケジュール意識を高める以外の解決方法もありそうだと感じています。 非エンジニアPMの生存戦略 「非エンジニアPMの生存戦略」では、以下の点について議論が行われました。 活躍するために必要なケイパビリティについて まずは圧倒的に自身の強みであると言えるだけのスキルや経験、ドメイン知識を獲得すること。その自身の強み=ケイパビリティが顧客価値の拡大に寄与する実績を積み上げて深さを出すこと。そのあとに、抽象化と転用でケイパビリティをさらに広げていくと良い、という意見がその場の総論となりました。 結局、エンジニア経験を積むべきか? 主張の一つとしては、見積もりに対する妥当性評価をするためにも一定のエンジニア経験は積むべきという意見がありました。 しかし、その意見は内製の開発組織ではなく、発注元企業の企画担当と請負または準委任の受託企業の関係性を前提としているものでした。ビジネスパートナー関係による利害関係が発生する場合についてであったため、PM とエンジニアが同じ顧客に向かって利害関係なく動く組織においては、必ずしもエンジニア経験は必要ではないといった対比の意見も出ており、開発体制や文化、事業フェーズなどによるという結論となりました。 PM組織の構造 「PM組織の構造」では、以下の点について議論が行われました。 事業部制における PM 組織のあり方 事業部長がトップにいる中での PM 組織におけるレポートラインの妥当性 (CPO といった機能職種としてのトップより PL 責任を持つ事業部長のキャリアしか道がなさそう)と、その組織のマネージャーの振る舞いをどうするかといった議論がなされました。 事業フェーズや経営上、事業上の課題により適切な組織デザインが思考されるべきで、一義的なものはないという意見が多く出ました。 懇親会での交流 懇親会では、他社のPMとリアルに交流する機会がありました。Xで知っていた方々と直接話せたのは特に有意義でした。数年ぶりに再会する方もちらほらいたりして、楽しい時間でした。 異なる環境や課題を持つ企業の話を聞くことで、自社の環境や課題を相対的に捉え、視野を広げる良い機会となりました。 最後に 今回のカンファレンスを通じて、多くの刺激を受けると同時に、自分自身のキャリアや現在の課題について深く考える時間を持つことができました。特に、大量のインプットを通じて質の高い仮説を構築する重要性や、AI時代におけるプロダクトマネージャーの役割について再認識しました。 Kaigi Passを活用して得たこの学びを、今後の業務にしっかり活かしていきたいと思います。
イベント概要 12月17日(火)にpmconf 2024のサイドイベントとして「Re:cycle〜pmconf 2024編〜」を開催しました! このイベントはReject Conライクなイベントとして通過しなかったプロポーザルをアップデートして発表することをコンセプトにIVRyさん、DMMさんとの共催で開催しました。 今回はこの勉強会からタイミーのプロダクトマネージャーである大嶋さん( @ta0o_o0821 )の発表を書き起こしイベントレポート形式でお伝えします。 オープニング お品書き 自己紹介 はい、それでは私のほうから15分ほどお時間をいただいて、「Go See! で見つけるプロダクト開発の突破口とその実践法」というテーマでお話しします。まずは自己紹介から入ります。 私は大嶋泰斗と申します。株式会社タイミーでプロダクトマネージャーをしています。入社は昨年の6月頃で、今ちょうど1年半ほどPdMとして関わっているところです。 バックグラウンドや職歴についてお伝えすると、これまでLINE、リクルート、そして現在はタイミーと、ずっとtoCサービスに携わってきました。個人的に、身近なユーザーや想像しやすい人たちの幸せや喜びにつながる体験をつくることが好きで、学生時代のインターンも含め、一貫してtoC領域でやってきました。キャリアの中ではデザイナーをしていた時期もありますが、基本的にはプロダクトマネージャーとして積み上げてきた形です。趣味はカメラ、漫画、料理などです。 会社紹介 会社紹介は簡単にしておきます。タイミーは、「働きたい時間」と「働いてほしい時間」をマッチングするスキマバイトサービスです。一般的な求人媒体型サービスと異なり、実際の稼働や労務管理、企業への支払いまでプロダクト上で完結します。単にマッチングで終わらず、その後のワーカー行動や稼働データ、時給とマッチング率の相関など、多面的なデータ分析や改善が可能である点が面白いところです。 データだけに頼った意思決定の失敗 では本題に入りましょう。先ほど別のセッションで「徹底的にやる」という話が出ていましたが、私がお伝えしたいのは「Go See!」、すなわち現場に足を運んで顧客について学び、プロダクト開発を前進させるアプローチです。 いきなりですが「顧客を完全に理解している」という方はなかなかいないと思います。データ分析が一般的となり、ファネル改善や需要予測、バナー最適化などはデータドリブンで効果を出しやすい領域です。しかし、アナログな現場や複雑なオペレーションが絡むと、データ分析だけでは行き詰まってしまうことがあります。 例として、マクドナルドの「サラダマック」を挙げました。当時、健康志向が高まっているデータから発想された商品ですが、実際の顧客はマクドナルドに来たらビッグマックやポテトといったジャンクなものを求めることが多かったため、売れ行きは伸びず撤退する結果となりました。データは有用ですが、それだけでは顧客心理を完全には掴めないケースがあるわけです。 出勤簿プロジェクト このような状況はタイミーでも起きました。その一つが「出勤簿プロジェクト」です。 物流企業の倉庫などでは1日に50~100人規模でワーカーが来ることがあります。誰がどんなスキルで、何回目の勤務なのか、持ち物は何が必要かなどを把握し、スムーズに受け入れたい。 しかし従来は情報が断片的で、それらを手作業で集約し、1日30分ほど準備に時間をかけていました。この「受け入れコスト」が利用拡大のボトルネックになっていたのです。 一見すると、管理画面で情報をまとめれば解決しそうに思えます。しかし社内外でヒアリングをすると、「現場にPCを持ち込めない」「自作ツールを使いこなしている拠点がある」「拠点ごとにオペレーションが異なる」「そもそも1日の定義が違う拠点もある」といった複雑性が次々と判明しました。「管理画面で見せればいいのか? 印刷したほうがいいのか?」といった基本的な方針すら分からなくなり、ソリューションが見えなくなってしまったのです。 Go see で得られたインサイト そこで活用したのが「Go See!」、つまり現場へ足を運んで直接観察することです。 東京、名古屋、栃木、群馬など各地の物流倉庫を回り、ワーカー受け入れの様子や顧客とのやりとり、働く現場を半日から1日かけて観察しました。 センター長、人事部長、現場責任者、受け入れ担当者などにも直接ヒアリングを行い、実態を把握します。 その結果わかったことが、やはり「紙が必須」という点でした。 拠点によっては様々な派遣媒体からワーカーを呼び、テーブル上に各媒体の出勤簿を並べて即時にメモを書き込む必要があります。 ロッカーキー番号や体調不良、直前キャンセルなど、想定外の事態が常に起きる中、PCを開いて操作する余裕はありません。紙ならその場で書き込みが可能で、柔軟な対応ができるのです。 また、とある別のPoC検証をしていた拠点はなかなか利用に至っておらず、ヒアリングしてみるとCSVの列の追加や削除の作業をなくしたいとか フィルターする手間をなくしたいとか、色々なお声をいただきました。 元々30分掛かっていた作業が5分程度で完了する様になったので、それで良いだろうと思ってましたし、何故、5分掛かることで利用に至らないのか理解が出来ていませんでした。 でも、実際に現地で作業しているのを観察してみると色々なことが分かりました。 朝8時から8時45分までの間に、電話対応、他社派遣スタッフへの対応、書類分け、上司の依頼業務、さらには100人規模で来るタイミーワーカーの点呼準備まで、1人でマルチタスクをこなしていました。たとえCSV整理が数分短縮できても、その数分すら大きな負担になるわけです。こうした状況を見なければ「なぜ利用されないのか」が分からなかったでしょう。 小さな一歩で大きな成果を得る 現地訪問をしなければ、未利用の原因は不明なままで、開発は進まず手詰まりになっていた可能性があります。 表面的な対応に終始し、ギャンブル的な改善を繰り返すことになったかもしれません。しかし、実際に現場を見て理解を深めることで、真の課題が発見でき、確かな改善へとつなげることができました。 ここで改めて指摘したいのは、ユーザー行動はあらゆる要因に左右されているということです。 プロダクト上のデータはクリックや検索、閲覧履歴といった動作しか示しませんが、その背後には割引キャンペーンや地域の習慣、友人からのおすすめなど、データ化されない影響要因が無数に存在します。 現地訪問をすることで、そうした背景に目を向けられます。 まとめとして、「Go See!」は小さな手間で大きな成果を得られるアプローチだと言えます。数字やデータでは見落としがちな現場固有の環境や不便さを直接観察することで、プロダクト開発の盲点を補完し、成功へと近づけます。また、チーム全体で顧客理解を共有すれば、その後の開発スピードや質が大幅に向上します。私たちのチームでは、エンジニアやデザイナー、入社直後のメンバーにも必ず現場に行ってもらい、全員が同じ高い顧客解像度を得るようにしています。その結果、スクラム開発でプロダクトオーナーとしての私が手離れできるほど、チーム自律的に開発が進むようになりました。 注意点として、Go See! は定性的アプローチなので、N=1的なバイアスをはらみますし、顧客特有の問題に左右されがちです。 そのため得られたインサイトは、小さなPOCを通じて検証し、さらにデータ分析を組み合わせることで、より確度を高めることが大切です。 最終的なリリース後はデータ分析を最大限活用して、価値の最大化をスピーディに図ることが望ましいでしょう。 以上で、「Go See! で見つけるプロダクト開発の突破口とその実践法」のお話を終わります。 ご清聴、ありがとうございました。 現場に足を運ぶプロダクト開発に共感する人は是非お話しましょう! product-recruit.timee.co.jp 求人はこちら! hrmos.co
目次 目次 はじめに RBS について rbs-inline と Steep について RBS に出会ってからの Ruby への向き合い方 単一の型を返す意識がついた メソッドの戻り値の型だけを見て実装する機会が増えた Ruby で型を書くのも良いなと思った はじめに こちらは Timee Product Advent Calendar 2024 の24日目の記事です。 前日は @beryu の iOSの職能チームが存在しない組織で、WWDCハッカソンを企画・開催しました でした。 こんにちは。バックエンドエンジニアの @dak2 です。 タイミーではバックエンドの Ruby on Rails アプリケーションに型定義情報(RBS)を記述して運用しています。 もう少し具体的に話すと、rbs-inline を利用して RBS ファイルを生成し、Steep によって型検査しています。後半で詳しく説明します。 自分はタイミーに Join するまで RBS に触れたことがなかったので、Ruby で型を記述するという体験が新鮮でした。 型を記述して運用する中で Ruby への向き合い方が変わってきたなあと感じているので、今回はそれを文字に起こしてみたいと思います。 RBS について RBS とは Ruby のプログラム構造、いわゆる型を記述する言語のことを指します。 Ruby ファイルとは別に .rbs という拡張子のファイルにクラスやモジュールの型を記述します。 class Foo def bar (str) "#{ str }" end end class Foo def bar: ( String ) -> String end 簡単な例ですが、上記のように foo.rb のクラスに対して、 foo.rbs ファイルに RBS を書いて型定義ができます。 *詳細は 公式リポジトリ を参照してください。 rbs-inline と Steep について rbs-inline & Steep は、弊社のフルタイム Ruby コミッタである soutaro が開発している gem です。 rbs-inline はコメントとして型を記述できます。このコメントをもとに RBS ファイルが生成されます。 下記左のようにメソッド上部にコメントを追記して、 rbs-inline コマンドを実行すると右のような RBS ファイルが生成されます。 class Foo # @rbs (String) -> String def bar (str) "#{ str }" end end class Foo def bar: ( String ) -> String end *詳細は 公式リポジトリ を参照してください。 Steep は Ruby の型検査器であり、実装と型宣言に矛盾がないかをチェックします。 上記のような RBS を記述した上で steep check コマンドを実行すると型チェックをします。 Steep は VSCode での拡張機能もあり、関数ホバー時に型定義を教えてくれたり、メソッドの補完や型エラーになっているコードを赤の下線で示したりしてくれます。 メソッド補完 未定義メソッドのエラー *詳細は 公式リポジトリ を参照してください。 ちなみに、弊社バックエンドのテックリードである shintani が Steep のエラーリファレンスをまとめた 記事 があるので、興味がある方はご覧ください。(自分もちょくちょく見ています) RBS に出会ってからの Ruby への向き合い方 自分の所属している Working Relations Squad というチームでは Done の定義の一環として、インクリメンタルな差分に対しては必ず rbs-inline を記述するようにしています。 *Done の定義の話について詳しく知りたい方は、 こちら の記事を参照してください。 rbs-inline を記述して型を意識することで、Ruby への向き合い方が変わったなあと思う点を下記に挙げてみました。 単一の型を返す意識がついた メソッドの戻り値の型だけを見て実装する機会が増えた Ruby で型を書くのも良いなと思った 単一の型を返す意識がついた 自分は戻り値の型が単一の方が扱いやすいと考えています。 その方が呼び出し側で戻り値の型ごとにハンドリングしなくて良いし、メソッド自体の再利用性も高まると思っています。 rbs-inilne を書くまではぼんやりとそういった意識はあったものの、あまり意識できていなかったように思います。 rbs-inline を書くことで、自分が追加したメソッドの型を定義するようになり、自然と戻り値の型がどうあるべきかに対して明確に思考が向くようになりました。 メソッドの戻り値の型だけを見て実装する機会が増えた Ruby を記述していると、このレシーバってこのメソッド使えるんだっけ?と思ってメソッドの中身を再度読みに戻った経験はありませんか?もしくはテストを見に行ったり、場合によってはコンソールで Object#class を実行して確かめたりなど。自分は何度もあります。 既存メソッドの中身を確認することはあるのですが、詳細まで深く確認するというケースは少し減ったかなと思っています。 メソッドなどの型情報を主なインターフェースとして捉え、呼び出し側で利用するみたいなケースが増えたなと。 Ruby で型を書くのも良いなと思った 良いか悪いかは別として、「メソッドの戻り値の型だけを見て実装する」ことで、余計な情報を知らずに済むようになってきて、本当に実現したい処理に集中しやすくなりつつあるなと思っています。 この状態は理想的で、やりたいことにフォーカスできると Ruby の高い表現力を活かして素早く価値提供できるんじゃないかと思います。 過去に TypeScript などで型を書いてはいましたが、型の意義が自分の中で腹落ちしていませんでした。デフォルトで型付けしないといけない世界線だとそれが当たり前だったので、Ruby の世界との差分が大きくてうまく解釈できていなかった感覚があります。 ですので、それまでは型を書くというのがただ退屈な作業に感じていましたが、“型のある” Ruby を書くことと、”型のない” Ruby を書く体験差分を通じて、上述したように型の意義が自分の中で腹落ちしてきたなと思っています。 「Ruby に型なんかいらないんじゃないかな?」と思ってた自分が、型の恩恵を受けた世界線はより Ruby の表現力にフォーカスできるんじゃないかと思い直していて、Ruby への向き合い方がまた一つ変わったような気がします。面白いなあ。 これからも型やっていき!の精神で書いていこうと思います。 明日は @naoya の 「【iOS】Live Textを活用した画像解析 です。」お楽しみに!
Timee Advent Calendar 2024 23日目の記事です。 こんにちは、タイミーでデータアナリストをしている yuzuka です。 先日、統計検定準1級に合格し、最優秀成績賞をいただきました🌸 弊社からは別のアナリストがすでに 統計検定準1級の合格体験記 を出してくれていますが、合格体験記はあればあるだけ良いと思うので、私も書こうと思います。 受験の動機 社会人7年目で文系職からデータアナリストに転向しましたが、やはり専門性で周囲に後れをとっていると感じ、統計の知識を早急に身につけたいと考えていました。 取得した資格は履歴書などにも書けるため、アナリストに転向してから早い段階で取得しておけば、自身のLearnabilityを証明する一助になる、という打算的な動機もありました。 学習開始時の状況 文系職からデータアナリストに転向して2年目 1年ほど前に統計検定2級を取得済み 一応理系の学部卒だが、数学が苦手で、積分や行列の計算はほとんどできない状態に戻っていた 今回は、そんな自分の統計検定準1級対策についてご紹介します。 数学に苦手意識のある方の参考になれば幸いです。 具体的な学習方法 1. ワークブックを写経する(80時間程度) 統計検定準1級の勉強は「日本統計学会公式認定 統計検定準1級対応 統計学実践ワークブック」が主軸になると思いますが、こちらの内容をすべてノートにまとめ直しました(まとめ直すといっても、ほぼ丸写しです)。 ワークブックの内容は、数学や統計の知識が浅い自分には少しハードルが高く、軽く目を通しただけでは内容があまり入ってきませんでした。 このままワークブックを読むだけでは理解が深まらないと判断し、とりあえず手を動かしながら理解を深めることにしました。 ワークブックは300ページ以上あり、大変そうに思われるかもしれませんが、トータルで見るとコスパは良かったと思います。 1日1時間でワークブック3〜4ページ分を目安に、約3ヶ月かけてまとめ終えました。 あくまで自分が手を動かして理解することが目的であり、ノートを見返す必要はないと割り切って、綺麗さよりもスピード重視で進めました。 写経だけで内容を完全にものにできるわけではありませんが、この後の理解の進み方が大きく違ったように思います。 実際のノートの一部です。 2. 統計検定準1級に必要な前提知識のおさらい(10〜20時間程度) 部分積分や行列式、固有値など、ワークブックに出てきたものでわからないものがあれば、ネットで例題を探して解いていきました。 ワークブックに取り掛かる前に予習しておく必要はなく、ワークブックの問題を解くうえでわからないものがあれば、その都度調べる程度で良いと思いました。 最初はギリシャ文字の読み書きも怪しかったため、スマホの待ち受け画面をギリシャ文字の一覧表にしていました。 読み方のわからない文字があると、内容もなかなか頭に入ってこないため、放置しない方が良いと思いました。 3. 問題集を解く(50時間程度) 公式問題集(過去問)とワークブックの問題を2〜3周しました。 YouTubeにワークブックの解説動画を上げてくださっている方がいらっしゃり、大変理解が捗りました( URL )。 試験では1問あたり3〜4分しかかけられないため、普段からスピーディーに解くことを意識しました。 まず問題を見て、どのジャンルからの出題なのか、計算量が多い問題なのかをぱっと見定めることが重要そうです。 本番で使う電卓を問題演習中にも使用し、メモリ機能も含めて使い慣れておきました。 私は こちら の電卓を使っていました。 問題集に対して過学習が起きないよう、ときどきワークブック全体を読み返すようにしていました。 おわりに ここまで、私の統計検定準1級の勉強法をご紹介しました。 公式問題集やワークブックの問題を解くだけでなく、ワークブック全体の理解に力を入れたことが、好成績に繋がったのかなと思います。 試験当日は、なるべく早い時間帯で受験した方がパフォーマンスが良いと思い、11:00開始の枠で受験することにしましたが、これも良かったのかもしれません。 当日の問題群がたまたま自分に合っていた可能性もあります。 1週間空ければ再受験できるそうなので、何回か受験してみるのも手かと思います。 何か一つでも、参考になっていれば幸いです。 ちなみに、弊社では合否に関わらず資格の受験費用を補助してもらえるため、思い切って受験しやすかったです。 We’re Hiring! 私たちは、ともに働くメンバーを募集しています! カジュアル面談も行っていますので、お気軽にお申し込みください 。 タイミー データ職種 採用ページ 個人的にもアナリストやデータ関連職の方と繋がりたいと思っているので、よろしければ X のフォローもよろしくお願いします。(弊社データメンバーとランチ会や合同勉強会のお誘いも……お待ちしております!)
これは Timee Product Advent Calendar 2024 の23日目の記事です。 こんにちは。タイミーでiOSアプリを作っている岐部( @beryu ) です。 もうすぐクリスマスですね!月初から我が家のリビングに置いてあるクリスマスツリーもだいぶ見慣れてきました。 さて、今年も例年通りApple社による開発者カンファレンス「 WWDC24 」が夏に開催されましたね。 弊社の体制としては”iOSチーム”という単位のチームが存在せず、プロダクトを機能領域ごとに分割した職種横断チームで機能開発をしています。そのため、普通に業務をする上ではWWDCで得た知識についてiOSエンジニアが積極的に試したりする場はそれほど多くなく、得た知識を非エンジニアの社員に展開するかどうかも含めて個々人に任せられていました。 今年、初めてそれを社内ハッカソンという形で社内にアウトプットする機会を作ったので、それについてまとめたいと思います。 企画したきっかけ WWDC開催期間中、弊社のiOSエンジニアは普段通り業務にあたりつつ、スキマ時間でセッションをキャッチアップする日々を送っていました。 そんな中、ふと思いつきでSlackに以下のつぶやきをしたのがことの始まりでした。 各チームのiOSエンジニアが参加しているSlackチャンネルの実際の書き込み この時点で参加者数の見通しは私を含んで4名。小規模なハッカソンを開催するには十分な人数だと判断し、準備に取り掛かりました。 準備 前述した通り、弊社にはiOSチームという組織は無く、プロダクトを機能領域ごとに分割したチームで働いています。 したがって、一介のエンジニアがハッカソン用にリソースを自由に割けるような体制ではなく、ハッカソンに参加する社員が所属するチーム全てから許可を得る必要がありました。 今回のハッカソンは遊びではなく、魅力的なプロダクトを開発するために有効な 仕事 です。 しかし、万が一文脈を知らない方がこの活動を見て「遊んでいるのでは?」と思われたとしたら、それは誰の得にもならない悲しい誤解なので、そのような事態は必ず防がなければなりません。 この準備フェーズでは、そんな事態を防ぐために出来ることを考えながら動くことにしました。 非エンジニア向けの資料作成 まず、ハッカソンにかけたコストに見合うリターンが得られる論理を明文化しました。 誰かに資料作成を命じられたわけではありませんが、業務に割く時間を削って行う活動ではあるので「納得してもらえる材料があるに越したことはないだろう」という考えで用意しました。 ドキュメントには社外秘の情報を含んでいるので詳細はお見せできないのですが、主に以下のような内容を含めました。 目的 タイミーが今WWDCハッカソンをやる意義 期待されるアウトプット ハッカソンのレギュレーション(ここ3〜4年のWWDCで発表された技術であれば何でも使って良い) ハッカソンで消費するリソース量 iOS Chapterでの相談 弊社にはiOSチームが無い代わりに、各チームのiOSエンジニアが横軸で繋がるコミュニティのような存在として”iOS Chapter”という仮想組織が定義されています。 iOS Chapterでは毎週定例会議を開催しており、その場でハッカソンの開催形式や所要期間について相談しました。 各々が別チームに所属しているのでそれぞれに事情があり、直近での同期形式での開催は現実味が薄いと判断し、以下の枠組みで合意しました。 1ヶ月弱の期間中で2営業日を割く 任意参加扱いとする マネージャーとの交渉 エンジニアリングマネージャー陣の定例会議の場で時間を拝借して、上述の資料をひっさげて開催させてほしい旨を交渉しました。 交渉とは言っても、私がこの話題を出した時点でその場が応援ムードになり、特に咎められることもなくスムーズにOKを頂けました。 自部署の全体定例での展開 共に現場で働くエンジニア・デザイナーの皆さんにも納得してもらった上で開催したかったので、対象のメンバーがほぼ全員集まるAll Handsという定例会議でも上述の資料を用いながら告知も兼ねて紹介しました。 ここでのリアクションも”わいわい”という形容がピッタリの、良い盛り上がりを感じるものでした。 ハッカソンの開発期間 参加者は定められた期間中の2営業日を使って、ハッカソンのための調査・開発を行いました。 このタイミングでも参加者はFixさせていなかったので、参加予定だった社員が途中で業務都合で参加できなくなったり、逆にこれまで参加表明していなかった社員が参加してくれたりすることもありました。 あくまで通常業務が優先であることを鑑みてこの形にしたのですが、結果的にはこの形にして良かったなと思っています。 成果発表会 弊社は(私も含め)リモートワークのメンバーが多く所属している事情もあり、成果発表会はオンラインで行いました。 発表順はその場でシャッフル関数で決めました 成果物 App Intentsの概要・実装方法の調査報告 発表の場では、WWDC24で発表されたApple Intelligenceとの統合を見据えながらApp Intentsの概要を説明しつつ、iOS版タイミーアプリにApp Intentsを組み込んだ例を実際のソースコードを添えて紹介していました。 タイミーのアプリ内部の構造が深く絡む内容だったのでここでは資料をお見せできないのが残念なのですが、App Intentsの実装の手軽さ、身近さを感じられる発表でした。 Live Activityによるリッチな通知機能の実現 これが私の発表です。 WWDC23で発表されたプッシュ通知経由での利用例を中心に、iOS版タイミーアプリにLive Activityを組み込んだらどんな体験になるかを紹介しました。 実際にデモで使用した動画は以下からご覧頂けます。 ※ターミナル画面にはぼかし加工を加えています www.youtube.com パスキーによる認証の導入 現在タイミーで採用している電話番号認証とは別の認証方法として、パスキーを紹介していました。 認証はセキュリティが重要な要素になるので技術的な話題も多い発表で、正直なところ聞くだけでもやや難易度が高い内容でしたが、一方で動作デモの場面ではシンプルでわかりやすいUXに聴衆が感動している様子が印象的でした。 この日の発表の中で唯一、実際に操作できるデモアプリを配布できていたことでも大きなインパクトを残していました。 TipKitによるアプリ内ヒントの提供 WWDC23で発表された、機能のヒントを表示するためのフレームワークの紹介でした。 恥ずかしながら私はTipKitの存在を知らなかったのですが、画面の使い方を教えるようなUIを低い実装コストで実現できる、実用的なフレームワークでした。 動作デモでは、普段の開発で「この画面のUI、少しわかりにくいかもしれないね」と話に挙がっていつつ実装リソースを割くことが出来ないままになっていた箇所に、シンプルなコードでヒントを実装する様子が披露され、目立たない存在ながらその有用性を強く実感できる内容でした。 社内からの反応 成果発表会の最中にGoogle Meetのコメント機能で感想を述べて頂いたり、終了後にアンケートにご協力頂いたりして社内の反応を集めたのですが、非常に好評でした! 例えば、以下のような声が挙がっていました。 よかったですーで終わらせたくない内容でした R&Dみが強い感じかと思ってたんですが、すぐにでも導入できそうな形に仕上がっていてすごい iOSを業務で触っていない方がハッカソンに出られるところがスキルとマインドのダブルですごい ※App Intentsについて発表したのは普段アナリストとして業務にあたっている社員でした 取り組みに再現性持たせたいですね 楽しかった! 今日発表された機能のリリースはいつですか? リリースされた機能 嬉しいことに、このハッカソンで披露された成果物のうち実際にリリースされた機能も存在します。 今年タイミーアプリに新設されたものの一つである”バッジ”という機能があります。ワーカー(タイミーを通して働くひと)が働き先で認定されると業務ごとのバッジを獲得でき、そのバッジ情報をもとに更にさらに自分のスキルに合った募集を受けることが出来るようになる機能です。 この機能で獲得したバッジはバッジ一覧という画面で一覧できるのですが、この画面にあるバッジ画像をタップ出来る事に気付けないという声が度々挙がっていた背景があり、そこに今回のハッカソンの成果物の一つだったTipKitを導入することになりました。 この機能はすでにリリースされており、iOS17以上のiPhone上で動作するタイミーアプリでご利用頂けます。 実際のバッジ一覧画面 また、他の発表された成果物に用いられていた機能についても実装に向けてPdMと相談し、優先度を付けて日々の開発に用いているバックログに追加しています。 やってみた感想 ハッカソンを実施するきっかけはSlackでの些細なつぶやきでしたが、周囲の応援もあってどんどん話が進んでいきました。賛同した社員が各々の発想と技術力をもって成果物を作成・発表し、非iOSエンジニアの興味を誘いつつ一部はスピーディにリリースまでされるという、理想的なハッカソンになったと感じています。 実施するにあたって社内のステークホルダーと丁寧に条件を握るステップも踏みましたが、その過程が終始ポジティブな空気だったことから、少なくとも弊社はこのような取り組みを快く思う人間性の方が多く所属しているのだと感じられて嬉しくもなりました。 弊社は組織の人数が日々増えているので開発力の高まりを感じる機会が多いですが、今回のように個の力を感じられる機会も非常に魅力的だったなと感じています。 こういった場を作ることを検討されている方にとって、この記事が少しでも参考になっていれば幸いです。
本記事は、  Timee Advent Calendar 2024  の 12/22 公開分の記事になります。 はじめに 前提:チームトポロジーとは? 組織規模とフェーズにおけるチームトポロジーへの誤解 備えとしてのチームトポロジー グロース期の組織への段階的なチームトポロジーの適用 チーム組成のトリガーを見極める 組成に備えてチームメンバーの志向を理解しておく 最後に はじめに 株式会社タイミーでVPoEをつとめております、赤澤 a.k.a chango( @go0517go )です! 2024年2月に私がタイミーにジョインして以来、タイミーのプロダクト開発組織全体で適用・実践している「チームトポロジー」について、開発生産性Conference 2024をはじめ、様々な場で関連するトピックをお話しする機会がありました。 そういった場でいただく反応やご相談の中に、「タイミーはチームトポロジーを適用できる組織規模で羨ましい」「うちは開発組織を立ち上げてこれから組織をグロースしていくタイミングなので、まだチームトポロジーは取り入れていない」といったお声がいくつかいただきました。 確かに、チームトポロジーは認知負荷への対処方法などを例にとっても、一定以上の規模や複雑性を持つプロダクト開発組織で効果が顕著に現れやすいフレームワークです。しかし、「チームトポロジーを取り入れる=定義された全てのチームタイプを組織内に揃えなければならない」というわけでは決してありません。また、前職のユーザベース、そして現職のタイミーでの経験を通じて、チームトポロジーは今後の事業・組織の成長期に生じる混乱や生産性低下を軽減するための“備え”として非常に有益な概念であると実感しています。 このアドベントカレンダーの記事では、そうしたプロダクト開発組織の立ち上げ期や成長期におけるチームトポロジーに対する誤解を解き、組織の初期段階からチームトポロジーを活用するためのTIPSについて書きたいと思います。 前提:チームトポロジーとは? この記事の前提となるチームトポロジー自体の説明は省略させていただきますが、当該書籍自体はもちろんのこと、本書の共訳者である吉羽 龍太郎(@ryuzee)さんの記事や、弊社CTOの山口(@ZIGOROu)や私の開発生産性Conferenceでの登壇資料などもご参照ください。 【資料公開】30分で分かった気になるチームトポロジー(吉羽龍太郎@ryuzee) 組織をスケールさせるための Four Keys とチームトポロジー(山口徹@ZIGOROu) 実践チームトポロジー:プラットフォーム性とイネーブリング性の戦略(赤澤剛@chango) 組織規模とフェーズにおけるチームトポロジーへの誤解 前述のような場でいただいたご質問やご相談を、規模やフェーズに関する誤解として整理すると、概ね以下の点に集約されると考えています。 エンジニアリング組織が立ち上げ期で小規模なため、チームトポロジーを適用できない(と考えている)。 ストリームアラインドチーム以外のチームタイプ、特にイネーブリングチームやプラットフォームチームを検討できるほどの規模や余裕がない(と考えている)。 冒頭でも記載した通り、チームトポロジーは一定の規模や複雑性を有するプロダクト開発組織で、その効果がより顕著に現れるフレームワークです。しかし、「一定の組織規模がなければチームトポロジーを取り入れられない」と解釈することは本質的ではありません。また、4つのチームタイプ(ストリームアラインド、プラットフォーム、イネーブリング、コンプリケイティッド・サブシステム)を全て明確に揃えることが、チームトポロジーの適用を意味するわけでもありません。 むしろ、組織がまだ小規模な段階から、将来的に必要となるチームの性質を内包しつつ、ストリームアラインドチームを中心に据えた思考で価値創出に集中することこそが、チームトポロジーの有用性を最大限に活かす鍵だと考えています。 参考:チームトポロジーで表現したタイミーのプロダクト開発組織 備えとしてのチームトポロジー 「チームトポロジーが適切に実施できる組織規模で羨ましい!」と言っていただいたことがあり、非常にありがたくも恐縮なのですが、実際のところ私の認識では、一定の規模になったからチームトポロジーが実践できるようになったのではなく、一定の規模と複雑性でも顧客への価値提供を継続強化するために、チームトポロジーなどを取り込む必要性が生じた結果、実践せざるを得なくなったというのが正しい表現かと考えています(私自身は前職・現職を通じてチームトポロジーが好きで、決して否定的な意図はないことをお伝えしておきます)。 顧客価値とプロダクトに向き合い、主に機能開発をリードするストリームアラインドチームのみでビジネス要求に十分応えられるうちは、わざわざ複雑なチーム構造や追加の支援チームを設ける必要はありません。組織がまだ比較的シンプルな状態で、エンジニアリングやオペレーション、デリバリープロセスに関わる領域が一つのチームで把握できる範囲に収まっているのであれば、そのまま突き進んで問題はないでしょう。むしろ、その段階で「チームトポロジーを取り入れなければ」「無理に他チームを設置しなければ」と考えてしまうと、まだ不要な段階で組織構造の複雑化や権限移譲のコストが増大し、スピードや柔軟性を損ねる可能性があります。 問題は、プロダクトや組織が成長し、技術的・ビジネス的な複雑性が増大したときに表面化します。たとえば、以下のようなケースが想定されます。 ストリームアラインドチームの責務や負荷が増大 当初は1つ、あるいは少数のストリームアラインドチームでエンドツーエンドの開発・運用が可能だったが、新機能の追加や顧客数の増加、利用技術の多様化によって、本来集中すべき領域以外への対応(インフラ構築、技術調査、セキュリティ対応など)が増え、コア業務に影響が出始める。 領域の専門性が著しく向上 ストリームアラインドチームが扱う技術スタックやドメインが増え、メンバー全員がすべてを理解し追従することが難しくなっている。高度な機械学習アルゴリズムや極めて複雑なインフラ設定、厳格な法規制対応が求められるセキュリティ領域など、専門性を強く求められる分野が増え、従来の体制では対応しきれなくなる。 新技術・新手法の導入が困難 担当範囲の拡大に伴い、各ストリームアラインドチームが自前で新技術を調査・学習・導入することが難しくなり、新たなスキルやプラクティスの普及が停滞する。 共通基盤整備の必要性 アプリケーションやサービス群が増え、それぞれがインフラやCI/CDパイプラインを独自に整える状況が続くと、重複した作業や不整合が発生し、共通基盤・共通サービスを整備するチームの必要性が高まってくる。 重要なのは、これらのチームタイプの追加や新たなトポロジーは、これまで維持できていた単純性が崩れつつある状態を補完するために必要となるもので、組織規模が大きくなったからといって自動的に投入されるべきものではないという点です。あくまでも顧客への価値提供を止めない、もしくは加速させるための“備え”として、チームトポロジーを「引き出し」に用意しておくという発想が求められます。 言い換えれば、今はまだモノリシックなチーム構造で回せているのなら、それは望ましい状態です。同時に、将来的な成長過程で複雑性が増し、上記のような課題に直面する可能性を見越して「いざ必要になったとき、どう変化できるか」「どのようなチーム間関係を設計すれば、スケール時にも価値提供を継続できるか」といったシナリオを事前に想定しておくことが、長期的な競争力や組織の持続可能性を高める鍵となるのです。 グロース期の組織への段階的なチームトポロジーの適用 ここまでのパートで、発生しうる技術的・組織的な「備え」としてチームトポロジーを活用する方針について記載しましたが、後半ではエンジニアリング組織の立ち上げから拡大していくフェーズにおいてどう段階的に適用してきたか、そしてその際の留意事項や反省点についても書きたいと思います。 チーム組成のトリガーを見極める これは前職でエンジニアリング組織を立ち上げ、人数がおおよそ30名を超えた段階までの話ですが、チームトポロジーの適用、特にイネーブリングチームやプラットフォームチームをどのタイミングで組成するかについては、あらかじめトリガー(条件)を設けていました。 わかりやすい例として、イネーブリング的支援とプラットフォーム基盤整備の両方を担うSREチームを挙げてみます。以下は、覚えている範囲で抽出した定性的な条件例です。 複数のバリューストリームが生まれ、共通的な信頼性向上施策(CI/CDパイプラインの標準化、モニタリング基盤の強化、インシデントレスポンス手順の標準化)が求められる段階になっている。 利用ユーザ数やトランザクション量が増加し、現行体制下でSLO/SLAを維持するための継続的改善・強化が不可欠になっている。結果として、ストリームアラインドチームが本来の機能開発領域以外のシステム運用、トラブルシューティング、パフォーマンス改善などに多くの時間を割く状態が生じている。 組織内に、信頼性向上やオペレーション自動化に強い関心を持つエンジニアが育ちつつあり、共通基盤整備やシステム品質改善に特化して注力できる人材が揃ってきた。 SREチーム組成後、仮にSREチームメンバーが一時的に不在であっても、ストリームアラインドチームのメンバーのみでプロダクトの継続的デリバリーが(1や2のような課題を抱えつつも)自走可能な状態である。 事業やプロダクト面では1や2が特に重要な判断軸となりますが、私自身がSREチーム組成の可否を見極める上で“ノックアウトファクター”と捉えていたのは、4の条件でした。過去に別の組織で、CI/CDやOps領域においてSREチームへの依存が過度に高まり、ストリームアラインドチームが自前で運用しきれない状況に直面した経験がありました。その反省から、「顧客への価値提供をストリームアラインドチームが自走完結できること」を前提とし、その状態を強化・加速するために「Center of Practice」としてSREチームが存在する、という関係性を実現できることが、SREチームを組成する上での重要な前提条件、すなわちトリガーだったのです。 組成に備えてチームメンバーの志向を理解しておく 開発生産性Conference 2024の発表では、 イネイブリングやプラットフォームを「性質」として捉える。 タイミーにおいては、イネイブリングチームやプラットフォームチームを定義しつつも、内部ではQA領域、SREおよびプラットフォームエンジニアリング領域を中心に、イネイブリング性とプラットフォーム性の両面を兼ね備えたチームが存在している(メインの性質がそのチームタイプとなる)。 といった点に触れました。セッションの際には言及しませんでしたが、こうした性質や振る舞いは、当然ながら各メンバーにも志向や特性として内在しています。そして、ここでは「イネイブリング志向」や「プラットフォーム志向」と呼べる個々人の志向は、キャリア開発の観点も含め、チーム組成において重要な判断材料としています。 エンジニアにとって、「仕組みや方法によって再現性を高める」「自分以外の人でも運用可能にする」といった点は、共通して大切にされる価値観だと私自身も考えています。ただ、その中でも特に、インフラやプラットフォーム領域に強みと志向を持ち、複数のプロダクトに対して共通基盤の整備や最適化を行うことに喜びを感じるエンジニアもいます。 チームトポロジーで提唱される「タイプ」や「モード」はあくまでフレームワークであり、最終的には「誰が、どのような価値創出に楽しさや挑戦を感じるのか」という個々の志向やキャリアパスと、組織が目指すチーム構造を結びつけることで、中長期的に継続的な顧客価値提供が可能な組織を形作ることができると考えています。 また、私の過去の経験からの反省点として、イネイブリングチームやプラットフォームチームを立ち上げる際には、ストリームアラインドチームでの所属経験を一定割合以上持つ人材をアサインすることが重要だと強く感じています。そうすることで、ドメイン知識や現場課題の理解度が高まり、表層的なアドバイスではなく、実質的なサポートが可能になります。 具体的な防止策として、タイミーでは次のような点に留意して取り組んでいます。 イネイブリングチームは、ファシリテーションモードでの支援だけでなく、コラボレーションモードを意図的に発生させ、ストリームアラインドチーム内で密に課題解決を行う。 転職入社のエンジニアにイネイブリングやプラットフォーム領域への強い志向がある場合でも、まずはストリームアラインドチームで経験を積んでもらい、現場への理解を深めた上で、本人のキャリア意向と合意を得た上でイネイブリングまたはプラットフォーム領域へシフトしていく。 これらのトピックについては、開発生産性Conference 2024の中でも各領域の事例を交えて言及しています。ご興味がありましたら、ぜひ以下の資料をご覧ください。 開発生産性Conference 2024:組織全体の成長や成熟の過程でインタラクションモードを変化させている事例 最後に 本記事で述べた立ち上げ期でのチームトポロジーの適用は、択一的な正解を示唆するものでは全くありません。その上で私自身の経験として、チームトポロジーを組織の立ち上げ期から理解し、今後発生しうる課題と共に適用パターンを思考し始めることが非常に有用だと感じています。 ストリームアラインドチーム以外のチームタイプが組成されていることが重要なのではなく、自組織が携わる事業やプロダクトの特性を踏まえた上で、今後の課題と組織的な対応策をチームトポロジーに従って想定しておくことがチームトポロジーの組織適用の第一歩だと考えています。 これを読んでくださった皆様に、事例の1つとして参考になる部分があれば幸いでございます。
この記事は Timee Product Advent Calendar 2024 シリーズ2の21日目の記事です。 はじめに タイミーでフロントエンドエンジニアをしている大川です。 PlaywrightでのUI自動テストを運用してきた中で学んだ、PlaywrightとMock Service Worker(以降、MSW)を組み合わせたAPIのモック方法を紹介します。 Playwrightのテスト中にモックレスポンスを定義する バックエンドと連携しているシステム全体をテストすることで正常に動作しているか確認していくことは大切ですが、普段のUI開発やUIで利用しているライブラリアップデート後の動作確認時などはAPIリクエストをモックしてPlaywrightのテストを実行するのも有効な場合があると思います。 APIリクエストをモックするためのライブラリにMSWを利用している場合は、開発中に定義したモックハンドラーを再利用してテスト実装を効率化できます。 PlaywrightとMSWを組み合わせる場合には、テストシナリオ内でレスポンスを定義するための工夫が必要です。以下にいくつかの方法をまとめました。 テスト対象のUIでMSWが動作している場合 MSWをwindowオブジェクト経由で操作可能にする 参考: Overriding MSW handlers in Playwright (GitHubディスカッションへのリンク) テスト対象のUIがバックエンドと結合している場合 playwright-msw を利用する 参考: valendres/playwright-msw (Next.js App Routerの場合)experimentalとして実装されているtest modeを利用する 参考: Experimental test mode for Playwright MSWのgetResponseを利用する(後述) 自前のフィクスチャを定義する playwright-mswでは、最後に追加されたハンドラーが最初に実行されるよう、すべてのハンドラー定義順序をリバースしている箇所があります。 参考: ハンドラーをリバースしているコード MSWにも実行順序のルールがあり、それを考慮してハンドラーを用意していると意図しないレスポンスになる場合がありました。 参考: Request handler | Execution order playwright-mswを利用せず、MSWのハンドラーをベースにモック定義するために getRespose 関数が利用できます。 参考: getResponse 以下のコードは getResponse を利用してAPIリクエストをモックしているフィクスチャと、フィクスチャの利用例としてのテストになります。 (フィクスチャの実装例) import { test as base, expect } from '@playwright/test'; import { http, HttpResponse, RequestHandler, getResponse } from 'msw'; const apiEndpoint = process.env.API_ENDPOINT; // NOTE: MSWのモックハンドラー定義の例(実際は別ファイルで管理されている想定) const initialHandlers = [ http.get(`${apiEndpoint}/user/:userId`, () => { return HttpResponse.json({ name: 'John' }); }), ]; // NOTE: テストコード中でもモック定義するためのビルダーを定義 class MockBuilder { private handlers: RequestHandler[] = initialHandlers; constructor(...overrides: RequestHandler[]) { this.handlers = [...overrides, ...initialHandlers]; } use(...additionalHandlers: RequestHandler[]) { this.handlers = [...additionalHandlers, ...this.handlers]; } reset() { this.handlers = initialHandlers; } getHandlers() { return this.handlers; } } const test = base.extend<{ mockBuilder: MockBuilder }>({ mockBuilder: [ async ({ page }, use) => { const builder = new MockBuilder(); await page.route(`${apiEndpoint}/**`, async (route) => { const actualRequest = route.request(); // NOTE: MSWとPlaywrightでRequest型が違うので再定義している const request = new Request(actualRequest.url(), { method: actualRequest.method(), headers: actualRequest.headers(), body: actualRequest.postData(), }); const response = await getResponse(builder.getHandlers(), request); if (!response) { await route.fulfill({ status: 404, }); return; } const json = await response.json(); await route.fulfill({ json, status: response.status }); }); await use(builder); // NOTE: テストコード中に上書きしたハンドラーをリセットする builder.reset(); }, // NOTE: auto: trueでmockBuilderをテストコード中に呼び出さない場合もセットアップしておく { auto: true }, ] }); export { expect, test }; (テストの実装例) test('ユーザーIDを編集できる', async ({ page, mockBuilder }) => { // NOTE: 定義済みのユーザー情報取得に加えてユーザーID編集APIをモックする mockBuilder.use( http.patch(`${apiEndpoint}/user/:userId`, () => { return HttpResponse.json({ result: 'OK' }); }), ); // NOTE: テスト開始 const newUserId = 'new_user_id'; await expect(page).toHaveURL('/user/123/edit'); const input = page.getByRole('textbox', { name: 'ユーザーID' }); await input.fill(newUserId); const button = page.getByRole('button', { name: '保存' }); await button.click(); await expect(page).toHaveURL('/user/123'); const userId = page.getByText(newUserId); await expect(userId).toBeVisible(); }); さいごに 今回はPlaywrightとMSWを組み合わせてAPIレスポンスをモックする方法を紹介しました。 PlaywrightもMSWもAPIが充実しており、いろんなテストシナリオに対応できて便利です。 弊社ではPlaywrightでのテスト以外にも静的解析、コンポーネントやフックの単体テスト、ビジュアルリグレッションテストなどを取り入れています。 自動テストを整備することで機能開発とその動作確認、ライブラリアップデートなどの運用をバランスよく実施していける仕組みをつくっていき、お客さまによりよいUI・UXをより早く提供していくことにつなげていければと考えています。
こんにちは。エンジニアリング本部 プラットフォームエンジニアリングチームの徳富です。 この記事は、 Timee Product Advent Calendar 2024 の 20 日目として、EXPLAINを使用した実行計画の見方についてご紹介します。 背景 タイミーでは、会社の成長に伴い、パフォーマンスチューニングが喫緊に求められています。このような課題に対処するため、クエリのパフォーマンスチューニングには EXPLAIN を使用した実行計画の確認が非常に重要です。しかし、実行計画の解釈には社内でばらつきがありました。この問題を解消するために、実行計画の見方を社内でまとめ、共有することにしました。ただし、この情報を社内だけに留めておくのはもったいないと考え、テックブログを通じて広く公開することに決めました。 実行計画の基本的な見方 MySQLの EXPLAIN 文は、SQLクエリがどのように実行されるかの詳細を提供します。実行計画には、以下のような重要な情報が含まれています: 項目 説明 id SELECT識別子で、クエリが複数の部分から構成されている場合(例えば、サブクエリやUNIONが使われている場合)に重要です。同一のidを持つ行は同じSELECT文に属しています。 select_type クエリのタイプを示します。例えば、 SIMPLE (単一のSELECT)、 SUBQUERY (サブクエリ内のSELECT)、 UNION (UNIONの一部)などがあります。 table クエリが参照するテーブル名。複数のテーブルが結合されるクエリでは、どのテーブルがどの順番で処理されるかを示します。 partitions クエリが参照するパーティション。指定されたパーティションを明確にすることで、クエリの実行効率が向上します。 type データへのアクセス方法のタイプ。 ALL (フルスキャン)、 index (インデックススキャン)、 range (範囲スキャン)などがあります。 possible_keys このクエリで使用可能なインデックスのリスト。適切なインデックスが選択されるかどうかの手がかりになります。 key 実際に使用されるインデックス。このインデックスがクエリのパフォーマンスに大きく影響します。 key_len 使用されるキーの長さをバイト数で示します。キーの長さは、インデックスを使用する効率に影響します。 ref インデックスに使用される列や定数。外部キーの結合や、定数との比較で使用されます。 rows 読み込まれる行数の推定値。クエリのコスト評価やパフォーマンスチューニングの際に重要です。 filtered フィルタ条件によって行がどれだけ絞り込まれるかのパーセンテージ。100%に近いほど、フィルタ条件によるデータの絞り込みが効果的です。 Extra クエリの実行に関する追加情報。例えば「Using index」はインデックスのみでデータが解決されていることを、また「Using temporary」は一時テーブルを使用していることを示します。 これらの情報を基に、実行計画を読み解き、クエリのパフォーマンスを最適化する方法を理解します。 各項目の重要な部分の説明( type, filtered, Extra ) type type はクエリがどのように実行されるかを示すもので、パフォーマンスの観点から重要です。以下は、最も効率的な順に並べた type の種類とその説明です。 項目 内容 const 単一の比較によるレコードの検索で、結果が1行だけに限定される場合に使用されます。通常、主キーやユニークキーの等価比較で見られ、非常に高速です。 eq_ref 主キーまたはユニークキーに基づくジョインで一つのレコードだけを指し示す場合に使用されます。各ジョイン段階で1行のみが処理されるため、効率的です。 ref インデックスを使用して複数の行がマッチする可能性がある検索です。非ユニークインデックスが使われることが多く、キーに基づく絞り込みが行われますが、constやeq_refほどには効率的ではありません。 range インデックスを利用した範囲検索です。特定の範囲内の値を持つ行を効率的に検索しますが、スキャンする範囲によっては処理が重くなる可能性があります。 index インデックス全体をスキャンしますが、テーブル自体は読み込まれません。これは特定のケースでは効率的ですが、全エントリの検査が必要な場合はコストが高くなります。 ALL すべての行をスキャンする必要があるテーブルスキャンです。インデックスがない場合や適切なインデックスが利用されない場合に使用され、パフォーマンスが最も悪いタイプです。避けるべきです。 filtered filtered の値が100に近いほど、行をフェッチした後に絞り込んだ量が少なかったことを意味します。一方で、この値が 100 ではない場合、インデックスが適切に設定されていない可能性があり、不要な行が多くフェッチされている可能性があります。この場合、クエリやインデックス設計の見直しが必要です。 補足情報 : インデックスが存在しないカラムの場合、MySQLのオプティマイザは統計情報を持たないため、 filtered の値は以下のように固定されることが一般的です: 等価検索( = value )の場合、 filtered は 10% と固定されます。 もしカラムがenum型の場合、 filtered の値は取りうる値の逆数(1/enumの値の総数)に基づいて計算され、それを100倍してパーセンテージで表示します。 より大きい( > value )の場合、 filtered は 33.33% と固定されます。 範囲検索( BETWEEN start AND end )の場合、 filtered は 11.11% と固定されます。 これらの固定値は、統計情報がない場合のオプティマイザの仮定に基づいています。したがって、適切なインデックス設計を行い、オプティマイザが正確な統計情報を基に、インデックススキャンなど適切なクエリの実行方法を選択できるようにすることが、パフォーマンスの改善につながります。 Extra 説明 : クエリの実行に関する追加情報を提供します。ここに表示される内容は、パフォーマンスのボトルネックを特定するのに役立つことがあります。 パフォーマンスが良い順に並べた内容(若干条件により前後する) : Using index : インデックスだけを使用してデータを取得し、テーブルへのアクセスを避けます。(カバリングインデックス)これはクエリが効率的であることを示す良い兆候です。 Using index condition : インデックスコンディションプッシュダウン(ICP)が使用されていることを示します。これはインデックス内でWHERE条件の一部を評価することにより、不要な行の読み込みを減少させ、クエリの全体的な実行時間を短縮します。 Using where : データを取得した後に追加の絞り込みを行っている状態を示します。この状況は、インデックスがWHERE条件をすべてカバーしていない場合や、適切なインデックス自体が存在しないに発生します。 Using filesort : MySQLが結果をソートするために一時ファイルを使用します。これはクエリのパフォーマンスに影響を及ぼす可能性がありますが、正しい結果を得るために必要な場合があります。 Using temporary : クエリ処理のために一時テーブルが使用されます。これはGROUP BYやORDER BYの処理で見られ、大量のデータを扱う際にパフォーマンスに影響を与える可能性があります。 Full scan on NULL key : ジョインやサブクエリでNULL値を持つキーをフルスキャンしています。このプロセスは非常にコストが高く、パフォーマンスに大きく影響を及ぼします。 チューニングの具体的な例 具体的なSQLのチューニング方法をご紹介します。使用する環境はこちらのGitHubリポジトリで公開されています: https://github.com/hirosi1900day/tech-blog-for-mysql レベル1: whereとgroup byを使ったクエリのチューニング 次のSQLクエリは、特定の日付における商品名ごとの総数量を求めるものです。 SELECT product_name, SUM (quantity) as total_quantity FROM orders WHERE order_date = ' 2021-01-02 ' GROUP BY product_name まず、このクエリの実行計画を確認してみましょう。 EXPLAIN SELECT product_name, SUM (quantity) as total_quantity FROM orders WHERE order_date = ' 2021-01-02 ' GROUP BY product_name; 実行計画の結果は以下の通りです: + ----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ | 1 | SIMPLE | orders | NULL | ALL | NULL | NULL | NULL | NULL | 12 | 10 . 00 | Using where ; Using temporary | + ----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+------------------------------+ 実行計画からわかるように、 ALL (全スキャン)が使用されており、インデックスが利用されていないことが明らかです。これはデータベースがテーブルの全行をスキャンしているため、データ量が多くなるとパフォーマンスが大きく低下する可能性があります。さらに、 Extra 列に Using where; Using temporary と表示されています。これは、 WHERE 句での絞り込み後に一時テーブルを使用して GROUP BY 処理が行われていることを示しており、効率的ではありません。 チューニングの実施 このクエリのパフォーマンスを向上させるためには、 order_date と product_name に複合インデックスを作成することが効果的です。複合インデックスでは、インデックスの列の順序が重要です。このケースでは、 WHERE 句で order_date を使用してデータを絞り込んでから、 GROUP BY で product_name を使うため、 order_date を第一引数に設定するのが適切です。 インデックスを追加します: CREATE INDEX idx_date_product ON orders(order_date, product_name); インデックスを追加した後の実行計画は以下のように改善されるはずです: EXPLAIN SELECT product_name, SUM (quantity) as total_quantity FROM orders WHERE order_date = ' 2021-01-02 ' GROUP BY product_name; 結果: + ----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | orders | NULL | ref | idx_date_product | idx_date_product | 6 | const | 2 | 100 . 00 | NULL | + ----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------+ この改善により、 type が ref に変わり、クエリはインデックスを使用してより効率的に実行されます。また、 Extra 情報もクリアされ、一時テーブルを使用することなく処理が行われていることが確認できます。 インデックスの順序の影響 SQLクエリのパフォーマンスを向上させるためには、インデックスの構成とその順序が重要な役割を果たします。ここでは、 product_name を第一引数とし、 order_date を第二引数とする複合インデックスの影響を考察します。 CREATE INDEX idx_product_date ON orders(product_name, order_date); この複合インデックスを用いたクエリの実行計画を見てみましょう: EXPLAIN SELECT product_name, SUM (quantity) as total_quantity FROM orders WHERE order_date = ' 2021-01-02 ' GROUP BY product_name; 実行計画は以下のようになります: + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | orders | NULL | index | idx_product_date | idx_product_date | 1028 | NULL | 1 | 100 . 00 | Using where | + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ ここで、 type index となっているため idx_product_date インデックスがフルスキャンされ、 Extra 列に Using where が表示されている点に注目します。これは、 order_date での絞り込みがインデックスを最適に活用していないことを示しています。また、 Using temporary が表示されないことから、 GROUP BY product_name 処理で複合インデックスが効果的に機能し、一時テーブルが不要になっていることがわかります。このことから、複合インデックスの第一引数の product_name だけが利用され、第二引数以降の order_date は単独でインデックスとしての機能を果たしていません。 さらに、同じ条件で product_name のみにインデックスを設定した場合の実行計画を見てみましょう: CREATE INDEX idx_product_name ON orders(product_name); EXPLAIN SELECT product_name, SUM (quantity) as total_quantity FROM orders WHERE order_date = ' 2021-01-02 ' GROUP BY product_name; 実行計画は以下の通りです: + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | orders | NULL | index | idx_product_name | idx_product_name | 1022 | NULL | 1 | 100 . 00 | Using where | + ----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+ この結果から、 product_name のみのインデックスでも同じ実行計画になることがわかります。 つまり、複合インデックスを使用している場合でも、第一引数( product_name )が単独で使用されることがある点に注意が必要です。 レベル2: GROUP BYとHAVING、ORDER BY を使ったクエリ 次のSQLクエリは、商品がどれだけ頻繁に複数購入されるかを調べるためのものです。具体的には、商品名ごとに注文総数と平均購入数量を集計します。 SELECT product_name, COUNT (*) AS total_orders, AVG (quantity) AS average_quantity FROM orders GROUP BY product_name HAVING AVG (quantity) > 1 ORDER BY average_quantity DESC ; このクエリの実行計画を EXPLAIN を使って確認すると、以下のような結果が得られました。 + ----+-------------+--------+------------+------+------------------+------+---------+------+------+----------+---------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+------+------------------+------+---------+------+------+----------+---------------------------------+ | 1 | SIMPLE | orders | NULL | ALL | idx_date_product | NULL | NULL | NULL | 1 | 100 . 00 | Using temporary; Using filesort | + ----+-------------+--------+------------+------+------------------+------+---------+------+------+----------+---------------------------------+ 全スキャンが行われており、インデックスが利用されていません。 Using temporary; Using filesort は、 GROUP BY と ORDER BY によって一時テーブルが使用され、結果がファイルソートされていることを示しています。これによりデータが多い場合、クエリのパフォーマンスが低下する可能性があります。 チューニングの実施 このクエリは product_name でグルーピングした後に quantity のカラムを使って HAVING や ORDER BY を行うため、 product_name と quantity に複合インデックスを追加することでデータそのものにアクセスすることなく、インデックスだけでデータの取得が行えそうです(カバリングインデックス)。これにより、データベースが効率的にデータにアクセスできるようになります。 インデックスを追加するSQLは次の通りです: CREATE INDEX idx_product_name_quantity ON orders(product_name, quantity); インデックス追加後、実行計画を再び確認すると、次のように改善されるはずです。 + ----+-------------+--------+------------+-------+--------------------------------------------+---------------------------+---------+------+------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+--------+------------+-------+--------------------------------------------+---------------------------+---------+------+------+----------+----------------------------------------------+ | 1 | SIMPLE | orders | NULL | index | idx_date_product,idx_product_name_quantity | idx_product_name_quantity | 1027 | NULL | 1 | 100 . 00 | Using index ; Using temporary; Using filesort | + ----+-------------+--------+------------+-------+--------------------------------------------+---------------------------+---------+------+------+----------+----------------------------------------------+ クエリの最適化は行ったものの、 Using temporary; Using filesort がExtra情報から消えない理由は、 GROUP BY によるグループ化と ORDER BY によるソート処理が原因です。カバリングインデックスが追加されたことにより Using index が表示され、インデックスから直接必要なデータを取得しているため、全行スキャンのコストは削減されましたが、集計とソートには依然として追加のリソースが必要です。 SQLクエリでは、 GROUP BY でグルーピングした結果を ORDER BY でソートする際には、MySQLが内部的にテンポラリーテーブルを作成し、そのテーブルにデータを格納後、ソート処理を行います。このプロセスはメモリやディスクスペースを消費するため、扱うデータ量が増えるとパフォーマンスの低下が発生します。 そのため、データベースでの処理負荷を軽減するために、クエリをさらに絞り込むか、あるいはアプリケーションレベルでデータを取得後に集計やソートを行うアプローチが考慮されるべきです。(その場合アプリケーションサーバーに負荷が発生することになるのでバランスが重要) レベル3: Joinとサブクエリを使ったクエリ 次のSQLクエリは、各ユーザーが注文した商品の中で、そのユーザーが注文した商品の平均数量を超える数量を持つ商品名を取得します。 SELECT users.username, orders.product_name, orders.quantity FROM users JOIN orders ON users.id = orders.user_id WHERE orders.quantity > ( SELECT AVG (orders.quantity) FROM orders WHERE orders.user_id = users.id ); このクエリを EXPLAIN で実行計画を確認した結果、以下のような内容が得られました。 + ----+--------------------+--------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+--------------------+--------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+ | 1 | PRIMARY | orders | NULL | ALL | user_id | NULL | NULL | NULL | 1 | 100 . 00 | NULL | | 1 | PRIMARY | users | NULL | eq_ref | PRIMARY | PRIMARY | 4 | testdb.orders.user_id | 1 | 100 . 00 | Using where | | 2 | DEPENDENT SUBQUERY | orders | NULL | ref | user_id | user_id | 4 | testdb.users.id | 1 | 100 . 00 | NULL | + ----+--------------------+--------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+ orders テーブルに対してフルスキャンが発生していますが、これはデータ量が少ない場合にインデックスを利用するよりも効率的だと判断されているケースです。特に注目すべきは DEPENDENT SUBQUERY という select_type で、これは外部クエリの結果に基づいて行ごとにサブクエリを繰り返し実行することを意味します。 例えば、100人のユーザーがそれぞれ5件の注文を持つ場合、サブクエリは500回実行されるため、効率が低下します。この問題を改善するためには、共通テーブル式(CTE)を使用し、ユーザーごとの平均数量を事前に計算してからメインクエリで利用する方法が有効です。 以下は、CTEを使用したクエリ例です。 WITH UserAverages AS ( SELECT user_id, AVG (quantity) AS avg_quantity FROM orders GROUP BY user_id ) SELECT u.username, o.product_name, o.quantity FROM users u JOIN orders o ON u.id = o.user_id JOIN UserAverages ua ON u.id = ua.user_id WHERE o.quantity > ua.avg_quantity; このクエリの実行計画は以下のようになります。 + ----+-------------+------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | + ----+-------------+------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+--------------------------+ | 1 | PRIMARY | orders | NULL | ALL | user_id | NULL | NULL | NULL | 1 | 100 . 00 | NULL | | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 4 | testdb.orders.user_id | 2 | 50 . 00 | Using where ; Using index | | 1 | PRIMARY | users | NULL | eq_ref | PRIMARY | PRIMARY | 4 | testdb.orders.user_id | 1 | 100 . 00 | NULL | | 2 | DERIVED | orders | NULL | index | user_id | user_id | 4 | NULL | 1 | 100 . 00 | NULL | + ----+-------------+------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+--------------------------+ CTEを使用することで、サブクエリの繰り返し実行を防ぎ、一度の計算で済ませるようになりました。ただし、CTEは派生テーブルとして扱われ、実行計画では DERIVED と表示されます。これにより、一部のデータがテンポラリーテーブルとして実体化されるため、必ずしも常にサブクエリより高速になるわけではないので注意してください。 DEPENDENT SUBQUERY が実行計画に現れた場合には、データ量が増えるとパフォーマンスが大幅に劣化する可能性があるため、注意が必要です。このような場合には、クエリの設計を見直すことを検討してください。 まとめ MySQLの実行計画を利用することで、クエリのパフォーマンスを把握し、最適化の方向性を明確にできます。今回は、具体的な例を通じて、インデックスを追加する方法とクエリを変更することでの改善方法を紹介しました。しかし、クエリのチューニングは状況に応じて最適な手法が異なるため、これらの方法以外にも様々なアプローチが考えられます。これからも実際の問題に遭遇した際の解決策をブログで積極的に共有していくことで、より多くの技術者がデータベースのパフォーマンス問題に対処できるよう支援していきたいと思います。 参考 この記事の内容に関連するさらなる情報は、以下の書籍で詳しく学べます。 書籍名 : MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ 出版社 : 技術評論社 出版日 : 2024年5月22日 この書籍は、MySQLの内部構造や動作原理について深く掘り下げており、実際の運用や管理におけるパフォーマンスチューニングの実践的なアプローチが学べます。
はい、亀井です。 yykamei という名前でインターネット上では活動しています。所属はタイミーです。 今回は Timee Advent Calendar 2024 の 19 日目の記事として、Slack の Huddles を使ったプラクティスとその背後にある考え、というタイトルで、筆を取らせていただきました。 仕事におけるコミュニケーションツールはいろいろありますが、その中でも Slack を使っている組織は多いのかなと思います。その Slack の機能の一つとして Huddles が 2021 年にリリースされました。当時はコロナ禍ということもあり、リモートワークが浸透してきた中でどのようにしてオンラインでのコミュニケーションを充実させるか?をまだまだ模索している中だったと思います。そんな中、 Discord のようにクイックに会話を開始できて、かつ、他のメンバーもリアルタイムで会話に参加できる機能として Huddles がリリースされ、当時の私は少し驚きました。 これまでも Zoom だったり、 Google Meet だったりでビデオチャットは可能でしたし、それこそ Slack Apps などを使えば、簡単にそれらのビデオチャットを呼び出してインスタントに会話が可能でした。しかし、 Slack の Huddles はそれが普段使っているチャットツールに組み込まれていると言うところに大きなアドバンテージがあると思います。そして、誰がどこのチャンネルで会話しているのか?が見える、というのも魅力的なポイントかと思います。 組織のカルチャーにもよりますが、 Slack を使っていてよく Huddles を利用している環境だと、自分が join しているチャンネルのいずれかで Huddles による会話が複数並行して行われることになります。実際のオフィスにおいても会議室は複数あって同じ時間帯に複数の会議室でミーティングが行われることは普通のことですが、それが可視化され、それぞれのミーティングに誰がいるのか?を知ることができるのは意外とメリットが大きいと感じます。どういうメリットかというと「賑やかでいいなー」というメリットです。はい、実利的なメリットというよりも感情面でのメリットです。 Slack を起動すると一目で賑やかさが可視化されるわけですよね。実際に賑やかなのかどうかはわかりませんが、人と人とが会話しているのだからおそらく賑やかなはずです。 Slack Huddles を使ったプラクティスは世の中にすでにたくさんあると思いますが、ここでは私が考えるもの、またなぜそれらを使うのか?を列挙してみようと思います。 Huddles を開いて作業する Communication は大事ですよね。その Communication を extreme にやるためには会話をする機会が必要です。では会話をする機会を増やすにはどうすればよいのか?という問いに対する答えがこのプラクティスです。リモートワークが主流ではなくメンバーの多くがオフィスで働いている場合は「ちょっといいですか?」という会話ができました。とはいえ「ちょっといいですか?」という声がけ自体、ハードルが高いと感じる場合もありますよね。私はありました。そして、リモートワークが中心の働き方になるとそのハードルの高さ自体感じることなく会話する機会は失われました。特にコロナ禍でこの問題が顕著になりましたね。この問題に対して、多くの組織があの手この手でバーチャルオフィスのツールを導入したり「雑談をする」という時間をカレンダー上でセットしたりして工夫をしてきました。そうした工夫のうちの一つが Huddles を開いて作業する、です。 誰かが Huddles を開いて何かをしていると「なんだなんだ?」という感じで興味を持ってくれた人が Huddles に参加してくれます。そうなったら雑談をしてもよいですし、軽く挨拶をかわして作業を続けてもいいでしょう。 このプラクティスは、チームが継続的に会話をすることに慣れていない、あるいは、会話をすることに対して価値を感じていない場合に有効です。 Communication が大事、という価値観をチームに押し付けてもよいのですが、そうするとチームからは信頼されなくなるでしょう。 Communication も大事ですがチームからの信頼も大事です。もしかしたら、誰もその Huddles に来ないこともあるかもしれません。それでもいいのです。ただそこにいるという状態をまず作り出すこと、それ自体に価値があります。そして、何かのきっかけでチームの誰かが話しかけてくれるかもしれません。そうしたときにその会話を楽しみましょう。そのうち「ここに来れば仕事上の相談ができるかもしれない」という雰囲気を作り出せるかもしれません。そこまでいけばチームが Communication の価値に気づいている可能性があります。 注意点としては、このプラクティスを行うと、ずっと Huddles にいる、という状態になってしまいがちということです。 Communication は大事ですが、人間性も大事です。休みましょう。8時間ずっとオンラインというのはどう考えても非人間的です。無理のない範囲で実施するようにしましょう。 また、もう一つ注意点があります。 Huddles から抜けることを促す、あるいはいつでも Huddles から抜けることを奨励しましょう。というのも、一度会話に入ったら途中で抜けることに躊躇する人もいるからです。そうなると、「抜けたいのに抜けられない」というふうになってしまってせっかくの Huddles の場がその人にとっては好ましくないものに変わる可能性があります。 Huddles というのは出たり入ったりが自由なものなのだ、という雰囲気を普段から醸成しておくとよいでしょう。 他のチームの Huddles に飛び込んでみる 他のチームが複数人で楽しそうに会話しているのをみると思わず飛び込んでみたくなりますよね。飛び込みましょう。わざわざ会議用のビデオチャットではなくあえて Slack で Huddles をしているのですから、これは誘われていると言っても過言ではありません。 なぜこれをするのでしょうか?自分のチーム外のことは普段の業務に関係することは少ないでしょう。その時間を使うぐらいなら普段の業務に時間を割り当てて締め切りが迫っている自分の作業に集中したくなります。もちろん、自分のタスクが逼迫しているなら無理をしてはいけません。ただ、もし自分の作業に余裕があるのであれば、他のチームの働き方を覗いてみると意外と発見があるものです。私が最近発見したことは次のようなものです。 あるチームのリファインメントのやり方が勉強になった あるチームのタスクの切り方が勉強になった 「モブプロをやる」と言っても「モブプロ」の定義はチームによって異なるようだ スプリントレビューに対する態度が勉強になった あるチームが適用したコードが自分のチームに開発に影響することがわかった 同じ組織とはいえ隣のチームは仕事の仕方が異なります。もしかしたら文化も異なるかもしれません。同じ言葉なのに異なる意味で会話していることもあります。そうした「異なるもの」に触れることは新たな洞察を得ることにもつながり、学びになります。自分のチームをもっと効果的にアウトカムを生み出すチームにするにあたって「これなら自分のチームに取り入れるとうまくいくのではないか?」という発見があるかもしれません。もちろん、こうした学びは書籍やインターネット上の情報を活用しても得られなくはないでしょう。ただ、そうした学びの実践を目の当たりにできる機会は少ないです。 Huddles でのリアルな会話を目撃することは、そうした体系立った知識がどのように活用されているかを発見する場として活用できるのではないかと思うのです。 加えて、他のチームの Huddles に入ることで単純にいろいろな人と知り合いになれます。やはり知り合いが社内に多いと困った時に頼りやすいですよね。実際、最近 SQL のパフォーマンスについて相談させてもらって非常に助かりました。「ちょっと話したことがあるから頼ったら助けてくれるかもしれない」というぐらいには簡単に知り合いになれるのがこのプラクティスの魅力です。 とはいえ、私も嫌われたくはないので空気を読んで Huddles に入るかどうかは見ます。たとえば、 Huddles を始める前後のやりとりで何かしら深刻そうな会話がテキストで行われていたり、なんとなく 1on1 のような空気感の会話なのであれば入らないようにしています。この読みが当たっているかどうかはわかりません。もしかしたらあまり深く考えずに飛び込んでもよいのかもしれませんが、そのあたりはもともとあった人間関係も影響すると思います。 Huddles にトピックを設定する Slack Huddles でトピックをセットできるのはご存じでしょうか?このトピックをセットすると Huddles に入っている人からも入っていない人からもどのような会話が行われているのか?がわかって便利です。トピックをセットする目的は、雑談ではなく「今これについて話している」というのを宣言し、 Huddles に入っていないがそのトピックに対して興味がある人を誘い込むことにあります。このプラクティスは、特定の誰かをメンションしたいわけではないが特定のトピックについて話したい、そして、そのトピックに関心のある人を呼びたいときに有効です。 プロジェクトやタスクなどすでに実施することが決まっている業務の場合、関係者は決まっているでしょう。しかし、次に何をするか?という探索などでは、柔軟な考えを取り入れたいですし様々な人の意見を聞きたいでしょう。多種多様な職種の人たちを集めてざっくばらんに話すことができればもしかしたら新しいアイディアが生まれるかもしれません。そうしたときにどうやって人を集めるか?というのは意外と難しいです。そこで Huddles のトピックを広告のように掲げることで関心のある人を呼び込むのです。 これは、「Huddles を開いて作業する」というプラクティスと組み合わせて使うこともできるでしょう。たとえば、パフォーマンスチューニングに取り組んでいるとします。その取り組み内容である「パフォーマンスチューニング」をトピックにセットするとどうでしょうか?チューニングが好きそうな人たちを呼べそうな気がしませんか? このように Huddles のトピックは人を呼び込むことを目的としています。呼び込んだ後に途中から雑談に変わったらトピックは変更すればよいでしょう。 最後に ここまで Slack Huddles のプラクティスをみてきました。どれも基本的なものですが、それを使う背景を考えてみると案外面白いですね。実はこれまでほとんど何も考えずにこうしたプラクティスを行ってきたのですが、こうしてアドベントカレンダーを書く機会をいただいたことでそのプラクティスの背景が言語化されました。この場を借りてアドベントカレンダーの機会をいただいたタイミーのプロダクト組織に感謝いたします。
こちらは Timee Product Advent Calendar2024 の18日目の記事です。前日は @ryopeko による「 RubyWorld Conference 2024に参加してきた 」でした。 こんにちは。タイミーでバックエンドのテックリードをしている @euglena1215 です。 タイミーではモノリスな Ruby on Rails アプリケーションに一定の規律を設けるために Packwerk を導入しています。 A Packwerk Retrospective であったように、Packwerk はあくまでツールであり鋭いナイフです。ツールは使い手が意図を持って扱わないとそれに振り回されて怪我をしてしまいます。 この記事では、それぞれのチェッカーがどんな目的を達成するために使えるものなのかを自分なりに整理してまとめてみます。   Packwerk 自体はあくまで依存グラフを作成するだけであり、どんな検査を行うかは決定しません。どんな検査を行うかを決定するのはパッケージごとのチェッカーの設定です。この記事を通して「Packwerk での議論が行われる際にはチェッカーの設定もセットで行われるようになるといいな」という微かな期待もあります。 前提 タイミーでは packwerk , packwerk-extensions で提供されているチェッカーのうち、Dependency Checker と Privacy Checker を利用しています。packwerk-extensions には他に Visibility Checker, Folder-Visibility Checker, Layer Checker が存在しますが、今回は省略させてください。 Dependency Checker Dependency Checkerの概要図 パッケージ間の依存関係を管理・検査するためのチェッカーです。これが唯一 Packwerk 本体が提供しているチェッカーになります。 機能の詳細は https://github.com/Shopify/packwerk/blob/main/USAGE.md#types-of-boundary-checks をご覧ください。 主な使い道 CI 上で実行するテストの枝刈りを行いテストの実行時間を削減する Dependency Checker はパッケージ間の依存関係を管理するため、全てのパッケージの依存関係を厳密に管理すれば、ある変更に対して影響を受けた可能性のあるパッケージを特定できます。影響範囲外のパッケージはテストの実行を省略可能なのでテストの実行時間を削減することが可能です。 これは Shopify のブログにもモチベーションの1つとして記述されています。 Instead of running the test suite on the whole application, we can run it on the smaller subset of components affected by a change, making the test suite faster and more stable. ref. https://shopify.engineering/shopify-monolith この用途で使うためには、全てのパッケージの enforce_dependencies を基本的には strict に設定する必要があります。 # packs/foo/package.yml enforce_dependencies : strict dependencies : - bar # bar パッケージで変更があれば foo パッケージにも影響を与える - baz # baz パッケージで変更があれば foo パッケージにも影響を与える しかし、この使い道で Dependency Checker を利用する際にはいくつかの注意点が存在します。 パッケージの循環依存をなくす必要がある 循環依存があると、全てのパッケージが影響を受ける可能性ありとしてマークされます。 例えば、A → B → C → D → A という依存があると、どのパッケージを変更しても全てのパッケージのテストを実行する必要があるため実行すべきテストを削減することができません。 循環依存があると全てのパッケージに影響が波及する図 そのため、どうやって循環依存を減らすかが重要です。 Shopify では、依存の方向を変え循環依存を減らすために ActiveSupport::Notifications の pub/sub のような機構を導入したと2020年の記事には記述されていました。 Inversion of control means to invert a dependency in such a way that control flow and source code dependency are opposed. This can be done for example through a publish/subscribe mechanism like  ActiveSupport::Notifications . ref. https://shopify.engineering/shopify-monolith が、2024年の記事では「結果としてコードが理解しにくくなったこともしばしばあった」と記述されていました。 We relied heavily on inversion of control, for example, to extract package references out of base layer code. These changes introduced indirection that, while resolving the violations, often made code harder to understand. ref. https://shopify.engineering/a-packwerk-retrospective ActiveSupport::Notifications をアプリケーションロジックで多用している Rails アプリケーションに出会ったことがないのでどのような書き味・読み味になるのかは私には分かりません。 しかし、同じように依存方向を反転させる ActiveRecord の Callback は馴染み深いのではないでしょうか。ActiveRecord の Callback は適切に使えば便利ですが、使いすぎると苦しめられることは Rails エンジニアのみなさんはよくご存知だと思います。同様のことが起きていないといいのですが果たして…。 Sorbet の利用が実質的には必須 テストの実行を省略するためには厳密な依存チェックが行えている必要があります。feature branch では CI が通っていてマージをしたのに、実は失敗するテストケースが存在していたなんて考えなくもないですよね? Packwerk はパッケージ間のクラス・定数の呼び出しを検出するツールです。そのため、引数として他パッケージで管理しているクラスのインスタンスが渡された場合、コード上にはクラスが登場しないので検知することができません。 # packs/foo/app/models/foo.rb class Foo # 引数 bar は bar パッケージで管理している Bar クラスのインスタンス def foo (bar) # bar.bar_method を呼びだすことで bar パッケージへの依存が発生しているが # packwerk は `Bar` が登場しない限り依存を検知できない! bar.bar_method end end Sorbet のような引数のクラスを評価するような実装があることで初めて検知することができます。 # packs/foo/app/models/foo.rb class Foo # sorbet ではランタイムで `Bar` を評価するので packwerk が依存を検知できる! sig { params( bar : Bar ).void } def foo (bar) bar.bar_method end end これは Shopify の Rails アプリケーションでの Sorbet による型アノテーションのカバレッジが十分に高いことが前提になっているからだと思われます。 こちらに関しては実質的に Sorbet に依存せずとも厳密なチェックができるようにカスタマイズするための機能提案が行われていますが、1年以上前から動きがありません。 github.com Shopify からすると今のままで困ってないわけですし、新たな機能を追加することで保守が大変になるので困るのは理解できます。fork して自分で保守を行う覚悟がないのであれば、この用途で使うためには Sorbet の利用が実質的に必須である状態は避けられなさそうです。 基盤パッケージが他パッケージに依存していないことを維持したい 全てのパッケージに関してパッケージ間の依存管理するのは諦めて、最低限基盤パッケージのような他パッケージに依存しないパッケージのみ検査を行うという使い道もあると思います。 この使い道で Dependency Checker を利用するなら以下のような設定になります。 # packs/no_dependency_package/package.yml # 他パッケージに依存しない基盤的なパッケージ enforce_dependencies : true | strict dependencies : [] # packs/normal/package.yml # 他パッケージに依存して動作する一般的なパッケージ enforce_dependencies : false dependencies : [] タイミーでは、 ApplicationRecord や ApplicationController など Rails が提供している基盤クラスを rails_shims パッケージとしてまとめています。このパッケージが具体の機能に紐づく module を include しているのは好ましくありません。 この使い方をすることで、明らかに依存してはいけない依存を検知することができます。 どの使い道が良いと考えているのか 完全に個人的な意見にはなりますが、Shopify の状況を見るに依存グラフを作成することによるテストの実行時間の削減は現実的ではないと考えています。 そのため、基盤パッケージの意図しない他パッケージへの依存の検出など本当にパッケージ間の依存を管理したい一部のパッケージのみ Dependency Checker を有効にし、一般的なその他パッケージは無効にしておくのが良いのではないかと考えています。 Privacy Checker Privacy Checker の概要図 packwerk の文脈での private なクラス・定数への参照を管理・検査するためのチェッカーです。元々 packwerk 本体が提供するチェッカーでしたが、packwerk-extensions に切り出されました。 機能の詳細は https://github.com/rubyatscale/packwerk-extensions?tab=readme-ov-file#privacy-checker をご覧ください。 主な使い道 パッケージの利用方法を絞りたいとき パッケージで扱っているロジックが複雑で「ここを変更した際は一緒にここも変更しないと不整合が発生する」といった暗黙的な依存が発生しているときは、安全な利用方法を Public API として提供しておくのが安心です。 この使い道で Privacy Checker を利用するなら以下のような設定になります。 # Public API を提供して利用方法を絞りたいパッケージ enforce_privacy : true | strict # パッケージ外からどのクラスを参照しても構わないパッケージ enforce_privacy : false 一方、packwerk の開発元である Shopify では Privacy Checker をうまく活用できなかったために packwerk 本体からは削除され、packwerk-extensions に切り出されたのも事実です。何が起きていたのかを分かる範囲でまとめておきたいと思います。 Under Deconstruction: The State of Shopify’s Monolith では「Privacy Checker を守ることだけを目的としたほとんど意味を持たない Public ラッパークラスが量産されていたことに気付いた」との記述がありました。 When we ignored the dependency graph, in large parts of the codebase the public interface turned out to just be an added layer of indirection in the existing control flows. This made it harder to refactor these control flows because it added additional pieces that needed to be changed. It also didn’t make it a lot easier to reason about parts of the system in isolation. ref. https://shopify.engineering/shopify-monolith また、Packwerk から Privacy Checker を削除し、packwerk-extensions に切り出すことが決まった GitHub discussion での議論 では「Privacy Check に違反したコードはただ public ディレクトリに移動されるだけ、もしくは package_todo.yml に記録されて忘れ去られるだけ。これらを対処するためにより悪いコードが生み出される」との記述がありました。ここでの「悪いコード」とは上記のほとんど意味を持たない Public ラッパークラスだと予想しています。 So far at Shopify, we didn't see much value in the privacy checks. Most of our packages have this option disabled and when we do have them enabled, people mostly fix this kind of violations mostly by moving files around, without improving the APIs, or, even worse, people just record the deprecations and forget about them. Those ways to solve this violation are actually creating worse code, instead of improving it. The public folders are full of different concepts mixed together, and people are getting annoyed with "Packwerk failing on me with something I can't fix" and just avoiding the tool all together. ref. https://github.com/Shopify/packwerk/discussions/219 このような状況になった実際の原因は Shopify に直接聞かないと分かりませんが、自分なりに予想するに Public API を定義することに価値を感じていない開発者に対しても Public API の提供および運用を強制させてしまったことにあるのではないかと考えています。 投資対効果に対して効果を感じられていない場合は、投資を最小化させるのが最も効果的な行動です。 どの使い道が良いと考えているか やはり他パッケージからの利用方法を絞りたいときに活用するのが良いと考えています。しかし、Shopify の事例を踏まえるとまずは Public API を提供することに価値があると感じられるパッケージでのみ有効にするのが良いのではないでしょうか。 新規で作ったパッケージに対して Privacy Checker を有効にし、Public API の定義を頑張ってみるのも良いと思います。 最初は全てを無効にした状態から始めてもいいじゃないか、気楽に行こう 個人的には最初は全てを無効にした状態で導入を行い、機能・ドメインごとでディレクトリが分かれているだけで他は特に何もしない Rails アプリケーションとして運用してみるのも悪くないと思います。 社内でこのような運用をしているパッケージが存在しますが、「触るファイルがまとまっているのでコードリーディングがしやすくなった」とのポジティブな声もありました。一方「普通の Rails のディレクトリ構造と違うのでギョッとする」という声もあります。 ただし、チェッカーを有効にする際は意図を持って有効にしましょう。繰り返しますが、Packerk はあくまでツールであり鋭いナイフです。ツールは利用者が意図を持って扱わないとツールに振り回されて怪我をしてしまいます。 全てを有効にした状態から始めると大量の package_todo.yml の荒波に飲み込まれてしまい、気付くと package_todo.yml を空っぽにすることが目的になることがあります。何度でも言いますが Packwerk はあくまでツールです。自分たちの開発スタイルを Packwerk に合わせるのではなく、Packwerk の設定を自分たちの開発スタイルに合わせましょう。   明日は yykamei の「Slack の Huddles を使ったプラクティスとその背後にある考え」です。お楽しみに!
モノリス特有の運用課題 こんにちは。バックエンドエンジニアの須貝です。 タイミーのバックエンドAPIはモノリスなRuby on Railsアプリケーションです。2024年12月現在、このリポジトリ上で10程度のチームが開発しています。 モノリスは利点も多いのですが、チームが増加するにつれて運用面でモノリス特有の難しさを感じることも増えてきました。例えば、SentryやDatadogで何かエラーや問題を検知しても「これはどこのチームの持ち物なのか」という責任があいまいになってしまい改善がなかなか進まない、基盤的なチームがエラーのトリアージをするにしても調査の負担が大きい、といった課題がありました。 SentryとDatadogにコードオーナーを送る 「まずどこのチームの持ち物なのかわかりやすくしよう」ということで、SentryとDatadogにコードオーナー(リポジトリ内の特定のファイルやディレクトリに責任を持つチーム)の情報を送信するようにしました。すでにバックエンドのリポジトリではGitHubのCODEOWNERSを導入していたのでこちらを利用しました。 なお、CODEOWNERSファイルのパースと、ファイル・クラスなどからコードオーナーを取得する処理ではCodeOwnershipというgemを活用しています。 github.com やることはシンプルでSentryとDatadogに情報を送信する際に、CodeOwnership gemを使ってコードオーナー名を取得し、tagのような形で付与してあげるだけです。以下、それぞれの実装例を紹介します。 Sentry編 まず、Sentryにコードオーナーを送信するところから見ていきましょう。 CodeOwnershipはbacktraceを渡してコードオーナーを返すこともできるので、Sentryにコードオーナー情報を送信する実装も簡潔です。以下の実装例のようにsentry-ruby gemの設定に数行追加するだけで済みます。 Sentry .init do |config| # ...諸々省略 config.before_send = ->(event, hint) do if hint[ :exception ] # コードオーナー名を取得 code_owner = CodeOwnership .for_backtrace(hint[ :exception ].backtrace)&.name || ' unknown ' # code_ownerというtagでSentryに情報を送信する event.tags[ :code_owner ] = code_owner end end end CodeOwnership.for_backtrace は渡したbacktraceを先頭から見ていき、コードオーナーが見つかった場合はそのコードオーナーを返す便利なメソッドです。 これでSentry側にtagとしてコードオーナー情報を送信するようになったので、Sentryの管理画面上でも確認できるようになっています。下記の画像のようにIssueの画面のTagsにコードオーナー名(この例ではWorking Relationsというチーム)が表示されています。 Sentryの画面で見た様子 さらに便利にするためにSentryから飛んでくるSlack通知でもコードオーナー情報を見られるようにしました。SentryのAlertルールの編集で「Set conditions」の「THEN」の「and show tags」の箇所に code_owner (上記のコード例で設定したtag名)を追加するだけです。 SentryのAlertの編集画面 これでSlack通知で下記のようにコードオーナー名(この例ではIronBankというチーム)が見られるようになりました。 SentryからのSlack通知 Datadog編 続いてDatadogのAPMでもコードオーナー情報を見られるようにしていきます。Controller(APIリクエスト)とSidekiqのjob(非同期処理)の2パターンに対応します。 まずControllerです。下記のようなModuleを書いてControllerでincludeします。 module DatadogTaggable extend ActiveSupport :: Concern included do before_action :set_datadog_tags end private def set_datadog_tags span = Datadog :: Tracing .active_span return unless span code_owner = CodeOwnership .for_class( self .class)&.name || ' unknown ' span.set_tag( ' code_owner ' , code_owner) rescue StandardError => e Rails .logger.warn( " Failed to set code_owner tag to span: #{ e.message } ( #{ e.class } ) " ) end end すると下記のようにDatadogのAPMでコードオーナー名(この例ではWork Wellというチーム)が確認できるようになります。 Datadog APMのTrace 次はSidekiqです。下記のようなMiddlewareを書いて上記のModule同様にDatadogのspanにtagをセットしてあげましょう。 class SidekiqDatadogMiddleware include Sidekiq :: ServerMiddleware # loggerを使用するため def call (worker, _job, _queue) span = Datadog :: Tracing .active_span if span code_owner = CodeOwnership .for_class(worker.class)&.name || ' unknown ' span.set_tag( ' code_owner ' , code_owner) end rescue StandardError => e logger.warn( " Failed to set code_owner tag to span: #{ e.message } ( #{ e.class } ) " ) ensure yield end end あとはSidekiqの設定で上記のMiddlewareを追加してあげれば完了です。 Sidekiq .configure_server do |config| # ...略 config.server_middleware do |chain| chain.add SidekiqDatadogMiddleware end # ...略 end おわりに 実際に上記の仕組みを導入してみて、自分がSentryのエラー通知をよく見ているというのもあり、ぱっと見で担当チームがわかるのはだいぶ助かるなあと日々感じています。主要なエンドポイントやJobは自分の中に脳内マッピングができている(気がする)ものの、さすがに限界を感じ始めていたのでこれ無しではもう生きていけません。後はSentryは最終的にはSlackでメンションまでできるようにしたいのですけど、あまり賢い方法が思いつかずに困っているので良い案のある方は教えてください。 また、上記では紹介しきれなかったのですが、ログにもコードオーナー名を付与しているのでDatadog Logsでもコードオーナーを見られるようにしています。コードオーナーを条件に入れてアラートを設定したり、ダッシュボードを作ることも可能になっているので、チーム単位でのオブザーバビリティの向上にも寄与するのではないかと考えています。 最後に自分が所属しているRailway(Rails WayではなくRailway)というチームではこんな感じでバックエンドアプリケーションの横断的な課題を解いたり、他のバックエンドエンジニアの開発生産性を上げたりするような活動を行っています。なんと、いま絶賛一緒に働いてくれる方を探しているので、カジュアル面談でお話しましょう。 product-recruit.timee.co.jp
英語YATTEIKI
Timee Advent Calendar 2024 13日目の記事です。 こんにちは!バックエンド・エンジニアの松岡です。 僕は2024年3月にタイミーに入社して、オフショア開発チームでブリッジ・エンジニアをしています。 チームメンバーの多くはオフショア先のベトナムの方々で、僕はそんなみんなと一緒にわいわい開発しています。 そんなチームではコミュニケーションの多くが英語で行われていますが、僕の英語力は、、、、 ということで今回は、英語学習について僕の取り組みを紹介します! 目次 過去の英語力 学習をはじめるきっかけ 目指すゴール CEFRとは B2を選ぶ理由 ゴールまでの学習量 学習方法 Speak 英語コーチ-イングリッシュおさる NHKゴガク 学習を継続する方法 現在の英語力 最後に 過去の英語力 まず以前の僕の英語力は中学生レベルでした。 どのように中学生レベルかというと、英語を学習したのが中学時代だけだったということです。最後の学習機会はきっと高校受験だったと思います。 社会人になってから何度か英語を使う機会がありましたが、今のように学習することはありませんでした。きっと当時の自分には英語学習は優先度の低いことだったのでしょう。 学習をはじめるきっかけ 僕はソフトウェア・エンジニアを20年以上やっています。 その過程でそこそこに技術力を成長させてきましたが、だんだんと自分に伸びしろを感じなくなっていました。 そこで、自分の成長に伸びしろがありそうなものを見渡したときに気になったのが英語でした。 そして次のような意欲や好奇心をもったことがはじまりでした。 伸びしろがあるとはいえ、若くない自分がいまから成長できるのか!?!?やってみるしかない! 英語を覚えたら僕の人生の可能性はどれくらい広がるだろうか?気になる!やるしかない! 一方で、それまで趣味としてやってきた技術の勉強をいったんやめる決断もしました。両立できるほど時間がないと感じたからで、それくらい真剣に取り組むことにしました。 目指すゴール 目指すゴールはCEFRのB2で、そのためにやることは600時間の学習です。 CEFRとは CEFRとはCommon European Framework of Reference for Languageの略で、外国語の習熟度を測る国際基準です。セファールと呼ぶようです。 ja.wikipedia.org A1からC2まで等級が6個あり、僕が目指すB2はその真ん中あたりです。 出典:「 各資格・検定試験とCEFRとの対照表 」(文部科学省) B2を選ぶ理由 B2を選んだ理由には エンジニア組織の英語化変革 EX という書籍の内容を参考にしています。 この書籍には 開発チームのすべてのコミュニケーションの英語化はB2が目安 であると書かれており、自分が目指すところはそこかなと思ったのでした。 エンジニア組織の英語化変革 EX[English Transformation]~グローバル時代に生き残る強い組織作りの鉄則~ 作者: 手島 拓也 , Marc Anderson , 水畑 建一 技術評論社 Amazon ゴールまでの学習量 次の解説によると、 英語初心者がB2を目指す場合は600時間の学習時間が必要 なようです。 prontest.co.jp 600時間は1日に1時間の学習で1.7年、1日に1時間半の学習で1.1年の長さです。 僕には適度かなという印象です! 学習方法 いろいろな方法をためしていますが、僕がお世話になった教材を3個紹介します! Speak www.speak.com 一番はじめに取り組んだのがSpeakというサービスでした。2023年4月 (約1年半前) にサブスクリプション契約していて、現在も続いています。 僕は勉強方法に無知だったんですが、その当時話題になっていたことが選んだきっかけでした。 diamond.jp 学習内容はAIと行う英会話レッスンです。 会話の相手がAIなのでとても気軽にできる ことがよいところです。 英語コーチ-イングリッシュおさる www.youtube.com 中学生のときは小笠原先生から英語を学びましたが、この度はイングリッシュおさる先生から英語を学びました。 下記の動画は先生の5時間の大作です。これを見て基礎を学びことによりその後の学習がより効果的になった気がします。 www.youtube.com NHKゴガク いまもっとも学習している教材はNHKゴガクです。次の2個の番組を聴いています。 ラジオ英会話 www.nhk.jp ラジオビジネス英語 www.nhk.jp 学習方法は次のとおりです。 リスニングとスピーキングはアプリ www.nhk.or.jp リーディングとライティングはNHKテキスト (本) www.nhk-book.co.jp よいところは3点です。 毎週新しいエピソードが配信されるため、学習する教材探しに困らないこと 各講座はそれぞれに毎週5個の新しいエピソードが配信されます。僕の場合は2個の番組を聴いているのでエピソードの数は合計10個です。 1個のエピソードの長さが短いため、続けやすいこと 1個のエピソードの長さは15分です。 重要な4個のスキルを学習できること 上記のとおり、リスニング、スピーキング、リーディング、ライティングの4つを学習できます。 学習を継続する方法 何事も継続することが一番難しいですよね。 僕のおすすめは 基本的に毎日やる です。 やる日とやらない日があると、やらない日の楽さを知っているのでやる日が少し辛く、よしやるぞ!と心を整えるまでにも時間がかかります。 一方で毎日にやる(やるしかない!)ということにすると上記は不要になります。 とはいえ、疲れていたり、予定があって時間がないときもあり、そんな日にはお休みします。 現在の英語力 さてさて、こんな僕ですが2024年10月に初TOEIC L&Rを受験してきました。 受験の目的は現在地を知るためです。 結果は590点で、CEFRで表すとA1です。目標は600点以上でしたのでくやしい結果でした泣。 TOEICの受験は今回で終わり、次回からはDuolingo English Testを受ける予定です。 englishtest.duolingo.com これを選ぶ理由は次のとおりです。 はやい インターネットで受験できる。 48時間以内に結果が出る。 やすい 試験料がTOEICやTOEFLより安い。 うまい 世界中の4500以上の教育機関で認められている。 最後に 現在までの学習時間はおよそ200時間でした。 ゴールまであと400時間、来年末までに達成したい! YATTEIKI!!! 次回のAdvent Calendarで結果発表できるかもしれません。 ということで来年またお会いしましょう〜!