Timee Product Team Blog

タイミー開発者ブログ

静かなSquadの進化: エンゲージメントが高まるきっかけ

こんにちは!株式会社タイミーでプロダクトマネージャーをしているAndrewです。 私はオフショアメンバーが関与するSquadに所属しています。このSquadはエンジニア組織の中でもユニークな環境であり、ここで直面した問題とそれを乗り越えるための取り組みをこの記事で紹介したいと考えています。

オフショアメンバーとのコラボレーションの課題

約半年前、オフショアメンバーのいるSquadと関わり始め、このSquadの各メンバーを知るきっかけとなりました。 最初は、日本メンバーとオフショアメンバーのコミュニケーションがスムーズでないと感じました。ミーティングでは通訳担当の方以外、オフショアメンバー全員がほとんど発言しませんでした。 また、ミーティングで話されている内容が彼らが理解できる言語で記録されていないケースが多く、情報の透明性に欠けていました。開発した機能の目的が何かを理解しない場面を見かけたこともありました。

しかし、これは単なる課題ではなく、Squad全体のポテンシャルを引き出すためのチャンスでもあると考えました。 エンジニアは機能を作るだけでなく、プロダクトに関心を持ち、価値を届けることに注力すれば、より多くのアイディアやソリューションが生まれ、良いプロダクトが作れるのではないかと。そのためには、オフショアメンバーが積極的にディスカッションに参加できるようにする必要がありました。 最初は言語の壁があると感じたため、心配もありましたが、様々な施策を試してみました。その一環として、ドキュメントの整理とオンライン言語交流会を実施してみました。

情報透明性を向上するためのドキュメント

「ドキュメントのメンテナンスが面倒くさい!」、「コードを読む方が早い!」と思う方は少なくないでしょう。 コードにコメントがないと、そのコードが何をしているか理解することが難しくなります。それと同様に、ドキュメントがないと、システムや機能の全体像を把握するのに時間がかかり、理想的な状態と言えません。 コードで説明できない背景や設計の意図、特定の決定の背後にある論理をコメントで説明することで、コードを読む人が理解しやすくなります。しかし、コードのコメントでわかりやすく説明できる内容はテキストベースの情報に限られているということもあり、コードは特定のメンバーしかアクセスできないため、ここでドキュメントが強力な情報共有手段になります。

ドキュメントに対する工夫

ドキュメントを書くときにわかりやすくするためにいろんなテクニックがあります。 テキストに色をつけたり、フォントサイズを変えたり、ページの余白を活かすことで視覚的なメリハリをつけることが重要です。 これにより、複雑な情報も読みやすくなり、重要な情報を強調することができ、逆に強調したくない情報を目立たなくすることもできます。デザインが好きな私にとっては、ドキュメントは良いデザインを表現することのできる優れたツールです。

わかりやすいドキュメントを書くことは簡単ではありません。いろんなドキュメントの種類の中で、エンジニア向けのものもあれば、ステークホルダー(非エンジニア)向けのものもあるので、伝わるように読み手のバックグラウンドや知識に合わせて書くことが必要です。 難しい単語ではなく、あえてシンプルな単語を選び、物事を説明することで言語の壁をなくせるので、自分の言葉の知識を見せびらかす必要なんてありません。

このような意識をもって書けば、ドキュメントを書くことは単調な作業ではなく、実は楽しい作業なんです。

バランスがすべて

口頭でコミュニケーションが取りづらい状況では、ドキュメントは尚更重要です。 ドキュメントがあることで、メンバーはいつでも情報にアクセスでき、フィードバックや提案もしやすくなります。ただし、あまりにも詳細な内容、たとえばコード実装のレベルの情報まで書いてしまうと、細かなコードの変更が生じるたびにドキュメントを更新しないといけなくなってしまいます。 同様に、テキストだけの長い文章を書いてしまうと、ドキュメントの恩恵が受けられなくなり、わかりやすさが損なわれ、逆に効果が薄れてしまいます。適度な詳細と抽象度のバランスを取ることが大切であり、ドキュメントが管理の手助けとなるよう心がけが必要です。

活用されるドキュメント

実際にこの取り組みを導入して、Squadの振り返りミーティングで「ドキュメンテーションをよく参照するようになって、仕事において役立った」「ドキュメントが整理されていてわかりやすい」というメンバーからの声がありました。 今後、ドキュメントを書くのは私ではなく、オフショアメンバー全員も同じようなケイパビリティを持たせたいので、以上に書いたポイントを彼らに教えました。 今ではオフショアメンバー全員がドキュメントを書くようになり、ステークホルダーから仕様確認の問い合わせが来ても、すぐ回答できるようになりました。これにより、プロジェクト全体の透明性が向上し、知識の共有がスムーズになりました。

長期的な視点で見れば、ドキュメントはSquad全体の知識の蓄積として機能し、メンバーが増えても情報の共有の負担は軽減されます。

オンライン言語交流会

ドキュメント整理が進む中、私たちはSquadメンバーがより積極的に参加し、アイディアを共有できる環境を整えるためにもう一つの取り組みを始めました。それが、オンライン言語交流会です。

知っている人とコミュニケーションを取ることは楽ですが、知らない人となると相手の意図がどのようなものかもわからないし、ミスコミュニケーションの原因にもなります。できるだけオフショアメンバーとのコミュニケーションを増やすことで、彼らもより気軽に話しかけてくれるだろうと期待しています。しかし、日本語もできず、英語にも自信がないメンバーとどのようにすれば交流できるのか悩んでいました。そこで、オンラインで言語交流会を試してみました。

オンラインで実施しやすいアイスブレイク

いきなり何かを英語で話せと言われても難しいですよね。 そこでアイスブレイクゲームを導入し、わざわざ自分でトピックを考える必要がなく、よりカジュアルに話せる環境にしました。最初は簡単なものから始め、英語で自分のカバンに入っているものを紹介したり、謎解きをしたりしました。 カバンに入っているものを紹介する中で、お弁当の中身だったり、意外なエアコン用のリモコンの話も出てきて、会話が弾みました。謎解きをするときもトリッキーな謎が多く、あるメンバーがたくさん正解を回答できて、他のメンバーからカンニングしているだろうと冗談で言われたりしていた覚えがあります (笑)。 これにより、お互いの趣味や考え方を知ることができ、メンバー同士のコミュニケーションが深まりました。

