yigarashiのブログ

スクラム運営やサービス開発を通して学んだことをまとめます。

BigQueryっぽい小技を組み合わせてGA4のデータのページタイトルを最新の値に正規化する

Google Analytics 4(通称GA4)は最近利用できるようになったGoogle Analyticsの新しいプロパティで、全てのデータを簡単にBigQueryにエクスポートできるのが大きな特徴です。私もGA4を使ってこのブログのアクセス解析を行っており、BigQueryでナイスなビューを作成してデータポータルで可視化しています。その中でも「ページごとの閲覧数」が意外と曲者だったので、それを紹介します。

「ページごとの閲覧数」と言ったとき、多くの人が期待するのは、各行にページのタイトルと閲覧数が並んでいるような表だと思います。しかし次のような課題があります。

  • ページのタイトルが不変とは限らない
    • 例えば、はてなブログはブログ名が各記事のタイトルに入るので、ブログ名を変えると全ての記事のタイトルが変わります。他にも「(追記あり)」と付けてみるだとか、タイトルが変わる要因は無数に考えられます
    • 実際GA4レポート画面の「エンゲージメント > ページとスクリーン」はページタイトルで集計されているので、ページタイトルが変わると表示回数などの集計も分かれます
  • ページのURLだけだと表から情報を読み取りづらい
    • タイトルが変わるならURLで集計したら良いと考えるわけですが、素朴にページのURLと閲覧数で表を作ってみると想像以上に見づらい表になります。タイトルだと一瞬で情報を掴めたのに、ひとたびURLに変えると各行をグッと睨んでどんなページだったか 考えないと何も読み取れないのです。不思議ですね

こうした課題を解決するために、以下のようにして正規化したタイトルを取得した上で集計するのが望ましいと考えました。

  • 各URLの最新のタイトルを取得する - (1)
  • ページのURLごとに閲覧数を集計する - (2)
  • (1)と(2)をJOINして最新のタイトルと閲覧数の組にする

ここからは実際にクエリを見ながら解説していきます。

ステップ1: 必要な期間から必要なレコードだけ抜き出す

今回は過去28日のpage_viewイベントがあれば十分なので以下のようにします。

WITH records_of_last_28_days AS (
  SELECT
    event_date,
    (SELECT value.string_value FROM UNNEST(event_params) x WHERE x.key = 'page_location') AS page_location,
    (SELECT value.string_value FROM UNNEST(event_params) x WHERE x.key = 'page_title') AS page_title,
    (SELECT value.string_value FROM UNNEST(event_params) x WHERE x.key = 'page_referrer') AS page_referrer
  FROM
    `xxx.yyy.events_*`
  WHERE
    _TABLE_SUFFIX BETWEEN FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 29 DAY))
    AND FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 1 DAY))
    AND event_name = "page_view"
),

BigQueryっぽい見どころは以下の2つです。

取り組んでいる時の様子です。

ステップ2: 各ページURLの最新のタイトルを取得する

以下のようにします。

location_to_title AS (
  SELECT page_location, page_title FROM (
    SELECT
      *,
      ROW_NUMBER() OVER (PARTITION BY page_location ORDER BY event_date DESC) AS rn
    FROM records_of_last_28_days 
  ) WHERE rn = 1
),

まず分析関数のPARTITION BYでpage_location(つまりページのURL)ごとにレコードを分割しそれを新しい順に並べます。ROW_NUMBERは分割したパーティションごとに行数を返す関数です。ここまでやると、WHERE rn = 1でpage_locationごとに最新のpage_titleが入っている行を取得できます。

この手法は数百GBのデータをMySQLからBigQueryへ同期する | メルカリエンジニアリングから着想を得ました。分析関数については公式ドキュメントのこちらのページが詳しいです。

ステップ3: page_locationごとにイベント数を集計してpage_titleとJOINする

以下のようにします。page_locationに加えてpage_referrerでもGROUP BYしてカウントしておくと、データポータルでいい感じにドリルダウンできるのでオススメです。

pv_by_location_and_referrer AS (
  SELECT page_location, page_referrer, COUNT(*) AS pv FROM records_of_last_28_days GROUP BY page_location, page_referrer
) SELECT page_title, page_referrer, pv FROM pv_by_location_and_referrer JOIN location_to_title USING (page_location);

データポータルで便利なデータの形というのはなかなか感覚が掴めておらず難しく感じます。


以上です。自分のブログにGA4のタグを入れて、BigQueryにデータを流しておくだけでおもちゃがひとつ増えるのでオススメです。

