月35人以上が開発するUbieのdbt開発のガードレール
こんにちは、おきゆきです。Ubieでデータ関連業務を担当しています。
4月9日に開催されたTokyo dbt Meetup #13にて、「dbtとLightdashを社内へ浸透させるまでの取り組み」というテーマで発表させていただきました。当日は多くの方にご参加いただき、たくさんのご質問、誠にありがとうございました!
その中で特にコメントが多かったのは、「データエンジニアが1人の状況で、dbtとLightdashを利用する月間PR作成者が35人以上というのは、具体的にどのようにデータマート開発を進めているのか?」「品質はどのように維持しているのか?」「データモデリングの知見はどのように共有しているのか?」といったご質問でした。
具体的には、以下のスライドで示した数値についてです。
https://speakerdeck.com/okiyuki99/integrate-dbt-and-lightdash-into-ubie?slide=11
この記事では、Ubieがどのようにして「月35人以上の開発者、月600件以上のPR」というdbt開発の民主化を維持・推進しているのか、その背景にある思想や、データマート開発の仕組み・ガードレールについてご紹介します。dbtを活用されているエンジニアの方や、データ民主化を推進したいと考えている方にとって、何かしらのヒントになれば幸いです。
目指したのはデータオーナーシップの装着
私たちがdbtやLightdashを導入した背景には、多くの組織と同様の課題がありました。具体的には、各チームがデータに対してオーナーシップを持ち、品質管理を行い、必要なデータは自ら開発し、そのデータを活用して自律的に分析を行える体制を築きたい、という思いです。また、少人数のデータエンジニアやアナリティクスエンジニアがボトルネックにならない体制を構築したいという狙いもありました。
登壇でお話ししたとおり、各チームが開発できる体制を築いた結果、気づけば月35人以上の開発者が参加するようになりました。それには速度と品質のバランスを取りながらdbt model開発を進めるためのガードレールの設定が重要と感じています。以下では、そのために具体的に取り組んでいることを紹介します。
各チームがdbt開発できるためにやったこと
論理レイヤーごとの規則と、自由に開発できるdm層の浸透
Ubieでは、他のメンバーの記事でも紹介しているとおり、dbt modelを4つの論理的なレイヤーに分けています(上流の2層はデータ公開のための仮名加工処理などを行うため、ここでは省略します)。導入当初は、各エンジニアが「なぜこのようにレイヤーが分かれているのか?」という点を十分に理解できていませんでした。そのため、まず各レイヤーの論理的な意味と、各チームがどのレイヤーをどのように開発に利用すべきかを丁寧に説明し、認識を揃えることから始めました。
その中で、各チームの開発を促進するには、実験的な試みも含め、他のデータとの依存関係をあまり気にせずに自由に開発できる環境が重要だと考えました。そこで、dm層(datamart) から開発を始めることを推進しました。
データパイプライン上の最下流のdm層で開発しやすいようにガードレールを整備*
dm層では、チームごとに比較的自由にデータセットを作成できます(Terraformで管理)。たとえば、「ユビー症状検索エンジン」開発チームであれば、dm_symptom_search_engine
のようなデータセットを作るイメージです。また、dm層のモデルは他のdbt modelから参照できない というガードレールを設けています。dm層で作ったマートは他から参照される心配がないため、モデルオーナーは自身のチームが満足して使えることを最優先に開発できます。これにより、自由に拡張・削除しやすくなり、開発効率が高まるというメリットがあります。またデータリネージ的にもラストであることが明確になります。
一方、dc層(data component)やdwh層(data warehouse)などは、基本的に他のチームからの利用が想定されるため、開発速度よりも品質担保や再利用性の向上が求められます。
セルフマージを可能にし、dbtやLightdash開発を活性化
上記のレイヤーごとの規則によって、Ubie全体のデータパイプラインの整合性を保ちつつ、開発をフルスロットルで進めるための適切なガードレールが設けられています。この規則が浸透したことでdm層における各チームの開発は加速しましたが、次にレビューがボトルネックとなりました。dbt modelやLightdashの軽微な修正、あるいは品質を最優先としないdbt model開発など、形式的なレビューが増加傾向にあったのです。
そこで、セルフマージを可能にするためのガードレール を整備し、開発者自身がマージできる仕組みを導入しました。これについては、データエンジニアのyoshがUbieのデータ利活用を支えるデータ分析基盤とCI/CDで詳しく紹介しています。
Data & Analytics 井戸端会議 #01 の資料抜粋
結果として、各チームのdbt model開発速度の向上に大きく寄与していると感じています。ガードレールの適用範囲拡大なども行われていますが、一方で、データガバナンスの観点からは、リスクや重要データの品質問題が発生する可能性も考慮する必要があります。この点については、データ分析基盤チームが中心となり、モデルオーナーの明確化や下流のdbt modelへの影響度合いを考慮しながら、ガードレールを適切に見直しています。たとえば、最上流のデータプライバシーに関わるデータ公開については、レビューを必須としています(私もここのレビューを担当しており、普段以上に慎重にデータを確認しています)。
dm層からdc層への「輸出」推進による再利用コンポーネントの生成
論理レイヤーの規則により、dm層では各チームが比較的自由にdbt model(データマート)を開発できます。その結果、dbt model開発が促進され、他のチームから利用したいデータや、再利用可能なロジックが見つかりやすくなりました。
ただし、dm層は他のレイヤーから参照できない規則があるため、dm層で生まれた有用なデータをdc層へ輸出する(再利用可能なコンポーネントとして生成する)取り組みも、各チームの状況やニーズに応じて進めています。
dm層からdc層への輸出イメージ図
たとえば、複数チーム間で以下のようなやり取りを行い、協力してdm層からdc層へのコンポーネント切り出しを進めています。
あるチームAのdm層のデータをチームBが使いたいので、チームAがdc層開発を進めてくれてる例。方針整理に入りつつ、社内のソフトウェアエンジニアがdc層の開発も進めている
これにより、Ubie全体のデータパイプラインの整合性維持と再利用性が向上し、結果として他のチームの開発効率が上がり、より深い分析や追加開発に取り組みやすくなるというメリットが生まれていると感じています。dc層の役割や開発規則については、ボトムアップでの理解浸透を図っており、今後さらに開発に参加するメンバーを増やしていきたいと考えています。
Ubie流SQLの書き方浸透で、SQLコードの読みやすさを維持
dbtの開発オンボーディングとは別に「良いSQLとは何か?」といったテーマで個別の学習機会も設けました。参加したソフトウェアエンジニアからは、「これまでは何となくSQLを書いていたが、CTEの有用性やコードの可読性向上を実感できて非常に良かった」といった声も寄せられました。
ここで学んだ書き方をdbt model開発にも適用することで、結果的に集計ミスが減り、他のメンバーによるレビューもしやすくなるなど、品質維持に寄与していると感じています。
Ubie流 SQLの書き方のオンボ資料
また、社内の生成AIツールDev爺には、SQLクエリをリファクタリングするためのプリセットも有識者によって整備されています。さらに、dbt開発リポジトリで利用しているCursorのProject Rulesにも、以下のようなSQL記述規則を定義しています。個人的な感想ですが、Project Ruleに良いSQLの例を含めたことで、生成AIが生成するSQLが自分の書くスタイルに近くなり、開発体験が向上しました(個人的な嗜好を反映してしまっている部分もありますが)。
あなたはBigQueryに関するプロフェッショナルなデータエンジニアです。データ集計に関する知識も豊富です。SQLを提示する際にはできるだけパフォーマンスにも考慮します。
SQLを記載する際は以下の原則を考慮します。
# 基本原則
## 1. CTE(Common Table Expression)を使う
- サブクエリを使用しないこと
## 2. 初出のテーブルの定義は `import_{dataset_name}_{table_name}` と定義する
- Import CTEは、1つのdbt model だけで作れること
- これを `import_{dataset_name}_{table_name}` という名称のCTE名で定義
- `import_{dataset_name}_{table_name}` の制約
- (Must) 1つのテーブルのみ参照する
- (Want) 複雑なロジックはなるべくかかない。
## 3. JOINしたり集計するCTEは `logic_{xxx}` と定義する
- `logic_{xxx}` CTEでは必ず `import_{dataset_name}_{table_name}` か `logic_{xxx}` を参照して定義すること
## 4. 最後のサブクエリは `final` として定義する
- 最後のSQLのステートメント句は `SELECT *, FROM final` とすること
## 5. SQL内のコメントを
- 特になぜこういう処理をしてるかという理由をクエリ内のコメントとして日本語で書くこと
## 6. SQLの予約語は大文字、テーブル名やカラム名は小文字にする
# 良いSQLの例
[SQLサンプル記述例]
まとめ
以上、簡単ですが、Ubieにおいて35人以上の開発者がどのようにdbt model開発を行っているかについて説明しました。
データ開発は、品質と速度のバランスが常に問われる領域だと感じています。開発者が毎回このバランスを考慮するのは認知的な負荷が大きいため、ガードレールや推奨される設計パターンを、生成AIを活用しつつCI/CDプロセスで自動的に検知・レビューできるような仕組みの整備も、データ分析基盤チームと協力して進めています。
今後もこのようなアナリティクスエンジニアリングやデータと生成AIに関する知見を発信していきたいと思いますので、よろしければXもフォローいただけると嬉しいです!
Discussion