伝わることの喜び

オフショアメンバーと会話を重ね、最初は英語に対して悲観的で、喋ろうとしないメンバーも自然と喋るようになりました。この記事を書く際に彼らにその理由を聞いてみました。 「英語の発音は下手ですが、理解してくれる人がいるので勇気が出る」「私の英語で喋っても笑われていないし、Andrewからの促進で一歩踏み出そうと思うようになって、ディスカッションに関与する重要性を理解できるようになった」という素敵なコメントをいただきました。 これらの言葉を通じて、協力し合いながら成長するプロセスに感謝しています。

このオンライン言語交流会のファシリテーションは最初は私が担当していましたが、現在はオフショアメンバーが積極的にファシリテーションすることもあります。今では毎回アイスブレイクゲームだけでなく、単純に英語で雑談することもできる環境になりました。 また、この会の名前の通り、英語に限らず、最近私が彼らから言語を学ぶ機会も得ました。

Squadの成長と今後

まだ完璧な状態には達していませんが、Squad全体がどんどん成長している実感があります。オフショアメンバーが積極的にコミュニケーションに参加し、自ら意見を言うようになったことは、大きな進歩と言えます。

異なる文化やバックグラウンドの人と仕事することは、チームが結束するまでには時間がかかりますが、結束ができた際には異なる視点から物事を判断でき、それによるメリットも大きいです。 私の過去の経験では、さまざまなバックグラウンドを持つ人々が集まれば、クリエイティブなアイディアが生まれやすくなります。そのため、このような多様性を大切にしていきたいです。

最後に、今後もSquad全体で協力し、より良いプロダクトを生み出すために努力していきたいと思います。

FirebaseAppDistributionをBitriseで導入してみた

こちらはTimee Advent Calendar 2023シリーズ1の19日目の記事です。

はじめに

こんにちは、タイミーでAndroidエンジニアをしているsyam(@arus4869)です。 この記事では、タイミーで実際に利用しているBitriseとFirebase App Distributionを用いたAndroidアプリの配布方法を実例を交えながら紹介していこうと思います。 この記事を通じて、同じようなツールを利用している他の開発者の皆さんにとって、参考になる情報を提供できればと思います。

BitriseとFirebase App Distributionについて

Bitriseは、モバイルアプリのCI/CDプロセスを自動化するプラットフォームです。ビルドからデプロイまでを簡単に管理でき、開発サイクルを効率化します。

Firebase App Distributionは、アプリのベータ版やテスト版をテスターに迅速に配布するためのツールです。新しいビルドをテスターに届け、フィードバック収集が可能です。

これらのツールを組み合わせることで、アプリ開発の流れをスムーズにしてくれます。

事前準備

BitriseとFirebase App Distributionを使用する前に、いくつかの事前準備が必要です。

まず、BitriseとFirebase、そしてGoogle Cloud Platform(GCP)のアカウントを準備します。

それぞれのツールを利用するには適切なアクセス権と設定が必要です。事前に準備しましょう。

詳細なアカウント作成方法については、それぞれの公式を参照してください。

参照URL

Bitrise

Firebase

Google Cloud Platform(GCP)

次は具体的な手順へ進めていきます。

手順1: Firebase App Distributionのグループとテスターの設定

Firebase App Distributionを使用してアプリを効率的に配布するためには、適切なグループとテスターの設定が必要です。 以下のステップに従って設定を行いましょう。

  1. Firebaseプロジェクトにアクセス

    Firebaseコンソールにログインし、対象のプロジェクトを選択します。

  2. テスターグループの作成

    「App Distribution」セクションに移動し、「テスターとグループ」タブを選択します。 「新しいグループを作成」をクリックし、グループ名(例:product_team)を入力します。

  3. テスターの追加

    新しいグループを選択し、「テスターを追加」をクリックします。 テスターのメールアドレスを入力し、グループに追加します。 テスターは開発用と本番用のアプリに分けてグループ化すると管理がしやすくなります。 この手順により、特定のテスターグループに対して、アプリのテストビルドを簡単に配布できるようになります。 グループとテスターの設定が完了したら、次にFirebaseの認証用サービスアカウントの作成をしていきます。

手順2: Firebaseの認証用サービスアカウントの作成

Firebase App Distributionでアプリを配布するためには、Firebaseの認証用のサービスアカウントを設定する必要があります。この手順では、その設定方法を説明します。

  1. Google Cloud Platform(GCP)でサービスアカウントを作成
    • GCPダッシュボードにアクセスし、「サービスアカウント」ページを開きます。
    • 「サービスアカウントを作成」を選択し、アカウントの詳細を入力します。
  2. 必要なロールを割り当て
    • サービスアカウントに以下のロールを割り当てます:
      • Firebase 品質管理者
      • Firebase App Distribution Admin SDK サービス エージェント
    • これらのロールにより、アカウントはFirebase App Distributionでのアプリ配布に必要な権限を持つことになります。
  3. 秘密鍵の生成と保存
    • サービスアカウントに対して新しい秘密鍵を生成します。
    • 生成された秘密鍵を安全な場所に保存し、後のBitriseの設定で使用します。

次は、このサービスアカウントを使用して、Bitriseでのプロジェクト設定を行います。

手順3: Bitriseの設定

次は、BitriseからFirebase App Distributionを通じてアプリを配布するためには、Bitrise上での適切な設定が必要です。以下のステップに従って、Bitriseでの設定を行いましょう。

  1. Bitriseプロジェクトのセットアップ:
    • Bitriseにログインし、新しいプロジェクトを作成または既存のプロジェクトを選択します。
    • プロジェクトのビルド設定を確認し、必要に応じて調整します。
  2. Firebase App Distributionステップの追加:
    • プロジェクトのワークフローを編集し、「[BETA] Firebase App Distribution」ステップを追加します。
    • このステップにより、ビルドが成功すると自動的にFirebase App Distributionを通じてアプリが配布されます。
  3. 認証情報の設定:
    • GCPで生成したサービスアカウントの秘密鍵をBitriseの「Code Signing & Files」タブにある「GENERIC FILE STORAGEセクションにアップロードします。
    • 「[BETA] Firebase App Distribution」ステップの設定で、Service Credentials Fileの項目に「GENERIC FILE STORAGE」セクションにアップロードしたサービスアカウントの秘密鍵のパスを設定します。