VSCodeで複数リポジトリを扱うなら結局Open Recentコマンドが最強という話

大学時代から長らくEmacsで暮らしていたのですが、最近ついにVSCodeに完全移行しました。各種設定やプラグインのエコシステムが使いやすかったり、コマンドパレットの感じがEmacsに似ていたりしていい感じです。

そんなVSCode、基本的には1フォルダ1ウィンドウの世界観なので、大量のリポジトリを相手にするときに困りがちです。何も考えずにフォルダを開いていくとウィンドウが散らかって生産性がどんどん下がります。典型的な解決策としてWorkspace機能がよく紹介されますが、以下の理由から個人的にはあまりフィットしませんでした。

  • 仕事で扱うリポジトリは巨大なものが多く、それをひとつのウィンドウにいくつも押し込めてしまうと複雑すぎて手に負えない
    • ひとつのウィンドウで複数リポジトリのファイルを開けてしまうというのも厄介で今度はタブの管理が大変になる
  • Workspaceの設定は基本的に手動管理なのでリポジトリの数に対してスケールしづらい

解決策 - Open Recentコマンド

こうした問題意識から解決策を色々探していたのですが、最終的にVSCodeにデフォルトで入っているOpen Recentコマンドが一番良いという結論に至りました。デフォルトのショートカットキーはおそらくCtrl-Rで、コマンドパレットからもFile: Open Recent...という名前で参照できます。実行すると以下のように最近開いたフォルダをインクリメンタルに検索できます。

VSCodeのOpen Recentの例

【追記】デフォルトでOpen Recentを使うと現在のウィンドウが置き換えられてしまって微妙に望みの挙動にならないので、設定からWindow: Open Folders In New Windowをonにして使っています。

あとはお好きな移動キーで開きたいフォルダのところまで移動して選択するだけです。既にウィンドウが開かれていれば一瞬で移動し、まだ開かれていなければその場でウィンドウが開きます。個人で使う場合は以下のように運用しています。

  • Macなら)VSCodeのウィンドウ用のデスクトップをつくる
  • 完全に新しいリポジトリを参照する時はリポジトリ直下でcode .としてウィンドウを開く
  • 開いたウィンドウは全て専用デスクトップ領域に放り込む
  • 一度でも開いたことのあるリポジトリはOpen Recentで移動する

このやり方は以下の点で気に入っています。

  • 1ウィンドウ1フォルダなので作業のコンテキストを区切りやすい
  • リポジトリの名前さえ覚えていれば一瞬でウィンドウを切り替えられる
  • ウィンドウがあるかないかを気にせずにリポジトリを移動することができる
  • 一度開くだけで検索対象になる上、インクリメンタルサーチなので、リポジトリの数に対してスケールしやすい
  • 各ウィンドウのターミナルが必ずリポジトリ直下をカレントディレクトリとして開く

ゴールデンウィークで開発環境整備に取り組む方も多いかと思われます。ぜひご参考ください。

『SCRUM MASTER THE BOOK』を読んでスクラムマスターを何もわかっていないことがわかった

読み始めるまでの話

社会人として働き始めて2年、Webアプリケーションエンジニアとしてコードを書きながら、プロジェクト管理やスクラム開発の運営に積極的に取り組んできました。特にここ1年は、偉大な先人が築いたスクラム開発の土台を引き継いで、実質的にスクラムマスターのような活動をしてきました。

この「実質的に」「のような」というのが最近の課題で、プロセスの改善やチーム運営の民主化、自己組織化の促進に取り組んでいるものの、自分も周囲も何を以ってスクラムマスターとするかが曖昧で、はっきりとスクラムマスターを名乗ることができていません。とはいえ、ハイパフォーマンスな開発チームを組織する上で重要な役割を果たせているという点に関しては、ある程度の合意が取れており、そうした役割を再生産していくためにも、明確に名前をつける機運が高まっていました。そこで、改めてスクラムマスターの定義を確認して議論を整理しようと考え、社内でおすすめされていた『SCRUM MASTER THE BOOK』を手に取りました。表紙には「『スクラムマスターは何をすれば良いのか』に答えてくれる本」とあり、頼もしい限りです。

学んだこと

スクラムマスターをひとことで表すと「アジャイル開発の導入とチームの自己組織化を支援するサーバントリーダー」ということになりそうです。特に本文では自己組織化の部分が重点的に書かれており、チームに気づかせ、チームに解決させるという考え方が様々な切り口で登場します。つまりスクラムマスターは、障害物を取り除いて回る秘書ではなく、チームを信じて行動を促すコーチであるということです。変化を起こし続けることが重要で、遊び心やポジティブさも重要な資質として書かれています。また、チームの成長を志し長期的な成果を求めることから、デリバリーの責任を負うべきではないとされています。

