はじめまして。id:takanamitoです。
バックエンドエンジニアとしてTVerに入社して3ヶ月が経ちました。 TVerに入ってみて感じたこと、開発組織が何に取り組んでいるのか書いてみようと思います。
続きを読むこんにちは、TVerでAndroidエンジニアをしている石井です。
株式会社TVerはDroidKaigi 2024のサポーターとして協賛することになりました。
DroidKaigiはエンジニアが主役のAndroidカンファレンスです。 今年で10年を迎えるDroidKaigiは、Android技術情報の共有とコミュニケーションを目的に、2024年9月11日(水) - 13日(金)の3日間開催します。(HPより引用)
オフライン会場: ベルサール渋谷ガーデン
TVerは昨年Androidエンジニアが2名入社し、昨年9月頃に完全内製化が完了しました。
品質や生産性向上のための取り組みとして、現在TVerのiOS/Androidアプリではリアーキテクチャに取り組んでいます。
AtomicDesignを採用した独自のデザインシステムの構築や、JetpackComposeによるUIの構築、Androidのアプリアーキテクチャガイドをベースとしたリアーキテクチャの実施などを中心にモダンな技術スタックに移行を進めております。
またTVerはモバイルアプリ以外に、AndroidTVやFireTVなどのTVアプリ開発にも力を入れています。 その中でDroidKaigiを通じてAndroid技術の今後の発展を祈り、些細ながらも協賛させていただくことになりました。
TVerアプリをより使いやすく便利にしていくための様々な機能開発を行っていくためにもまだまだAndroidエンジニアが必要な状況です。 TVerのミッション「#テレビを開放して、もっとワクワクする未来を」に共感いただけるテレビの未来を支えるエンジニアの方をお待ちしております!
みなさんこんにちは、TVerでEngineering Managerをしている高橋 (@ukitaka) です。
8/22-8/24で開催されたiOSDCに参加してきましたので、 少々遅くなりましたが #iwillblog しておこうかなと思います!
個人的な話にはなってしまうのですが、iOSDCオフライン参加するのはかなり久しぶりで 2018年に登壇して以来6年ぶりでした。
当時の発表資料 speakerdeck.com
もはや界隈から忘れ去られているかもなとドキドキしながら会場入りしたんですが、いろんな方々にお声がけいただいただけではなく、がんばってスポンサーしたりパンフレット書いたりした甲斐あってか「いまTVerにいらっしゃるんですよね!?」と所属まで知っていただけていたのでとても安心しましたし、嬉しかったです。
iOSDCは相変わらず文化祭のようなワイワイ感があって最高ですね・・スポンサーブースも各社気合い入っていて本当にお祭りみたいでした。来年も開催されるのであれば、次こそはTVerブースを出そうと心に誓いました。
Day1 の14:30~ はスポンサーセッションで弊社iOSエンジニアの小森が登壇し、リアーキテクチャ戦略についてお話しさせていただきました。
発表聞いていただいた方はわかったかもしれないですが、実はTVer iOSアプリのリアーキテクチャはまだ始まったばかりで「これからやっていくぞ!」という発表でした。来年もiOSDCが開催されるのであれば、このリアーキテクチャがうまくいったのか?うまくいかなかったとしたら何が起こったのか?みたいなところをぜひお話しできればと考えています!
iOSDC Japan 2024、本当にめちゃくちゃ楽しかったです。運営の方々、スポンサーセッションを聞いてくれた方々、話しかけてくれたみなさん本当にありがとうございました。
そして発表スライドにもあった通り、iOSチームの状況は以下の通りなので一緒にTVer iOSアプリを作っていってくれる方々のご応募をお待ちしております!!
こんにちは。TVerでバックエンドエンジニアをやっている伊藤(@kanataxa)です。
TVerをより多くの方に利用していただくために、バックエンドチームでは機能開発と並行して開発サイクルの高速化や品質向上にも取り組んでいます。
その中で2024/7に組織変更が行われ、「開発サイクルにフォーカスする」ことを目的としてEnabling Teamが立ち上げられました。 今回はそのEnabling Teamについてです。
TVerのバックエンドチームの現状と合わせて、これから何をしていくのかを書いていきたいと思います。
バックエンドチームはTVerサービスの内製化に向けてできたチームです。2022/4にリニューアルが行われたので、まだできて数年の歴史の浅いチームです。 またサービス規模に対して当初は数名ととても小さなチームでした。
このような状況のため、日々の開発の中で犠牲にしなければならなかったものも多く、属人化を許容し、いくつかの要件もサービス特性的に致命でない部分に関しては割り切らないといけないこともありました。
しかしながら、この2年でチームは拡大し十数名のエンジニアが所属することとなり、ようやくいくつかの開発を複数人で行えるようになってきました。 そしてサービスの成長やあり方の時間的変化に伴い徐々に組織的に開発を行うことが求められ、これらの諦めてきた部分が機能開発において大きな障壁になりつつあります。 暗黙的な何かが大きくなってしまい、新規メンバーの初速が出にくくスケールが難しい状態になっているのです。
サービスを成長させていくためにもまだまだチーム拡大を行いたいという想いもあり、認知負荷の軽減や、よりデファクトスタンダートに則った開発サイクルが強く求められるようになってきました。
TVerのバックエンドチームの現状 でも述べたとおり、より多くの機能開発をはやく行うには抜本的に開発サイクルを見直す必要が出てきました。 その課題にフォーカスを当てることのできる組織体系にしようとした結果、Enabling Teamが立ち上げられました。
将来的にはよりTeam Topologiesにあるような組織を目指したいと考えていますが、現時点ではより限定的な課題に対して「バックエンドエンジニア×リードタイム」の最適化を目指すためのチームです。
つまり、機能開発チームがメンバーの能力になるべく依存しない状態で、より多くのサービス課題に注力できるように「当たり前を整備していく」ためのチームです。
まだまだ数名の小さいチームであるため、役割を限定的にしつつ中長期的なゴールを定めています。 そのゴールを達成するために、今期はなにをやる/やらないのかということを半期に1回見直すこととしています。
2024/7時点での取り組みを以下でいくつか紹介します。*1
バックエンドチーム全体の課題として、リリース時にオミットしたものが多く存在しています。 チーム全体の開発サイクルを最適化するには、これらの課題の多くを消化する必要があります。*2
今は高度な技術の導入・検証より、当たり前の世界を作るために手を動かすことを重視しています。
「なぜ」の部分が残っていないと意思決定を把握することが途端に難しくなります。 そのためEnabling TeamではADRによるドキュメント駆動での開発を行うこととし、機能開発チームに対してはDesign Docの導入推進を行い、テンプレート作成等の支援を行っております。
またドキュメントのメンテナンスコストをそのまま開発コストに転換できるような、OpenAPIやDDLに対するスキーマ駆動開発の導入も進めています。
余談ですが、TVerのバリューのうち1つ「我々は仲間である」と関連させて、「未来の仲間への投資」としています。
目の前の課題もいつかは終わりが来ます。その後を見据えて「開発」に対してどのような計測ができるのか準備を進めています。
3つほど紹介させていただきましたが、他にも監視通知やアプリケーションログ・エラーの標準化、GitHub移行など開発の足回りの整備を進めています。 またSREと連携してトイルの撲滅や、New Relicをより活用するための実装支援や仕組み作りも今後行っていく予定です。
今回はTVerのバックエンドチームにおけるEnabling Teamの立ち上げについてざっくり書いてみました。 まだまだ小さく歴史の浅い組織で課題が山積みですが、サービス成長を支えるべく、よりよい技術組織を目指して日々取り組んでおります。
TVerではエンジニアを積極的に採用中ですので、 TVerのミッション「テレビを開放して、もっとワクワクする未来をTVerと新しい世界を、一緒に。」に共感いただける方、ぜひお気軽にご応募ください!
はじめまして!
TVerのSREチームでオブザーバビリティ推進を担当している鈴木 彩人と申します。
6/15(土)に札幌で開催されたCloudNative Days Summer 2024にて登壇しました! event.cloudnativedays.jp
本イベントのダイヤモンドスポンサーであるNew Relic様から声をかけていただいたため、貴重な体験ができると思い登壇することにしました。
(弊社では会社の費用でカンファレンスに参加できる非常に良い制度があります)
公式サイトより引用。
CloudNative Days はコミュニティ、企業、技術者が一堂に会し、クラウドネイティブムーブメントを牽引することを目的としたテックカンファレンスです。 最新の活用事例や先進的なアーキテクチャを学べるのはもちろん、ナレッジの共有やディスカッションの場を通じて登壇者と参加者、参加者同士の繋がりを深め、初心者から熟練者までが共に成長できる機会を提供します。
参考 : https://cloudnativedays.jp/
登壇資料は以下になります。 speakerdeck.com
今回の登壇ではサービス規模拡大に伴いカスタマーサポートと開発チーム双方の運用負荷が増加している課題をNew Relicを活用して改善した事例について話しました。エンジニアではないメンバーがここまでNew Relicを活用して課題解決する話は珍しいと思い、今回の登壇テーマにしました。
登壇では実際の活用事例を交えて弊社のカスタマーサポートと開発チームの連携について紹介しましたが、顧客満足度の向上の重要性について話せなかったため、本ブログにて補足させてください。
顧客満足度に関連する法則としてグッドマンの法則というものが存在します。
グッドマンの法則とは要約すると以下になります。
上記法則から、多くの利用者にサービスを提供をする上で顧客満足度の向上が非常に重要であることが分かります。
オブザーバビリティを確保することで顧客満足度向上に繋がることから、引き続きオブザーバビリティの推進を頑張っていきたいと思います。
ちなみに今回の登壇資料では最新の会社紹介資料のスライドを引用しております。TVerを知る上でとてもわかりやすい資料となっておりますのでぜひご覧下さい。
200人弱の前で話す機会は滅多にないため、貴重な体験ができたことに大変感謝しております。(緊張で少しぎこちない登壇にはなってしまいましたが…笑)
登壇後にXや満足度アンケートの結果を確認したところ、ポジティブなご意見が多かったので、登壇した甲斐がありました。
イベントや懇親会では様々なエンジニアから貴重なお話を聞くことができ、モチベーション向上にも繋がったため、こういった機会があればまた登壇したいと思います。
こんにちは、TVerでエンジニアリングマネージャーをしている高橋 (@ukitaka) です。 TVerは今年もiOSDCに協賛させていただくことになりました!
昨年のiOSDCの時点では「iOSエンジニアがいなくても泣かない!配信サービスのiOSアプリにおける オブザーバビリティの導入と改善」というタイトルで発表があった通り、TVerにはiOSエンジニアが不在の状況だったのですが、昨年1名iOSエンジニアが入社したところからチームが立ち上がり、今年4月には完全内製化が完了しました。さらに5月には元iOSエンジニア(?)の自分もエンジニアリングマネージャーとしてjoinし、徐々に体制が整いつつあります!
さらに品質や生産性を向上させていくための取り組みとして、現在TVerのiOS/Androidアプリではリアーキテクチャに取り組んでいます。UIKitやRxSwiftなどを中心とした現在の技術スタックからSwiftUIやTCA、Swift Concurrencyなどのモダンな技術スタックに移行を進めていっているところです。
ありがたいことにTVerはサービスは順調に成長しており、MUB 3,500万、月間4.5億回再生などの数字から見ても分かる通り非常に多くの利用者を抱えるサービスになっています。
そのような状況の中でのリアーキテクチャ実施にはいくつか考慮しなければならないポイントがあります。 例えば不具合発生時のリスクをいかに小さくするかです。 当然ながら影響範囲が大きいほど不具合は発生しやすくなってしまいますし、大規模サービスであれば1%の不具合発生率であっても数十万人に影響することになってしまいます。また いかにサービスグロースを止めずにリアーキテクチャを推進するか?という観点も重要になってきます。 iOSDC2024のTVerのスポンサーセッションでは、そのあたりを考慮しながらどのようにリアーキテクチャを推進していくのかについて、弊社iOSエンジニアの小森が発表させていただきます。 8/23 (金) Day1 Track Aにて14:30〜発表予定ですので、ご興味ある方はぜひ覗いてみてください!
リアーキテクチャのようなベースの内部品質をあげていくための取り組みだけでなく、TVerアプリをより使いやすく便利にしていくための様々な機能開発を行っていくためにもまだまだiOSエンジニアが必要な状況です。 TVerのミッション「#テレビを開放して、もっとワクワクする未来を」に共感いただけるテレビの未来を支えるエンジニアの方をお待ちしております!
こんにちは、TVerでデータ分析をしている高橋です。
弊社の分析業務の多くは BigQuery に蓄積されているログを使った分析で、大量のログを扱うため前処理から集計まで全てSQLで行っています。
本記事では、SQLを書く上で特に気を付けているテーブル結合時のケアについて紹介します。
「ホーム画面を開いてから10分以内にコンテンツを再生した割合を知りたい」という依頼が来ました1。
この集計は訪問ログと視聴ログを使い、ホーム画面に訪問したログを10分以内に再生した or 再生してない
の2種類に分ければできそうです。
ここで、集計に用いるテーブルを簡単に紹介します。
ホーム、マイページ、番組ページ、エピソードページなどに訪問したタイミングで発報されるログです。
ユーザー毎に時系列順に並べることで、サービス内でのページ遷移が分かります。
-- view_logs サンプルデータ SELECT TIMESTAMP("2024-03-01 19:30:00") AS viewed_at, "hoge" AS user_id, "/home" AS url, UNION ALL SELECT TIMESTAMP("2024-03-01 19:32:00"), "hoge", "/mypage/fav" UNION ALL SELECT TIMESTAMP("2024-03-01 19:35:00"), "hoge", "/episodes" UNION ALL SELECT TIMESTAMP("2024-03-01 21:45:00"), "fuga", "/home" UNION ALL SELECT TIMESTAMP("2024-03-01 22:25:00"), "piyo", "/home" UNION ALL SELECT TIMESTAMP("2024-03-01 22:30:00"), "hogera", "/home" UNION ALL SELECT TIMESTAMP("2024-03-01 22:32:00"), "hogera", "/search" UNION ALL SELECT TIMESTAMP("2024-03-01 22:34:00"), "hogera", "/home"
viewed_at | user_id | url |
---|---|---|
2024-03-01 19:30:00 | hoge | /home |
2024-03-01 19:32:00 | hoge | /mypage/fav |
2024-03-01 19:35:00 | hoge | /episodes |
2024-03-01 21:45:00 | fuga | /home |
2024-03-01 22:25:00 | piyo | /home |
2024-03-01 22:30:00 | hogera | /home |
2024-03-01 22:32:00 | hogera | /search |
2024-03-01 22:34:00 | hogera | /home |
視聴中の行動が記録されているログです。これを見るとシークバー移動のタイミングなどが分かります。
今回は視聴開始した時刻の情報だけ使用します。
-- サンプルデータ SELECT TIMESTAMP("2024-03-01 19:35:30") AS begin_at, "hoge" AS user_id, "begin" AS action, UNION ALL SELECT TIMESTAMP("2024-03-01 22:26:00"), "piyo", "begin", UNION ALL SELECT TIMESTAMP("2024-03-01 22:27:00"), "piyo", "begin", UNION ALL SELECT TIMESTAMP("2024-03-01 22:35:00"), "hogera", "begin"
begin_at | user_id | action |
---|---|---|
2024-03-01 19:35:30 | hoge | begin |
2024-03-01 22:26:00 | piyo | begin |
2024-03-01 22:27:00 | piyo | begin |
2024-03-01 22:35:00 | hogera | begin |
今回の集計を行うには、どのようなクエリを書けばよいでしょうか?
素朴にやるなら、
/home
)のログを抽出するでしょうか。書いてみましょう。
SELECT view_logs.*, play_logs.begin_at, play_logs.begin_at IS NOT NULL AS has_played, FROM ( SELECT * FROM view_logs WHERE url = "/home" ) AS view_logs LEFT OUTER JOIN play_logs ON view_logs.user_id = play_logs.user_id -- 訪問後 10 分以内に再生してるか AND play_logs.begin_at BETWEEN view_logs.viewed_at AND TIMESTAMP_ADD(view_logs.viewed_at, INTERVAL 10 MINUTE) ORDER BY viewed_at
viewed_at | user_id | url | begin_at | has_played |
---|---|---|---|---|
2024-03-01 19:30:00 | hoge | /home | 2024-03-01 19:35:30 | true |
2024-03-01 21:45:00 | fuga | /home | false | |
2024-03-01 22:25:00 | piyo | /home | 2024-03-01 22:26:00 | true |
2024-03-01 22:25:00 | piyo | /home | 2024-03-01 22:27:00 | true |
2024-03-01 22:30:00 | hogera | /home | 2024-03-01 22:35:00 | true |
2024-03-01 22:34:00 | hogera | /home | 2024-03-01 22:35:00 | true |
一見良さそうに見えますが、以下の問題があります。
user_id=piyo
の 1回の/home
訪問に2回の再生が紐づいている(JOIN によってレコードが増えた、いわゆる重複)
user_id=hogera
の2回の/home
訪問に1回の再生が紐づいている
/home
訪問だと考えられるが、1回目の/home
訪問も再生に寄与したと見なされてしまうこのまま集計すると間違った示唆を与えてしまうおそれがあります。
順番に解決していきましょう。
このケースは短時間で複数回再生した場合に発生します(ザッピング的な再生など)。
今回の集計で興味があるのは10分以内の再生有無だけなので、/home
訪問後最初の視聴が紐づくログだけ残すようにしましょう。
この処理は以下のようなQUALIFY
句によって実現することができます。
QUALIFY -- 最初の視聴だけ残す ROW_NUMBER() OVER(PARTITION BY user_id, viewed_at ORDER BY begin_at ASC) = 1
ちなみに viewed_at, user_id, url
をキーとしてGROUP BY
しても同様の処理が可能です。
個人的にはSELECT
文を変更する必要がない QUALIFY
句を使用することが多いです。
短時間で回遊したのちに再生した場合などでしばしば発生します。
このケースは視聴ログの突合条件に次の/home
訪問までに再生しているかという条件を追加することで排除できそうです。
はじめに、view_logs
に次の/home
訪問した時刻の列を追加します。
SELECT *, -- 次の /home 訪問時刻 LEAD(viewed_at, 1) OVER(PARTITION BY user_id ORDER BY viewed_at) AS next_viewed_at, FROM view_logs WHERE url = "/home"
この列を使い、以下のような処理を突合部分に追加します。
-- 次の /home 訪問までに再生してるか (次の /home 訪問がなければ無視) AND IF(view_logs.next_viewed_at IS NOT NULL, play_logs.begin_at < view_logs.next_viewed_at, TRUE)
最終的にこのようになりました。
SELECT view_logs.* EXCEPT(next_viewed_at), play_logs.begin_at, play_logs.begin_at IS NOT NULL AS has_played, FROM ( SELECT *, -- 次の /home 訪問時刻 LEAD(viewed_at, 1) OVER(PARTITION BY user_id ORDER BY viewed_at) AS next_viewed_at, FROM view_logs WHERE url = "/home" ) AS view_logs LEFT OUTER JOIN play_logs ON view_logs.user_id = play_logs.user_id -- 訪問後 10 分以内に再生してるか AND play_logs.begin_at BETWEEN view_logs.viewed_at AND TIMESTAMP_ADD(view_logs.viewed_at, INTERVAL 10 MINUTE) -- 次の /home 訪問までに再生してるか (次の /home 訪問がなければ無視) AND IF(view_logs.next_viewed_at IS NOT NULL, play_logs.begin_at < view_logs.next_viewed_at, TRUE) -- 最初の視聴だけ残す QUALIFY ROW_NUMBER() OVER(PARTITION BY user_id, viewed_at ORDER BY begin_at ASC) = 1 ORDER BY viewed_at
viewed_at | user_id | url | begin_at | has_played |
---|---|---|---|---|
2024-03-01 19:30:00 | hoge | /home | 2024-03-01 19:35:30 | true |
2024-03-01 21:45:00 | fuga | /home | false | |
2024-03-01 22:25:00 | piyo | /home | 2024-03-01 22:26:00 | true |
2024-03-01 22:30:00 | hogera | /home | false | |
2024-03-01 22:34:00 | hogera | /home | 2024-03-01 22:35:00 | true |
viewed_at
, begin_at
どちらも一意化することができました。
このCTE を GROUP BY
することで目的の集計をすることができます。
テーブル結合時のケアについて2つの事例を紹介しましたが、これらの事象の発生を集計値だけ見て気付くことは非常に難しいです。
そのためレビューの際にはクエリのロジックを確認することは勿論のこと、中間テーブルの出力を確認したり個票チェックをしたりすることで集計ロジックの確からしさを検証しています。
同時に「レビューしやすいクエリ」を書くために、ロジックを考えたり社内ルール整備をしたりなどを日々行っています。この取り組みは分析チームが一丸となり、相当な力を入れて取り組んでいます2。
TVerでは一緒に分析業務をしてくれる方を募集しています。
カジュアル面談も受け付けていますので、「こういう取り組みのことをもっと知りたい」「普段どんな分析してるか知りたい」と思った方は以下よりお気軽にご連絡ください。お待ちしております!
1: 一例なのでかなり大味な依頼になっており、この集計結果を受けて具体的なアクションを行うことが難しいと予想されます。実際にこのような依頼が来た場合は、ホーム画面から再生までのユーザーの振る舞いや遷移についての仮説出しをしてからクエリを作成することが望ましいです。