これらのステップにより、BitriseはFirebase App Distributionと連携し、アプリのビルドと配布を自動化できるようになります。設定が完了すれば、BitriseからFirebase App Distributionを通じてアプリをテスターに配布する準備が整います。 参考程度に「[BETA] Firebase App Distribution」に設定している項目を記載します。

項目 説明
Service Credentials File Bitriseのアップロードした秘密鍵のパス $BITRISEIO_development_service_account_key_URL
App Path APK及びAABファイルが格納されているパス $BITRISE_APK_PATH
Firebase App ID Firebaseコンソールのプロジェクトの設定から見れるアプリ ID 1:1234567890:android:0a1b2c3d4e5f67890
Release Notes テスターが見るリリースノート メインブランチにマージされたビルドです。
Test Groups 手順1で設定したテスターグループ product_team

まとめ

この記事では、BitriseとFirebase App Distributionを活用してAndroidアプリを配布するための手順を紹介しました。以下は、ポイントのまとめです。

  • 事前準備が鍵:BitriseとFirebase、GCPのアカウント設定から始め、必要なツールと情報を確認することが重要です。
  • Firebase App Distributionの設定:適切なテスターグループとテスターの設定を行い、アプリのテスト版を簡単に配布できる環境を作ります。
  • Bitriseの自動化設定:ビルドプロセスをBitriseで自動化し、Firebase App Distributionを通じてアプリを配布する設定を行います。

このプロセスを通じて、テストアプリの配布が可能となります。あとはテストアプリをどのように使ってもらえるか、開発者以外のメンバーにもフィードバックをもらえるようにドキュメント化することをお勧めします。本記事が、みなさまの開発に役立つことを願っています。

全社的なデータ活用の取り組みをお話しします

こんにちは、データ統括部BIグループ所属のtakahideです。
本記事では、BIグループで取り組んでいる全社的なデータ活用に関してご紹介します。

この記事を通して、少しでも「社内のデータ活用を進めたい」と思っている方のお役に立てたら幸いです。
Timee Advent Calendar2023の12月18日分の記事です。

課題感

まずは、データ活用を進めるに至った経緯を簡単に説明させてください。

タイミーは、データを用いた意思決定を大切にしているのですが、
近年の組織拡大にともない、データ活用を全社的に推進するニーズが高まっていました。

一方で、データを利用するメンバーのスキルとニーズとの間に乖離が存在していることが分かってきました。
そこで「データ活用を全社的に推進するPJT」をBIグループ中心に立ち上げることになりました。

データ活用に役立つスキル

データ活用といっても人によって解釈が異なるため、
データアナリストが日々の業務で用いているスキルの言語化から始めました。

機械学習やプログラミングといった一部の職種で使われるスキルは除外して、
どの職種の方でも役に立つ汎用的なスキルを選択しています。

「課題の発見→課題の解像度の向上→検証に必要なデータの解釈→結果のまとめ」といった流れを意識しています。

講習会の開催

スキルの整理ができたので、次にスキルの意義や使い方を伝える講習会を行うことにしました。
データの価値を実感してもらうことを優先して「データ抽出」の講習会を実施して、
顧客の行動を数値で見る体験をしてもらいました。

具体的には、簡易にデータを扱えるツールとしてLookerの講習会を開催しました。
講習会をキッカケにLookerの利用者が増え、データを見る習慣が作られたと考えています。

最初はお試しで始めた講習会も、現在は毎月実施していて、新入社員の方がデータに触れる機会を作っています。
(詳細はこちらのブログをご覧ください)

次に行ったのが「論理的思考」「データ構造の理解」「データの解釈と可視化」の講習会です。

これらはデータ抽出の前後に用いることが多く、「データ抽出」の後に実施することで理解が深まると考えました。
下記のように、First Step→Second Stepと段階を踏んで進めてきました。

少人数の相談会

ここで、講習会を通じて見えてきた課題とその対応をお伝えできたらと思います。

課題として分かったことは大きく二つで、
「講習会の難易度」と「質疑応答のしづらさ」になります。

「講習会の難易度」ですが、
講習会では必要最低限の内容をコンテンツに盛り込んでいるものの、
参加者の習熟度の違いを吸収しきれていませんでした。

また、「質疑応答のしづらさ」ですが、
講習会の参加人数が50~100人になるため、質問しづらい雰囲気がありました。
特に、どのレベルの質問をして良いかが分かりづらいという声が挙がりました。

これらを踏まえて、少人数の相談会を進めようとしています。
テーマを絞った数人〜十数人の会にすることで、活発な意見が出ることを期待しています。

全社向けの講習会を定期開催しつつ、補完的に相談会を実施することで、
講習会の内容の充実にも繋がると考えています。

データ活用スキルの指標化

最後に、データ活用の測定に関してお話しできたらと思います。

全社のデータ活用を推進する上で、データ活用スキルの習熟度の把握が重要になります。
タイミー全体でデータ活用がどの程度進んでいるかが分かることで、
データ活用のPDCAが回しやすくなり、スキル習得のモチベーションにも繋がると考えます。

おわりに

この記事では、BIグループが実施している、データ活用の取り組みを紹介しました。
改めて「社内のデータ活用を進めたい」と思っている方のお役に少しでも貢献できていたら嬉しいです。

今回は全体の紹介で、個別の詳細はお伝えできていなかったので、
そちらも機会がありましたらお話しできたらと思います。

We’re Hiring!

タイミーのデータ統括部では、ともに働くメンバーを募集しています!!
カジュアル面談も行っていますので、少しでも興味がありましたら、気軽にご連絡ください。
また、データ統括部のマガジンでは有益な情報を共有していますので、ぜひ、フォローお願いします!

RailsフロントエンドをNext.js(SPA)に移行した〜バックエンド視点での振り返り〜

好きな水風呂の温度は16℃でお馴染み edy2xx です。

Timee Advent Calendar 2023 の16日目を担当します。

本記事では今年完遂したUIリニューアル(SPA化)を通してタイミーで実施した工夫や学びを普段バックエンドの開発を担当する私の視点からお伝えします。

先日のイベントでの登壇内容を補完した内容となっています。気になる方は下記資料もご覧ください。

speakerdeck.com

イベントの方はプロジェクト終盤での断捨離やリファクタリングなどがテーマになっていたので本記事ではプロジェクト進行過程全般での知見をシェアしていきます。

プロジェクト概要