このあたりが印象に残った主張で、他にもスクラムマスターとしてうまくやっていくための様々なスキルが解説されており、どれもためになるものばかりでした。また、スクラムマスターのレベルについても解説されており、開発チームだけでなく、プロダクト開発や会社といった大きなスコープにスクラムマスターとして働きかけていく道筋も提示されています。

自分の振る舞いと考え方を改める

この本を読んでまず真っ先に思ったのは「自分はスクラムマスターではなかった」ということです。チームメンバーとスクラムマスターを兼務する場合の罠に見事にハマっており、目の前の問題を自分で解決することばかり考えていました。自分がプロセスを改善していれば物事が勝手にアジャイルになると信じていましたが、SCRUM MASTER THE BOOKの教えはそれとは全く逆のことです。アジャイルなチームとアジャイルなデリバリーの仕組み - yigarashiのスクラム開発ブログではプロセスとチームというようにアジャイル開発の構成要素を分解したように書いていますが、実際はプロセスのみに注目していたところに、チームや文化に着目する視点が与えられて考えが広がったのでした。チームを信じ、チームが成長することを喜べるようになる必要があります。

そうした流れから、単にアジャイル開発プロセスを整備するだけの人から、チームをアジャイルにするスクラムマスターになろうとしています。そのために振る舞いや考え方を意識的に変化させています。さしあたって以下のようなことに取り組んでいます。

  • ポジティブな雰囲気を作る
    • ふりかえりではポジティブな話題に着目しやすいようにアクティビティを調整する
    • ネガティブな話題ではポジティブな言い変えをしたり良かった面をコメントする
  • チームに任せる
    • なんとなく解決策を持っている時もチームがアクションを出せないか待つ
    • 議論が多少白熱してもチームで収束させられないか様子を見る
    • ※そもそも解決したがりで解決策を思いつきがちなために今のキャリアを歩んでいるので、この部分は本当に難しいです。しばしば自分で解決策を喋りまくってしまうことがあります。また一方で、自分でも解決策がわからないことは正直に言った上で議論を始めるなど、丸投げとコーチングを履き違えないように気をつけています
  • オーナーシップを委譲する

こうした取り組みを行う一方で、自分が専任のスクラムマスターになりたいかということも考えさせられます。今のところ答えはノーで、どちらかというとエンジニアリングに関わり続け、エンジニアリングマネージャーとして技術面のリードやピープルマネジメントに手を広げられると面白そうな気がしています。偉大なスクラムマスターとしてのスキルをサブセットとして取り込んで、より面白いキャリアを歩みたいものです。

アジャイルなチームとアジャイルなデリバリーの仕組み

最近少しずつアジャイル開発の解像度を上げようとしていて、それが少し整理されてきたのでざっくり考えていることをまとめてみようと思います。

アジャイル開発をやりたいと言った時、取り組むべき領域は大きく2つに分けられると思います。それはデリバリーの仕組みとチームの2つです。そしてそれぞれに段階や考えるべきことがあると思います。

アジャイルなデリバリーの仕組み

アジャイル開発の大筋は、作ったものに対していち早くフィードバックを集め学習しプロダクトに反映することです。そのために、スクラムをはじめとしたイテレーティブな開発プロセスによって軌道修正の機会を増やしたり、CI/CDの整備のようなエクストリームプログラミングのプラクティスによってイテレーティブな開発にかかるコストを小さくしたりといった、仕組みの整備が広く語られています。アジャイル開発というとまずこういった話題を思い浮かべる人が多いでしょうし、実際に型から入るというのは現実的なやり方だと思います。

この仕組みのスコープをさらに2段階に分解できます。ひとつは、ユーザーへのリリースはビッグバンだが、ステークホルダの間でのみ内々にフィードバックループを回してイテレーティブに開発を進めている段階です。これは開発環境へのデリバリーという意味ではアジャイルなデリバリーができていますが、ユーザーのフィードバックの反映という意味ではアジリティを獲得できていません。これまではこの段階を乗り越える方法論が不足しており、多くのアジャイルチームが伸び悩んだのではないかと感じています。

