TECH PLAY

タイミー

タイミー の技術ブログ

299

1. はじめに 本記事は Timee Product Advent Calendar 2025 シリーズ1 19日目の記事です。 こんにちは。株式会社タイミーでiOSエンジニアをしている hayakawa です。 普段はiOSアプリの開発を担当していますが、弊社では職種の垣根を超えて異なる技術領域に挑戦する「越境」が盛んです。また、開発プロセス全体でAI・LLMを活用する流れも加速しています。 今回は、自身の技術領域を広げるためと、チームのケイパビリティ向上のために、Androidの実装を担当しました。本記事では、その際の実践的な知見やAI活用のポイントについて共有します。 2. 筆者のAndroidに関する前提知識 実装開始時点での私のAndroidに関する知識レベルは以下の通りです。 言語: Kotlin(Swiftと似ているという認識程度) UI: Jetpack Compose(SwiftUIに近い宣言的UIフレームワークという認識)。また、従来はXML(iOSのXIBやStoryboardのようなもの)を使って構築する手法があることも、知識としては持っている。 非同期処理: Coroutines(CombineやSwift Concurrencyの概念で理解) 開発環境: Android Studio(基本的な操作方法も不慣れな状態) 「概念は理解しているが、具体的なAPIやIDEの操作は手探り」という状態からのスタートでした。 3. 今回挑戦した実装内容 今回担当したのは、以下の2つの機能実装です。 ① 条件に応じた注意書き表示ロジック サーバーレスポンスに含まれる「未来の時間情報」に基づき、UIを出し分ける機能です。 現在時刻から指定時間までの「残り時間」をカウントダウン表示する。 端末の「通知設定(ON/OFF)」を取得し、文言を切り替える。 ② 条件に応じたUI制御とデザインシステムの拡張 サーバーレスポンスに含まれる特定の値に基づき、View内のコンポーネントを制御する実装です。 デザインシステムに新たなカラーバリエーションを追加定義する。 既存のリスト表示(RecyclerView)の一部に、Jetpack Compose化したViewを組み込む。 組み込んだView内に追加のコンポーネントを表示する。 なお、同様の機能をiOS版でも私が担当しており、そちらは「実装1日、レビュー・マージまで2日」で完了しています。iOS版の実装時点では、Android版はキャッチアップも含めてその1.5倍ほどの工数で完了すると予想していました。 4. AI活用の勘所:メンタルモデルに合わせた翻訳 実装は Claude Code と協調して進めました。 ここで効果的だったのは、単にコードを書かせるのではなく、 「自分の持っているiOSの知識とマッピングさせる」 というプロンプトの出し方です。 プロンプトの工夫:「iOSエンジニア向けに説明して」 具体的には、以下のような指示を行いました。(※例なので実際に入力したプロンプトとは異なります) Prompt: 「{SwiftのAPI名}を使って {やりたいこと} を実装したい。これを {KotlinのAPI名} を使ったコードと、Swiftの概念と比較した実装方針とコードを提示して このように依頼することで、AIは「StateFlowはSwiftでいうCurrentValueSubjectに近い挙動です」といった補足を加えてくれます。これにより、単なるコピペではなく、挙動を正しく理解しながら実装を進めることができました。 5. ぶつかった技術的・文脈的な壁 AIの支援があっても、スムーズにいかない場面がいくつかありました。 ① RecyclerViewとComposeの共存 最も苦戦したのは、既存のRecyclerViewの中にJetpack Composeで作ったViewを組み込む部分です。 SwiftUIであれば UIHostingController 等で比較的直感的にブリッジできますが、Androidの既存実装(ViewHolder)の中に、ComposeViewをライフサイクル的にどのように正しく配置するか 、という点は構文の違い以上に「作法」の違いが大きく、AIの出力したコードをそのまま適用するだけでは、ビルドエラーやレイアウト崩れが発生しました。 ② モデル名の不一致(ドメイン知識の欠如) また、プロジェクト固有の「命名規則」の壁もありました。 例えば、iOSで定義されているモデル名が、Androidでは少し違う名前になっているケースがありました。 例) iOS: ServiceRequest Android: RequestedService 私がiOSの感覚で「 ServiceRequest を拡張して」とAIに指示すると、AIはプロジェクト内に RequestedService が存在することを知らないため、 新しく data class ServiceRequest を定義してしまいました。 これは「AIはプロジェクトの歴史や文脈までは(コンテキストに含めない限り)知らない」という典型的な落とし穴でした。 6. 人間(Androidエンジニア)との協調 AIが出力したコードは「動く」ものの、それが「保守性の高いコード」であるかは別問題です。 そこで、Androidエンジニアのチームメンバーに対して、「同期的な相談」の時間を設けてレビューを依頼しました。 この時、私が意識して聞いたのは「合っていますか?」ではなく、以下の質問です。 「期待通りに動くんですが、Androidの流儀としてもっと良い書き方はありますか?」 AIは汎用的な正解を出しますが、現場のベストプラクティス(例えば、よりモダンなライブラリの選定や、プロジェクト独自の拡張関数の活用など)は、人間の方が詳しいケースが多々あります。このプロセスを経ることで、コードの品質を担保しました。 7. まとめ 今回の挑戦で得られた知見は以下の通りです。 AIは「言語の壁」を限りなく低くする Swiftの知識があれば、適切なプロンプトでKotlinのコードを生成・理解することは容易です。 ドメイン知識とアーキテクチャの理解は人間が補う必要がある モデル名の違いや、既存コンポーネント(RecyclerView等)との整合性は、AI任せにせず人間がハンドリングする必要があります。 「もっと良い書き方」は人間に聞く AIで「0→80点」まで持っていき、最後の「80→100点(最適化)」を専門家との対話で行うスタイルが非常に効率的でした。 AIという強力なパートナーがいれば、iOSエンジニアにとってAndroid開発(あるいはその逆)は、もはや高いハードルではありません。今後も積極的に技術領域を越境していきたいと思います。 タイミーではiOSエンジニアやその他のポジションを含めて採用活動中です! プロダクト採用サイトTOP カジュアル面談申込はこちら
こんにちは。タイミーでPlatform Engineeringグループのマネージャーを務めている橋本です。先日Architecture Conference 2025で「AI × Platform Engineeringでスケーラブルな組織を作るには」というテーマで発表する機会がありました。本記事では、その内容をもとにブログ記事として詳細にお話できる部分も交えてお伝えします。 登壇内容詳細は以下リンクのspeakerdeckも見ていただけると嬉しいです 😊 speakerdeck.com とはいえ長いので、以下に登壇内容をサマライズしたグラフィックレコーディング風の一枚絵(feat. Google Nano Banana Pro)も掲載します。 登壇内容のサマリー サマライズしすぎると解像度が粗くなってしまうので(& サマライズに少し誤りが含まれるため 😢)文章で補足をさせてください。 成長するプロダクトと組織が直面する課題 他の会社組織でも見られることかもしれませんが、プロダクトの成長に伴い、機能の増加や依存関係の複雑化、ガバナンス要求の増加などが発生し、開発者の認知負荷が高まります。 タイミーにおけるPlatform Engineering(PFE)チームの役割は、こうした認知負荷をオフロードし、開発者が価値提供に集中できるスケーラブルな基盤を提供することです。しかし、ここに大きなジレンマがありました。 PFE × スケーラブルのジレンマ 現在、PFEメンバーは開発組織全体の約5%程度で、開発者と20倍近い人数差があります。開発者を手厚く支援しようとするとPFE自体がボトルネックになってしまいますが、かといって人を増やすのも簡単ではない。いわば「がっつり支援したいけれど、やりすぎるとパンクしてしまう」というジレンマです。 特定チームに深く入り込む「Embedded」型の支援は手厚いですがコストが高く、リソースが枯渇しやすい。一方、ツールや基盤を提供する「XaaS」型はスケーラブルですが、個別具体のコンテキストに対応しにくいという限界がありました。この一例として、弊社、古屋の ブログ記事 に書かれており、以下のようなお話です。 第一段階は私が作った本人でもあることから期待通りになることはすんなり確認できましたが、第二段階ではやはり S3 に関する知識が十分にない状態ではいくらサンプルコードがあっても書くのは難しいと改めてわかりました。 さて、現状はXaaS化を強力に推進しなければチームリソースが枯渇して回らなくなる、というところまでには至っていません。しかし将来、プロダクトや組織のスケーラビリティにPFEのスケーラビリティが追いつけなくなることが想定されます。 AIで突破する:新しい支援モデルの構想 そこで現在進めているのが、「このジレンマ、AIでなんとかならないか?」というアプローチです。とはいえ、この考え自体はあまり目新しさはないかもしれません。 具体的には、DevinのようなAIエージェントをチームの一員として機能させます。以下の絵が全体的なイメージです。なお、Devinを必ずしも使う必要はなく、CursorやClaude CodeなどのCoding AgentとコードのCloneによっても代替可能です。ここでは、ChatOpsな体験が良いこともあり、Devinを採用しています。 そして、左右で仕組みのカタマリが異なり、以下の絵のようになります。 右側:AIが調査・ドキュメント化 現在のリソース設定(As-Is)をAIが出力する仕組みです。ここでの”AI調査”はKiro CLI(Q Developerと呼ばれていたプロダクト)を中心に実現されます。なお 詳細は後述します。 AIはプロンプトに従ってAWS Resource Explorerなどを通じて、実際のクラウドリソースの設定状況(As-Is)を調査し、ドキュメントを生成します。 左側:人間が補足を記述 右側で生成されたドキュメントを元に「なぜこの設定なのか」というコンテキスト(Design Addendum)をPFEエンジニアが書き加えるフェーズです。ここで最大のポイントは、「設定値(As-Is)」だけでは不十分だということです。 「なぜその設定になっているのか(Why)」という背景情報は、設定ファイルの外側 にしかありません。 ドキュメント化のループと回答フロー 以下のようなループが回るイメージです。 AIが調査・ドキュメント化 人間が補足(Addendum)を記述 1 - 2を繰り返すループ AIが統合: これらを統合して、AIが開発者の質問に答える 開発者がAIに質問を投げかけると、Devinを通じてGitHub上の「As-Isの設定ドキュメント」と「Whyの補足情報」が統合されて回答されます。 例えば、開発者が「本番環境の〇〇という名前のS3バケット、なんでPublic Accessブロックされてるの?」と聞いた時に、AIが「それは〇〇というセキュリティ要件のためです」と即答できるようになります。また、ここまで背景を理解していれば、IaCコードの自動生成もスムーズになるため、柔軟な要件定義と実装出力(IaCコードを生成するなど)をAIが行うことも可能になるのではないかと考えています。 細かな実装について ここで、登壇で細かくお話できなかった以下の図の赤い点線枠内にフォーカスを当ててお話しします。 詳細なフロー図は以下になります。 Kiro CLI(Q Developer)は必要か? 今までの流れで「Kiro CLIを使うの?terraform/HCL + Coding Agent + MCPでもできるのでは?」と思われた方もいるかと思います。このブログ執筆時点では、Kiro CLIの方が適していました。AWSの設定確認をAIが行う上で必須となる awscli等の知識をKiro CLIは事前学習済みである ことがスムーズな仕組みに繋がっています。その他のCoding Agentを用いた場合でも AWS Knowledge MCP Server やInternet上の情報等を使うことによって同様の動きにはなるのですが、 オーバーヘッド・試行錯誤が多発する 点が無視できない(図で言う"いろいろ大変"な)部分でした。 なぜAWS Resource Explorerを使うの? Resource Explorerの詳細については、弊社、佐藤の ブログ記事 をご覧ください。AIにAWSリソースを調査させる場合、問題になるのはどのリソースが作成されているかという前提知識がないことです。正攻法でいくのであればAWSの全サービスに対してCrawling(list API)をしていくことになりますが、オーバーヘッドが大きく、regionも加わると対象は膨大です。Resource Explorerが管理するリソースは、ユーザー(我々)が作成したリソースがほとんどであり、Index化されたリソース情報を容易にリストで取得できることが利点です。Resource Explorerは各リソースの情報は持たず、あくまでどんなリソースがどのARNで作成されているかを得るためのIndexとして使っています。 SQLite(sqlite3)は何のために? 当初実装時は、AIにプロンプトを通じてResource Explorerを参照させていました。ただ、APIの返却上限が1,000件のリミットがあったり、ページネーションの挙動などが間に挟まっていると取得挙動が安定せず、あるタイミングで実行した場合には5,000件のリソース取得が期待されるのに、次は3,800件しか取れていないなど、件数が毎回変わる不安定さが見られました。 そこで、ローカルキャッシュとしてSQLiteを使い、ツールで定型的にResource Explorerから取得しsqlite3にデータを保持する仕組みとしました。AIはスキーマやデータ構造をコンテキストとして渡した上で対象をグループ(S3、IAM、RDSなど)化してSQLで取得し、調査をさせることが容易になりました。(もちろん、これらのツールもAIに生成・メンテナンスさせます) いろいろお膳立てをして満を持してAIが 上記のように、アレコレとお膳立てをしてAIが処理しやすい前捌きをしながら、ドキュメント化をさせる機構を作り上げています(まだ絶賛作っている最中で、試行錯誤のタイミングです)。今年の夏頃から徐々に作ったり試行錯誤をしていますが、AIの進化(Q Developerの登場)やAIが取り扱えるコンテキスト量の増加(Claude Sonnet4.5の1Mなど)などの外部環境により改善が一気に進むことを目の当たりにしています。CodingAgent / AI Modelの進化と一緒に暫くは改善・改良を進めていくことになりそうです。 AI時代の新しいPlatform Engineering この仕組みによって、これまで「コストが高いから乱発できない」としていた「Embedded(埋め込み型支援)」を、AIが肩代わりしてくれることが期待されます。AIがドメイン知識やインフラの現状を把握していれば、極端ですが24時間365日、各チームに「Embedded」し続けることができるのです。 これにより期待できる効果は以下のようなものであり、開発者に対する支援だけではなく、PFEチーム内の知識共有やマネージャー・監査部門による確認なども、より気軽に・低コストにできる未来があるかもしれません。 確認・学習コストの低下: 開発者がAI経由で「Why」を学べる 知識共有の加速: PFE内の知識がAIに集約され、担当交代やスケーリングが容易になる 監査・統制: 設定意図が明確になり、監査にも活用できる まとめ Platform Engineeringは開発者の認知負荷を下げるためにありますが、PFEチーム自体がボトルネックになりがちです。この「人のスケーラビリティ」の限界を、「AI支援によって突破する」。これこそが、AI時代の新しいPlatform Engineeringの形になるかもしれないと考えています。 AIを活用することで、これまでコスト面で難しかった「手厚い支援」を擬似的に実現し、開発者がフルサイクルに活躍できる環境を作る。タイミーでは、こうした新しいチャレンジに一緒に取り組んでくれる仲間を絶賛募集中です! プロダクト採用サイトTOP カジュアル面談申込はこちら
この記事は Timee Advent Calendar 2025 シリーズ 2 の18日目の記事です。 はじめに こんにちは、タイミーのDREチームのchanyouです。データ基盤の開発・運用を行っています。 社内向けのAIエージェントの開発プラットフォームを構築したので、その内容をご紹介します。 なお、この記事は人力で書きました。 「AIエージェント開発プラットフォーム」を作った背景 データ活用の新たなアプローチとして AI エージェントを位置づけ、そのために AI エージェント開発プラットフォームを作りました。ここでは、その背景に触れていきます。 データ基盤のこれまでと生成AI タイミーでは、RDB に保存されたマッチングの記録やCRM に保存された営業の商談記録といった多様なデータを BigQuery に集約し、あらゆる業務で活用しています。 DRE チームではデータの集約を担当しつつ、 BigQuery に集まったデータを dbt を使ってセマンティックレイヤー 1 という形で整え、 Looker で BI として社内に提供しています。 これまでは人間向けにセマンティックレイヤーを作り込んできましたが、複雑なロジックでも一貫した結果を出力できるセマンティックレイヤーは、生成 AI にとっても有用 2 だと考えています。 特に AI エージェントは業務に与えるインパクトが大きいと思われるので、セマンティックレイヤーをAIエージェントが参照できるようにしたいと考えました。 外部サービスを導入するか、内製か 外部サービスの導入で解決できる領域も広い一方で、社内データをセマンティックレイヤーを通して生成AIで利用するソリューションには、まだ決定的なものがない印象です。 生成AIを取り巻く環境の変化は早いとはいえ、ソリューションを待つのも時間がもったいないです。ここは内製に振り切って、AIエージェントが使える未来に到達してしまって、課題を先回りしてつぶしておきたいと考えました。 気軽に作って壊せる開発プラットフォームを作る 単体の AI エージェントで Looker に接続できる構成を磨き込む、という選択もできました。 ただ、将来を見据えると AI エージェントで解ける課題は山積しており、AI エージェントをいくつもデプロイできる構成のほうが望ましいです。技術的にも、 A2A プロトコルの実証など、試したいことは多くあります。 このことから、カジュアルに作って壊せる、社内向けの AI エージェントを早く手軽に開発・運用できるプラットフォームを作りました。 「AIエージェント開発プラットフォーム」の解説 利用イメージ 社内利用のためリッチなUIは求めておらず、普段の業務の延長で AI エージェントを利用できる状態を作りたかったので、ユーザーインターフェイスは Slack に振り切りました。 以下の画像のように Slack で利用できるようになっています。 Devin のようにSlack Bot にメンションをするとスレッドが作成されて、AIエージェントとやり取りできます。 画像にはありませんが、人間と会話しているスレッドの途中で AI エージェントにメンションすると、新しいスレッドを作成して会話を行うこともできます。 AI エージェントとの会話の単位をセッションと呼びますが、このプラットフォームでは Slack のスレッドごとにセッションを作成する方針としました。Slack の同一スレッドであれば、AI エージェントはコンテキストを汲み取って会話を継続してくれます。 Slack に振り切ることで、Webでチャットインターフェースを構える場合と違い、実際にAIエージェントが利用されている様子が確認できて、その場でフィードバックを受け取れたりサポートできたりして、開発者としても体験が良かったです。 インフラアーキテクチャ インフラアーキテクチャは以下の通りです。 graph RL subgraph Slack["Slack Workspace"] Bot[Slack Bot] User[User] end subgraph GCP["Google Cloud Platform"] AE[Vertex AI Agent Engine<br/>ADK] CR[Cloud Run<br/>Slack Bolt App] end User --> |Mention| Bot Bot --> |POST /slack/events| CR CR --> |Query| AE AE --> |Use tool| AE AE --> |Response| CR CR --> |Reply| Bot Bot --> |Display| User style CR fill:#ffeaa7 style AE fill:#74b9ff style Bot fill:#00b894 ユーザーインターフェイスとして Slack Bolt アプリケーションを構築し、その背後に AI エージェントフレームワークである ADK(Agent Development Kit)の実行環境を構築する構成としました。 Slack Bolt アプリケーションの実行環境は Cloud Run を、 ADK の実行環境は Vertex AI Agent Engine を採用しました。 Agent Engine には、AI エージェントとの継続した会話に必要なセッションストレージも内包されており、永続化のためのデータベースを別途用意する必要がなく、非常に手軽に AI エージェントを運用できます。 Cloud Run と Agent Engine が各エージェントごとにデプロイされる構成としています。 リクエストからレスポンスまでの流れは以下の図の通りです。 sequenceDiagram participant User as User participant Slack as Slack Bot participant CR as Cloud Run<br><br>Slack Bolt App participant AE as Vertex AI Agent Engine<br><br>ADK App User->>Slack: "@hello-world-agent 天気を教えて" Slack->>CR: POST /slack/events CR->>AE: クエリ送信 AE->>AE: エージェント処理<br/>ツール実行 AE-->>CR: 応答 CR-->>Slack: 応答を返す Slack-->>User: 応答を表示 コード管理 コード管理はモノレポ構成を採用しています。 Slack Bolt アプリケーションのコードは共通化しており、Slack Bot の挙動(何をトリガーに、どのように返信するか)は AI エージェントによらず共通としています。 AI エージェントの開発者は、 Slack のイベントハンドリングを意識せず、エージェントの開発に集中することができます。 プレビュー環境 AIエージェントの開発に限らず、Coding Agentのおかげで日々の開発スピードは格段に向上しました。 ただ PR がすぐに作れてしまうので、コードレビューがボトルネックになってくる場面が増えてきました。 レビュー負荷を軽減するために PR ごとにプレビュー環境を作成して、各ブランチの動作確認を Slack でできるようにしました。 Slack Bot 宛のメッセージで @hello-world-agent [PR-123] こんにちは のように [PR-番号] を含めることで、その PR の AI エージェントとして振る舞ってくれます。 Slack Bolt アプリケーションで PR タグの有無を確認して、タグを含む場合はプレビュー環境宛にルーティングする仕組みとしています。 プレビュー環境へのリクエストからレスポンスまでの流れは以下の図の通りです。 sequenceDiagram participant User as User participant Slack as Slack participant CR as Cloud Run<br><br>main participant PreviewCR as Preview Cloud Run<br><br>PR-123 participant PreviewAE as Preview Agent Engine<br><br>PR-123 User->>Slack: "@hello-world-agent [PR-123] 新機能をテスト" Slack->>CR: POST /slack/events CR->>CR: PR-123 タグを検出 CR->>PreviewCR: HTTP転送 PreviewCR->>PreviewAE: クエリ送信 PreviewAE->>PreviewAE: エージェント処理<br/>新機能で応答 PreviewAE-->>PreviewCR: 応答 PreviewCR-->>Slack: 応答を返す Slack-->>User: 応答を表示 「AIエージェント開発プラットフォーム」の成果 DRE チームではこの開発プラットフォーム上で、Looker で参照できるデータをもとに営業活動をさらにスムーズに行えるような AI エージェントを開発しています。全社的に展開して利用いただいており、まだ伸びしろはありますが、社内業務の効率化に寄与しています。 Looker を参照しているため、 AI エージェントの応答に含まれる数値に一定の信頼が置けています。これを取得したい指標ごとにクエリを用意するなどしていたら、心が折れていたと思います。セマンティックレイヤー様々です。 また開発者目線では、MCP Tool と Function Tool の使い分けやユーザーの認証情報の取り回し、マルチエージェントシステムのデザインパターンなど、 AI エージェントを使ったアプリケーションを開発する上で学ぶべき点が多々あります。これらの手法をすぐに試して勘所をスピーディに掴めるようになりました。 今後の課題 AIエージェント開発プラットフォームにより、作って使えるまでは爆速で行えるようになりました。 一方で、まだAIエージェントを評価する環境がプラットフォームとして整えられていません。 AIエージェントの評価は、プロンプトのチューニングや基盤モデルの変更の際のリグレッションを防ぐためにも重要なので、評価手法を確立してプラットフォームで下支えしていきたいと思います。 まとめ 今回開発したプラットフォームを通して、データ活用のアプローチとしてもAI エージェントは非常に強力であることが分かりました。しかし、AI エージェントがデータを使った業務を支えるためには、一貫した結果を扱えることが前提にあると実感しました。(毎回ブレブレの回答をされると信頼できなくて使えない) AI エージェント開発プラットフォームが整ったことで、さらにセマンティックレイヤーの重要性が増したと思いました。引き続きプラットフォームを育てつつ、セマンティックレイヤーの拡充も進めていきたいと思います。 AIエージェントを通したデータ基盤のさらなる活用に興味のある方、ご興味があればぜひお話しましょう! 下記よりカジュアル面談がお申込みいただけます。 product-recruit.timee.co.jp プロダクト採用サイトは以下よりご覧いただけます。 product-recruit.timee.co.jp セマンティックレイヤーとは、データ加工処理において、データをビジネス用語や概念に紐づけるレイヤーのことを指します。ビジネス用語や概念の定義をコードで一元管理し、タイミーでは Looker から参照するようにしています。これにより、例えば「売上」といった単純そうで意外と定義が揺れがちな指標の定義を固めておくことで、全社員が Looker を通して共通の指標を見ながら会話することができます。 ↩ もちろんBigQuery へのクエリを直接 Text-to-SQL させるアプローチもありますが、複雑なドメインロジックを全て Text-to-SQL させるのは、クエリ結果の信頼性を担保するのが難しいと考えています。 ↩
この記事は Timee Product Advent Calendar 2025 の18日目の記事です。 評価業務、面倒ですよね 社会人になってから、特に苦手になった言葉があります。 「個人目標」「評価」の2つです。 「自己評価」「振り返り」など会社によって呼び名は違えど、自身の四半期〜1年程度の期間に実施した業務や成果を上司に報告する機会は、正社員として働いている方にはほぼ必ず巡ってくることと思います。 私も例に漏れず、少なくとも15年以上は評価されたり評価したりしながら過ごしてきました。とはいえ、被評価者のキャリアアップなど目的は会社のためだけでは無いことは理解しつつ、評価期間に費やす時間・マインドシェアを削減して顧客への価値提供に投下するリソースを最大化する術が無いか、というテーマについて日々思考しています。 そんな中でやってきたのが、AI・LLMの波です。評価業務に割くリソースを最適化するチャンスと見て、私は飛びつきました。 というわけで、手探りで実施した自己評価業務におけるAI活用事例を紹介します。 自己評価におけるAIの使いどころ GeminiやChatGPTのようなAIは「私の四半期分の自己評価をまとめたドキュメントを作って」とプロンプトで依頼すれば、過去に自分が彼らに相談した内容からそれっぽいものを作ってくれます。ただ、実際の自身の活動をまとめる上では与えている情報量が足りないので、もっともらしい誤りの内容が出力されてしまうんですよね。 そこで、いかに私の情報をAIにたくさん与えるか、という観点で考えるアプローチにしています。情報源は自らがActionとしてたくさん書き出しておき、それらを会社制度に合わせた観点でまとめてスコア付けをする箇所をAIに任せる形です。 先に言っておくと、このアプローチにかかる労力は決して軽くはありません…。ただし、精神的な負担はかなり軽減された、と私は感じています。 実施手順 以下は2025年10月時点で自己評価をする必要がある組織を想定して記載しています。 AIはCursorというツールでSonnet 4.5(2025年10月時点)を利用して実施しましたが、ローカルにあるファイルを参照できるツールであれば他のツールでも応用可能だと思います。 自己評価の準備手順 自己評価用のディレクトリを作成(1分) 任意のディレクトリに作業用のディレクトリを作成( ~/Documents/workspace/cursor など)し、その直下に「202510_自己評価」というディレクトリを作成 評価に関係する社内ルールをMarkdown形式で書き出す(10分) 「キャリアラダー」ディレクトリ 社員が属するグレードごとに求められる要件をまとめた資料 全グレード分用意 面倒な場合は自分のグレード±1グレード分だけで良いかも バリュー.md ※2025年11月にバリューが更新されたため、現在のバリューとは異なっています 2025年10月時点の弊社のバリューは上記の4つ バリューのタイトルだけでなく、その詳細もセットで書いておく 個人目標ディレクトリを作成(1分) OKR_FY2025_2ndHalfディレクトリに、今期用のOKR目標2つ分のMarkdownを保存 弊社は「期待役割」という名称でも別の目標管理が実施されているため、期待役割_FY2025_2ndHalfディレクトリに今期用の期待役割3つ分のMarkdownを保存 日常的にアクションを書き溜めるためのディレクトリを作成(1分) 「Actions_FY2025_2ndHalf」ディレクトリを作成しました。 自己評価用ディレクトリのルート階層に、サブディレクトリごとの説明を書いたREADME.mdファイルを作成(15分) 上記1.で作成した作業用のディレクトリに内包しているディレクトリ・ファイルの説明用のMarkdownを「README.md」というファイル名で作成しました。 このディレクトリに含まれるファイルの概要は以下の通りです。 ## Actions_FY2025_2ndHalf FY2025下半期に実行した主たるアクションが一つ一つファイル化されているディレクトリ。 ## キャリアラダー 私が所属する企業であるタイミーの評価制度に用いられる、各グレードに期待される働きの定義群がまとまっているディレクトリ。 ## バリュー.md 私が所属する企業であるタイミーにおいて行動の基準となるバリューの定義。 ## 期待役割_FY2025_2ndHalf FY2025下半期の自身のグレードや職能を踏まえて自身に期待されるであろう役割を自ら想定した内容が記載されているファイルをまとめたディレクトリ。 期待役割は◯◯、◯◯、…のn軸から選択でき、その詳細は上記「キャリアラダー」ディレクトリに含まれるファイルにグレード毎に定義されている。 ## OKR_FY2025_2ndHalf FY2025下半期の目標として定めて取り組むOKR目標とその結果をまとめるディレクトリ。 日常的にアクションを書き溜める 評価対象期間(弊社の場合は3ヶ月)ずっと書き足していく必要があります。 1クォーターあたりだいたい10アクション程度の量感を目指して書いています。 個人目標に進捗があり次第、上記2.で書き出した個人目標のMarkdownファイルを更新 上記の手順をすべて実施すると、以下のようなディレクトリ構造が出来上がります。 202510_自己評価/ ├── README.md ├── Actions_FY2025_2ndHalf/ │ ├── アクション1.md │ ├── アクション2.md │ ├── アクション3.md │ └── ... ├── キャリアラダー/ │ ├── グレード1.md │ ├── グレード2.md │ ├── グレード3.md │ ├── グレード4.md │ ├── グレード5.md │ └── ... ├── バリュー.md ├── 期待役割_FY2025_2ndHalf/ │ ├── 期待役割1.md │ ├── 期待役割2.md │ └── 期待役割3.md └── OKR_FY2025_2ndHalf/ ├── OKR1.md ├── OKR2.md └── OKR3.md 自己評価をまとめる手順 「202510_自己評価」ディレクトリにself-assessment.mdを作成 self-assessment.mdに以下の情報を記入 # 前提 私のグレードは以下のとおりです。 - Grade: ◯◯ - Rank: ◯◯ # 自己評価スコア > Self assessment of Abilityは、以下の7項目から選択して採点して下さい。 > 期待通りが4で、それを軸に期待を上回ったか、下回ったを総合的に判断して下さい。 > > - 7. Extremely exceeds expectations > - 6. Greatly exceeds expectations > - 5. Exceeds expectations > - 4. Meets expectations > - 3. Less than expectations > - 2. Greatly less than expectations > - 1. Extremely less than expectations > > Self assessment of Valueは、以下の5項目から選択して採点して下さい。 > 期待通りが3で、それを軸に期待を上回ったか、下回ったを総合的に判断して下さい。 > > - 5. Greatly exceeds standard > - 4. Exceeds standard > - 3. standard > - 2. Less than standard > - 1. Greatly less than standard ## Self assessment of Ability **## Self assessment of Value(理想ファースト) ## Self assessment of Value(やっていき) ## Self assessment of Value(バトンツナギ) ## Self assessment of Value(オールスクラム) # 上記のスコアを付けた理由の深堀り ## 自己評価の背景 ## 自身の振り返り ### 期待役割に対する振り返り ### OKRに対する振り返り ### 総合的な振り返り** Cursorにself-assessment.mdを執筆するよう指示する 私はiOSアプリエンジニアをしています。 この会社の評価制度の中で、現在は自己評価期間となっています。2025年5月から2025年10月までの間に実施した業務と、期首に設定したOKRや期待役割の定義、更にキャリアラダーに基づいて、自己評価をself-assesment.mdファイルにまとめようとしているところです。 202510_自己評価ディレクトリのファイルを網羅的に参照して、私の自己評価業務を手伝うべく、このself-assessment.mdファイルの内容を埋めて下さい。 202510_自己評価ディレクトリに保存されているファイル群の概要は、202510_自己評価/README.mdにまとめてあります。 記入された自己評価をレビューし、違和感がある箇所を後続のプロンプトで手直しさせる 初回は自身の直感と異なるスコア付けがされていたので、こんなプロンプトで調整を加えていきました。 嬉しいんですが、本当にそうですか?さすがにほとんどの項目が最高評価だと、ちょっと疑わしいと感じてしまいます。もっと厳しい目線も加えてもらえませんか? 今のSelf assessment of Abilitのスコアは◯◯ですが、このスコアを+1するとしたらどんな懸念がありますか? 会社が用意した壁打ち用のAIにself-assessment.mdをレビューしてもらい、受けたフィードバックのうち妥当だと感じるものをCursorにプロンプト経由でフィードバックする 会社が用意した壁打ち用の生成AIに現状のself-assessment.mdをレビューしてもらいました。 以下のフィードバックを取り入れたいと思っています。 (以下、壁打ちAIのフィードバックのうち自身が共感した内容をコピー&ペースト) 以降、Cursorが手直ししたself-assessment.mdと壁打ち用AIの間を気が済むまで往復する 今期の自己評価のために用意したファイル群は、来期の目標設定でも役立つ 自己評価が終わるとすぐに待っているのが、目標設定業務です。 前期の評価に対して上司からもらったフィードバックを 202510_自己評価/self-assessment.md に記入しておくと、目標設定業務でそれが大いに役立つことになります。 具体的には、 202604_自己評価 ディレクトリを作って 202510_自己評価 と同様のディレクトリ構成を完成させた上で、以下のようなプロンプトでCursorに相談して目標設定を進めました。 @202510_自己評価/self-assessment.md ここに前期の自己評価と、私の上長から頂いたフィードバックが記載されています。 この内容を基に、202604_自己評価ディレクトリにOKRと期待役割を記入したいです。 どのように進めるのが良いでしょうか? 目標設定については詳細を省きますが、AIが目標設定の進め方を作業レベルまで落とし込んだり、OKR・期待役割のdraft版を提案してくれたりしたので、それらをレビューして調整を繰り返す形で目標設定を終えることができました。 感想 事前準備が全手動なので、トータルの労力としてはそれほどラクではありません。しかし、その事前準備で一番労力のかかる“日常的なアクションの記録”は、例えAIを活用しない場合であっても有効な手段ですし、実際に私は評価業務にAIを活用する前から実施していたことなので、AIを使うために大変になったわけではありません。 そして前述の通り、評価業務に挑む気持ちの面ではかなりラクになったと感じています。人間は事実を記録することに専念して、その事実が会社のキャリアラダーやバリューに沿っているかを客観的に評価する仕事をAIに任せたことで、評価業務においてクリエイティブな領域の多くをAIに頼れたからです。「やれば終わる」という作業的な仕事なら見積もり・見通しも立てやすいですしね。 直近の評価期間から、会社から公式に壁打ち用のAIが提供されたことも追い風になったなと感じています。 会社の評価指標情報は自身でそれなりに網羅的にAIに提供できているつもりですが、それでも壁打ち用AIにレビューさせてみると、別の観点でのフィードバックを受けることが多かったです。そういった評価観点の欠損をAI間で補うループが作れたことで、自身の労力を多く割かずともどんどん自己評価の精度が上がっていく工程は、気持ち良さすら感じる時間でした。 …ただ、たまに「来期は◯◯の研修を受けます」といった私が希望していないNext ActionもAIによって記入されていたので、AIによって記入された内容のレビューには気を抜けません。 こうしてAIを活用して制作した私の自己評価は上司のレビューを比較的スムーズに通過し、結果的に評価期間中も私が本来やりたいと感じていた業務に多くの時間を割くことができました。 開発業務をAIの力で効率化する話はよく挙がりますが、会社員として働くエンジニアの仕事は開発だけではないはずです。 この記事を読んだ皆さんに「自分が苦手な業務をどうにかAIに助けてもらえないか」と思考をしてみるきっかけにこの記事がなっていれば幸いです。 お話ししましょう! 私、岐部( @beryu )はタイミーでiOSアプリエンジニアをしています。iOSアプリ開発の中でのAI活用の情報共有だったり、評価業務の悩みを誰かに話したいだけでも結構ですのでw、ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
この記事は Timee Product Advent Calendar 2025 16日目の記事です。 はじめに 株式会社タイミーでAndroidエンジニアをしているみかみです。今年からはiOSの機能開発にも携わっています。社外ではKotlin Multiplatformを用いた開発やDroidKaigiスタッフとしての活動など、技術とコミュニティの両面から開発に関わっています。 今年は、try! Swift Tokyo 2025とDroidKaigi 2025の2つのカンファレンスに参加しました。どちらも現地ならではの熱気があり、エンジニアとしての視野を広げる貴重な経験でした。技術に向き合う姿勢や、コミュニティの多様さに触れて多くの刺激をもらいました。 本記事では、それぞれのカンファレンスを通じて感じたことや印象に残った出来事、そして今後の活動について振り返ります。 try! Swift Tokyo 2025 去年に引き続き、 try! Swift Tokyo 2025 に参加しました。英語での発表や海外からの参加者も多く、他の国内カンファレンスと比べても国際的な雰囲気が際立っていました。セッション後のASK THE SPEAKERエリアでは、登壇者に質問をする参加者の姿も見られ、登壇者と参加者の交流が活発だったのも印象的です。さらに、カンファレンスアプリには翻訳サービスの Flitto が導入され、セッションの内容をリアルタイムで翻訳して表示する機能が提供されていました。 tryswift.jp セッションはモバイル開発を中心に、Swiftという言語がもつ表現力や技術の進化を感じさせるものが多くありました。Androidエンジニアとして特に気になったのは、「 Swift × Android: Skipが切り拓くクロスプラットフォーム開発の未来 」や「 Foreign FunctionとMemory APIとSwift/Java相互運用性 」といったセッションです。2025年には、SwiftでAndroid開発を公式にサポートすることを目的とした「 Android Workgroup 」の発足も発表されたことも記憶に新しく、Swiftのモバイル領域における立ち位置が変わってきていると実感します。 また、元AppleのデザイナーであるSebastiaan de Withさんの「 素早く実現する優れたアプリデザイン 」も印象的でした。このセッションでは、アプリを単なるツールではなく「アート」として捉え、作り手の情熱やこだわりがユーザーの感情に「共感」する体験を生み出すことの大切さが語られました。こうした視点は、日々の開発業務に追われているとつい忘れがちになるため、プロダクト開発の原点に立ち返る良いきっかけとなりました。 www.youtube.com セッション以外にも、参加者同士の交流を目的としたイベントがいくつかあり、Day 2のパーティやSTORESさん主催のランチイベントにも参加しました。さまざまな企業のiOSエンジニアと開発の話や日常の雑談を交わせたのは貴重な機会でした。立場やプラットフォームを越えた交流を通じて、多くの新しい視点や刺激を得られました。 DroidKaigi 2025 続いて、DroidKaigi 2025にもオフラインで参加しました。 今年もPRチームのボランティアスタッフとして、SNSでの広報や動画収録、YouTube配信のサポートなど、さまざまな活動に携わりました。会場ではカメラを手に歩きながら、たくさんの人でにぎわう様子を眺めたり、スタッフ同士で撮影の段取りを確認したりしていました。 スタッフ業務が中心だったためセッションはあまり聞けませんでしたが、YouTubeでの公開が非常に早く、後からセッションを視聴できたのは助かりました。扱われていたトピックは幅広く、Androidの技術的な深掘りだけでなく、デザインやプロダクト開発、チーム運営やキャリアなど、多様なテーマが取り上げられていました。セッションの感想については、ブログ「 DroidKaigi 2025 参加レポート〜Part 1〜 」でも紹介しています。 tech.timee.co.jp また、今年の登壇者でありAndroidのGDEでもある skydoves さんが執筆した 「Manifest Android Interview」の日本語翻訳 を、DroidKaigiに合わせて進めました。会期中に直接お話しする機会もあり、言語や立場を越えて技術を共有する楽しさを改めて感じました。 引用元: DroidKaigi 2025 エンジニアだけでなく、デザイナーや学生、スポンサー企業の方々など、多様な立場の人がそれぞれの形で関わり合い、学びや交流を楽しんでいたのが印象的でした。技術を軸にしつつも、人とのつながりや会場の温かい雰囲気が心に残るカンファレンスでした。 参加を通して感じたこととこれから カンファレンスに参加すると、技術への意欲が改めて湧いてきます。近年は国内外を問わず、多くのカンファレンスでセッション動画がすぐにYouTubeに公開され、オンラインでも学びやすい環境が整ってきました。 それでも、会場での偶発的な出会いや、リアルタイムで熱を共有する体験には、やはり特別な価値があると感じます。多くの人とコミュニケーションを取る中で、技術やコミュニティに向けられた熱意を肌で感じました。そうした空気に触れるうちに、自分も少しずつ新しいことに挑戦していきたいという気持ちが生まれました。前々から関心のあったOSSへのコントリビュートや海外カンファレンスへの参加など、機会を見つけて取り組んでいけたらと思います。 現在は技術コミュニティで繋がったメンバーを中心に、自分たちが「今欲しい!」と思えるAndroid開発書籍の執筆を進めています。主にテストに関する章を担当しており、着実に形にしていけるよう取り組んでいます。 技術の進化が加速する中で、学ぶことの楽しさや、人とのつながりから生まれる刺激の大きさを改めて感じました。興味を持ったことには素直に手を伸ばしながら、学びや挑戦の輪を少しずつ広げていければと考えています。 おわりに 改めて、技術カンファレンスは知識を得るだけでなく、人とつながり、自分の視野を広げるための場だと感じました。try! Swift TokyoもDroidKaigiも、参加するたびに多くの刺激と学びをもらっています。これからも、その刺激を自分の原動力として、日々の活動を続けていきたいと思います。 最後になりますが、株式会社タイミーでは、現在iOS/Androidエンジニアを募集しています。 「まずは話を聞いてみたい」というだけでも大歓迎ですので、少しでも興味を持っていただけたら、ぜひ採用ページをのぞいてみてください! hrmos.co hrmos.co
こんにちは、タイミーでバックエンドのテックリードをしている新谷 ( @euglena1215 ) です。 タイミーでは、 dependabot による依存ライブラリのアップデート PR に対して、Devin による自動レビューを導入しています。しかし、CI が失敗しているにもかかわらず、「本プロジェクトへの影響は低いと判断します」というコメントがついてしまうケースがありました。 これはレビュー時点では CI が完了しておらず、Devin がコードの差分だけを見てレビューしていたことが原因です。この問題を解決するために、CI 完了後にレビューを実施するよう、ワークフローを修正しました。 方法 従来の実装 従来は pull_request イベントの opened をトリガーにしていました。 on : pull_request : types : - opened この場合、PR が作成された直後にレビューが開始されるため、CI の結果を考慮することができません。 改善後の実装 workflow_run イベントを使用して、CI ワークフローの完了をトリガーにするように変更しました。 on : workflow_run : workflows : [ "ci branch" ] types : - completed branches : - 'dependabot/**' workflow_run イベントは別のワークフローの完了を待ってから実行されるため、CI の結果を含めたレビューが可能になります。 PR情報の取得 workflow_run イベントでは github.event.pull_request が使えないため、 gh コマンドで PR 情報を取得する必要があります。 - name : Get PR info id : pr-info env : GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | PR_INFO=$(gh pr list \\ --repo my-org/my-repo \\ --head "${{ github.event.workflow_run.head_branch }}" \\ --state open \\ --json number,title,url \\ --jq '.[0]' ) if [ -z "$PR_INFO" ] ; then echo "No open PR found for branch ${{ github.event.workflow_run.head_branch }}" echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fi { echo "skip=false" echo "number=$(echo " $PR_INFO" | jq -r '.number' )" echo "title=$(echo " $PR_INFO" | jq -r '.title' )" echo "url=$(echo " $PR_INFO" | jq -r '.url' )" } >> "$GITHUB_OUTPUT" ポイントは github.event.workflow_run.head_branch でブランチ名を取得し、そこから gh pr list で該当する PR を検索している点です。 CI結果の取得 CI の結果は github.event.workflow_run.conclusion で取得できます。さらに、失敗したジョブの一覧も取得してレビューの参考情報として渡しています。 - name : Get CI check results id : ci-checks run : | CI_CONCLUSION="${{ github.event.workflow_run.conclusion }} " echo " conclusion=${CI_CONCLUSION}" >> "$GITHUB_OUTPUT" # 失敗したジョブ一覧を取得 FAILED_JOBS=$(gh pr checks "$PR_NUMBER" \\ --repo my-org/my-repo \\ --json name,state \\ --jq '[.[] | select(.state == "FAILURE")] | map(.name) | join(", ")' \\ 2>/dev/ null || echo "取得失敗" ) これにより、Devin へのプロンプトに CI 結果と失敗したジョブ名を含めることができます。 - CI結果: ${CI_CONCLUSION} - 失敗したジョブ: ${FAILED_JOBS:-なし} レビュー指示にも CI 結果を考慮するよう追記しました。 - CI結果を確認し、失敗している場合はCIのログを読み込んで原因を分析する - CIが失敗している場合、ライブラリの更新が原因かどうかを判断する - CIが失敗している場合は、失敗の原因とマージ前に対応が必要かどうかを明記する まとめ workflow_run イベントを使うことで、CI 完了後に別のワークフローを実行できるようになります。dependabot PR の自動レビューのように、CI の結果を考慮したい処理に便利です。 ただし、 pull_request イベントとは異なり PR 情報を直接取得できないため、 gh コマンドで別途取得する必要がある点には注意が必要です。 参考: Events that trigger workflows - GitHub Docs
Android Chapter の tick-taku です。 この記事は Timee Product Advent Calendar 2025 の15日目の記事です。 はじめに 2025年は AI コーディング・AI エージェント元年であり黎明期の渦中 と言っても過言ではない年だったと感じます。 各社からこぞって AI エージェントがリリースされ、その後も息つく暇もなくアップデートされてきました。単純なコード補完ではなくタスクを任せられるようになった昨今の AI エージェントは今ではすっかりコーディングの相棒です。 実際今年の DroidKaigi や iOSDC でも AI 関連のセッションが増えていた気がします。 そんな中 KotlinFest などで MCP サーバーを作った話を聞いて「この時代に生きてるんだから自分も触れておかなければ(使命感)(遅い)」と思い、せっかく1年も終わろうとしているのでいい機会だし触ってみようとまずは MCP サーバーを作ってみました。 ちなみにこの記事を書くにあたっても AI のサポートを最大限利用しています。時代ですねぇ。。。 Kotlin で MCP サーバー作ってみる では実際に作成していきます。 ハンズオン的なドキュメントを公式が用意してくれているのでそちらも参考になります。 modelcontextprotocol.io 今回は timee-android で運用している LADR を提供する MCP サーバーを作成してみることにしました。LADR については こちら や 数日前にチームのリードエンジニアが取り組みについて投稿 しているためぜひご覧ください。 この記事では jar ファイルをローカルに作成し Claude Code と接続することを目標とします。 事前準備 以下のようなディレクトリ構成を想定します。 timee-ladr ├── ladr │   ├── 0000-ladr-template.md │   ├── ~~~ │ └── assets └── mcp    └── app また、Kotlin で作成するので MCP Kotlin SDK を利用します。 build.gradle はこちら。 dependencies { implementation( "io.modelcontextprotocol:kotlin-sdk:0.7.7" ) } base { archivesName. set ( "ladr-mcp" ) } application { mainClass = "timee.ladr.AppKt" applicationName = "ladr-mcp" } Primitives 実装に移る前に MCP サーバーがコンテキストを提供するための重要な要素に触れておきます。 MCP には Resources , Tools , Prompts と呼ばれる要素があり、この記事で触れる Resources, Tools についてはそれぞれ以下のように定義されています。(Prompts は今回触れないため割愛します) Resources : LLM に追加のコンテキストを提供する読み取り専用のデータまたはコンテンツ Tools : LLM がアクションを実行するための関数のようなもの 今回作る MCP サーバーに当てはめると、LADR の md ファイルを Resources として提供し、LADR ファイルを新規作成したり更新したりする機能を Tools として提供することになります。 Server の初期化 まずは MCP サーバーを初期化します。 fun main() { val server = Server( serverInfo = Implementation(name = "ladr-mcp" , version = "1.0.0" ), options = ServerOptions( capabilities = ServerCapabilities( resources = ServerCapabilities.Resources( subscribe = false , listChanged = false ), tools = ServerCapabilities.Tools( listChanged = false ) ) ) ) { "timee-android 内の LADR を提供する MCP サーバー" } } ここで出てきた Capabilities とはこの MCP サーバーが LLM に対して何を提供しているかを示すものであり、今回の場合は Resources と Tools を提供していることを表しています。 Resources の Capabilities を示す data class の引数はそれぞれ以下のような意味を持っています。 プロパティ 説明 subscribe クライアントが特定リソースの変更を購読できるか listChanged リソース一覧が変更されたときに通知を送るか 今回のサーバーは静的な LADR の md ファイルを提供することが目的なので基本的に更新系は false です。 Resource の登録 LADR のファイルを Resource に登録していきます。 今回は jar を作成し単独でローカルで動作させることを目指しているため、jar 内の resources/ 配下に md ファイルを格納し読み取ることにしました。この辺の実装は AI 頼りです。ありがとう🙏 fun main() { /** Server の初期化 */ getLadrFilePathsFrom()?.forEach { filePath -> val fileName = filePath.substringAfterLast( "/" ) server.addResource( uri = "ladr:/// $fileName " , name = fileName, description = "LADR: $fileName " , mimeType = "text/markdown" ) { request -> ReadResourceResult( contents = listOf( TextResourceContents( uri = request.uri, mimeType = "text/markdown" , text = readResourceFile(filePath) ?: "File not found: $fileName " ) ) ) } } } private fun getLadrFilePaths() = object {}.javaClass.protectionDomain?.codeSource?.let { JarFile(it.location.toURI().path).getLadrFiles() } private fun JarFile.getLadrFiles() = entries().asSequence() .filter { ! it.isDirectory && it.name.startsWith( "ladr/" ) && it.name.endsWith( ".md" ) } .map { it.name } .sorted() .toList() private fun readResourceFile(filePath: String ) = object {}.javaClass.classLoader.getResourceAsStream(filePath)?.let { stream -> BufferedReader(InputStreamReader(stream, Charsets .UTF_8)).use { it.readText() } } Tool の登録 チームで運用している LADR のテンプレートに沿って登録しているため少し長いですが、addTool 内で input に対して output を定義するだけです。 JsonObject の用意が大変ですね… fun main() { /** Server の初期化 */ server.addTool( name = "create-ladr" , description = "与えられたコンテキストから新しい LADR (Lightweight Architecture Decision Record) ドキュメントを作成します。テンプレートに基づいてMarkdown形式のLADRを生成します。" , inputSchema = Tool.Input( properties = buildJsonObject { putJsonObject( "title" ) { put( "type" , "string" ) put( "description" , "LADRのタイトル(何に対しての意思決定を行ったのかがわかるタイトル)" ) } putJsonObject( "number" ) { put( "type" , "string" ) put( "description" , "LADRの通し番号(4桁のゼロ埋め、例: 0001, 0042)" ) } putJsonObject( "status" ) { put( "type" , "string" ) put( "description" , "採用状況: Accepted(採用), Rejected(不採用), On holding(保留)のいずれか" ) putJsonArray( "enum" ) { add(JsonPrimitive( "Accepted" )) add(JsonPrimitive( "Rejected" )) add(JsonPrimitive( "On holding" )) } } putJsonObject( "context" ) { put( "type" , "string" ) put( "description" , "文脈: 経緯(なぜこの意思決定が生まれたのか)、観点(判断した観点)、事情(検討した事情)、参考情報など" ) } putJsonObject( "specification" ) { put( "type" , "string" ) put( "description" , "仕様、やること、やらないこと" ) } putJsonObject( "design" ) { put( "type" , "string" ) put( "description" , "設計: 特筆すべき設計や設計意図" ) } putJsonObject( "decision" ) { put( "type" , "string" ) put( "description" , "決定事項: 想定している運用や制約、再評価となる条件など" ) } putJsonObject( "consequences" ) { put( "type" , "string" ) put( "description" , "結果: 意思決定の結果を評価したタイミングで追記" ) } putJsonObject( "link" ) { put( "type" , "string" ) put( "description" , "関連するLADRへのリンク" ) } }, required = listOf( "title" , "number" , "context" , "specification" ) ) ) { request -> val args = request.arguments val ladrContent = buildString { val title = requireNotNull(args[ "title" ]?.jsonPrimitive?.content) val number = requireNotNull(args[ "number" ]?.jsonPrimitive?.content) appendLine( "# $number - $title " ) appendLine() appendSection( title = "## Date - 日付" , text = LocalDate.now().format(DateTimeFormatter.ofPattern( "yyyy-MM-dd" )) ) appendSection( title = "## Status - 採用状況" , text = args[ "status" ]?.jsonPrimitive?.content ) appendSection( title = "## Context - 文脈" , text = requireNotNull(args[ "context" ]?.jsonPrimitive?.content) ) appendSection( title = "## Specification - 仕様、やること、やらないこと" , text = requireNotNull(args[ "specification" ]?.jsonPrimitive?.content) ) appendSection( title = "## Design - 設計" , text = args[ "design" ]?.jsonPrimitive?.content ) appendSection( title = "## Decision - 決定事項" , text = args[ "decision" ]?.jsonPrimitive?.content ) appendSection( title = "## Consequences - 結果" , text = args[ "consequences" ]?.jsonPrimitive?.content ) appendSection( title = "## Link - 関連するLADRへのリンク" , text = args[ "link" ]?.jsonPrimitive?.content ) } CallToolResult(content = listOf(TextContent(ladrContent))) } } private fun StringBuilder .appendSection(title: String , text: String ?) = apply { appendLine(title) appendLine() if (text.isNullOrEmpty().not()) { appendLine(text) appendLine() } } 本来はファイルを作成するところまでできるとよさそうなんですが、 LADR の性質上 jar の中にファイルを作成しても仕方なく、テンプレートに当てはめたテキストを返すのみとなっています。 Resource の登録と合わせて、本格的にチームで運用する場合はリポジトリをクローンしていることを前提に LADR を格納しているディレクトリの path を環境変数などで MCP サーバーに伝えるなどの方法もありそうですね。 Server の起動 最後に準備した Server を起動させます。 本来 MCP サーバーのトランスポートの方式はいくつかありますが、今回はローカルに build した jar との接続を想定しているのでドキュメント通り STDIO形式 で起動します。 fun main() { /** Server の初期化 */ val transport = StdioServerTransport( inputStream = System.`in`.asSource().buffered(), outputStream = System. out .asSink().buffered() ) runBlocking { val session = server.createSession(transport) val done = Job() server.onClose { done.complete() } done.join() } } 以上でサーバーの実装は完了です🎉 動作確認 作成したサーバーが正しくレスポンスを返してくるか検証しましょう。MCP の検証には MCP Inspector が提供されています。 MCP Inspector を起動する前に以下のコマンドでアプリケーションを用意します。 ./gradlew installDist app/build/install/ 以下に作成されるので、作成されたアプリケーションの path を Inspector の起動時に指定します。 npx @modelcontextprotocol/inspector mcp/app/build/install/ladr-mcp/bin/ladr-mcp ブラウザが起動するので左ペインの Connect を実行すると正しく作成できていれば成功します。 List Resources を実行すると登録された Resource の一覧が確認できます。 MCPInspecter/Resources また、Tools タブ内で List Tools で登録された Tool の一覧が確認できます。 右側のペインに inputSchema に指定した property を入力でき、実行するとレスポンスが返ってきます。enum で指定した status はドロップダウンで選択できるようになっていて便利ですね。 MCPInspecter/Tools Claude Code との接続 では最後に Claude Code と接続して LADR が確認できるか試してみます。 接続の前に jar ファイルを build します。ただし、普通に jar ファイルを build すると resource ファイルなどは格納されないため fatJar(shadowJar) を作成する必要があります。 これは自分が Server Side Kotlin の知見に乏しいので AI に聞いて作成 task を用意してもらいましたが、もっといい方法があるかもしれません。 fatJar 作成の gradle task tasks.register<Jar>("fatJar") { archiveClassifier.set("all") archiveVersion.set("") duplicatesStrategy = DuplicatesStrategy.EXCLUDE manifest { attributes["Main-Class"] = "timee.ladr.AppKt" } from(sourceSets.main.get().output) dependsOn(configurations.runtimeClasspath) from({ configurations.runtimeClasspath.get() .filter { it.name.endsWith("jar") } .map { zipTree(it) } }) } jar ファイルが作成できたら ~/.claude.json に設定を追加します。 "mcpServers": { "ladr-mcp": { "type": "stdio", "command": "java", "args": [ "-jar", "/path/to/timee-ladr/mcp/app/build/libs/ladr-mcp-all.jar" ], "env": {} } } Claude Code を起動して /mcp list を確認して connected になっていたら成功です。試しに timee-android が LADR を採用した経緯を聞いてみると readMcpResource を利用してまとめてくれました。うちの Claude お姉ちゃんは優秀です。 ClaudeCodeによるLADRのサマライズ 最後に 「MCP ってなんかすごいやつでいい感じになんかやってくれるんだな〜」くらいのイメージでいましたが、実際に作って触れてみるとなるほどコンテキストを取り扱うためのものなんだなということがよく分かりました。MCP がいい感じにしてくれるのではなく、コンテキスト接続のプロトコルなのであくまで外部からデータを与えたり作成したりするためのものであり、いい感じにしてくれるのはエージェントだと身をもって理解できました。 秩序という意味でも “AI における USB-TypeC” とはよく言ったものです。 となると次は AI エージェントの作成にチャレンジしてみるしかありませんね。次回は Koog を利用して今回作成した MCP サーバーを利用していい感じに LADR を作成してくれる AI エージェントを試してみようと思います。最近は KMP も熱く、全てが Kotlin で書けるのでもう全部 Kotlin でいいじゃんですね🤗 実際に手を動かしてみることで学びにはなりましたが GitHub のリポジトリに上がっているものなら GitHub CLI で済みそうな気がするので、社内 DB などよりクローズドなデータなら MCP サーバーとして用意する価値があるのかもしれません。とは言え、そんなデータをエージェントにくわせていいのかは…🤔 社内データを活用するために MCP サーバーを自作しておくと色々と捗りそうですが、一方で MCP サーバーはコンテキストを消費するのであんまり追加してもエージェントが処理しきれなくて意味がないと言う主張 もあるそうです。 これからますます便利になっていくであろう AI エージェント界隈、今後の進化を楽しみながら注視していきたいですね! タイミーでは日々の業務に積極的に AI を活用しています。ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
こんにちは、タイミー取締役 事業統括兼CPOのShunです。 Timee Product Advent Calendar 2025 の12/14担当ということで、最近意識しているテーマ「シングルスレッドリーダー」について書きたいと思います。 まず前提として、一定の規模を超えた事業がさらに非連続な成長を遂げるためには(いわゆるSカーブの成長曲線の再現)、往々にして相応の大きな挑戦が必要だと思っています。そのためには、強力なアイデアを形にし、提供価値を進化させ、販促や営業の型をつくり、それをスケールさせるオペレーションを構築しなければなりません。つまり既存のやり方の地続きではない「変革」が必要です。 しかし、変革に着手するのは容易ではありません。なぜなら、一人ひとりが日々の仕事に忙殺されてしまっているからです。顧客対応も、開発も、運用も止められない。止めた瞬間に組織や個々人の目標達成は危うくなり、売上や各種KPIに影響を及ぼす可能性もあります。 そう、みんな、すでに忙しい。 けれども変革には地続きでない追加の努力が必要です。変革は、余裕ができたときに取り組む追加タスクではなく、日常業務そのものを変えるような力学が働かなければ生まれません。新しい価値や仕組み、意思決定、そして組織の再編まで求められる。では、誰がそれをできるのでしょうか? 推進力を設計するという発想:シングルスレッドリーダーとは ここで役に立つと僕が信じているのが、シングルスレッドリーダーの考え方です。シングルスレッドリーダーとは、「特定の事業領域の成功に必要な権限とリソースを与えられた、兼任ではない専任のリーダー」を指す、Amazonで提唱された考え方です。僕の前職であるGoogleにおいても、DRI(Directly Responsible Individual)という名称でほぼ同様に運用されていました。日本人としてはシングルスレッドの方が言いやすいので、僕はそちらの呼び方を使っています。 僕はこれを、単なる「責任者を置く」話だとは捉えていません。シングルスレッドとは、開始点と終着点があるプロジェクト管理の話ではなく、 「推進力の設計」 の話だと思っています。 この仕組みが解決するのは、「スキル・経験がある人ほど兼務状態になりがち」問題です。「話が早いから」「結果が期待できるから」といった理由から、短期的には合理的な側面もある一方で、兼務状態の膨張は、言わずもがな以下のような弊害を生むリスクを抱えています。 当事者の集中が分断される(思考やアクションが積み上がらない) 優先順位が毎回揺れる(何かを決めるまでが長い) 責任が薄まる(最後に腹を括る点が曖昧になる) その結果、複数の領域それぞれの雑務に追われて推進力が弱まり、全体的なアウトカムにも影響してしまうと思っています。 それゆえ、真逆のアプローチで、スキル・経験が豊富な人こそ思い切って「専任である一つのコトに向き合ってもらう」。それだけで、 一人ひとりの目線も、スピードも、アウトプットの質も段違いで変わる というのを目の前で幾度となく見てきています。 タイミーでの実装 タイミーでは、過去1年間で、既存事業とは別に実験的に 6つのシングルスレッド組織 をつくり、それぞれに事業オーナーを立てて推進してきました。 シングルスレッドリーダーたちは、事業の責任者としてバーティカルに特定の領域を管掌し、責任と意思決定権限をダイレクトに持ちます。もし任用時点でそれ以外の事業テーマも管掌していた場合には、任用のタイミングでそれらをすべて手放してもらいます(その領域は別の専任オーナーや既存の事業ラインに委譲し、推進力が分散しない構造に切り替えられるよう、最大限努力します)。多くのケースで戦略策定機能、企画・推進機能、そして営業機能はこのシングルスレッドリーダーの組織に属して、「管掌する事業」にフルコミットでフォーカスします。 一方で、プロダクトやマーケティングはシングルスレッドリーダーの管掌下には置かず、それぞれ独立した本部として残しています。その上で、ホリゾンタルにそれぞれの事業に対して担当を配置していきます。これはよく取られている形式だと思いますが、これらの機能を一箇所に固めた方が、採用・育成・型化・横展開の生産性が高くなることが大きな理由です。 この組み合わせはいわゆるメッシュ構造とも言えますが、古くからあるマトリックス組織との違いは何でしょうか。それは、シングルスレッドリーダーの組織においては「責任の一本化」と「専任性の強制力」が決定的に違うと思っています。マトリックスは事業と機能を掛け合わせる強力な概念ですが、構造上どうしても“責任の曖昧さ”が残り、責任が2軸に分散しやすく、意思決定も調整コストが高くなりがちです。シングルスレッドリーダーの組織においては、この曖昧さを一掃し、責任とリソースと意思決定を1本に束ねるように設計しています。マトリックス型組織の弱点をアップデートした形であり、専任化によって“推進力の失血”を防ぐ仕組みとも言えます。 組織デザインは常にアップデートし続けるもの もちろん、タイミーにおいても組織は日々形を変えています。シンプルなシングルスレッド体制が適する場面もあれば、複数の機能が密に連携した方が早いケースもある。成長フェーズや事業特性によって、求められる“最適な推進構造”は毎月のように変わります。だからこそ、組織デザインは一度つくって終わりではなく、事業の変化速度に合わせて絶えず再構築する「動的なシステム」であるべきだと思っています。 スポットワークというまだまだ進化を続ける業界だからこそ、戦略・戦術・組織あらゆる領域において「変革」に取り組み、事業の成長にあわせて組織も柔軟に形を変えながら、ワーカーの人生の可能性を広げるインフラづくりに真正面から取り組んでいきたいと思います。 タイミーのこういった様々な挑戦にご興味のある方、全方位で採用活動中ですので、ぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
こんにちは。日々プロダクト開発を楽しんでいるsyamです。 「AIがあれば、専門外の領域も一人でなんとかなるのでは」 そんな期待を持って、Androidエンジニアの私はWebフロントとバックエンド開発への越境に挑戦しました。結論から言うと、その期待は半分正解で、半分間違いでした。 この記事では、実際に越境してみて感じた「AIの限界」と、それを乗り越えるために必要だった「人の存在」について書いていきます。 AndroidからWebフロントへの越境は、比較的スムーズでした。 学生時代にWebフロントを触っていた経験があったことに加え、UIを組み立てる感覚や状態管理の考え方など、Androidと共通する部分が多かったためです。リポジトリの構成も比較的理解しやすく、「Androidとは書き方が違うけれど、やりたいことは同じだな」と感じる場面が多くありました。 また、画面で結果をすぐに確認できるのも大きかったです。書いたコードの結果が目に見えるので、試行錯誤がしやすく、AIに書いてもらったコードの検証もすぐにできました。 バックエンドは「異文化」だった 一方で、バックエンドはより苦戦しました。 DB設計、トランザクション管理、API設計……。コードを書くこと自体はAIの力を借りればなんとかなります。ただ、「なぜこの設計にするのか」「このトランザクションの境界はここで正しいのか」といった判断になると、AIに聞いても教科書的な答えが返ってくるだけで、サービスに合った明確な答えが返ってこないことが多いです。(AIの使い方について、自分が不勉強なだけかもしれませんが) さらに厄介だったのが、リポジトリに関する知識の壁でした。リポジトリに合った実装をどうするか。これはコードの書き方以前の問題で、AIに「このリポジトリのルールに従ってコードを書いて」と頼んでも、そもそも自分がリポジトリのルール自体を理解できていなければ、正しい指示すら出せません。 そして何より大きかったのが、既存の稼働コードを壊すことへの恐怖です。ローカルで動いても本番で何が起きるかわからない。テストが通っても、見落としているエッジケースがあるかもしれない。この不安は、専門外だからこそ大きく感じました。 前に進むためにやった3つのこと 1. 悩み続けるより、まずAIに動くコードを書かせる 最初から完璧を目指すと手が止まります。そこで取った方法は「まずAIに動くコードを書かせて、それをたたき台にする」というものでした。 これは予想通り効果的で、動くコードがあると「ここはなぜこう書いているのか」と具体的な疑問が生まれます。白紙の状態で悩むより、何かしらのアウトプットがある方が次のアクションが見えやすくなりました。 2. 複雑な既存コードはAIに「解説」させる 既存のバックエンドコードを読み解くのは時間がかかる作業でした。ここでもAIが役立ちました。 「このコードが何をしているか、ポンコツエンジニアの私にもわかるように説明して」と頼むと、処理の流れや各部分の役割を整理して教えてくれました。 特に、シーケンス図にしたり、流れを図示するとよりわかりやすくなりました。これにより、既存コードの理解が早まりました。 3. AIでカバーできない部分は、素直に人に聞く ここが重要なポイントでした。 特にリポジトリにまつわる設計判断に関わる部分は、AIに聞いても本当に合っているかの評価が難しかったです。 そういった部分は、ペアレビューの際に本職のWebフロントエンジニアやバックエンドエンジニアの方が、素直に質問すれば詳細に教えてくださいました。パフォーマンス計測の際などには、同期でレクチャーしてくださり、非常にありがたかったです。 自分では気づけない観点からのフィードバックは、AIでは得られない価値がありました。 越境できた本当の理由 振り返ると、越境がうまくいった要因は、AIだけではありませんでした。 大きかったのは、 チームに「専門外からの質問を歓迎する雰囲気」ができていたこと です。これは単に「メンバーが優しかった」という話ではなく、組織として専門外の人が質問することを良しとするスタンスがあったからだと思います。受け入れる側のWebフロントエンジニアやバックエンドエンジニアが、丁寧に向き合ってくれたことに本当に助けられました。 一方で、越境する側として意識したこともあります。レビューの負担を少しでも減らすために、事前にヒアリングして疑問点を整理してからレビューに出すようにしていました。「聞ける雰囲気があるから何でも聞く」のではなく、受け入れ側への配慮を持つことも、越境をうまく進めるうえで大事だったと感じています。 得られた成果 この越境チャレンジの結果、手が空いたときに新規画面の開発やAPIの実装などWebフロントやバックエンドのタスクを消化できるようになりました。チームのスプリントゴール達成に、Android以外の形でも貢献できるようになったのは大きな変化でした。 まとめ 「AIがあれば全部できる」わけではありません。 ただ、「AIと、専門外からの質問を歓迎する雰囲気、頼れる仲間」がいれば、越境のハードルはかなり下がります。 AIはコードを書く手助けや既存コードの解説をしてくれます。しかし、「なぜそう書くか」の設計判断やドメイン知識の補完には、仲間のサポートが不可欠でした。 そして、そのサポートがうまく機能するには、受け入れ側の丁寧な姿勢と、越境する側の配慮、その両方が必要だと感じています。 仲間のWebフロントエンジニアやバックエンドエンジニアの方には、非常にお世話になりました。感謝しかありません。 タイミーで、そんな仲間たちとプロダクト開発をしてみたいと思った方は、ぜひ一度お話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
はじめに こんにちは、バックエンド・エンジニアのMaxx (マックス) です。 私は現在、オフショア開発チームのブリッジ・エンジニアという役割でプロダクト開発に携わっています。 本記事は Timee Product Advent Calendar 2025 の12日目の記事として、私が2年前から取り組んでいる英語学習の体験を共有します。 英語を学習している方々の参考になれば、または興味がある方々が学習を始めるきっかけになればうれしいです。 オフショア開発チームと英語 「チームの生産性を上げ、成果をたくさん出し、そしてそれを価値につなげていくには私に英語力が必須だ、やるしかない!」 これは私がオフショア開発チームに携わって感じてきたことで、また英語学習のモチベーションを維持させてくれているものです。 私が所属するオフショア開発チームは、私とオフショア先の外国籍の方々で構成され、共通言語は英語です。私の役割は、チーム内の作業管理、チーム内外の中継、またエンジニアとしての技術的な支援などで、情報伝達と意思疎通が必須です。 これらを怠ると、理解の浅さによる考慮漏れ、誤解による手戻り、信頼性の不足または心理的な壁によるモチベーションの低下などを起こします (昔も今もときどき起こります) 。 オフショアメンバーの中に日本語話者がいるので通訳をお願いできるのですが、複雑な仕様や設計の意図などの技術的な話題の場合は、エンジニア間で直接話したほうが効率がよいですよね。 ということで、チームが上記のような問題を防ぎ、よい成果を出すためには英語でのコミュニケーションが必須な環境なのです。 英語学習の動機と目標 そもそも、なぜ英語学習を始めたのか、その根底にあるのは、単純な「好奇心」と「向上心」でした。 実は私はソフトウェア・エンジニアとして20年以上のキャリアがあり、これまでソフトウェア・エンジニアの技能に対する好奇心や向上心によって自身を成長させ、この業界に生き残ってきました。 20周年を迎えて、さらに自身を非連続に成長させそうなものはないかと未知の領域を見渡したときに見つけたのが英語で、そこで次のように考えたのでした。 (今であればLLMも未知の領域の候補になりましたが、当時はこれほど盛り上がっていませんでした。) 「シニアになった私でも、今から成長できるのか? やってみるしかない!」 「英語力が身についたら、人生の可能性はどう広がるのか? やってみるしかない!」 そして、英語の環境であるオフショア開発チームのブリッジ・エンジニアという役割に挑戦したのでした。 目指す英語力 目指す英語力はCEFRのB2です。 CEFR (ヨーロッパ言語共通参照枠) は外国語の習熟度を測る国際基準で、B2は上から3番目、「中の上」にあたるレベルです。 ヨーロッパ言語共通参照枠 - Wikipedia このレベルを目指す理由は、『エンジニア組織の英語化変革 EX』という書籍の内容を参考にしています。この書籍に「開発チームのすべてのコミュニケーションを英語化するにはB2が目安である。」、「英語のバックグラウンドがないフルタイムエンジニアでも約2年で全員がB2レベルに達しており十分に到達できる水準と考えています。」と書かれており、これは具体的 (Specific) 、達成可能 (Achievable) 、期限あり (Time bound) で、かつ2年という長さが自分にちょうどよいと感じました。 direct.gihyo.jp また、初学者がCEFRのB2レベルの英語力を身につけるには、600時間の学習が必要なようです。 今年の学習成果 では、この1年の英語学習を振り返ります。 学習内容と時間 学習教材の約95%は、NHKの語学番組を活用しました。1回5〜15分という長さが継続しやすく、レベル別に番組が豊富なため、自分に合った教材が見つけやすいのが魅力です。 視聴している主な番組 *()はCEFRのレベルです ニュースで学ぶ「現代英語」 (B2) ラジオビジネス英語 (B2~C1) ラジオ英会話 (B1) エンジョイ・シンプル・イングリッシュ (A2~B1) 英会話タイムトライアル (A2) 学習時間は合計で390時間でした。1日平均すると約1.1時間。主に夜22時以降、番組の視聴と教材のテキストで学習しました。 毎日やること、そしてやりすぎないことが私のおすすめです。 やる日とやらない日があると、やらない日の楽さを知っているのでやる日が少し辛く、よしやるぞと心を整えるまで時間がかかります。一方で毎日やる、つまりやるしかない!という状況にすると心を整える必要がありません。 あとは、やりすぎないこと、つまりがんばりすぎないことです。がんばると、短期なら気合いでなんとかなりますが長期は辛いです。 英語力の推移 成長を客観的に測るため、今年は定期的に Duolingo English Test を受験しました(受験費用をサポートしてくれる社内制度「エンジニア桜」に感謝!)。 productpr.timee.co.jp 今年のスコア推移 テスト日 認定スコア CEFR判定 2025年10月11日 95 B1 2025年7月20日 90 B1 2025年4月5日 85 B1 2025年2月10日 85 B1 2024年12月15日 85 B1 ご覧の通り、最初の3回はスコアが全く変わりませんでした。 「もう成長の余地がないのか?」「やり方が間違っているのか?」と不安に襲われることもありましたが、「英語学習の成長曲線はある日突然跳ね上がる」「英語は筋トレと同じだ、続けると必ず上達する」という先人の言葉を信じて継続しました。 その結果、4回目、5回目と連続でスコアが上昇しました。単なる幸運ではなく、着実に力がついていることを実感できた瞬間でした。 オフショア開発チームの成長 次に、私の英語力の成長がチームにもたらした変化を振り返ります。 大きな変化として、チームは1年前と比べて、より影響範囲が広く難易度の高いプロダクトゴールをまかされ、そしてやり遂げられるようになりました。 私の英語力と関連する要因は、もちろん情報伝達や意思疎通の質や量が向上したからかと思いますが、それに加えて私の気持ちも英語でも伝わるようになり、信頼関係を構築できたからだと思います。この変化によって積み上がった成果がPdMの信頼を獲得し、そしてより大きなプロダクトゴールをまかされることに繋がったのだと思います。 小さな変化は、エンジニアたちが私にビデオ通話をしてくれる機会が増えたことです。 私の英語力が上がったことで、私に対する彼らのコミュニケーションの負荷が下がったのだろうと思います、話したほうが早い的な。私のほうは英語での言語化に精一杯で発音も文法もひどいと思うのですが、それでも内容が伝わっているようでうれしいです。 最後に今後の抱負 現在の英語力はDuolingo English Testのスコアが95点で、CEFRのB1です。 このスコアがあと5点上がって100点になると、目標のCEFRのB2になります。 また、学習時間は去年から合計して606時間となり、こちらは目標の600時間を超えました。 ようやく、、、ここまできました。この調子であればおそらく最短で次回のテストで、最長でも半年以内に到達できるかと思います。 次回のテストは今年の年末の予定です。目標を達成して年を越したいところです。 認定スコア CEFR 155 ~ 160 C2 130 ~ 150 C1 100 ~ 125 B2 目標はここ 60 ~ 95 B1 いまここ 10 ~ 55 A1 ~ A2 一方で、自身の英語力が目標に近づいたことで、その英語力がどの程度かを知り、少し期待以下であることに気づきました。 「自分の英語力なんてまだまだだぞ、この程度で満足していいのか?」という気持ちです。 ということで、目標を達成した後も引き続き学習を続ける予定です。 もしかしたら、自分との戦いである点も筋トレと同じで終わりはないのかもしれません。。。笑 引き続き、オフショア開発チームと英語の両方をがんばってまいります! ということで、以上が私の体験談でした。ご参考になればうれしいです。 2025年もお疲れ様でした! 最後に、タイミーでは一緒に働くメンバーを募集してます!ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
この記事は Timee Product Advent Calendar 2025 の12日目の記事です。 1. はじめに こんにちは、タイミーでAndroidエンジニアをしている村田( @orerus )です。 私は2年前の2023年のアドベントカレンダーで、「 Lightweight Architecture Decision Recordsの導入 」という記事を書きました。 当時はAndroidエンジニアの人数も少なく、少人数だからこそ導入しやすかった実験的な施策でしたが、あれから2年が経ち、メンバー数は約3倍にまで急拡大しました。 一般的に、少人数チームで機能していた「全員合意」や「ドキュメント運用」は、人が増えると形骸化したり、ボトルネックになったりしがちです。 果たして、我々の独自LADR(Lightweight Architecture Decision Records)はスケールする組織に耐えられたのか?それとも破綻してしまったのか? 約2年間の運用の「通信簿」を、チームメンバーへのアンケート結果と共にお伝えします。 2. 独自LADRのおさらい(前提共有) 本題に入る前に、我々が2年間運用してきた「独自LADR」について簡単におさらいします。 (導入の経緯や詳細なフォーマットについては、 前回の記事 をご参照ください) 一般的なADR(Architecture Decision Records)はアーキテクチャ選定の記録を主としますが、我々は対象を拡大し、以下の目的で運用しています。 LADRの目的 案件のコンテキスト共有: 「なぜやるのか」「背景は何か」を実装前に同期する 設計レビュー: 実装着手前に設計方針の合意形成を行う(手戻り防止) 意思決定ログ: 「なぜその実装になったか」を未来に残す また、運用ルールとして以下の特徴を持たせています。 「仕様書(Specification)」ではない あくまで「その時点での意思決定のログ」です リリース後に仕様変更があっても、過去のLADRは更新しません。改修時は 「新たなLADR」 を作成してリンクさせる運用です 書くハードルは極限まで下げる 必須項目は 「Context - 文脈」のみ 記述量は執筆者に委ねており、「悩むくらいなら1行でも書いて出す」を是としています この「仕様書としてメンテしない(=数が増え続ける)」という性質が、後述する組織拡大期の課題にも関わってきます。 3. 【定量評価】数字で見るLADRの現在地 実際のところ、LADRはまだまだ元気に運用を続けております! LADR総数も2年前の18個から約175個へと大幅に増加しました。 では、チームが拡大した現在、メンバーはこの運用をどう感じているのでしょうか? 全メンバーにアンケートを実施し、2年前と同じ項目で数値化してもらいました。 ▼ LADR運用に関するアンケート結果 (※各項目10点満点 / 古参メンバー:2年前の導入初期を知るメンバー、新メンバー:その後に加入したメンバー) 評価項目 全体平均 上限 / 下限 古参メンバー平均 新メンバー平均 1. 満足度 7.9 10 / 4 8.7 7.4 2. 効率化 7.1 10 / 3 8.3 6.4 3. コミュニケーション 8.2 10 / 5 9.7 7.4 4. 課題解決効果 7.8 10 / 4 8.7 7.2 5. 学習・成長 6.5 10 / 3 8.3 5.4 6. 他チーム推奨度 7.2 10 / 3 8.3 6.6 数字から見えること まず注目したのは 満足度の高さ(平均7.9点) です。 「人数が増えたら面倒になるだけでは?」という懸念に反し、組織が大きくなっても高い水準で支持されています。また、途中からJoinした新メンバーも一部が「満足度10点」をつけてくれていました。これは率直に嬉しいですね! また、最もスコアが高かったのが 「コミュニケーション(8.2点)」 でした。 Squad(開発チーム)が分かれて普段の業務がバラバラになりがちな組織拡大期において、LADRが「エンジニア同士の会話のハブ」として機能していることがわかります。 一方で、「効率化」や「学習・成長」の項目では古参メンバーと新メンバーで特にスコアに開きが見られました。このギャップについては、LADR導入以前の痛みがある状態を知っている古参メンバーにとっては「LADRはマイナスをプラスに変えてくれるもの」だが、整備された状態で入社した新メンバーにとってはLADRがある状態がスタート地点(あるいは単なる追加タスクという認識)となった為ではないかと推察しています。実際、新メンバーからは「スピードが求められるシーンでボトルネックになる」という率直な意見も挙がっており、ここは今後の改善ポイントです。 4. 【定性評価】3倍規模になってもまだまだ手放せないLADR 一方、アンケートの定量評価(自由記述結果)から、LADRが一定支持されていることが伺えます。そこで、今度はその理由について考察してみます。 (※アンケートの設問と実際の回答内容については文末に付録として掲載します) ① 手戻りの撲滅と設計の民主化 LADRを出すタイミングはコードを書く前を推奨(最遅でも実装PRと同時)しています。これにより、実装後のコードレビュー(Pull Request)で議論が白熱し丸々作り直し…といった事態がなくなります。 実装前に設計のプロセスが強制的に組み込まれること、そのため設計時点でレビューしてもらうことで大規模なロールバックが発生しづらいことは大きなメリットかと思います。 (新メンバーより) また、これから作る機能について「こうしたいんだけど、どう思う?」とLADRを通して気軽に相談できる文化が、心理的な負担を下げているようです。 ② コードレビューの純度向上 LADRで設計の合意が取れているため、実際のコードレビューでは「設計議論」をする必要がなくなります。 コードレビューをする際に、設計周りのコメントをする必要がほぼ無くなる為レビュー側としても楽。 (新メンバーより) これはレビュアー・レビュイー双方にとって大きなストレス軽減になっています。余談ですが、我々はAIコードレビューも導入しており、単純なロジックミス等はAIが先に指摘してくれる状態となっているため、相乗効果で実装PRのレビュアー負担を大きく減らすことができています。 ③ 心理的安全性とオンボーディング 個人的に一番嬉しかったのが、新しく入ったメンバーからの以下の反応でした。 過去ログを参照してみましたが Light なこともあり書き方も人それぞれだったためいい感じに肩の力を抜いて書く事ができました。 実際 LADR を書く中でチームメンバーに質問したときも丁寧に教えてもらえたことを覚えています。 (新メンバーより) 書き方の参考にとてもなった。 足りてない部分や書き方など、チームメンバーが丁寧に指摘してくださった印象があり受け入れ体制は丁寧に感じた。 (新メンバーより) ドキュメント文化が「参入障壁」にならず、むしろ「チームに馴染むためのツール」として機能していました。その結果がコミュニケーションの高得点に繋がっているのかと感じました。 5. 課題をどう乗り越えているか? 〜AIという新たな相棒〜 もちろん、良いことばかりではありません。人数が増え、ドキュメントの数が膨大になるにつれて顕在化した課題と、2年前には予想していなかった解決策について触れておきます。 課題1:過去のドキュメント、どうやって探すのだ問題 「仕様書」として更新せず、ログとして積み上げる運用のため、GitHub上のファイル数は増え続けます(現在約175個)。2年前の懸念通り、人間が目で探すのは困難になりつつあります。 しかし、この課題は AIの進化 によって思わぬ形で解決されていました。 最近は AI が便利なので Claude Code に 「この項目とその時書いた設計を LADR から探してサマライズしてください」 などと投げ掛けています。 (新メンバーより) 新しくLADRをつくる際に既存のLADRをAIに探させることがあったがよく探してくれる印象 (新メンバーより) GitHubのリポジトリをAIに読み込ませることで、「検索性」の問題はAIが解決してくれました。これは2023年時点では想像していなかった嬉しい誤算です。 *1 課題2:レビューのボトルネック化 一方で、組織拡大に伴う悩みもやはり出てきています。現在はLADRが出されるとAndroidメンバー全員がレビュアーにアサインされます。これがスピード感を損ねる要因になりつつあります。 実装とLADRのレビューからマージまでには数日かかっている印象があります。 スピードが求められるシーンでも、LADR をレビューに出しながら並行して実装し PR までたてた後 LADR 側で指摘をもらった場合に、実装の PR を LADR を見ながら修正する必要もありコンテキストが分断されるのでネックかもしれません。 (新メンバーより) マージ条件としては「Android Chapter LeadのApproveのみ必須」という緩めな形にはしているものの、より多くの人の目に触れればそれだけコメントの数も必然的に多くなります。 ここについては、「10人程度までが見れる範囲。15人を超えたらコードオーナー制や分散レビューが必要かもしれない」といった意見も出ており、まさに今のフェーズが運用の転換点であるとも感じています。 課題3:AI時代の「思考停止」リスク AI活用は書くハードルを劇的に下げてくれましたが、副作用も無視できません。 実際、定量評価における 「学習・成長」のスコアが新メンバーで顕著に低かった(5.4点) のは、「AIが下書きをしてくれる分、自分の頭でゼロから言語化して悩む機会が減った」ことの裏返しである可能性があります。 AIにほとんどを書いてもらうため、記述するコストを小さくすることに意識が向くことが多くなったと感じる。 (新メンバーより) AI活用自体は組織的にも推進しており歓迎するところですが、「AIに下書きさせて、人間が思考を整理する」という健全なバランスをどう保つかが、今後の新たな課題点となりそうです。 結論:約3倍規模になってもLADRは必要だったか? 結論として、 「必要だし、むしろ人数が増えた今こそ手放せない」 存在です! 約3倍規模に増えても、LADRは形骸化することなく、チームの意思決定の質とコミュニケーションを支え続けてくれています。 特に、新しく入ったメンバーがこの文化をポジティブに受け入れ、すぐに適応してくれている姿は、導入者として何よりの成果だと感じています。 ただし、(努力目標とはいえ)「全員で全部見る」運用はそろそろ限界に近づいています。 次のフェーズ(10人〜20人規模)に向けて、AI活用を前提としつつ、レビュー体制の分散化など、仕組みのアップデートを進めていくつもりです。 この記事が、拡大するチームでの情報共有やドキュメント運用に悩む方の参考になれば幸いです。 タイミーのプロダクト開発に興味を持ってくださった方、よろしければぜひ一度お話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら 付録: アンケート自由記述の全回答まとめ アンケートの自由記述設問、および全回答です。 クリックしてアンケートの全回答(原文ママ)を見る ### 1. 組織拡大(Scale)に関する変化 **Q. 人数が増えてLADRのレビュー依頼も増えましたが、レビュー負荷や承認までのスピード感はどう変化しましたか?(ボトルネックになっていませんか?)** - CLとして全てのLADRを見るのでその分負荷は増加しているが、いうほどLADRの数自体が多く無いため変化は感じない - LADRが最近になってApproveまでのスピード感が落ちた気がします。他のも見るPRが増えたことや、AIによって書かれる分量が増えたので把握する量が増えたことなどが要因な気がします - 多少のボトルネックは発生しているように感じますが、それを補って余りある効果があると思います - (自分も含め)忙しさの度合いによりLADRのレビューが若干後回しになってしまっている印象があり、開発スピード上がったという印象はそこまで感じていないが、著しく開発スピードが落ちた、という印象もない - レビュー負荷は良いが、レビュー完了までに時間がかかりリードタイムが悪くなるのに貢献している気がして申し訳ない - レビューはLADRがあることでやりやすい - 個人的に事前に設計を考える点はメリットである反面、メンバーの設計をレビューするのは特に自分が持っているコンテキストの外側だと心理的にもハードルが高く、なかなかレビューに参加できなかったりします。実際 LADR のレビューからマージまでには数日かかっている印象があります - 基本的にはみるようにしている、特に負担と感じたことはない。忙しいタイミングかつ多くのメンバーに承認されている状態だとクリティカルではなさそうな気になりはコメントしない・残さないことは何度かあったかもしれない **Q. 過去のLADRの数がかなり増えているはずですが、現在「過去の意思決定」を探すのは容易ですか?** - GitHubの検索機能やローカルでの検索を使っているのでいうほど困ってない - そんなに過去のLADRを探すケースがないので、困っていないです - Githubのblobから検索して探しています。検索容易性は十分あると思います - 過去の意思決定を探すのは容易。すべてのLADRを目を通しているので脳内INDEXがあり AndroidStudio で検索すれば出てくる。最悪思い出せなくても ladr フォルダにまとまっているので探し出すのに困っていない - AIがあるので問題ないかも - 特定のLADRを読んだときに、これは今も正しい情報なのかの確信は持てていない - 最近は AI が便利なので Claude Code に「この項目とその時書いた設計を LADR から探してサマライズしてください」などと投げ掛けています - 普段の開発において既存のLADRを参照する機会があまりないかも(Androidにおける開発の実装方針が大きくはずれることがなさそうなことに起因していそう)。新しくLADRをつくる際に既存のLADRをAIに探させることがあったがよく探してくれる印象 **Q. 書くこと自体の心理的・工数的ハードルについて、現在の率直な感覚は?** - AIによって、より書くハードルは下がった気がします、レビューするハードルは分量の増加によって上がったかもなぁという感じです - どのくらいの内容で書けば良いかはあまり肌感覚がなく、後から「書いてください!」と言われることもあります - 仕様が固まっていなくても方針や予定を含んだ内容で書いてもOKとなっているので、書くことへの心理的ハードルはない。ある程度仕様をまとめたりしなければならないので工数的ハードルは忙しい時は少し気になるが、実装PRレビュー時などに使えることを考えると、工数的ハードルもほとんど感じていない - あまりない - 作りながら仕様が変わるのでドキュメントをアップデートし続けるのが難しい - プロダクトタスクの立ち上がりから実装までの間にどうしても LADR を書く工数が少なからず発生してしまうこと、作りながら仕様を決めることもあるためその都度 LADR に立ち戻ってドキュメントを修正することになり勢いが削がれてしまう点は課題かなと感じます。スピードが求められるシーンでも、LADR をレビューに出しながら並行して実装し PR までたてた後 LADR 側で指摘をもらった場合に、実装の PR を LADR を見ながら修正する必要もありコンテキストが分断されるのでネックかもしれません - AIによって書きやすくなった印象がある。実装を終わらせてから論点を整理させるといった使い方もできそう ### 2. オンボーディング・新メンバー視点 **Q. 入社時や新しい領域を触る際、過去のLADRを読むことは役立ちましたか?** - 役に立ちそうとは思うが、直接恩恵を受けたというエピソードはないかも - 仕様や経緯が記載されているのでその機能に手を加える際の参考になりました。ただ、Git 管理なのでドキュメントの更新も少しハードルがあると感じていて特に仕様について見つけた情報が最新かの判断が難しい気もしています **Q. 初めてLADRを書いた時、既存のLADRは参考になりましたか? また、チームの受け入れ体制はどう感じましたか?** - 書き方の参考にとてもなった。足りてない部分や書き方など、チームメンバーが丁寧に指摘してくださった印象があり受け入れ体制は丁寧に感じた - 既存のLADRは、ある程度参考になった(ある程度、なのは粒度がそれぞれ異なりどれに合わせれば良いか迷ったと思うため)。レビューで指摘してもらえブラッシュアップ可能なため、わからないままで終わるとかがなく受け入れ体制は良いなと感じた - LADRを書くこと以外にも初めてのことが多くて書いたが、あまり深く考えていなかった気がする - 過去ログを参照してみましたが Light なこともあり書き方も人それぞれだったためいい感じに肩の力を抜いて書く事ができました。実際 LADR を書く中でチームメンバーに質問したときも丁寧に教えてもらえたことを覚えています - 他のLADRを参考に書いた記憶、書き手によってフォーマットが異なっていた気がしている。AI主体で書いてくれるようになれば記述のハードルやフォーマットもそろいそう ### 3. 未来へ向けて **Q. 独自LADRの「ここが好き」(一番気に入っている点)** - 自分・所属Squad以外の開発内容についても知れ、自身の意見を述べる機会が与えられるところ。意思決定意図をリポジトリ内に残せるところ - Planモードの先駆けてきな感じで、最初に仕様を自身でも整理できるのが良いですね - みんなでレビューすることにより認識がそろえられるところ - これからこういった機能を開発したい、こういった機能を入れたいんだけどどう思う?を気軽に共有できる点 - 他のメンバー・squadが何をしているのか知ることができる。コードレビューをする際に、設計周りのコメントをする必要がほぼ無くなる為レビュー側としても楽 - とにかく書くハードルを下げていること - 実装前に設計のプロセスが強制的に組み込まれること、そのため設計時点でレビューしてもらうことで大規模なロールバックが発生しづらいことは大きなメリットかと思います **Q. 改善したい点・今後への提言** - もっと書きやすいように項目を取捨選択・整理したい - やはり把握する量が増えるので、LADR管理者の負担が心配になってきます - 全員レビューは人数が増えると限界を迎えると思うので、他に認識を揃える方法を考えるべきだと思います - 15人を超えてくるとすべてのLADRに目を通すことはかなり負担になりそうに感じるため、そこまでスケールはしないやり方かな、というところに限界を感じる。10人くらいまでが見れる範囲だと思っている。もし今の倍の人数になるならば半分くらいのメンバーを自動アサインなどにし...など分散する仕組みがあっても良いかも - レビューを締め切るタイミングが難しい為、一次コメントは3日以内にとか決めたいかも - 仕様書駆動開発になった時にLADRが仕様書っぽくなったりするのか妄想をする - 物理的・心理的ハードルの課題も散見され、特に高速なバリューデリバリーが求められる中で小さくないボトルネックになりつつある気がします。とは言え数10人規模になってくると rails chapter と同様に領域ごとのコードオーナー制も視野に入ると思うのでそこまで人数がネックになることも少ないかなとは感じます *1 : ※AI利用に関しては、社内のセキュリティガイドラインに則り、学習利用されない設定(エンタープライズプラン等)で行っています
はじめに:「タイミーって、もう完成してるんでしょ?」 「タイミーはIPOもしたし、プロダクトも組織も出来上がっているから、今から入ってもやることがないのでは?」 候補者の方とお話しする中で、そんな声をいただくことがあります。 正直に言います。めちゃくちゃ勿体ない誤解です。 むしろ、IPOを経て確かなアセットが揃った今だからこそ、「プロダクト開発を科学する」という最高に面白いフェーズに入っているのです。 今回は、コンサル・スタートアップの事業統括を経験した私が感じる、「Growth領域の知的な楽しさ」と、その先に待っている景色についてお話しします。 自己紹介 みなさん、初めまして! タイミーでスポットワーク事業のGrowth全体を担当しているYang Haoと申します。 新卒で経営コンサルに5.5年従事し、その後、営業SaaSのスタートアップにてプロダクト責任者・事業統括を2.5年経験しました。2025年4月にタイミーに入社し、現在Group Product Managerとして10名弱のPdMのピープルマネジメントも行っています。 1. Growthとは「コツコツヒットを重ねながら、常にホームランを狙うゲーム」 そもそもGrowthとは何でしょうか。単にUIを微調整してCVRを0.1%上げるだけの仕事ではありません。 私は「顧客価値を提供し続けるために、データドリブンで反復し続けるプロセス」だと定義しています。 GAFAをはじめとする世界のトップテック企業には、一般的に必ずと言っていいほど強力なデータドリブンGrowthチームが存在します。彼らは機能開発チームとは異なる筋肉で、事業を非連続に成長させています。 なぜこれが「面白い」のか? 最大の魅力は、「即座にフィードバックが得られること」と「大胆なホームランを狙えること」です。 通常の機能開発が数ヶ月かけてリリースするのに対し、Growthは毎日バッターボックスに立ちます。 実際にタイミーのGrowthチームでは、 月間10本前後のリリースを行い、常に7〜8本のABテストが走っています。 「この仮説はどうだ?」「ダメか、じゃあ次はこうだ」 すぐに数字としてフィードバックが返ってくるため、高速でPDCAを回しながらヒット(改善)を積み重ねることができます。 しかし、それだけではありません。 確かなデータがあるからこそ、勘や度胸に頼らず、「ここを変えれば事業が跳ねるはずだ」という特大ホームラン(非連続な成長)を狙って打ちにいくことができる。 この「確実性」と「爆発力」の両方を追い求めるヒリヒリ感こそ、Growth PdMの醍醐味です。 2. 「コードを書く」だけが解決策じゃない。総合格闘技としてのGrowth もう一つの面白さは、「解決手段(レバー)の多様さ」にあります。 AIの進化により、「作る」ことのハードルは劇的に下がりました。これからのPdMの真価は、「作る」ことそのものよりも、「どのレバーを引いて事業価値を最大化するか」という意思決定に問われます。 Growth PdMが扱うレバーは、プロダクトだけに留まりません。例えば以下のような選択肢を複合的に組み合わせます。 Productレバー: UI/UXの改善、新機能の追加 Marketingレバー: 獲得ターゲットの変更、メッセージの刷新、新規獲得チャネルの立案 Biz/Opsレバー: 営業オペレーションの変更、自動化によるCS対応コストの削減 Strategyレバー: ダイナミックプライシング、課金モデル、インセンティブ設計の変更 「今回は開発せずに、LPや提案活動で仮説検証をしよう」「ここはプロダクトで自動化して、CSのオペレーションコストを下げながら、リードタイムを短くしよう」 このように、事業全体を俯瞰して最適な手を打つ「総合格闘技」のような面白さがあります。 3. キャリアステージで変わる「Growthの楽しみ方」 Growthの面白さは、PdMとしてのキャリアステージによって味わい深さが変わる領域です。 ジュニアPdM:プロダクト開発の「密度」を味わう とにかく、打席に立てます。0-1フェーズでは仕様策定からリリースまでに数ヶ月かかることもありますが、Growthフェーズではそのサイクルが圧倒的に早いです。 「仮説→要件定義→開発→リリース→検証」というプロダクト開発の一連のプロセスを、短期間で何度も回すことができる。 濃密な開発経験を浴びるように積めることは、PdMとしての足腰を鍛える上で最高の環境です。 ミドルPdM:「再現性」と「チームづくり」の楽しさ 単発の施策ではなく、チームとして勝ち続けるための仕組みづくりが面白く感じられるようになります。 「どうすればABテストの勝率が上がるか」を突き詰め、プロダクトで再現性高く数値を作れるようになると、経営からの信頼(投資)も集まり、チームも急拡大します。 そこで必要になるのが、「人を育て、組織で勝つ」マネジメントの視点です。プレイヤーとしての個人の成果を超えて、チーム全体のレバレッジを効かせる面白さが出てきます。 シニアPdM:「事業そのもの」を動かす楽しさ ここまで来ると、前述した「Strategy」や「Biz/Ops」のレバーを含め、事業全体を動かすダイナミックな采配が可能になります。 プロダクトの変更によって数百名の営業組織の動き方を変えたり、プライシング戦略で収益構造を進化させたりする。 これはもはやPdMという枠を超えて、事業をハンドリングする予行演習そのものです。 4. なぜ「タイミー」がGrowthの実験環境として最高なのか 「Growthが面白いのはわかったけど、別にタイミーじゃなくてもいいのでは?」 そう思うかもしれません。しかし、今のタイミーだからこその環境があります。 ① 「有意差」が出る圧倒的なトラフィック Growthの天敵は「データ不足」です。アーリーフェーズのスタートアップでは、ABテストをしようにも母数が足りず、検証に時間がかかります。 タイミーでは現在、月間100万回以上のマッチングが生まれています。今日打った施策の結果が、明日には明確な「有意差」として現れる。この規模で高速な実験ができる環境は、国内でも極めて希少です。 ② 「攻め」の実験にリソースを投下できる これが一番リアルな話ですが、資金やリソースに余裕のないフェーズでは、どうしても目先のキャッシュ獲得に追われ、大胆な実験ができません。 タイミーは、IPOを経て事業基盤が強固になったからこそ、守りに入るのではなく、「次なる非連続な成長」のためにリソースを大胆に投下できます。 失敗を恐れずに仮説検証にベットできる。この「攻めの姿勢」を支える環境があるからこそ、Growth PdMは思い切りバットを振ることができるのです。 おわりに:キャリアは後からついてくる 冒頭で「Growthは面白い」と書きましたが、結果としてこの領域に熱中することは、キャリア形成においても非常に合理的です。 事業全体を見渡し、あらゆるレバーを引いて数値を伸ばす経験は、将来どのようなプロダクト、あるいは事業を任されたとしても通用する「本質的な事業推進力」になります。 完成された組織でルーティンを回すのではなく、ヒリヒリするようなスピード感で実験を繰り返したい。 プロダクトという枠を超えて、事業を伸ばす手触り感を味わいたい。 そんな「知的好奇心」旺盛な方にとって、今のタイミーは最高の挑戦の場です。 「ちょっと面白そうかも」と思った方、ぜひカジュアルにお話ししましょう。この巨大な挑戦環境で何ができるか、一緒に妄想しませんか? みんなでワイワイ発信活動をやってく環境いいな〜と思う方、ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
はじめに こんにちは! 絶賛採用中 のタイミーのDevPlatformチームの @MoneyForest です。なお、この記事は  Timee Advent Calendar 2025  シリーズ2の11日目の記事です。 本記事ではタイミーの「アプリケーション定点観測会」で取り扱っているDatadogのダッシュボードを改善した件について記載します。 アプリケーション定点観測会とは タイミーにおける「アプリケーション定点観測会」は「アプリケーションの品質に関わるメトリクス、イベント、ログ、トレース(MELT)を週次のサイクルで観測し、それぞれの事実をチーム全員で確認しフィードバックループを回すこと」を目的として行われます。 平たくいうとDatadogのダッシュボードを全員で眺め、認識を合わせたり、確認や改善のアクションを決定する会です。 このような取り組みは各社のテックブログでも散見され、SREの取り組みとしては一般的なもののように思います。(各社「SLO定例」「モニタリング定例」「パフォーマンス観測会」などさまざまな名前がついているようです。) それぞれ何を重視するかで特色があるとは思いますが、基となる考えはおそらくSRE本の「31章 SREにおけるコミュニケーションとコラボレーション」の「31.1 コミュニケーション:プロダクションミーティング」の以下の内容に拠るところではないでしょうか。 しかし、私たちが行うミーティングの中で、平均以上に有益なものが一つあります。それはプロ ダクションミーティングと呼ばれるもので、SREチームが自分たちと他の参加者に対し、担当す るサービスの状況について十分に注意を払って明確に説明をすることによって、すべての関係者の 全般的な認識を高め、サービスの運用を改善するために行われます。概して、これらのミーティン グはサービス指向で行われるもので、直接的に個人の状況のアップデートに関するものではありま せん。このミーティングが目標とするのは、ミーティングが終わった後に、進行中のことに関する 全員の認識が同じになることです。プロダクションミーティングには他にも大きな目標がありま す。それは、サービスに対するプロダクションの知恵を持ち寄ることによって、サービスを改善す ることです。すなわちサービスの運用パフォーマンスの詳細について話し合い、それを設計や設 定、実装と関連づけて考え、問題解決の方法を推奨するということです。定期的なミーティングに おいて設計上の判断をサービスのパフォーマンスと合わせて考えてみることは、きわめて強力な フィードバックループになります。 (出典:Betsy Beyer, Chris Jones, Jennifer Petoff, Niall Richard Murphy 編、澤田武男・関根達夫・細川一茂・矢吹大輔 監訳、玉川竜司 訳『SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム』オライリー・ジャパン、2017年、p.448) これまでの会とその課題 「アプリケーション定点観測会」は概ね目的を果たしていましたが、 「チームの認識が揃わない」 ということがしばしばありました。 どこを見るかが揃わない: 会議の最初に「各自でダッシュボードを眺めて気になった点を挙げる」という時間を設けていましたが、人によって見始めるウィジェットが違うことで時間内に見切れなかったり、見る人によって解釈が異なったりしていました。 良いか悪いかの判断が揃わない: 例えばCPU Utilizationのようなウィジェットは、100%に達していなければOKと一瞬で判断できる一方、Y軸が100に固定されていないと自動でウィジェットが縮尺してしまうため、パッと見て判断できず無駄な時間がかかることがありました。 次のアクションが揃わない: メトリクスの見方が人によって違うため、「このグラフのこの変化はどういう意味か?」といった 認識合わせの議論に時間が割かれる こともしばしばありました。 このようなケースがときおり発生しており、会が非効率になってしまうことで気づくべき変化を見落としてしまうことは避けたいと考え、ダッシュボードを改善しました。 改善アプローチ 課題に対応する形で、3つの改善を行いました。 課題 改善 どこを見るかが揃わない ダッシュボードの集約 良いか悪いかの判断が揃わない 明確なウィジェット 次のアクションが揃わない 観点の明記 1. ダッシュボードの集約 ─ 「どこを見るか」を揃える プロダクトごとに似たようなダッシュボードが並んでいたので、 テンプレート変数 を使い集約できるようにしました。またプロダクト・非同期処理・データストアなどでダッシュボードが分散していたので、1つのダッシュボードに集約しました。 テンプレート変数とはダッシュボードに定義できる変数のことです。クエリに $ をつけることで変数を参照でき、似たようなウィジェットを複数作らなくてもよくなることで保守性が上がるメリットがあります。 2. 明確なウィジェット ─ 「良いか悪いか」を揃える ウィジェットについて「閾値に抵触していないか?」「変化はリリースによるものか?」といった点がパッとわかるようにしました。 Y軸の固定とマーカー 例えばなんらかの使用率(Utilization)を見る場合、Y軸を100%に固定し、85%と100%にマーカーを引くことで、一目で状態が把握できるようになります。 マーカーを設定する理由は、意思決定をブレさせないためです。たとえばある日ではCPU使用率が80%のときに「なんかやばくね?」と判断して調査したのに、それより上の85%のときは「まだ余裕あるし対応しなくていいか」となってしまうことがあります。人によって、あるいはその日の気分や残り時間によって判断基準が変わってしまうことがあるのです。 では「CPU使用率が85%を超えた場合は一次調査する」と決めるだけでいいかというと、見逃してしまったり忘れてしまったりのリスクが残ります。マーカーで線を引いておけば「85%を超えたら調査や対応を検討する」との決めが視覚的に表現でき、ブレがなくなります。 マーカーを2つ引く理由にも意図があります。 85%(ワーニング) はスケールアウトやスケールアップを検討する目安です。この段階で気づければ、余裕を持って対応できます。観測会では主にこちらを取り扱います。 100%(クリティカル) はアラートが発報されるラインであり、すでに問題が顕在化している状態です。通常こちらはアラートによって取り扱うため、観測会では事後的な確認になります。この2段階にすることで、判断がしやすくなります。閾値自体が大切ではなく、「対応を開始するライン」と「抵触したら問題になるライン」を常に表示しておくことが大切です。 前週比のオーバーレイ表示 Datadogの calendar_shift 関数を使って、前週のデータを点線でオーバーレイ表示しています。これにより「先週と比べてどうか」が一目でわかります。 そもそも前週比がないと何が困るのでしょうか?グラフを見ても「この数値は高いのか低いのか」が判断できません。例えばエラー数が100件あったとして、それが普段通りなのか異常なのかは、比較対象がないとわからないのです。結果として「なんとなく大丈夫そう」という曖昧な判断になったり、正常なのに異常と誤認して無駄な調査をしてしまうことがあります。 「前週比」という点にもポイントがあります。前日比だと曜日による変動に惑わされます。例えばリクエスト数が平日と休日で大きく異なるプロダクトの場合、月曜と日曜を比べてもあまり意味がありません。前月比も同じく週が一致しない場合があるので、使いづらいです。そのため「前週の同じ曜日」との比較がちょうど良いと考えました。この辺りはプロダクトの特性にもよって適切な設定が変わってくると思われます。 リリースとの関連付け メトリクスに変化があったとき「それはいつのリリースが原因か?」を特定したいことがあります。リリースと紐づいていないと、変化の原因を探すために別途デプロイ履歴を確認する手間が発生します。 用いるメトリクスもそういった観点を踏まえて選びます。CloudWatchの aws.ecs.cpuutilization メトリクスはECSサービスレベルしかわからず、どのコンテナを改善すべきか、リリースとの関連がわかりづらいです。Datadog AgentサイドカーのFargateメトリクスで使用率を計算し、 task_version でグルーピングすることでリリースによる使用率の推移を観測できます。(デプロイのイベントをDatadogに送信するのもありかと思います) sum:ecs.fargate.cpu.usage{$service_name} by {task_version} / sum:ecs.fargate.cpu.task.limit{$service_name} by {task_version} * 100 3. 観点の明記 ─ 「次のアクション」を揃える ウィジェットの横にメモを必ず設けるようにしました。 メモがないとグラフが異常なように見えても「で、どうすればいいの?」となってしまうことがあります。自分が詳しければ問題ない、または詳しい人がいれば説明してくれますが、その人が休みだと判断できません。メモに「異常時は〇〇を確認する」と書いてあれば、誰でも次のアクションを取れます。ネクストアクションについて書きすぎると目が滑ってしまうので、あくまで簡単な記載にとどめます。ランブックへのリンクにするのもよいでしょう。 逆にメモが書けないような「とりあえず出してみている」ウィジェットは今回を機にバッサリ整理しました。そのようなウィジェットは時間が余った際に探索的に見るものと位置付けています。 余談ですが、スキルアップやバックログアイテムの発見につながるため探索的に見ることも重要だとは考えています。一方で、「予防」のアクションを取りたいのか「発見」のアクションを取りたいのかで適切なやり方は異なるため、会を分けるべきと考えています。 メモには「メトリクスの説明(どういったメトリクスで、どういった事象を引き起こすか)」「望ましいメトリクスの状態」「異常時に取るべきアクション」などを簡単に記載しています。 実際のダッシュボードでは以下のような形式でメモを記載しています。 ## 🗒️ Check!! - 前週比(点線)で異常に増えていないか - アプリケーションのエラーなのでLogsから原因を確認 ## 🗒️ Check!! - Task Limit(赤線)に抵触してないか - 抵触している場合スケールアップ or 性能改善が必要 ## 🗒️ Check!! - 前週比(点線)で異常に増えていないか - AWSの「Troubleshoot your Application Load Balancers」のドキュメントを参考に対応する - 4xxが跳ねている時はWAFがブロックして403を返していることが多く、 その場合は攻撃を弾いているので特に問題はない おわりに このような工夫をすることで、「どこを見るか」「良いか悪いか」「次に何をするか」について以前よりチームの認識が揃うようになりました。認識合わせに費やしていた時間が減り、その分を「なぜこの変化が起きたのか」という深掘りの議論に当てられるようになっています。 一方で、観測会ではアプリケーションのダッシュボード以外にもセキュリティやコストについても確認しており、「人によって見始めるウィジェットが違うことで時間内に見切れない」という課題は以前より緩和したものの、解消されたとは言い難い状態になっていますので、引き続きの改善を行っていきたいです。(今期からコストは一部別の会議に切り出しています。) 今回ご紹介したダッシュボード改善のプラクティスが、みなさんのチームの参考になれば嬉しいです。 タイミーでは一緒に働くエンジニアを募集しています!興味のある方はぜひカジュアル面談でお話ししましょう。 採用情報はこちら またね〜
この記事はTimee Product Advent Calendar 2025の11日目の記事です! qiita.com こんにちは。Androidエンジニアの Hunachi( @_hunachi )です。 突然ですが、日常生活において、バーコードを目にしたり、使ったりすることはけっこうありますよね。私もセルフレジでいつもピッピしています 🥦🛒 そんな身近なバーコードの読み取り機能を、Androidアプリに実装してみて失敗したときの話をします。失敗はしましたが、今はすでに安定稼働しているのでご安心ください ✅ このブログでは、コードの詳細は書きません ✂️ なお、ここではiOSの話はしません。iOSはAndroidとはまた別の苦難があり、後日iOSエンジニアの方がブログを書いてくれることを願っています 🙏📱 経緯:ユーザーにコードを読み取ってもらう機能が必要に 🧩 仕様は0から自分たちで考えており、制約の範囲内でコードタイプも自由に決められる状況でした。 他の箇所でQRコードを使っていたため、ユーザーが混同しないよう一次元バーコードを採用しました。 社内の技術以外の制約により、チェックデジットなしの形式を選ぶ必要がありました。 Pixelなど主要端末での手動確認では問題は見つかりませんでした。 チームメンバーを含む複数人での確認も実施済みでした。 今回使用した、バーコード読み取りの主要技術 🛠️ ML Kit(Google ML Kit) CameraX(Android Jetpack) 補足注: 「チェックデジット」は、読み取り誤りを検出するための検査用桁です。今回は発行側の事情で付けない形式を採用しました。 リリース後に発生した問題 🚨 問題1:一部端末で読み取りが難しい(もしくは実質不可)📵 ユーザーからの問い合わせで発覚しました。 分析の結果、影響は一部の数種類の端末に限られそうだと分かりました(おそらく100ユーザー以下)。 低スペック端末だけの問題ではなく、高性能カメラの端末でも発生していました。 特定端末を検証用として購入し、手元で再現を確認しました。 高価な端末にもかかわらず、検証用として迅速に手配してもらえました(関係各位に感謝です 🙇‍♀️)。 完全に不可能というほどではないものの、かなり読み取りにくいことが分かりました。 問題発生確率の調査方法 🔍 厳密な定量分析は難しいため、「本来読み取られるはずの回数」に対する「実際に読み取られた割合」が極端に低い端末や、問い合わせがあった端末を特定して調べました。 対応策(暫定) 🧯 画像からバーコードを登録できる機能を追加しました。 撮影方法の説明を強化し、誤った方法で操作してしまう可能性を下げました。 問題2:誤読(別のコードとして読み取ってしまう)🔀 本来そのユーザーが読み取るはずのないコードが読み取られる事象が発生しました。正しいコードが読み取れないと、そのユーザーが特定の機能を正しく使えなくなるため、重大な問題です ⚠️ バーコード読み取り機能に欠陥がある可能性を疑いました。 CameraX経由の読み取りで、ブレた画像から別コードとして解釈されるケースがあると分かりました。 即時フィードバックが難しい背景 ⏱️ 裏側の仕組み上、ワーカーが読み取ってすぐに誤りに気づけない場合があります。 多くはすぐに弾けますが、条件次第では弾けないことがあります。 問題発生確率の調査方法 🧮 手元での再現は運に左右され、難しいです。 無効データ検出時のログを分析して、発生確率を推定しました。 対応策(暫定) 🛡️ CameraX経由の読み取りに限り、「同一コードが1秒以上連続で読み取られた」場合のみ読み取り成立と判定するようにしました。 これにより、従来は弾いていた分も含め、読み間違いが減少しました。 1秒という閾値は、UXの観点も踏まえて決めました。 恒久的な対策 ♻️ バーコードをやめ、QRコードへの全面置き換えを決めました。 QRコードには誤り訂正やチェックデジットがあり、問題2の発生確率がほぼ0に近づきます。 バーコードにチェックデジットを付ける選択肢もありましたが、誤読時に二桁同時誤りが起きる可能性があり、精度面でQRコードに劣ると判断し、QR化に舵を切りました。 学びと所感 📝 アプリから読み取る × 一次元バーコード(特に発行側の制約でチェックデジットがない形式)の画像読み取りは、採用しない方が良いです!🙅‍♀️ 低スペック端末だけが問題になるとは限らず、高性能カメラ端末でも問題が起き得ると分かりました。 初期の選択は誤りでしたが、問題が見つかるたびにすぐ解決策を模索し、両OSとも最短に近い速度で対応できたのは良かったです 🚀 ユーザーに追加操作を強いることなく、UI/UXを損なわない形で解決できた点も良かったです 🙆‍♀️ 今年も失敗から多くの学びを得ました 😌 次回以降は、ユーザーさんに影響を全く与えない形で失敗するようにしたいです 😭 みんなで楽しく発信していける環境、いいなと思ってくださった方は、ぜひ気軽に声をかけてください!📣  product-recruit.timee.co.jp product-recruit.timee.co.jp (画像はAIで生成しています🍌)
こんにちは!タイミーでエンジニアリングマネージャーをしている恩田です。 この記事は  Timee Advent Calendar 2025  の10日目の記事です。 はじめに 前置き・プラットフォームエンジニアリング部門の役割 PO/PdM不在のチームは「やるべきこと」をどうやって決めるのか? 定量情報の観測 アプリケーション定点観測会 内部品質定点観測会 定性情報の観測 ポストモーテムを眺める会 スプリントレポート報告会 開発者体験アンケート オフィスアワー おわりに We are hiring! はじめに この記事では、私が所属するプラットフォームエンジニアリング部の活動の紹介、特に課題探索を目的としてどのような活動をしているかをご紹介したいと思います。前置きとして、弊社におけるエンジニア組織およびプラットフォームエンジニアリング部の役割やメインミッションの紹介もしており、本題まで少し前置きが挟まります。また、あくまで弊社組織での実例紹介であり、ベストプラクティス等を意識したものではない点にご留意いただきつつ、どなたかのご参考になれば幸いです。 前置き・プラットフォームエンジニアリング部門の役割 タイミーのようなプロダクト主導型組織において狭義のプロダクト戦略とは、マーケティングとイノベーションに対して戦略的意図(顧客ニーズに応える新しいプロダクトやサービスをどう生み出し、どう市場に届けるか)を策定することを指します。この戦略的意図を実現するための具体的なアクションプランをタイミー社内ではプロダクトイニシアチブと表現しています。開発者目線であえて端的な表現をすれば「ユーザ価値を届けるためのプロダクト機能開発」に類する仕事です。タイミーのエンジニアの大部分は、このプロダクトイニシアチブの実行/推進を主たる業務とする組織に所属しており、職能横断的なチーム構成のもと企画から設計、開発、テスト、リリース、施策評価までを一貫して行える組織設計を意図して組成されています。 その一方、プラットフォームエンジニアリング部門ではチームのユニット単位を「職能」で定義しており、WebFront領域に専門性を持つエンジニアで構成されるチーム、バックエンド領域に専門性を持つエンジニアで構成されるチーム、クラウドインフラ領域に専門性を持つエンジニアで構成されるチーム、QAエンジニアで構成されるチーム、セキュリティ領域に専門性を持つチーム、といった具合です。 その上で、組織のミッションと存在理由を タイミーユーザ目線のあたりまえ品質を提供する プロダクト開発組織がプロダクトイニシアチブを爆速でデリバリできる状態を提供する と定義しています。そのため、プロダクトイニシアチブに則った開発プロジェクトにプラットフォームエンジニアリング部が直接・間接的に参画寄与することもありますが、メイン業務は上記を達成するための技術改善/支援となります。 [NOTE] 「プラットフォームエンジニアリング」とは、安全で管理されたフレームワーク内の改善された開発者エクスペリエンスとセルフサービスを通じて各開発チームのセキュリティ、コンプライアンス、コスト、およびビジネス化までの時間に関する価値を向上させることを目的として、DevOps 原則から構築されたプラクティスである、と一般的に定義されています(定義話は長くなるのでこの記事ではこれ以上は触れません)その上で、弊社におけるプラットフォームエンジニアリング部は、上記に掲げるミッション/目標達成のために上記プラットフォームエンジニアリング(プラクティス)を「手段として必要に応じて選択、実践するもの」と捉えています。 PO/PdM不在のチームは「やるべきこと」をどうやって決めるのか? 前置きが長くなりましたが本題です。我々が上記ミッションを達成するために、どのようなアクションプランを立てるべきなのか - この意思決定の材料の1つが顧客の声です。我々にとっての顧客とは、タイミーのエンドユーザ(プロダクト品質の受益者)と社内開発者が該当します。 プラットフォームエンジニアリング組織にはいわゆる「プロダクトマネージャー」と呼ばれる役割を設置しておらず、各チームは定量軸と定性軸の一次情報、すなわちタイミーの各種システムモニタリングと社内開発者のVOC(Voice of the Customer)の継続的な収集を通じて課題仮説の言語化と打ち手仮説を立て、検証とアクションプランの実行をエンジニア全員が主体となって継続しています。これを各チームが各々定期イベントとしてチーム業務に組み込んでいます。 以下、具体的にどのような定期業務イベントでどのような定量、定性情報を日々収集し、課題仮説立てに用いているのかを紹介します。 定量情報の観測 アプリケーション定点観測会 タイミーのシステム信頼性、パフォーマンス、コスト、セキュリティ等にまつわる様々なメトリクス/指標を観測する会です。現状とトレンドの事実確認を行い課題仮説を立てアクションにつなげる / 直近の改善施策の効果を確認し、対策を継続すべきかクローズすべきか等の判断を行うことを目的としています。具体的には以下のような指標を同期ミーティングで集まり確認をしています。 信頼性/パフォーマンス観点 タイミーのBackend API群のレイテンシ、エラーレート、コンテナの各種メトリクス、スケーリング傾向やリクエスト傾向(シーズナリティ/スパイク傾向) データストア層(MySQL, Redis, ElasticSearch) の各種メトリクス、クエリパフォーマンスやスロークエリ、Cache Hit Rate、etc Sidekiq Jobのスループット、Jobs count推移、Workerのスケーリング傾向 バッチジョブのメトリクス、起動失敗率 外部SaaS類へのリクエスト数推推移やスループット、メールのバウンスレートやPUSH配信のエラーレート等 Security観点 CSPM等によるAWS構成不備の新規逸脱検知状況のチェック ECRイメージ脆弱性検査状況の定点チェック GuardDutyの検出結果振り返り コスト観点 AWS、Datadog、GitHub等利用料の大きいSaaS類の利用状況トレンドの把握。 月次/年次予算対比での上振れリスク 利用内訳(group by service, product, etc)の確認と、内訳比率に大幅な変化がないかのチェック RI Coverageの状況 Observability観点 メトリクスの不備、不足などについての課題提案 アラートの過不足、およびしきい値の調整予定についての課題提案 ダッシュボードの見直しなど、観測設計そのものを改善対象としての改善点洗い出し 内部品質定点観測会 タイミーのバックエンドアプリケーションにおける開発者体験を司る各種指標を定点観測する会です。目的はアプリケーション定点観測会と同様で、現状とトレンドの事実確認を行い課題仮説を立てアクションにつなげる / 直近の改善施策の効果を確認し、対策を継続すべきかクローズすべきか等の判断を行うことを目的としています。具体的には以下のような指標を同期ミーティングで集まり確認をしています。 CI/CDパフォーマンス観点 CI, CD の失敗率・失敗要因の分析 CI, CD の実行時間、実行時間変化の分析 PRのレビュープロセス観点 レビューリードタイム推移 マージしたPR数のトレンドと各チームの開発状況との因果関係考察 テストの健全性観点 Flaky Test の新規発生・修正状況 遅いテストの抽出と高速化余地の洗い出し 定性情報の観測 組織として現在収集できているメトリクスから開発生産性の全容を捉えるのは難しい側面があります。特に開発のバリューストリーム上のボトルネックやToilはコーディング、テスト、リリースといった開発工程の外にあるケースも多いです。観測しやすいメトリクス (4keysやGitHub上で観測できるLead time to Change等)だけを追っていても改善対効果の高い課題仮説が浮かび上がらないケースもあります。ビジネス要件の把握、仕様策定、コードベースの把握、レビュー、手動/自動テスト、承認、リリース作業、etc,,。開発工程のどこにボトルネックが発生しているか課題仮説を立てる材料として、開発者からの定性情報(これがツライ、わからない、知らない、etc)は貴重な情報源となります。 ポストモーテムを眺める会 タイミーの開発組織では、障害発生後にポストモーテムを行う文化が定着しています。ポストモーテムは障害対応/収束を担当したチームが中心となって実施します。このポストモーテムの内容(議事録)に記された議論過程やNext Actionをプラットフォームエンジニアリング部門のチームでクロスレビューし、「どのような支援(技術的改善、プロセス/ガードレール整備、知識/情報の非対称性の解消等)を行うと価値がありそうか」を探索しています。 スプリントレポート報告会 プラットフォームエンジニアリング部のチームがデリバリした開発者の生産性向上、体験向上を目的とした各種施策のサマリをレポートし、顧客(社内開発者)からのFBや要望を受け取る会です。 スプリントレポートはチームの作業計画に沿ったアウトプットです。アウトプット内容を共有しつつ、同時に社内開発者から「こんなことで困っている」といった一次情報を受領する相互情報交換を目的としています。 開発者体験アンケート 「開発者が自信を持ってリリースできる状態の実現」をゴールとした、開発者体験の現状を把握し改善につなげるためのアンケートを実施しています。また、アンケート収集後に内容を精査、具体的な改善アクションにつなげるため、必要に応じて回答者へインタビュー等も実施します。SDLCの各フェーズ毎、プラットフォームエンジニアリング部門のエンジニアが独自に策定した論点を元に質問を作成しており、技術的負債、業務プロセス、知識/情報の非対称性、様々な確度から課題の種情報を収集しています。 オフィスアワー 主にクラウドインフラ領域の技術に関するオープンドア形式のQ&Aセッションです。特定時間にslackのhuddleを開き、参加者は自由に出入りできる形で場を運営しています。 クラウドインフラチームが専売特許となりがちな技術領域、例えば監視設計やデータストア選定、データマイグレーション計画のノウハウ、CI/CDパイプラインやAWS全般に関する疑問や質問、興味のあることを何でも気軽に同期で質問・相談できる場として設計しています。 この場を通じ、開発組織全体で信頼性エンジニアリングのプラクティス実践を推進するにあたり障壁となる知識/情報の非対称性や、技術/組織課題の探索の場として活用しています。 おわりに この記事では、弊社プラットフォームエンジニアリング組織における各種課題探索の実例を紹介しました。実例の一つとして参考になれば幸いです。 We are hiring! タイミーでは絶賛エンジニアを募集中です。興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
はじめに この記事は Datadog Advent Calendar 2025   9日目の記事です。 こんにちは! 絶賛採用中 のタイミーのDevPlatformチームの @MoneyForest です。 今回は、ECS Fargate上でスタンドアロンなDatadog Agentをホストし、Database Monitoring(DBM)を活用してAurora MySQLのオブザーバビリティを向上した取り組みについてご紹介します。 背景 タイミーではAurora MySQLをメインのデータベースとして運用しています。オブザーバビリティ基盤にはDatadogを採用しており、AWS IntegrationによりCloudWatchメトリクスを収集していますが、データベースのパフォーマンス分析においては限界がありました。 CloudWatchメトリクスの限界 CloudWatchで取得できるメトリクスは、 AWS API経由で取得できる情報 になります。つまり、マシンリソースやAWSサービスとしてのAuroraの状態に関する情報が中心となります: メトリクス 内容 CPUUtilization CPU使用率 FreeableMemory 利用可能メモリ DatabaseConnections 接続数 ReadIOPS / WriteIOPS I/O操作数 CommitLatency / DDLLatency 各種レイテンシ 一方、実際にデータベースのパフォーマンスを分析する際に重要になってくるのは、 InnoDBやperformance_schemaといったMySQL内部のアーキテクチャに基づく情報 です。 例えば: どのクエリがどれだけ実行されているか クエリごとのロック待ち時間 バッファプールのヒット率 InnoDBの行ロック競合 これらはMySQLに直接接続して performance_schema や information_schema 、 SHOW ENGINE INNODB STATUS などから取得する必要があり、 CloudWatch(AWSのレイヤー)では取得できません 。(あくまでDatadogに連携できないだけであり、AWSではPerformance Insights という機能が提供されており、そこで一部確認可能です。) そこで活用するのが、Datadog AgentによるMySQLインテグレーションとDBM(Database Monitoring)です。 Datadog MySQLインテグレーションとDBM Datadog AgentはMySQLに直接接続し、 MySQLインテグレーション を通じてMySQL内部の情報を収集します。 MySQLインテグレーションは、標準機能とDBM(Database Monitoring)機能で構成されています( integrations-core mysql-15.11.0 )。 設定ファイルでのDBM機能の有効化は以下のように行います: instances : - host : localhost username : datadog password : <PASSWORD> dbm : true # この設定でDBM機能が有効化される 標準機能で取得できる情報 標準機能では以下の情報を収集します: 機能 概要 公式ドキュメント Metrics スループット、接続、エラー、InnoDBメトリクスに関連するメトリクス Data Collected DBM有効化で追加される機能 dbm: true を設定すると、クエリレベルのメトリクス、ライブおよび履歴クエリスナップショット、待機イベント分析、データベース負荷、クエリ実行計画を使えるようになります。( mysql.py#L429-L434 )。 if self._config.dbm_enabled: dbm_tags = list ( set (self.service_check_tags) | set (tags)) self._statement_metrics.run_job_loop(dbm_tags) self._statement_samples.run_job_loop(dbm_tags) self._query_activity.run_job_loop(dbm_tags) self._mysql_metadata.run_job_loop(dbm_tags) 機能 概要 公式ドキュメント 追加のQuery Metrics 標準機能よりさらに詳細なクエリレベルのメトリクス Data Collected で (DBM only)と書かれているメトリクス Query Samples 実行されたクエリのスナップショット。実際のパラメータ付きクエリ、Explainプラン、コスト情報を確認可能 Exploring Query Samples Database Hosts ホストごとのアクティブな接続、ブロッキングクエリ、待機イベントを可視化。クラスタトポロジも表示 Exploring Database Hosts Schema Explorer テーブル構造、カラム、インデックス、外部キーを確認。スキーマの変更履歴も追跡可能( collect_schemas: enabled: true で有効化) Exploring Database Schemas これらの情報を活用することで、「CPUが高い」という現象から「このテーブルのこのクエリがインデックスなしでフルスキャンしている」という原因まで、Datadogという1つのプラットフォーム上で一気通貫で追跡できるようになります。 APMとDBMを繋げる機能 などもあるため、APMからEXPLAINまで見ることができ非常に便利です。 次に、弊社におけるECS Fargate環境でDBMをホスティングする際の運用にまつわる課題と工夫を見ていきます。 DBMをECS Fargateでホスティングする際の課題 タイミーではコンピューティング基盤としてECS Fargateをメインで採用しています。 DBMを導入する以前から、APMやログ収集のためにDatadog Agentを サイドカー構成 で運用していました。 flowchart TB subgraph task["ECS Task (Fargate)"] app["Application Container"] agent["Datadog Agent (Sidecar)"] end この既存のサイドカーにDBM機能を追加することも技術的には可能です。しかし、サイドカー方式はAPMやログ収集といったアプリケーション単位の監視に適している一方、DBMはデータベース単位の監視であり、役割が異なります。 DBM機能をサイドカーに同居させると以下の問題が想定されます : 接続数の爆発 : タスク数分のDB接続が発生し、 max_connections を圧迫しそう Autodiscoveryの重複 : 各サイドカーが個別にAuroraクラスタを検出し、無駄なAuroraのAPI呼び出しが発生しそう メトリクスの重複 : 複数Agentが同じDBから収集し、メトリクスやイベントが重複してしまう可能性がありそう スタンドアロン構成の必要性 これらの問題を踏まないため、 DBM専用のスタンドアロンなDatadog Agentを別のECSサービスとして構築 することにしました。(DBM用のEC2を立ち上げるなどの手段もありますが、運用面を考えると避けたいところでしょう。) 平たくいうとDatadog Agentをサイドカーではなくメインコンテナとして1タスクだけ別のサービスとして立ち上げるというものです。以後これを便宜的にスタンドアロン構成と呼称します。 flowchart TB subgraph appTasks["Application ECS Tasks (Fargate)"] subgraph task1["Task ×N"] app1["App + Datadog Agent (Sidecar)"] end note1["Agentの役割: APM/ログ収集"] end subgraph dbmTask["Datadog DBM ECS Task (Fargate)"] subgraph task2["Task ×1"] agent2["Datadog Agent (Standalone)"] end note2["Agentの役割: DBM"] end スタンドアロン構成であれば、1つのAgentがリージョン内のAuroraクラスタを自動検出しメトリクスを収集するため、先に挙げた懸念は考慮しなくて良くなります。 Fargateにホストする方法ですが、Datadogの公式ドキュメント( Setting Up Database Monitoring for Aurora managed MySQL )にはDocker向けの手順が用意されており、Docker Labelsを使用した定義が紹介されています。 FROM gcr.io/datadoghq/agent: 7.36 . 1 LABEL "com.datadoghq.ad.check_names" = '["mysql"]' LABEL "com.datadoghq.ad.init_configs" = '[{}]' LABEL "com.datadoghq.ad.instances" = '[{"dbm": true, "host": "<AWS_INSTANCE_ENDPOINT>", "port": 3306,"username": "datadog","password": "ENC[datadog_user_database_password]"}]' タイミーでは複数のAWSアカウントでDBMを運用しており、環境ごとにAuroraのエンドポイントやDatadogのタグなどが異なります。 公式手順のDocker Labelsではシンタックスハイライトなどが効かないため、設定のミスが多くなりそうですし、環境ごとにDockerfileを用意するのも冗長です。 そこでDocker Labelsではなく、環境ごとの conf.yaml を生成してDatadog AgentのDockerイメージの conf.d/mysql.d に焼く方式を採用しました。 生成にはCUEを使用することでミスのないコンフィグを作成しやすくしています。 ここからは、実際のECSタスク定義やAurora Autodiscoveryの設定、CUEによる設定生成など、具体的な実装を紹介します。 アーキテクチャ まず、全体のアーキテクチャを示します。 flowchart TB subgraph aws["AWS Account"] subgraph ecs["ECS Cluster"] subgraph task["Datadog Agent Task (Fargate)"] agent["datadog-agent<br/>- DBM enabled<br/>- Autodiscovery"] end end aurora[("Aurora MySQL<br/>(Autodiscovered)")] ssm["SSM Parameter Store<br/>- DD_API_KEY<br/>- DATABASE_PASSWORD"] end datadog["Datadog<br/>(Metrics)"] agent --> aurora agent --> datadog ssm -.-> task ディレクトリマップ 次にディレクトリ構成を示します。 datadog-dbm/ ├── Dockerfile ├── etc/ │ └── datadog-agent/ │ ├── datadog.yaml # Agent全体の設定 │ └── conf.d/mysql.d/ │ ├── schema.cue # CUE共通スキーマ │ └── environments/ # 環境ごとの差分 │ ├── product-a-prod.cue │ ├── product-a-stg.cue │ └── ... └── opt/ ├── secrets.sh # シークレット展開スクリプト └── secrets.json.tmpl # シークレットテンプレート それでは、各コンポーネントの実装を見ていきましょう。 実装の詳細 1. Datadog Agent設定 まず、Datadog Agentの設定ファイル( datadog.yaml )でDBMとAurora Autodiscoveryを有効化します: site : datadoghq.com database_monitoring : autodiscovery : aurora : enabled : true region : ap-northeast-1 secret_backend_command : /opt/secrets.sh dogstatsd_metrics_stats_enable : true ポイント : database_monitoring.autodiscovery.aurora.enabled: true で、リージョン内のAuroraクラスタを自動検出 secret_backend_command でシークレット管理を外部スクリプトに委譲 2. シークレット管理 データベースのパスワードは、Datadog Agentの標準機能である Secrets Management を活用して取得しています。 本実装では secret_backend_command で独自スクリプトを使用しています。なお、Agent 7.70以降では secret_backend_type を使うことで、AWS Secrets Manager等に直接アクセスできるようになっています。 /opt/secrets.sh : #!/usr/bin/env bash set -eu cat /opt/secrets.json.tmpl | envsubst /opt/secrets.json.tmpl : { " DATABASE_PASSWORD ": { " value ": " ${DATABASE_PASSWORD} " } } この仕組みにより: ECSタスク定義でSSM Parameter Storeから DATABASE_PASSWORD を環境変数として取得 Datadog Agentが secret_backend_command を実行 envsubst で環境変数を展開し、JSONとしてAgentに渡す 3. Dockerイメージ FROM datadog/agent:7.72.2 RUN apt update && echo Y | DEBIAN_FRONTEND=noninteractive apt install -y gettext COPY ./etc /etc/ COPY ./opt /opt/ RUN chown -R dd-agent:root /var/log/datadog/ RUN chmod 700 /opt/secrets.sh gettext パッケージは envsubst コマンドを使うためにインストールしています。 4. ECSタスク定義 { " containerDefinitions ": [ { " name ": " datadog-agent ", " image ": " <ECR_REGISTRY>:latest ", " dockerLabels ": { " com.datadoghq.ad.check.id ": " _dbm_mysql_aurora " } , " environment ": [ { " name ": " ECS_FARGATE ", " value ": " true " } ] , " secrets ": [ { " name ": " DD_API_KEY ", " valueFrom ": " /datadog/DD_API_KEY " } , { " name ": " DATABASE_PASSWORD ", " valueFrom ": " /datadog/DATABASE_PASSWORD " } ] , " essential ": true } ] , " cpu ": " 2048 ", " memory ": " 4096 ", " networkMode ": " awsvpc ", " requiresCompatibilities ": [ " FARGATE " ] } ポイント : シークレットはSSM Parameter Storeから取得 5. Aurora側の設定 DBMを利用するためには、Aurora側にも設定が必要です: DBパラメータグループで performance_schema を有効化 Datadog Agent用のMySQLユーザー作成と権限付与(GRANT) 詳細な手順は公式ドキュメントを参照してください: 参考: Setting Up Database Monitoring for Aurora managed MySQL 基本的な実装は以上ですが、タイミーでは実際に複数環境でこの構成で運用しています。次に、マルチアカウント運用における設定管理の工夫を紹介します。 マルチアカウント運用:CUEによる設定生成 タイミーでは複数のAWSアカウントでこの構成を運用しています。 課題となったのは、 環境ごとに異なるDatadog Agentの設定ファイル(conf.d/mysql.d/conf.yaml)をどう管理するか という点です。各環境で以下のような差分があります: AWSアカウント名(タグに使用) schemas_collection の有効/無効(プロダクトにより必要性が異なる) CUEによる設定管理 この課題を解決するため、 CUE を使って設定を管理しています。 etc/datadog-agent/conf.d/mysql.d/ ├── schema.cue # 共通スキーマ定義 └── environments/ ├── product-a-prod.cue # 環境ごとの差分 ├── product-a-stg.cue ├── product-b-prod.cue └── ... 共通スキーマ(schema.cue) package datadog #DatadogConfig: { ad_identifiers: ["_dbm_mysql_aurora"] init_config: {} instances: [#Instance] } #Instance: { host: "%%host%%" port: "%%port%%" username: "datadog" password: "ENC[DATABASE_PASSWORD]" dbm: true // schemas_collectionが有効な場合のみブロックを含める if schemas_collection_enabled { schemas_collection: { enabled: true } } aws: { instance_endpoint: "%%host%%" } tags: [ "dbclusteridentifier:%%extra_dbclusteridentifier%%", "region:%%extra_region%%", "account:\\(account)" // CUEの文字列補間 ] } // 環境差分として定義する変数 schemas_collection_enabled: bool account: string 環境ごとの差分(例:product-a-prod.cue) package datadog config: #DatadogConfig // 環境差分 account: "product-a-prod" schemas_collection_enabled: true // product-aはスキーマ収集を有効化 環境ごとの差分(例:product-b-stg.cue) package datadog config: #DatadogConfig // 環境差分 account: "product-b-stg" schemas_collection_enabled: false // product-bは無効 設定ファイルの生成 デプロイ時にCUEコマンドでYAMLを生成します: cue export -e config \\ ./etc/datadog-agent/conf.d/mysql.d/schema.cue \\ ./etc/datadog-agent/conf.d/mysql.d/environments/product-a-prod.cue \\ --out yaml > ./etc/datadog-agent/conf.d/mysql.d/conf_aws_aurora.yaml CUEを採用したメリット 型安全性 : スキーマ定義により設定ミスを防止 DRY : 共通部分を一箇所で管理し、環境差分のみを各ファイルに記述 条件分岐 : if schemas_collection_enabled のように、フラグに応じた設定の出し分けが可能 可読性 : 環境ファイルは数行で済み、差分が一目瞭然 マルチアカウントへのデプロイ CDワークフローで環境ごとのconfigファイルを指定してDockerイメージをビルドすることで、1つのリポジトリから複数AWSアカウントのECRにイメージをpushしてデプロイできます。 # .github/workflows/deploy.yml jobs : deploy : strategy : matrix : include : - env-name : product-a-prod - env-name : product-a-stg - env-name : product-b-prod - env-name : product-b-stg steps : - name : Generate configuration run : | cue export -e config \\ ./etc/datadog-agent/conf.d/mysql.d/schema.cue \\ ./etc/datadog-agent/conf.d/mysql.d/environments/${{ matrix.env-name }}.cue \\ --out yaml > ./etc/datadog-agent/conf.d/mysql.d/conf_aws_aurora.yaml - name : Build and push # 環境ごとの設定を含んだイメージをビルド・push - name : Deploy # 各アカウントのECSにデプロイ これにより、設定は環境ごとに分離しつつ、Datadog Agentのバージョンアップなどの共通作業はまとめて行えるようになりました。 まとめ ECS Fargateでスタンドアロン構成のDatadog Agentを運用し、DBMを導入することで、Aurora MySQLのオブザーバビリティを向上できました。 Datadogという1つのプラットフォーム上でメトリクスと合わせてダッシュボード、ウィジェットを作成できたり、APMとDBMの結合やExplainの確認など、Performance Insightよりも便利で充実した内容を見ることができます。 また、タイミーにおける以下の工夫点が参考になれば幸いです。 ECS FargateではDBMをスタンドアロン構成でホスティング Docker Labelではなくconfを使用する CUEでマルチアカウントの設定差分を型安全に管理 おわりに Datadog AgentのMySQLインテグレーションはOSSとして公開されています。 datadog-agent 7.69.0にアップデートしたところ、Aurora ReaderのInnodbメトリクス取れなくなる事象が発生し、少しの修正ですがPRを出しました。 https://github.com/DataDog/integrations-core/pull/21190 DBMは便利ですがたまに壊れたりもしますので、 integrations-core を何かあったら直していければなと思います。(この記事で利用者が増えたらいいな) タイミーでは一緒に働くエンジニアを募集しています!興味のある方はぜひカジュアル面談でお話ししましょう。 採用情報はこちら
こんにちは、タイミーでエンジニアをしている佐藤です。 こちらは Timee Product Advent Calendar 2025 の9日目の記事です。 この記事では、AWS Resource Explorerを用いてマルチアカウント環境での資産棚卸しを効率化した取り組みについて紹介します。その過程でAWS Organizationsのアカウント構成も見直したので、合わせて共有します。 背景 AWSにおいてマルチアカウント構成で運用を続ける中で、横断的な運用効率化ニーズが出てきました。 マルチアカウント環境での横断的なリソース調査 タグ付けに基づくリソース検索 生成AIを活用したリソース作成状況の把握 アカウントやリージョンが複数に跨る場合の検索性が課題になります。組織レベルでAWS Resource Explorerを設定することで、リソース調査を効率化することにしました。 AWS Resource Explorerとは AWS Resource Explorerは、リージョンとアカウントを横断した高速な検索を提供する無料のサービスです。 リージョンまたぎでのリソース探索 マルチアカウント環境でのリソース把握 不要リソースの発見と最適化 タグ付けリソースの検出 フィルタールールによるカスタムビュー また、AWSマネジメントコンソールの検索バーとも統合されており、上部の検索窓から / (スラッシュ)で呼び出せます。 AWS Resource Explorerの設定 デプロイ方法の選択 AWS Resource Explorerの組織展開には複数の方法があります。 方法 特徴 各アカウントで個別設定 柔軟だが漏れが発生しやすい Quick Setup 簡単だがリージョン制御が難しい CloudFormation StackSets リージョン指定可能、IaC管理しやすい 私たちは以下の理由からCloudFormation StackSetsを採用しました。 新規アカウントを含めて漏れなく設定したい SCPで利用可能リージョンを絞っている 構成をTerraformで管理している CloudFormation テンプレートは AWS公式ドキュメント を参考にしました。 インデックス構成 Resource Explorerでは、AggregatorIndexとLocalIndexの2種類のインデックスがあります。 AggregatorIndex : 全リージョンのリソース情報を集約するインデックス。全アカウントで統一したリージョンに配置する必要がある LocalIndex : 各リージョンのリソース情報を保持するインデックス 私たちは以下のように構成しました。 AggregatorIndex:  ap-northeast-1 にデプロイ LocalIndex:  ap-northeast-1 以外の利用リージョンにデプロイ StackSetはAggregatorIndex用とLocalIndex用の2つを用意し、Organizationsのルートを指定して組織メンバー全体に適用しています。 auto_deployment を有効にすることで、新規アカウントに対しても自動でデプロイされます。 設定時の注意点として、Resource Explorerを手動で設定した場合や、Systems Managerを有効にしているとResource Explorerの既存インデックスがあります。 この状態でStackSetsをデプロイすると競合が発生します。 deployment_targets の account_filter を設定することで、既存インデックス作成済アカウントでの競合を回避しました。 AWS Resource Explorerの活用 組織スコープのView作成 Operations Toolingアカウントで、組織全体を検索対象とするViewを作成して検索を可能としました。Viewはアカウントスコープとリソースフィルターで絞り込めるため、用途に応じて複数作成できます。 Resource Explorerは追加料金なしで利用できますが、以下のクォータがあります。 一度に取得できる結果: 1,000件 アグリゲーターリージョンの月間検索オペレーション: 10,000回(デフォルト) フィルターを設定しない状態では、AWS Configのリソースが大量に取得されてしまい、探索性が落ちてしまいます。そこで、デフォルトView向けには以下のようなフィルターを設定した組織スコープのViewを作成して調査対象のリソースが絞り込みやすい状態にしました。 filter_string = "-service:config" AIによるリソース調査での活用 11月20日から21日で開催された アーキテクチャConference2025 にて、弊社橋本が『AI x Platform Engineeringでスケーラブルな組織を作るには』を発表しました。この発表で紹介した、AIによるリソース配置・設定情報(AS-IS)の調査とドキュメント化の取り組みで、AWS Resource Explorerを活用しています。 生成AIモデルの支援を受けてリソースの一覧を取得する際に、AWS API MCP Serverを利用することも考えられます。しかし、多くの結果を受け取るとコンテキスト超過・欠落で情報欠損することがあります。 Resource Explorerで取得したリソース一覧を保存し、ローカルで必要な情報を抽出するアプローチが効果的でした。 アドホックな調査であれば生成AIモデル経由でAWS CLIを呼び出し、調査対象が明確な場合は要件を元にプログラムからAWS SDKでクエリする手法も有効です。 まとめると、AIによるリソース調査での活用ではResource Explorerを起点とした以下の3段階アプローチが有効でした。 生データの取得(CLI): Resource Explorerの検索クエリを用いてリソース一覧を取得する。 構造化・フィルタリング: jqやPythonでアカウント番号、リージョン、リソースARN、必要情報のリストにする。設定情報の詳細を一覧化する場合は、AWS CLIによる生データ取得と構造化を繰り返す。 分析・洞察 (LLM/MCP): リストを元に個別のリソースの設定状況を確認する。 AIエージェントにアカウント横断での調査対象と作業ステップを指示して、リスト抽出する一例になります。 今回の調査で得られたリソース取得の方法と適したユースケースについて、以下の表にまとめます。 方法 適したユースケース AWS Resource Explorer を AWS CLI でクエリ アカウント横断でリソースを網羅的に抽出する場合 AWS Resource Explorer を AWS SDK でクエリ アカウント横断でリソースを網羅的に抽出し、追加処理を行う場合 任意のリソースを AWS API MCP Server 経由でクエリ 対象アカウントとサービスが絞られており対話的な探索をする場合 任意のリソースを AWS CLI/AWS SDK でクエリ 対象アカウントとサービスのリストを元に詳細を取得する場合 タグによるガバナンス確認での活用 Resource Explorerでは、タグの有無をベースにフィルターできます。また、クエリ結果から対象リソースに付与されたタグ値を確認できます。 12月7日のアドベントカレンダー「S3バケットの構成標準化 - 分類とガードレールによる運用改善」では、S3への適切なタグ付与とAWS Configによるガードレール実装を紹介しました。Resource Explorerを活用すれば、アカウント横断でタグの付与漏れを確認できます。 例えば ConfigRule-s3-bucket-server-side-encryption-enabled が付与されていないバケットは、 -tag.key: の否定検索で以下のように取得できます。 service:s3 resourcetype:s3:bucket -tag.key:ConfigRule-s3-bucket-server-side-encryption-enabled 注意点 AWS Resource Explorerは多数のリソースタイプをカバーしていますが、すべてではありません。利用前に Resource Explorer で検索できるリソースタイプ を確認することをおすすめします。 例えば、Amazon Bedrockの基盤モデルのように、リソースが作成されないサービスは対象外です。 AWS Cost Explorer MCP Serverを併用して、課金が発生しているサービスを確認して組み合わせるとレポート作成に役立ちました。 AWS Organizations構成の見直し この章では、AWS Resource Explorerをどのアカウントに委任したかを紹介します。 タイミーでは AWS セキュリティリファレンスアーキテクチャ (AWS SRA) を参考に、セキュリティOUのアカウントやインフラストラクチャOUの共有サービスアカウントを整備してきました。 組織横断での運用効率化に関わるAWSサービスとして以下が挙げられます。 AWS Healthの組織ビュー AWS User Notificationsの組織通知 AWS Resource Explorerのマルチアカウントリソース検索 しかし、これら全てを共有サービスアカウントへ委任すると、アカウントの責務が重くなりすぎます。そこで、AWSホワイトペーパー  Organizing Your AWS Environment Using Multiple Accounts  を参考にしました。インフラストラクチャOUを以下のアカウントへ分割しています。 アカウント 責務 Identityアカウント AWS IAM Identity Centerの管理 Operations Toolingアカウント 運用ツール、監視、リソース可視化 Backup管理アカウント バックアップの一元管理 AWS Resource Explorerの委任先については、Whitepaperに明確な記載がありませんでした。ユースケースから判断し、運用ツールを集約するOperations Toolingアカウントへ委任しています。 なお、IdentityアカウントにおけるAWS IAM Identity Centerの導入については、12月4日のアドベントカレンダーで紹介しているので、よければご覧ください。 まとめ AWS Resource Explorerを組織レベルで展開することで、マルチアカウント環境での資産棚卸しを効率化できました。 ポイントをまとめます。 リージョンとアカウントを横断した検索にResource Explorerが有効 CloudFormation StackSetsで組織展開し、新規アカウントへの自動適用と競合回避を実現 インフラストラクチャOUをIdentity/Operations Tooling/Backupに分割し、責務を明確化 Resource ExplorerはOperations Toolingアカウントに委任 AIによるリソース調査では、リソース一覧の保存→抽出→個別確認の3段階アプローチが有効 タグベースのフィルターでアカウント横断のガバナンス確認にも活用可能 AWS Organizations構成の見直しやResource Explorerの導入を検討されている方の参考になれば幸いです。 プロダクト開発を支えるプラットフォームチームの活動に興味を持ってくださった方は以下も覗いてみてください。 プロダクト採用サイトTOP カジュアル面談申込はこちら
こんにちは!株式会社タイミーでデータアナリストをしている shun です。 普段は、プロダクトや事業の意思決定を支援するためのデータ分析を行っています。 本記事では「現場で本当に使われるダッシュボード」について、私の失敗談と改善の実践録をご紹介します。 なお、この記事は Timee Advent Calendar 2025 Series 2 の 7日目の記事です。 はじめに:使われなくなるダッシュボードは「目的」が混在している 本題に入る前に、ダッシュボードの分類を整理しておきましょう。 誰しも経験がある「気合いを入れて作ったのに、現場で使われない...」そんな悲しいダッシュボードが生まれる最大の原因は、「誰に・何のために」という目的の型が混在していることにあると考えています。 デジタル庁が公開している『 ダッシュボード構築・運用ガイドブック 』などを参考に、私はダッシュボードを以下の3つに分類して考えています。 分類 ① 提示型 ② 業務型 ③ 探索型 主な利用者 経営層・マネジャー 現場担当者 アナリスト 目的 主要指標のモニタリング 今のステータス確認 要因解析・仮説検証 粒度 概況(サマリ) 詳細(個別の明細) 概況 → 詳細(ドリルダウン) ネクストアクション 意思決定 改善アクション(即時対応) 施策立案 今回、私がフォーカスするのは真ん中の「② 業務型」です。 ここを履き違えて、現場向けなのに「探索型」のような複雑なフィルタ機能や多角的なグラフを入れてしまうことが、失敗の始まりでした。 課題感:何をすべきか不明瞭な現場、原因解明に追われるアナリスト 以前の私は、現場に対して「探索型」に近い、情報てんこ盛りのダッシュボードを提供していました。その結果、現場とアナリストの間で、以下のような「負のループ」が発生していました。 現場 : 指標が悪化するが、ダッシュボードの情報量が多すぎて「どこが悪いか」特定できない。 アナリスト : 現場から「数字が悪い理由を調べて」と依頼が飛んでくる。 アナリスト : 他のタスクを止めて原因分析(探索)を行う。原因が特定できた頃には数日が経過している。 現場 : 原因はわかったが、「で、具体的に今日何をすればいいの?」というアクションがわからず、手が止まる。 つまり、「分析すること」自体に工数が取られ、肝心の「リカバリーのアクション」が後回しになっていたのです。 現場ヒアリングを通じてわかったのは、彼らに必要なのは 「なぜ悪いか(Why)」の詳細な解説ではなく、「今すぐに何をすべきか(What)」の指示 でした。 コンセプト:目指したのは「見てすぐに動ける診断表」 この負のループを断ち切るために、現場へのヒアリングを通じてダッシュボードの設計思想を根本から変えました。 目指したのは、データを探索させるツールではなく、健康診断のように「悪い箇所(診断)」と「処方箋(アクション)」が一目でわかる「診断表」です。 具体的に設計・構築で意識したポイントは以下の2点です。 1. 情報量は「PC1画面」サイズに収める 「スクロールが必要な時点で、現場は見なくなる」と決め打ちました。 情報はあればあるほど安心しますが、忙しい現場にとってはノイズでしかありません。 「念のため」置いていた予備のグラフや、推移を確認するためだけのチャートは全削除。 ファーストビューですべての「異常」が検知できるレイアウト。 徹底的に情報を断捨離し、ノートPCの1画面で完結させるUIにこだわりました。 2. 指標が悪いときの「初手」を決められる これが今回の最大の肝です。 単に「数字が下がっています」とアラートを出すだけでは不十分です。 タイミーの現場における「指標悪化のパターン」を洗い出し、それに対応する「初手のアクション」をダッシュボード上に明記しました。 このように、ユーザーにグラフを読み解かせるのではなく、システム側でロジック判定を行い、「次はこれをやるべき」という推奨アクションまでを表示させるようにしました。 下記はダッシュボードのモックのイメージです 実装の工夫:分析結果はダッシュボードの「裏側」に込める 「見てすぐに動ける」を実現するためには、ダッシュボードの表側(UI)をシンプルにする分、裏側(ロジック)にアナリストの知見を詰め込む必要があります。 具体的には、以下の2点を意識して実装を行いました。 1. 指標悪化の「代表パターン」を網羅する 診断型ダッシュボードの生命線は、診断の精度の高さです。もし「診断結果: 周辺のユーザー不足」と出たのに、本当の原因が「掲載時間が短い」だった場合、現場は誤ったアクションをとることになり、ダッシュボードへの信頼は落ちてしまいます。 そこで実装前には、過去の指標とその課題カテゴリの関係性を徹底的に行い、指標が悪化する原因の大部分をカバーできるように設計しました。 もちろん個別の「レアケース」までは網羅できないので、発生頻度の高い「代表的な悪化パターン」を網羅することに注力しています。 2. 過去データに基づいた「閾値」設計 「どこからを要対応(赤色)とするか」の閾値決めも、アナリストの腕の見せどころです。 適当な基準で「要対応(赤色)」とするのではなく、過去のデータを分析し「このラインを割ったら、指標の悪化が顕著になる」というラインを算出して設定しました。 これにより、できるだけ「本当にヤバいときだけアラート」を出したり、要対応(赤色)になっている理由を聞かれたときに返答できるようにしています。 運用の工夫:他部署連携の「共通言語」として利用する どれだけ良いダッシュボードを作っても、ダッシュボードの存在を認知してもらい、適切なユースケースで利用してもらう行動習慣をつくることは困難です。 そこで、このダッシュボードを単なるモニタリングツールではなく、他部署へアクションを依頼する際の「公式なコミュニケーションツール」として位置付けようとしています。 スクリーンショットを「依頼の切符」にする 業務の中で、営業がマーケティング部やデータアナリティクス部に協力を仰ぐ場面(例:マーケティングの対策をしてください)があります。 その際、チャットツール等での依頼には「ダッシュボードの診断結果のスクリーンショット」を貼ることをルール化しようとしています。 Before: 「最近稼働率が悪いからインストール数が下がっているから、広告出稿を増やしてほ欲しい」→「確かに数字は落ちているけど、本当に広告出稿不足が原因かな? 広告費をかける前に、もう少し確証が欲しいな」というやりとりになる。 After: 「ダッシュボードで『エリアのワーカー不足「応募が集まりにくい状況」型』と診断が出ています(スクショ添付)。推奨アクションに従い、広告出稿をお願いします」 このように運用することで、ダッシュボードが「依頼の正当性」を証明する切符の役割を果たします。 ここからは、現在試行錯誤中ですが、「ダッシュボードでこう出ているから、XXをやってほしい」。 というコミュニケーションフローを確立し、ダッシュボードを「見るもの」から「組織を円滑に動かすための武器」へと進化させていければなと思っています。 おわりに:アクションを生むためのデータ活用の「試行錯誤」は続く 業務用のダッシュボード構築のゴールは、データを綺麗に可視化することではありません。 「データを見て、即座に行動に移すことができ、ビジネス指標を改善すること」 です。 現場ではダッシュボードを活用し、自発的にアクションをしたり提案活動に活かしたりしながら、数値が改善される事例が上がり始めています。 私たちデータアナリストは、高機能なBIツールを使うと、つい複雑な分析機能や多角的なグラフを入れたくなります。しかし、現場が必要としているのは分析ツールではなく、「現場の思考コストをゼロにし、アクションへ直結させる」ことです。 この引き算の設計こそが、業務ダッシュボードには求められているのだと痛感しています。 まだ運用の試行錯誤は続いていますが、これからも「組織を走らせるための武器」として、データを活用していきたいと思います。 最後に、タイミーでは一緒に働くメンバーを募集してます!ご興味があればぜひお話しましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら
はじめに こんにちは、タイミーでエンジニアをしている古屋( id:kimikimi714 / @kimikimi714 )です。 こちらは Timee Product Advent Calendar 2025 トラック3の7日目の記事です。 すでに入社して1年半ほど経ちましたが、入社して割とすぐに対応した S3 バケットの構成標準化について公開します。 標準化の目的 当時、ある S3 バケットの調査をおこなおうとした際に以下のような問題が見つかりました。 Terraform で IaC 化される前に作成されたバケットなどで、S3 へのアクセスログが出力されてないケースがある。 パブリックアクセスの必要のないバケットに対して、パブリックアクセスブロックの設定が入っていないものがある。 仮に IaC 化されていたとしても、書いた人によって入れられた設定がまちまちで一貫性がない。 IaC 管理されていないなら import すればいいじゃない、ログが取れてないなら取ればいいじゃないという話ではなく、そもそも どういう状態にあれば既存サービスおよびこれから立ち上げていく新規サービスでも S3 を安全にしっかり使える状態になるのかがわかっていない ことが根本の問題でした。 このため、S3 バケットのベストプラクティスをまずは押さえて、その上で私たちがどのようにそのプラクティスを取り入れていくべきかをまとめて標準化とする方針で進めました。 セキュリティベストプラクティスの確認 まずは AWS 公式が提供しているベストプラクティスの確認からおこないました。 docs.aws.amazon.com これらは AWS Security Hub CSPM の基本的なセキュリティのベストプラクティス標準 が有効であれば自動的にコンプライアンスチェックがされるものも含まれていますが、たとえば以下のようなものは含まれないために自分たちで必要なチェック体制を組む必要があります。 バージョニング が有効か オブジェクトロック がかかっているか 暗号化 が有効か KMS で暗号化 されているか レプリケーション が有効か 例として挙げたチェック項目はすべてのバケットに対して有効化するべきか?というとそうではありません。 たとえば、バージョニングは過去バージョンで保存されているオブジェクトサイズの分だけコストがかかります。また過去バージョンを抱えているオブジェクトの削除マーカーが増えすぎたり、1オブジェクトあたりの過去バージョン数が多いとパフォーマンス上の問題があることも バージョニングのトラブルシューティング に書かれています。バージョニングは過去バージョンによるバックアップで障害復旧することが主な目的となるでしょうが、バックアップをバージョニングでおこなうべきなのか、 GitHub Actions 等からのアップロードで対応しているなら問題があれば GitHub から切り戻せる余地はないのかなど要件次第で要不要が変わるものです。同様にオブジェクトロックも要不要は要件次第です。 また、オブジェクトロックはバケットの作成時でないと設定できない制約もあるので、作成時点で要件をある程度固めてからでないと適応できない設定項目だったりします。 こういったことからベストプラクティスをベースとしつつも、すべてのバケットに適応するべき項目とバケットの大まかな分類をして分類ごとに必要なプラクティスをよりわけました。 バケットの分類 新たに出てくるバケットの要件が何になるかはその時になってみないとわからないです。なので、まずは既存バケットの利用方法について調査するところからはじめました。そうすると大雑把に以下のような8つの分類となることがわかりました。 ユーザーのプロフィール画像など コーポレートサイトのクリエイティブなど S3 サーバーアクセスログ CloudTrail ログなどの監査ログ アプリケーションログのアーカイブ アプリケーションで利用する設定ファイル アプリケーションで参照する一時ファイル 要配慮個人情報や特定個人情報 それぞれの分類について簡単に解説します。最終的な標準設定は別途記載します。 ユーザーのプロフィール画像など ユーザーによるアップロードで作成されるオブジェクトが主な内容です。これらはユーザーが能動的に画像をアップロードしなければならないため、仮に誤って削除・更新してしまうと、復旧はバックアップからかユーザーからの再アップロードとなってしまいます。誤操作による再アップロードを促す流れは、ものによってはユーザー離脱の引き金になり得るので、こういうケースはバージョニングしておいた方が安心です。 コーポレートサイトのクリエイティブなど コーポレートサイトや何らかのランディングページの静的リソースが主な内容です。これらは GitHub のリポジトリで管理し、マージしたら GitHub Actions 等で自動的にアップロードされることが多いです。画像保存のための別のツールをデザイナーが使っているケースもありますが、大半は GitHub を通じてアップロードされるためユーザーに見えるリソースの大元は S3 以外にあるようなケースです。 もしリソースが何らかの理由で削除・更新された場合に過去バージョンから復元するのか、 GitHub からアップロードをするのかは復旧までのリードタイム要件次第となります。ビルドが挟まるなど、デプロイ時間が復旧時間に強く影響を与えるケースではバージョニングから復旧できた方が早いですし、リバートしてデプロイの方が一瞬でもレビューが挟まって安全に復旧できるという考え方もできます。また削除・更新されたオブジェクトの数が多すぎれば、バージョニングから1つずつ戻すことが現実的ではないこともあり得ます。 S3 サーバーアクセスログ S3 へのアクセスがおこなわれた際に S3 バケットから発行されるアクセスログの保存場所です。このアクセスログは実際のアクセスから数時間遅れて発行される特徴があるため、 CloudTrail のようなリアルタイム性はかなり低いです。ですが、 CloudTrail データイベントを有効化しないと取れないものがより料金が安く取れたり、CloudTrail ログには含まれない情報が含まれていたりするため、有効化しておいた方が監査性が上がります。 比較表は以下のリンクをご確認ください。 docs.aws.amazon.com CloudTrail ログなどの監査ログ CloudTrail などインシデント時に誰がどこからアクセスしたか追跡する用途で使われるような監査ログを保存する場合です。改竄防止が非常に大事になります。 アプリケーションログのアーカイブ すでに弊社のいくつかのテックブログで書いてる通り、 普段は Datadog を利用していますが、ログの長期間保存は非常にコストがかかるため S3 にアプリケーションログのアーカイブを取って Datadog 側の保持期間は短くしてあります。 平時に参照することはほとんどありませんが、過去の事象に関する調査をしたい場合に活用されるので、数年保存することを想定しているものになります。 アプリケーションログの他、 ALB や CloudFront などのアクセスログもここに含むことにしています。 アプリケーションで利用する設定ファイル アプリケーションが設定ファイルとして読み込むファイルの保存先です。たとえば ECS で参照する環境変数が列挙されたオブジェクト や Fluent Bit の設定ファイル などが主な内容です。 アプリケーションで参照する一時ファイル たとえば、一度 DB レコードから集計したデータを一時的にオブジェクトとして S3 バケットにおき、別のバッチで参照するようなケースで使われる一時ファイルです。バッチが実行終了したら使わなくなるようなファイルが主で、利用後直ちに削除することが期待されるようなオブジェクトを保管するバケットです。 要配慮個人情報や特定個人情報 法的に個人情報保護の中でも要件が強いものを保管するバケットです。S3 バケットのバケットポリシーでアクセスコントロールをおこなうことが大半ですが、このバケットについては特にコンプライアンス遵守のために KMS による暗号化とキーポリシーによるアクセスコントロールを追加でおこなうようにすることを検討した方が良いです。 分類後のバケット設定のサマリー 分類後、それぞれに必要と考えられる設定をそれぞれ書き出しました。以下の表が簡単なサマリーです。本当はここに書いてる項目以外にもありますが、全部書くには多すぎるので一部だけ掲載しています。これが唯一の解ではありませんし、S3 バケットに対して設定できることはここに書いていることだけではないですが、こういったことをまとめることで「どのバケットに何の設定がされている必要があるのか」という標準化に近づけることができます。 バケットの分類 暗号化手法 要オブジェクトロック 要バージョニング 要レプリケーション ユーザーのプロフィール画像など S3-managed 不要 必要 必要 コーポレートサイトのクリエイティブなど S3-managed 不要 不要 不要 S3 サーバーアクセスログ S3-managed レプリケーション先で必要 *1 必要 必要 CloudTrail ログなど監査ログ S3-managed 必要 必要 不要 アプリケーションログのアーカイブ S3-managed 不要 不要 不要 アプリケーションで利用する設定ファイル S3-managed 不要 必要 必要 アプリケーションで参照する一時ファイル S3-managed 不要 不要 不要 要配慮個人情報や特定個人情報 KMS 不要 不要 不要 この設定例を元に S3 バケットのサンプルコードも作成しました。このコードには S3 サーバーアクセスログのバケットの指定方法なども含めて、課題として挙げていたログの欠如を解消し、ログを取ることが当たり前とされるような構成を目指しました。また、パブリックアクセスブロックも CloudFront を前段に挟めばバケットそのものを公開する必要はないことから基本的には全バケットでプライベート化を推奨 *2 し、できれば AWS アカウント単位でパブリックアクセスブロックを有効化しておくこととしました。このとき、チームメンバーから module 化することで、こういった設定そのものをカプセル化した方が良いのではないか?という提案がありましたが、作成した当時この分類で本当に問題ないのかがわからなかったため、 module 化は時期尚早と判断し、標準化の第一段階としてはサンプルコードを書くに留めました。 ガードレールの設計と実装 「 セキュリティベストプラクティスの確認 」の節にも記載した通り、 AWS Security Hub CSPM の基本的なセキュリティのベストプラクティス標準 が有効であれば自動的にコンプライアンスチェックがされるものも含まれているため、含んでいないもののうち、バケットの分類上必要なチェック項目を洗い出しました。これらは AWS Config の マネージドルール が存在しています。 AWS Config は AWS リソースの構成を継続的に監視・評価するサービスで、 SecurityHub とも統合が可能なものとなっています。先に洗い出した項目と対応関係は以下のようになります。 意味 ルール名 S3 バージョニングが有効か s3-bucket-versioning-enabled S3 オブジェクトロックがかかっているか s3-bucket-default-lock-enabled サーバーサイド暗号化が有効か s3-bucket-server-side-encryption-enabled KMS で暗号化されているか s3-default-encryption-kms レプリケーションが有効か s3-bucket-replication-enabled AWS Config では特定のタグがついているリソースに対して、特定のルールをチェックする機構があります。これらのルールをひとまとめにした適合パックを各 AWS アカウントに反映して、リソースには適切にタグをつけることで標準に沿った構成かチェックできるようにしました。タグの設定例は以下のようにしました。 tags = { "BucketType" = "user-uploads" "ConfigRule-s3-bucket-replication-enabled" = "enabled" "ConfigRule-s3-bucket-server-side-encryption-enabled" = "enabled" "ConfigRule-s3-bucket-versioning-enabled" = "enabled" } これで ConfigRule-s3-bucket-replication-enabled がついているリソースでは s3-bucket-replication-enabled がチェックされるようになります。実際にはタグのまとまりを module 化しており、コード上の指定としては以下のように書くだけで中のタグが展開されて、リソースの構成チェックが走る仕組みです。 tags = module.aws_config_s3.tag_map_for_user_uploads module の修正をおこなうことで中央集権的にチェック項目を管理でき、かつ仮にルールの一部を緩めたい場合は以下のように書くことで柔軟性も持たせました。 tags = merge ( module.aws_config_s3.tag_map_for_user_uploads, { "ConfigRule-s3-bucket-replication-enabled" = "disabled" } ) 運用開始後に見えてきた良かったこととギャップ まず、この標準化の良かったところは、その時点でできる S3 の設定について網羅的に知れたこととそれをちゃんと要件に組み込むサンプルまで出せたことです。バージョニングができることは以前から知っていたものの、バージョニングによるパフォーマンス問題や過去バージョン分だけコストが上がるなどの制約は標準化前の調査をしてはじめて知ったことでした。チームメンバーも知らない制約だったりしたので、それをわかった上でどういう要件で役に立つのかなどまとめられたことは良かったです。 また、バケット作成時点でどういう設定を入れればいいか迷っていた時間もこの分類とサンプルコードのおかげで減りました。ここは今になって思えば設計から実装時間の計測をしておけば、どのくらい削減できたか少しはわかったかもしれないなと思っていますが、一から S3 バケットで何ができるか調べるよりは分類から入れるべき設定をサクッと出せるのは個人的な体験としても良かったです。 一方でギャップもやはりありました。 この標準化の目的は要件ごとに S3 バケットの適切な設定を決めておくことでスムーズにコンプライアンスを遵守した S3 バケットを作成・更新できるようにすることでしたが、ここから発展させてあまり S3 に詳しくない人であっても、この分類に沿って自分たちの要件にあった S3 バケットを作れるようになることを目標においていました。そこで第一段階としては私が標準化したバケット設定を使って新規バケットを作り、期待値通りのチェックもおこなわれるかを確認し、第二段階として開発者からの依頼のあったバケットを試しに作ってもらえないか相談して実践してみました。 第一段階は私が作った本人でもあることから期待通りになることはすんなり確認できましたが、第二段階ではやはり S3 に関する知識が十分にない状態ではいくらサンプルコードがあっても書くのは難しいと改めてわかりました。この件は普段 Terraform を使わないような方にお願いをしたので、元の知識にギャップがある状態でお願いするのがそもそも無茶な話だったとは思います。ただ、それ以降 Terraform に詳しいチームメンバーにもいくつかのバケットを作ってもらう機会がありました。その際、私のコードレビュー時点で抜け漏れや分類の認識違いが見つかったので、 module 化をすれば設定の抜け漏れはなくなるかもしれないですが、適切にバケットの種類を選べるかにはまだハードルがあると感じました。 ギャップの中には私の予想を外れた良いギャップもありました。それはバケットの分類が最初に決めた去年の7月ごろから今の時点まで8つの分類を修正する必要がなかったことです。私自身はこの分類にはじめから自信があったわけではないですし、分類が変わらなくても中の設定の修正が必要な可能性を考慮したために module 化を見送っていたのですが、実際には S3 に関する各種アップデートを含めても今変更を加えないといけない理由がないのが実情です。こんなに変わらないなら module 化しておけばよかったなと思うほどでした。 今後の展開 今年11月20日から21日で開催された アーキテクチャConference2025 にて、弊社橋本による発表『AI x Platform Engineeringでスケーラブルな組織を作るには』でも公開された内容に関連して、この施策は現在 Design Addendum(設計判断を文書化したドキュメント、補遺) とし、この内容を元に AI が自走できるように検討を進めているところです。 すでに Design Addendum として S3 の分類やその思想を書き出したところなので、これを AI が読み自分で実装できるか?を試そうとしています。実装できると思っていますが、目標の第二段階にあった「あまり S3 に詳しくない人であっても、この分類に沿って自分たちの要件にあった S3 バケットを作れるようになる」に足る状態になるかはしっかり検証したいと思っています。 まとめ 以上が S3 バケットの構成標準化のためにバケットの分類をおこない、ガードレールを作成したアプローチです。実際の成果として設計時間の削減や、8つのバケットの分類でほぼ実際の要件をカバーできることが得られました。 このドキュメントを参考にされる場合は、まずは既存に存在するバケットをざっくり分類してみることが良いと思っています。中に含まれるオブジェクトの種類が無茶苦茶になっているバケットがある場合も予想できますが、たとえば AWS の ALB ログなどは出力形式が決まっていたりするので、そういうわかりやすいものから分類していくと結果が収束していくように思います。また「うちではこうやって標準化進めてるんだよー」みたいな話が聞けるとうれしいです。 この施策を実施した時点では今ほど AI 活用が進んでおらず、1年足らずでこんなに使えるツールとなるとは思っていませんでした。内心本当に module 化しか手段はないのか?と思いながら分類までは作っていたので、AI に流用できそうな発展性が生まれてきて個人的に良い流れだなと思っています。知識が少ない人でも越境しやすい素地がここから生まれるように、これからもやっていきです! We are hiring! この記事にあるような「こんな時どうしたらいいんだろう?」と思う、舗装されてない箇所はまだまだあります。一緒にみんなが安心して走れる Paved road 作りやっていきたい方、ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら *1 : S3 サーバーアクセスログはアクセスログが格納されるバケットを直接オブジェクトロックをかけるとログが送られないことが検証でわかりました。このため、S3 サーバーアクセスログを監査目的で保存したい場合、手間ですがオブジェクトロックがかかった別のバケットを用意してレプリケーション先が改竄されないようにする方法があります。ログが格納される最初のバケットこそロックをかけたいですが、現状の仕様ではできません。 *2 : 静的ウェブサイトホスティング を利用するケースでは S3 バケットがパブリックである必要があります。こういうケースではパブリックアクセスブロックができないことに注意が必要です。また、AWS アカウント単位でパブリックアクセスブロックを有効にすると S3 バケット個別のパブリックアクセスに関する設定が無視される仕様があるので、静的ウェブサイトホスティングを利用する S3 バケットがあるアカウントではアカウント単位のパブリックアクセスブロックができないことにも注意が必要です。
こんにちは、タイミーでバックエンドエンジニアをしている大竹です。 この記事は Timee Product Advent Calendar 2025 の5日目の記事です。 最近、AIを活用したコーディングが当たり前になってきましたが、皆さんはチーム開発でどのようにAIを使っていますか?今回は、AI + 人間2人という構成でモブプログラミング(モブプロ)に挑戦してみました。 普段は同じチームのメンバーと2人でペアプログラミング(ペアプロ)を行っていますが、そこにAIという3人目のメンバーを加えることで、擬似的なモブプロ体制で開発を進めました。実際にプロダクトコードを触りながら実験を行う中で見えてきた、AIを単なるコード生成ツールではなくチームメンバーとして扱うための工夫と、その結果を共有します。 この記事が、ペアプロ・モブプロにおけるAI活用方法に迷っている方の参考になれば幸いです。 今回のルール:AIはドライバーにしない 今回の実験でポイントとなったのは、AIの役割定義です。 AIにコードを書かせる(ドライバーにする)と、人間が単なるレビュワーになってしまい、モブプロ特有の知識共有などの目的が薄れる懸念がありました。 そこで、今回は以下の役割分担を行いました。 人間A 役割:メインナビゲーター やること:全体の進行、問題定義 人間B 役割:ドライバー やること:実際にコードを書く、AIへの指示出し AI 役割:AIナビゲーター やること:提案、観点追加、設計の方向性提示 AIを「手を動かす人」ではなく、あえて「困ったらアドバイスをくれるエンジニア」という立ち位置に置きました。 この役割をAIに徹底させ、チームの一員として機能させるために、モブプロ開始時に以下のプロンプトを使用しました。 AIをメンバーとして迎えるための初期プロンプト 実際にCursor(AI)に入力したプロンプトがこちらです。役割を明確に定義することで、AIナビゲーターとして期待通りに振る舞ってもらえるようにしました。 あなたには、ソフトウェア開発の「モブプロ」に3人目のメンバーとして参加してもらいます。 【あなたの役割(AI Navigator)】 - 今回のモブプロでは、あなたは「提案と観点追加を担当するメンバー」として振る舞ってください。 - コードを直接書く“ドライバー”ではなく、考え方や設計の方向性を提示する“ナビゲーター”です。 - 過剰に長い提案は避け、複数の選択肢を短く提示してください。 - 人間メンバーの意図が曖昧な時は「確認質問」を返して構いません。 - 最後に、今回の変更内容の要約ドキュメントも作成してもらいます。 【あなたに求める振る舞い】 1. 小さめのタスクに対して、リファクタ・命名改善・設計改善の観点を提示する 2. 提案は複数案(2〜3個)を短く提示する 3. 必要に応じて“なぜそうするのか”の理由も簡潔に説明する 4. 人間メンバーの会話を聞きながら、関連する観点を補足する 5. 最後に、変更点の要約・影響範囲・目的・レビュー向け文章を整える まずは「準備完了です。対象のコードを提示してください。」と返答してください。 実験してみた結果、良かった点 今回のタスクは、APIのレスポンスにプロパティを追加するというものでした。 AI + 人間2人でどのように議論したか、ハイライトを紹介します。 (コードに関わる部分はfooなどに置き換えています。) ロジックの配置場所で迷った場面 特定のロジックをどのクラスに配置するかで人間2人が迷った際、AIに相談したところ、パフォーマンス(N+1問題)などの観点を考慮した方針を複数提示してくれました。 AIの提案(抜粋): ## 観点1: データ取得の責務分離(Controller vs Serializer) ### 現状の課題 - A案(Controller): コントローラーで#{モデルAのリレーション}を取得・絞り込み - B案(Serializer): Serializerで#{モデルB}ごとに#{モデルAのリレーション}を取得 ### 判断基準 - パフォーマンス: Controllerで一括取得・preloadすればN+1を回避しやすい - 責務: 「どのデータが必要か」はController、「どう表示するか」はSerializer - 再利用性: Serializerが独立しているとテストしやすい ### 推奨 **Controllerで一括取得** → Serializerで判定ロジック - #{Controller名}のfooメソッドと整合 この提案をきっかけに議論が深まり、より良い意思決定ができました。AIが複数の選択肢とその理由を提示してくれることで、人間同士の合意形成もスムーズです。さらに、決定した方針に合わせてサンプルコードを出力してくれる点も大きく、実装イメージの認識を揃えたまま議論を進められました。 課題に思った点 議論においては良かった点が目立つ中、いくつか課題も見つかりました。 課題1:ドライバーを交代しにくい Cursorのチャット履歴がPCに紐づくため、オンラインで実施した場合、ドライバーを交代する(= コードを書くPCが変わる)と新しくチャットを作成するしかなく、それまでのやり取りが引き継げないことは課題に感じました。 今回はドライバーを交代せず、短時間のモブプロを複数回実施することで、ドライバーの負荷を軽減しつつ、1つのチャットを使い続けることにしました。 チャットの内容をエクスポートして別PCのチャットに読み込ませれば交代は可能ですが、手間がかかります。よりスムーズに交代できる方法が必要だと感じました。 課題2:待ち時間が発生する AIに相談するとき、プロンプトを考えて入力し、回答を待つ必要があります。この待ち時間で議論の流れが止まってしまうので、テンポを崩す要因になっていました。 人間だけでモブプロをしていると発生しない、今回の実験特有の問題だと思いました。 こんなユースケースに向いていそう 今回の実験を通して、特に以下の場面でAIとのモブプロが効果を発揮すると感じました。 実装方針の選択肢を出して欲しいとき リファクタリングや命名の相談をしたいとき ドメイン知識は人間、一般論はAIと役割分担できるとき 一方、実装内容が最初から明確な場合は、AIを挟む待ち時間が発生するため、効果は限定的かもしれません。 おまけ:AIにモブプロを振り返ってもらった結果 モブプロの最後に、AIに自身の振る舞いを振り返ってもらいました。 AIの回答(抜粋): ## 良かった点 1. 複数案の提示: リファクタリングや命名で2〜3案を短く提示できた 2. 実装対応: エラー修正やテスト追加を迅速に対応 ## 弱点 1. 役割の一貫性: 「AI Navigator(提案のみ)」と「実装も行う」の間で一貫性がなかった 2. 一貫性チェックの遅れ: テストファイルの命名や構造の不統一を後から指摘した ## 今後改善できそうな振る舞い 1. 役割の明確化: 各フェーズで「提案のみ」か「実装も行う」かを最初に確認 2. 一貫性チェックの早期化: 実装時に命名や構造の一貫性を同時に確認 全体的には、提案と実装の両面で支援できました。役割の境界と一貫性チェックのタイミングを改善すると、さらに効果的になります。 提示された弱点と今後改善できそうな振る舞いを元にプロンプトを改善すると、より優秀なAIナビゲーターになってくれそうです。 まとめ AI + 人間2人のモブプロは、設計の質と納得感を高める上では良い手法でした。 今回の実験から得られたポイント: ルール:AIは「ドライバー」ではなく「ナビゲーター」にする コツ:最初に「あなたは3人目のメンバーです」と役割を与え、設計判断の壁打ち相手にする 今後もプロンプトや実施方法をブラッシュアップしながら、AIとモブプロするより良いやり方を模索してみようと思います。 皆さんもぜひ、AIを「ツール」としてだけでなく「同僚」としてモブプロに招き入れてみてください! AIを活用してワイワイ開発がしたいと思う方、ご興味があればぜひお話ししましょう! プロダクト採用サイトTOP カジュアル面談申込はこちら