まずプロジェクトの概要です。大雑把に言うとフロントエンドの技術基盤を移行しながらUIリニューアルを実施しました。

それだけだと「何のことだ?」となるので前提からご説明します。

タイミーでは単発のアルバイト求人の掲載を行う事業者用のWeb管理画面を提供しています。

リニューアル前はRailsエンジニアがテンプレートエンジンのSlimを活用し、フロントエンド開発も担っていました。Railsで作られたサーバは動的に生成したHTMLをレンダリングする責務も持っていました。いわゆる Server Side Rendering(SSR) です。タイミー創業当初から変わらない技術スタックで構成されていて価値も生み出し続けていました。

しかし、サービス拡大にともない、古くから存在する Model や Controller への継ぎ足しの実装などが生まれ、Railsアプリケーションのバックエンドとフロントエンドの密結合なロジックが増えたり Fat Controller に頭を悩ませられるシーンが発生し始めました。

一方のフロントエンドでは保守性や情報設計(Information Architecture)を始めとして品質向上を狙ったり、jQueryに代わるモダンWebフロント技術の導入、またフロントエンド専任のエンジニアの採用拡大も狙っていました。

上記の理由などからUIリニューアル及びフロントエンドの技術基盤を刷新するプロジェクトがスタートしました。

フロントエンドの技術は下記の構成で、Railsリポジトリとは別で切り出しました。

Webページ構成やレンダリング手法もこのタイミングから Server Side Rendering(SSR) から Single Page Application(SPA) に変更しました。

いざ始まったUIリニューアル

UIリニューアルを進める上で不確実性を大いに孕んでいたので管理画面全体を一気にビッグバンリリース的に旧バージョンから新バージョンに切り替える方針は見送りました。

方針としてはハレーションを抑えるためにアクセス数の少ない画面から徐々にリプレイスしていき、顧客が最も利用する事業のコア機能を終盤にリプレイスする計画を立てました。

同時にDatadogやGoogleAnalyticsなどの監視・分析ツールを用いて、アクセス数の解析、利用デバイス比率の特定をしたり、売上規模の上位を占める事業者を選定し、移行に際して障壁となるポイントが無いかを探っていきました。

従来デザイン使い続けたい問題

進行する中で発見されたのが「従来のデザインを使い続けたい」というユーザーニーズです。

背景には、既存UI/UXに対してのメンタルモデルが形成されているので変化を忌避してしまったり、画面構成やボタン単位で指定オペレーションを厳密に組んでいるケースなどがありました。

強行突破する方法もあると思いますが、打ち手として新デザインのリリース後も従来の旧UIに切り替えられる仕組みを導入しました。デバイス変更やセッション切れ等で、従来デザインを選択したユーザーが新デザインに切り戻されることを回避したり、データ分析観点で活用するために従来デザインを選択した状態はDBに永続化しました。

プロジェクト終了時には削除する前提だったので明確さやアクセスのし易さを優先しました。フロントエンドは使用状況をAPI経由で取得する方針を採用しています。

[従来のデザインに切り替える] ボタンを配置

この仕組みのお陰でリリース後に利用者に多少影響があっても完全にロールバックすることなく、利用者ごとの判断に委ねることが出来ました。新デザインを許容できる利用者には新デザインを提供し、どうしても従来のバージョンを利用したい方には一定期間の従来バージョン利用を許容する運用をしました。

事前告知による期待値調整も勿論大事だと思うのですが、石橋を叩きすぎずにリリース後の ”動くソフトウェア” を通しての実際のユーザーのフィードバックから次の意思決定に繋げられるアプローチを経験できたのは大きな財産になりました。

画面移行の約80~90%程度が終わる頃には新デザインがマジョリティになったので、上記機能はほぼ役目を終え勇退しました。

影響範囲を限定する話での余談ですが、カナリアリリースの仕組みがあると影響を抑えられた障害などもあったので今思い返すと導入しておけると良かったです(プロジェクト中には追加出来なかったので今後導入していきたい)。

ユースケースロジックを切り出すFormObjectを活用

プロジェクトも佳境に差し掛かった中で、とあるエンドポイントに対応するAPIを実装する工程が最も難を極めました。タイミーのコアドメインに関わる機能を司っている Controller のリプレイスです。元々が Fat Controller だったケースでして既存の仕様理解や根本的なリファクタリングが求められたのを覚えています。

リクエストを受けてからHTMLを返却する一連の処理の中に、データベースへの書き込みや外部APIへの通信、ビジネスロジックに関係する処理など様々な責務を Controller が抱えていました。テスタビリティも低く、RSpecで書いていた controller spec では仕様は網羅されていませんでした。

その際に Controller の責務を一部切り出す方法として FormObject を採用しました。ユースケースに対応したロジックを FormObject クラスに閉じ込め、Controllerはクラスのパブリックメソッドの呼び出しとリクエストの受付、及び適切なレスポンスの返却に特化する様な作りにしました。

FormObject には単に移行するだけではなく「テストピラミッド」も意識し、テストケースの割合が結合テスト < ユニットテスト になるように必要に応じて責務の見直しも行いました。

テストピラミッド

結果的に Fat Controller は消え去り、元々はAPIリクエストの受付からレスポンスまでの過程を通して結合テストでカバーしていたテストケースをユニットテストで再現できるようになりました。Controller は RSpec の request spec にて受け付けるリクエストとレスポンスを明示し、副作用のうち代表的なケースをテストすることで不要に FormObject 側のユニットテストと重複することも回避しました。

Controller の上部に君臨し続けていたTODOコメントを削除できたときは非常に感慨深かったことを今でも覚えています。

古来からタイミーを見守ってきた長老の様なTODOコメント

プロジェクトの中断

これは少しオフトピック的な話ですがプロジェクト進行のメタ的な話として触れておきます。

一般的にプロジェクトのみならず仕事を進める中で優先度が高い別Issueが割り込まれることはありますよね。今回のUIリニューアルプロジェクトでも起きました。

プロジェクトの性質的には「技術改善」をテーマとし基本的に従来機能を踏襲しながらUIリニューアルを進行する前提でした。そのため、割り込み的な機能開発に対してスイッチングコストが発生したり、リニューアルとの優先順位付けが必要になったりと苦労した覚えがあります。移行期間全体が約3年あったうち合計1年ほどはチーム構成やミッションが根本から変化し、別テーマの開発を担当する時期もありました。