しかし最近、データ駆動戦略のような方法論の台頭によって、その壁を乗り越え、ユーザーへのデリバリーも含めた全ての開発プロセスアジャイルにする方法論が広く示されたように思います。それこそがアジャイルなデリバリーの仕組みにおける2段階目で、本番へのリリースを繰り返して、ユーザーの行動データを元に仮説検証を繰り返し目的不確実性に対処していくやり方です。このあたりは『DMM.comを支えるデータ駆動戦略』を読んだ - yigarashiのスクラム開発ブログでも少し書きました。

アジャイルなチーム

デリバリーの仕組みにおける段階を進めるのと半ば独立に、チームのアジリティという軸があるように思います。つまり、チーム自身がどれくらい素早く変化に適応し成長できるかの度合いです。自己組織化と言い換えてもよいでしょう。これは見逃されがちですがアジャイル開発における非常に重要な要素だと思います。

仮にアジャイルにデリバリーするための枠組みが整ったとしても、チームがアジャイルでなかったとしたらどうでしょう。たとえば、最初に腕力のある人が開発プロセスやテンプレートを整備しまくって、それをただ使っているという状態です。それではより大きな変化が訪れた時に対処できず、徐々に衰退していきます。変化とは、ライブラリが更新されたり、新しいツールの導入を迫られたり、新サービスが台頭したり、技術革新が起こったりといった、サービス開発に訪れるあらゆる変化のことです。我々は、仕組みを育てるのと同時に、こうした変化にアジャイルに対処するマインドを育てなければいけません。そのために、ふりかえりやスクラムマスターによるティーチング・コーチングといった支援が重要になるでしょう。

また、アジャイルなチームを育てる上で、組織構造の問題が如実に現れるのではないかと考えています。というのも、チームのアジリティは、デリバリーの仕組みに比べてスケールしづらいように感じるからです。ある程度の人数でもアジャイルなデリバリーの仕組みを運用することは現実的で、バックログの流量が増えさえすればある程度スケールします。しかし文化を育てるという活動をスケールさせるのは難しく感じます。最終的には、チームが関わるプロセスをアジャイルカイゼンできるようになりたいわけですが、そのプロセスに関わる人が多く多様であるほど難易度が上がります。例えば、ふりかえりの参加者が15人にもなると、ふりかえりの手法を毎回変えて試すといった大胆なファシリテーションは望めません。この点から、アジャイルなデリバリーの仕組みは運用できているが、長期的に見るとアジャイルなチーム作りに支障があるので、チームや会議体を再編しましょうといった議論が可能になるかもしれません。

まとめ

アジャイル開発を大きく2つの領域に分けてそれぞれ解像度を上げて深めてみました。これらは完全に独立というわけではなく、どちらかというと豊かな相互作用を起こす関係にあると思います。仕組みによって考え方が変わったり、アジャイルなマインドが育つことによって仕組みの整備が進むといったようにです。今回の議論をもとに、ひとつひとつのカイゼンアクションを尖らせつつ、アジャイル開発を根気強く育てていきたいものです。

embulk gem installはインストールに失敗してもexit statusが0になる

表題の通りです。Embulkでプラグインをインストールする際にはembulk gem installコマンドをよく使うと思うのですが、このコマンドはパッケージのインストールに失敗してもexit statusが0になりません。本家のissueを見ると、この現象はembulk gemembulk bundle以下の全てで発生するようです1。現在stableのv0.9では直らず、v0.11で直るだろうと書かれています。

これで何が起こるかというと、DockerでEmbulkを動かしている時に、コンテナを動かすまでイメージビルドが失敗していることに気がつけません。つい最近その事故に遭って、依存パッケージのアップデートで依存関係が壊れてembulk-output-bigqueryのインストールに失敗していました2。日時のデータ転送が全て失敗した通知で異変に気付き、ログを追いかけて行った結果embulk gem installがサイレントに失敗していたことに気付きました。幸い復旧は用意だったので、exit status 0は勘弁してよ〜とキャッキャしながら対応しました。エラー自体は以下のような文言がこっそり出力されているという感じです。

ERROR:  Error installing embulk-output-bigquery:
  representable requires Ruby version >= 2.4.0.

イメージが壊れていることに気づけないのは困るので、以下のようになんとも味わい深い1行をDockerfileに加えて応急対応をしました(実際は他のいくつかのgemも||でつないで指定しました)。

RUN bash -c 'l="$(embulk gem list)" && if [[  ! $l =~ "embulk-output-bigquery" ]]; then exit 1; fi'

せめてGemfileとかを使って管理すると良いかもしれないな〜と思いながら放置してあります。差し当たり報告でした。頑張っていきましょう。