結果として生存期間が短い前提で作られた機構が想定より長く存在し続けることになりました。例えば先に述べたデザイン切り替えの仕組みや、Rails, Next.js それぞれのリポジトリが互いに依存する様な暫定的なロジックなどです。

当時の自分はイレギュラーに対してある種「当たり前」くらいで考えていたのですが、水面下ではコードベース上に認知負荷を高める要因が生まれていました。新規参画メンバーから質問が来ることもありましたが、当時は影響を理解したり、言語化することは出来ていませんでした。

今思い返すと内部品質低下に繋がることから、プロジェクト完遂までのリードタイムを短縮したり、テーマが変化する前に最低限の内部品質改善をするように働きかける余地はあったなと感じます。

プロジェクトが順調に進んだり完了することだけが学びではなく、むしろ想定外の中に自分の枠を超えた学びのきっかけがあると気付かされた瞬間でした。

プロジェクト完遂

過程では様々な事があり、ここでは語り切れないのですが3年の歳月を経てプロジェクトは完遂しました。RailsリポジトリからはHTMLレンダリングにまつわるコードやCSSライブラリなど5万行を超えるコードが削除されました。特に1つのPull Requestで1万行を超えるコード削除をした時は非常に爽快でした。

Webpackerやyarnなどにも別れを告げ、外部顧客向けの機能についてはRailsリポジトリはWebAPIの開発に特化する様な存在になっています。GraphQL化を進める様なチャレンジも生まれました。

WebFrontも1名でプロジェクトがスタートしましたが今では技術コミュニティが生まれたり、技術顧問の方が付いていたりと2020年当時と比べてだいぶ拡大しています。いい話ですね。

おわりに

プロジェクトが終われど、フロントエンドにとってはスタートラインに立ったとも言えますし、バックエンド側も保守性が多少増したとは言え、会社の成長を支える面でいうとまだまだ改善余地があります。

会社に興味がある方がいたら、タイミーワーカーで運営が成り立っているTHE 赤提灯という居酒屋が新橋オフィス近くにあるので是非飲みに行きましょう。

一応採用ページも貼っておきます。

product-recruit.timee.co.jp

忘れていましたが、声を大にして改めて言っておきたい。

「タイミーはSPA化やり遂げたぞ!!!!」

タイミーAndroidChapter式LADRを導入してみた

こちらはTimee Advent Calendar 2023シリーズ1の15日目の記事になります。

こんにちは、タイミーでAndroidエンジニアとして働いている @orerus こと村田です。

私は現在タイミーのAndroidChapter(弊社は特定領域のメンバーの集まりのことをChapterと呼称しています)の一員で、喜ばしいことに来年早々にメンバーが大きく増加する予定です・・・!

以前はAndroidChapterのメンバーが全員同じチームで開発を行っていましたが、タイミーのエンジニア組織拡大に伴い全員が異なる開発チーム(弊社ではSquadと呼称しています)に所属する形へと変化しました。その為、各SquadにはAndroidエンジニアが少人数しか所属しておらず、Androidアプリに関するPRレビューについてはSquadを跨いで行うことになります。必然、故も知らないPRレビューが飛んでくる為、PRレビューの負荷が上がり工数的にも心理的にも辛いものとなってしまいました。

そこで今回は、そういった組織のスケールに伴い発生する課題解決の為の施策の一環として、弊社AndroidChatperで導入した独自のLADR(Lightweight Architecture Decision Records)について紹介したいと思います。

また、導入して得た学びや見えてきた改善点についても共有できればと思います。

施策の背景、課題

先述の通り、タイミーでは現在Androidメンバーが全員異なるSquadに所属し、それぞれが異なるプロダクトゴールに向けた機能開発を行っています。

AndroidChapterで毎日10分程度のデイリーMTGは行っていますが、そこでは現在取り組んでいるもの・これから触るコード領域、困りごと等の共有に留まります。(各Squadのデイリースクラムもある為、あえて軽い内容にしてあります)

そのような状態で開発サイクルを回す中で、いくつかの大きな課題が見えてきました。

  • PRレビュー
    • レビューに入る前に、PRが行っている開発内容を把握する為の多大なコンテキストスイッチコストがかかる
    • コンテキストを十分に理解できていない為、仕様や設計の妥当性の判断が困難で表面的なレビューになりがち
    • PRという完成形で初めてレビュー依頼が飛んでくる為、仕様や設計で致命的な問題があった場合に後戻りが難しい(修正コストが高い)
    • PRレビューコストが高い為、PRレビューを行う際にまとまった時間を確保する必要がありレビュー完了までのリードタイムが長くなる
  • 改修/影響調査
    • 昔のコードや自身が触っていないコードについて、開発当初の意思決定根拠や設計意図が分からず、影響調査に時間がかかったり変更妥当性の判断が難しい

そこで、これらの課題を解決するために実験的に導入を始めたのが独自LADRでした。

独自LADRについて

ここでは我がAndroidChapterで定義している独自LADRについて紹介します。なお、ここでいうLADRは弊社AndroidChapter用に独自改変したLADRですが、AndroidChapter内では単に「LADR」と呼称している為、この記事でも以後は単にLADRと記述します。

そんなAndroidChapterで導入している独自LADRですが、株式会社ラクスの鈴木さんが公開されているLADR-templateをベースにさせていただいています。
また、鈴木さんの記事にある通り、LADRの本来の用途はアーキテクチャ選定において「導入しなかった」記録を残す為であると認識していますが、AndroidChapterではLADRの目的や対象をそこから拡大解釈して利用しています。

目的や対象を具体的にどのように拡大解釈しLADRを運用しているかについては、実際に私が作成した「LADRを導入する為のLADR」を以下に添付しますので、こちらをご覧いただければ雰囲気が掴めるかと思います。(「Context - 文脈」「Specification - 仕様、やること、やらないこと」の項目のみご覧いただければ十分です)



ご覧の通り、LADRのドキュメント自体は軽量なものになっている事が分かるかと思います。ここがLightweight形式を採用した最大の理由でして、ハードルを下げることでまずはこのドキュメントが書かれることを一番の狙いとしています。

LADRのサンプル

先ほどお見せしたLADRは機能開発を伴うものではなかった為にいくつか項目を省略してますが、機能開発時のLADRでは必要に応じて詳細な仕様を記述したり、実装時の設計についても触れています。

以下、実際に作成されたLADRのサンプルです。(雰囲気が伝わるようにシンプルなものと重めなものを抜粋しています)


サンプルA. 機能追加のためのLADR



サンプルB. 不具合修正のためのLADR



このようなLADRを作成した後、AndroidChapterにLADRのレビュー依頼をPRとして投げ、そこでLADRに書かれたコンテキストや仕様、設計について開発着手前にレビューを受けるようにしています。

LADR導入の経緯

先述のLADRについての記事を拝見したときに「これだ!」と思い、すぐさま「LADR導入の為のLADR」、および「LADRのテンプレート」を作成し、AndroidメンバーにPRという形で投げつけました。LADRについて解説するより、まず実物を見せ、そこから話をするのが早いと感じた為です。

PR上でLADRの狙いや、他ドキュメントとの棲み分けについてなどの多少のラリーはありましたが、好意的に受け入れられすんなりと実験導入が始まりました。

運用を初めて2ヶ月程度ですが、現在以下の18個のLADRが作成されています。



導入後の成果と学び、および見えてきた改善点

AndroidChapterメンバーからのFB

まずは2ヶ月運用してきて、AndroidChapterのメンバーはどう感じているのかを探るためにアンケートを実施してみました。
アンケート結果について、AndroidChapterメンバーの中でLADR作成経験済み、作成機会がまだ無い状態の2人の回答を紹介します。

「ありのままを記事に書きたいので率直に書いてね」とお願いをしておいたので、きっちりと本音が書かれているかと思います・・・!
※なお、アンケートの設問はChatGPT先生にご協力願いました

その結果がこちらです。

まずは分かりやすく数値で評価してもらいました。



一番右の私の回答分はバイアスがかかりまくっているのでスルーして、他メンバーの評価は以下のような感じです。

  • 満足度はどちらも7と「やや良い」点数
  • プロセスの効率化や課題解決度合いの感じ方はLADR作成機会があったメンバーは全体的に高め、機会が無かったメンバーは低め
    • Bの方が「LADR導入によって解決された課題の効果度」の項目の3点について、「まだ自身で作成したことがないために自分事感が薄い」とのFBをもらっています

次に、フリーテキストで回答してもらったものを原文ママで掲載します。
※2人分の回答をマージしています

まずは導入による成果についての質問についてです。



こちらにより以下のことが言えそうです

  • A、Bの両者ともに「コンテキスト共有」については効果を明確に感じている
  • LADRを作成する機会があったメンバーは仕様/思考の整理に効果を感じている
  • LADRを作成する機会がなかったメンバーもPRレビューの効率化には効果を感じている
    • ただし、自身でLADR作成を行っていないと課題解決の実感が薄い

次に改善点についての質問です。



こちらにより以下のことが言えそうです

  • LADRの制度自体の課題点はまだ発生していない
  • 一方、ドキュメントの参照のしやすさ、ドキュメント作成のオンボーディングといったLADRの活用方法についての改善点が挙がっている
    • この点は当初から想像していた懸念点であった為、今後の明確な改善点だといえる
  • 組織がスケールしてもコンテキスト共有に関する課題解決の効果が見込めそう

自身の振り返り

また、私自身が感じているLADRのメリットデメリットも軽く挙げておきます。

メリット

  • 何にせよ意思決定のログが残るようになった
    • メンテを保証できない仕様書にするより、意思決定のログと割り切って扱っている
  • PR作成時のコンテキスト共有が楽になったように感じた
    • 「実装→ドキュメントを書く」が「ドキュメントを書く→実装」と順序が変わったのが大きいのかもしれない
  • 設計段階の指摘や懸念点の伝達が事前にできるようになった
  • 実装者以外のメンバーのコンテキストの理解度合いが深くなったように感じた

デメリット

  • LADRの作成の工数がかかる
    • 以前もPR本文に一定のコンテキストを書いていたので、劇的に工数が増加した訳では無い
  • LADRのPR、機能開発のPRと、レビューが必要となるPRの数が増える

学びと改善点のまとめ

以上から、学びと改善点について乱暴にまとめると以下のようなことが言えるかと思います。

  • 課題解決に一定の効果は確実に出ており、デメリットを上回るメリットがあるといえる
  • LADRドキュメントの参照性に課題がある
  • LADRドキュメントを作成するまでのハードルがまだ感じられる
    • オンボーディング時に1度作成してもらう等のケアが検討余地としてありそう

当初抱えていた課題は解決できたのか

最後に、当初挙げていた課題がどの程度解決できたかの所感をまとめます。

  • 課題1: レビューに入る前に、PRが行っている開発内容を把握するためのコンテキストスイッチコストがかかる
    • 解決状況: ✅ 完全に解決
  • 課題2: コンテキストを十分に理解できず、仕様や設計の妥当性判断が困難
    • 解決状況: ✅ 完全に解決
  • 課題3: PRという完成形で初めてレビュー依頼が飛んでくる為、仕様や設計で致命的な問題があった場合の修正コストが高い
    • 解決状況: 🤔 様子見
    • 現状では問題なし。設計難易度の高い開発での対応を今後注視
  • 課題4: PRレビューコストが高く、レビュー完了までのリードタイムが長い
    • 解決状況: 🟢 一定の解決
    • PRレビューコスト減少、ラリーの回数も減少
  • 課題5: 昔のコードや自身が触っていないコードの開発意図が分かりづらい
    • 解決状況: 🟢 様子見、ただし解決の兆しあり
    • まだ運用期間が短く見返す機会が少ないが、LADRのルールを見直す際に当初作成したLADRドキュメントが役立った

結論として、全体的には解決に向かっていると感じています。ただし、組織のスケールアップに対する対応として導入した施策であるため、今後も組織がスケールした際の変化に注視しつつ、より良い形へと進化をさせ続けていく所存です。

dbt jobの分割方針について考えてみた

Timeeのカレンダー | Advent Calendar 2023 - Qiitaの12月13日分の記事です。

はじめに

こんにちは
okodoooooonです

dbtユーザーの皆さん。dbtモデルのbuild、どうやって分割して実行してますか?
何かしらの方針に従って分割をすることなく、毎回全件ビルドをするような運用方針だと使い勝手が悪かったりするんじゃないかなあと思います。

現在進行中のdbtのもろもろの環境をいい感じにするプロジェクトの中で、Jobの分割実行について考える機会があったので、現状考えている設計と思考を公開します!(弊社は一般的なレイヤー設計に従っている方だと思うのでJob構成の参考にしやすいと思います)

この辺をテーマに語られていることってあんまりないなあと思ったので、ファーストペンギンとして衆目に晒すことで、いい感じのフィードバックをもらえたらなーと思ってます。

弊社のデータ基盤全体のデザインについて把握してからの方が読みやすいと思うので、そちらをご覧になりたい方はこちらの記事からご覧ください。

これまでの弊社のデータ更新周りについて

これまでの弊社のdbt Job設計の概念図を作るとしたらこんな感じになります。

  • 走っているクエリがフォルダ単位/モデルファイル単位などバラバラな粒度で実行されている
  • リネージュを意識したJobの走らせ方になっておらず更新頻度の噛み合わせが悪い
  • 明確なルールがないので意図しないタイミングで同じモデルがビルドされたりする
  • 明確なルールがないのでビルドから漏れているモデルがひっそり存在している

dbtコマンドで示すとこんな具合のカオス感

dbt build --select models/path_to_model_dir1/model1-1.sql
dbt build --select resource_name
dbt build --select models/path_to_model_dir2/model2-1.sql
dbt build --select models/path_to_model_dir3
....

改善のための試行錯誤 v1

  • レイヤーごとにtagを付与して以下のようなビルドコマンドが走らせられるようにしました。
dbt build --select tag:staging_layer
dbt build --select tag:dwh_layer
dbt build --select tag:dm_layer
  • 更新頻度tagの概念を設計して、以下のようなビルドコマンドを走らせられる構成を検討しました。
dbt build --select tag:hourly_00 # 毎時00分にビルドするものに付与するタグ
dbt build --select tag:daily_09 # 毎日9時にビルドするものに付与するタグ

これらの設計をしてみて、レイヤー単位でJobを走らせることを可能にできたのと、更新頻度をdbt上で管理可能な形にはできました。

しかし、ユーザーのデータ更新ニーズに基づいたJob設計になっていないことから、ユーザーニーズベースでのJob管理方針の検討を進めました。

データ活用ユーザーのデータ更新ニーズの整理

弊社のレイヤー設計とデータ活用ユーザーのアクセス範囲は以下のような形になります。

  • アナリストはステージング層以降の層に対するアクセス権を保持していますが、現状アナリスト業務はモデリング済みテーブルではなく、3NFテーブルに対するクエリを作成する作業が支配的です(モデリング済みテーブルの拡充とその布教をもっと頑張りたい)。
  • データ組織外の社内ユーザーはデータ組織が作成したアウトプットを通して分析基盤のデータを活用できるような状態になっています。
    • ※弊社内ではデータ組織以外のユーザーにBigQueryのクエリ環境を開放しておらず、LookerやLookerStudioなどのアウトプットを経由して社内データにアクセスする状態にしています。

それぞれの層ごとの現在顕在化しているデータ更新ニーズを整理すると以下のようになります。

  • アナリストと野良アウトプットによる「ステージング層のなるはや更新ニーズ」
    • アナリストのアドホッククエリ
      • 分析要件は予測ができず、要件次第では最新のデータが必要となるため、なるべく早く更新されることが期待されます。
    • マートから作成されずステージングテーブルから作られるアウトプット
      • 更新頻度ニーズのすり合わせなく作られて提供されるものも多く、要件次第では最新のデータが必要となるため、なるべく早く更新されることが期待されます。
  • データ活用ユーザーによる「アウトプット更新ニーズ」
    • データ活用ユーザーに提供するデータマート経由のアウトプットは更新頻度を擦り合わせられているものだけになっている(今はまだ数が少ないだけだが)ため、hourly,daily,weekly,monthlyなどの頻度ごとの更新設計が可能となっています。
    • 例えば弊社のよく使われるLooker環境はhourlyの頻度で更新が要求されています。

単純なジョブ設計をしてみる

単純なジョブ実行案① ステージングなるはや更新プラン

ソースシステムごとのデータ輸送完了後にそのソースシステムを参照しているモデルを全てビルドするプラン

  • コマンド例
    • salesforceのデータ輸送完了後のコマンド:
      • dbt build --select tag:salesforce_source+
      • dbt build --select models/staging/salesforce+
    • アプリケーションDBのデータ輸送完了後のコマンド:
      • dbt build --select tag:app_source+
      • dbt build --select models/staging/app_db+
  • 実装方法

この場合の更新ジョブを概念図に表すと以下のようなものになると思います(上流から下流へのビルド時のデータリネージュを黄色の三角形で表現しています)。 メリデメをまとめると以下のようになるかと思います

  • メリット
    • 下流が全てビルドされるのでビルド漏れが発生しない。
    • 常にモデルの鮮度が最新になる = アナリストのstaging最新化ニーズは満たせている
  • デメリット
    • 更新がそこまで不要なモデルに対してまでビルドが実行される。
    • BigQueryのクエリ料金が高額になる

単純なジョブ実行案② ユーザーニーズに合わせるプラン

ユーザーのデータ更新ニーズに合わせて更新頻度をアウトプットごとに設定。更新ニーズの頻度ごとに最新化されるようなプラン

  • コマンド例
    • 1時間おきにビルドされて欲しいアウトプットを更新するコマンド: dbt build --select +tag:hourly
    • 1日おきにビルドされて欲しいアウトプットを更新するコマンド: dbt build --select +tag:daily

この場合の更新ジョブを概念図に表すと以下のようなものになります(下流から上流へのビルド時のデータリネージュを赤色の三角形で表現しています)。 メリデメをまとめると以下のようになります。

  • メリット:アウトプットの更新頻度が最適化される。
  • デメリット
    • stagingのモデル作成の重複が発生する。
    • 出力ニーズが存在しないstagingテーブルなどがビルド対象から漏れてしまう

ではジョブ分割をどのように設計するか

  • ステージングなるはや更新に合わせてリネージュの上流から実行すると下流で無駄なビルドが発生してしまう。
  • ユーザーニーズベースでリネージュ下流から実行すると上流で無駄なビルドが発生してしまう。

この二つの問題の折衷案を考える必要があると考えました。

現状のジョブ実行案

更新ジョブの概念図は以下のようなものになります(上流から下流へのビルド時のデータリネージュを黄色の三角形、下流から上流へのビルド時のデータリネージュを赤色の三角形で表現しています)。

  • ソースデータ ~ ステージング層のビルド
    • ソースデータが連携されたらステージング層までを下流に向かってビルド
    • コマンド: dbt build --select tags:source_a+ --exclude tags:dwh_layer tags:dm_layer
  • DWH層~アウトプットの間のビルド
    • ステークホルダーと定めた更新頻度に合わせて上流に向かってビルド
    • コマンド: dbt build —select +tags:daily_00_00 —exclude tags:staging_layer tags:datalake_layer

こうすることで、ステージング層までは常に可能な限り最新化されつつ、アウトプットは要件ごとに更新頻度が最適化された状態となります。

ただ、DWH層やデータマート層に多少のビルドの重複は発生してしまいますが、そこは許容しています。

本当はこうしたい案

アナリストがモデリング済みのDWH層を使うことが常態化するような世界観になってくると、上図のように「レイク~DWHまでは最新鮮度」「それ以降はユーザーニーズベースで更新」って流れがいいのかなと思っています。

また source_status:fresher+などのstate管理をうまく使って、更新があったものだけをビルドするような方式を模索していきたいです。

おわりに

dbtの環境リプレイスとともにこのJob設計も実戦投入しようと考えているので、想定していなかったデメリットが発覚したり、改善点が見つかったら改善していこうと思います!

弊社データ基盤でもストリーミングデータが取り扱えるようになったので、そのデータの使用が本格化すると ストリーミング✖️バッチ のJob構成などを考える必要があり、まだまだ俺たちの戦いはこれからだ。と思います

ていうかみなさんどうやって分割して実行してるの!!!教えてほしすぎる

We’re Hiring

タイミーのデータ統括部はやることがまだまだいっぱいで仲間を募集しています!興味のある募集があればこちらから是非是非ご応募ください。

https://hrmos.co/pages/timee/jobs

私個人としてはこちらのアナリティクスエンジニアの募集への応募をとてもお待ちしております!

https://hrmos.co/pages/timee/jobs/16822514041183191415

ML基盤を再構築したはなし

こんにちは、タイミーのデータ統括部、DRE(Data Reliability Engineer)グループ & DS(Data Science)グループ所属の筑紫です。

DSグループではML基盤の構築・運用保守を担当しています。

本記事では、ML基盤を再構築した話を紹介したいと思います。

Timee Advent Calendar2023のシリーズ 2の12月13日分の記事です。

経緯と課題

DSグループでは、様々なプロジェクトのML基盤を構築しています。

当初は、1つのCloud Composerの上に、全てのpipelineを載せて運用していました。

以下に当初の構成のイメージ図を示します。

ただ、このML基盤についてはいくつか課題がありました。

本番環境しかない

本番稼働しているpipelineが複数ある中、環境が本番環境しかありませんでした。

Cloud Composerも本番のインスタンス1つしかなく、例えばCloud Composerのimageのupdateが必要な際もupdateして既存本番に影響がないか検証できる環境がない、などの問題が発生しており、保守に耐えられない状況でした。

サービスレベルの異なるpipelineが同じワークフロー上に乗っている

当時ML基盤の予測結果を社外に提供する取り組みも開始している中で、社内利用のML pipelineと社外利用のML pipelineが同じワークフローツール(Cloud Composer)上に乗っている状況でした。

ML基盤でもその結果を社内で使用するものと社外で使用するものでは、サービスレベルが変わってきます。

異なるサービスレベルを持つ複数のpipelineが一つのワークフローに乗っている状況では、あるpipelineの挙動が他のpipelineにも影響を及ぼし、サービスレベルに影響を与える可能性があるため、運用上好ましくありませんでした。

インフラ反映までのプロセスが長い

GoogleCloud環境はTerraformで管理していますが、そのリポジトリはDREグループ管理になっており、DSグループがリソースを作成する際に、以下の手順を踏む必要がありました。

  1. DSグループで修正のPullRequest(以降PR)を作成
  2. DREグループにレビューを依頼
  3. DREグループのレビュー通過後にマージ & デプロイされるので、反映結果を確認

このような手順を踏むので、

  • 検証のために、試しにリソースを作成したり変更したりする際に、都度PRを作成し、レビュー依頼しないといけない
  • 他チームのレビューを挟むので、GoogleCloudのリソースを作成するまで、リードタイムがかかりがち

という問題があり、DX(Developer eXperience)に影響を与えていました。

ソリューション

まず本番環境しかない問題については、開発環境を作り、Cloud Composerを本番環境、開発環境それぞれに配置することにしました。

それに合わせてGoogleCloudのprojectも分けることにしました。

1つのproject上に本番環境と開発環境を構成するよりも、分けた方が事故が起こりにくく、Terraform上の管理もしやすかったためです。

また、サービスレベルの異なるpipelineが同じワークフローに載っている問題について、サービスレベルが大きく異なるユースケースごとにCloud Composerを分けることにしました。

コストとのトレードオフになりますが、運用・保守のしやすさを考えると、社内で使うユースケースと社外などConsumerなどに提供するユースケースでCloud Composerを分けた方が良いという判断からこの構成にしました。

結果、下図のような構成になりました。

その上で、上記環境のTerraformのコードをDRE管理のリポジトリと分けて、DSグループ管理の別リポジトリで管理する方針としました。

また、ブランチ戦略を整理して、featureブランチにpushする度に開発環境にデプロイされることにしました。

これにより、デプロイ都度レビューを挟まないといけないフローが、featureブランチにpushするだけでデプロイされるようになり、検証までのリードタイムを改善できたと思ってます。

結果

最終的に、数ヶ月かけてこの新環境の構築とpipelineの移行を行いました。

結果、上記の3つの課題については、解消することができたと思ってます。

特に、開発環境ができたことと、DSグループ管理のTerraformリポジトリができたこと、featureブランチのpushでデプロイできるようになったことから、インフラの検証のしやすさは格段に上がったと思っており、DSグループ内のDX向上に寄与できたのではと思っております。

We’re Hiring

タイミーのデータ統括部では、データサイエンティスト、DRE、Data Analystなど、様々な職種のメンバーを募集してます!

https://hrmos.co/pages/timee/jobs

カジュアル面談からでも対応できますので、少しでも気になった方は申し込み頂けると嬉しいです!