/home/by-natures/dev*

データ界隈で働くエンジニアとしての技術的なメモと、たまに普通の日記。

2023/06/20 読んだ記事まとめ(データエンジニアリングとソフトウェアエンジニアリングの違い)

最近仕事をしていて、データエンジニアと肩書きはいただいてますがソフトウェア開発をすることも多く、データエンジニア・ソフトウェアエンジニアの2つに違いはどの程度あるのか?ただ役割を細分化しただけなのかが気になっていました。

そんな時に目についた記事がこちら:

medium.com

物理学の博士をもつ、ベルギーの様々な企業でデータエンジニアやデータサイエンティストとして働く Niels Cautaerts 氏による投稿です。2つの違いが簡潔に言語化されており、さらにアジャイル開発やテストの文脈での違いに焦点を当てて分かりやすく解説されています。

ちょっと長い記事なのですがまとめてみました。太字だけ追えばざっくり理解できるようにしたので、ぜひご覧ください。和訳要約には ChatGPT を利用しましたが、専門用語も多いので割と自分で加筆修正しています。分かりづらい箇所があればご指摘ください。

<<もくじ>>

"データエンジニアリングはソフトウェアエンジニアリングではない"

近年、データエンジニアリングはDevOpsに収束しているように見えます。両方とも、クラウドインフラ、コンテナ化、CI/CD、そしてGitOpsを採用し、信頼性の高いプロダクトをクライアントに提供しています。一部のツールに共通するこの傾向から、データエンジニアリングとソフトウェアエンジニアリングの間には大きな違いがないという意見を多くの人々が持つようになりました。その結果、データエンジニアリングがまだ「粗削り」であることは、単にデータエンジニアがソフトウェア開発の実践の採用に遅れているからだと考えられています。

しかし、この評価は誤解を招くものです。データエンジニアリングとソフトウェアエンジニアリングは多くの共通のツールと実践を共有していますが、重要な領域で大きく異なります。これらの違いを無視し、データエンジニアリングチームをソフトウェア開発チームのように管理するのは間違いです。本投稿では、データエンジニアリングの独自の課題を解説します。

データパイプラインはアプリケーションではない

ソフトウェアエンジニアリングは主にアプリケーションの開発に関わります(この投稿の文脈では、アプリケーションは非常に広い意味で定義され、ウェブサイト、デスクトップアプリケーション、API、モノリシックなメインフレームアプリケーション、ゲーム、マイクロサービス、ライブラリなどが含まれます)。これら全てが共有する特性は以下の通りです:

  • ユーザーに新たなインタラクションを提供し、価値を提供します。ゲームを遊べたり、ウェブサイトを閲覧できたり、APIは他のソフトウェアで利用できます。
  • 独立した機能が多数存在します。ウェブサイトはページ数を増やすことができ、ゲームはレベルやプレイ可能なキャラクターの数を増やすことができ、APIはエンドポイントを追加することができます。したがって、アプリケーションは決して完全に完成することはありません。
  • アプリケーションが作成する状態量は比較的小さく、大部分のソフトウェアが状態を持たないステートレスであることが目指されます。
  • 他のソフトウェアやサービスとは疎結合です。優れたソフトウェアは任意の環境で独立して機能するべきであり、これがマイクロサービスやコンテナの人気の理由です。

一方、データエンジニアはデータパイプラインの構築に関心があります。データパイプラインはデータを生成元から取得し、変換して消費者が利用する場所に置きます。通常の目標は、新しいデータを継続的に更新するためにパイプラインをスケジュールに従って自動化することです。しかし、アプリケーションとは対照的に、データパイプラインは以下の特徴を持ちます:

  • 直接的な価値を提供しません。パイプラインのユーザーは存在せず、パイプラインが生成するデータセットだけが下流の消費者にとって価値があります。
  • 顧客にとって関連性のある機能は一つだけで、要求されたデータセットを生成することです。そのため、明確な完成の時点がありますが、上流システムやユーザー要件の変更により、パイプラインは継続的なメンテナンスを必要とします。
  • 大量の状態を管理します。パイプラインは、自身が制御しない他のソフトウェアから既存の状態を処理し、自身が制御する状態に変換するために設計されます。多くのパイプラインはデータセットを増分的に構築するため、パイプラインは非常に長期間実行されるプロセスと見なすことができます。
  • 避けられない密結合があります。データパイプラインの目的はまさにデータソースとの結合です。パイプラインはソースが安定し信頼性がある場合でのみ安定した信頼性を持つことができません。

これらの根本的な違いは、ビジネス、ITマネジメント、そしてソフトウェアエンジニアさえもしばしば理解していない、データエンジニアリングの独自の課題をもたらします。それらを見ていきましょう。

パイプラインは構築が完了していなければ顧客にとって無価値である

多くの組織はソフトウェア開発チームを何らかのアジャイルの形で管理しています。このフレームワークの中心的な哲学は、短いイテレーションでソフトウェアを構築しリリースすることで、ソフトウェアが顧客に価値を提供する速度を最大化することです。これにより、可能な限り早く最小限の実行可能な製品(MVP)を提供し、迅速なフィードバックループを確保し、チームが常に最優先の機能に取り組んでいることを保証します。

しかし、この考え方はデータエンジニアリングにそのまま当てはまりません。

データパイプラインは、顧客価値を増加させる小さなイテレーションで開発することはできません。データパイプラインにはMVP相当のものはありません。それは顧客が求めるデータセットを生成するか、しないかのどちらかです。

したがって、データパイプラインの開発はアジャイルフレームワークにきれいに収まりません。複雑なデータパイプラインは単一のユーザーストーリーに対応しますが、通常は完了するために複数のスプリントを必要とします。非技術的な管理者はこの点をあまり考慮せず、データエンジニアをスクラムチームに無理に組み込もうとします。結果として、ユーザーストーリーはタスク、例えば「APIコネクタの構築」や「取り込みロジックの構築」に置き換えられ、スクラムボードはマイクロマネジメントの道具になってしまいます。

管理者が自分が管理するものの本質を理解していないと、彼らは不適切で実行不可能な決定を下す傾向があります。パイプライン開発の進行が遅いことに苛立ったマネージャーがデータエンジニアに対して、顧客が「一部のデータを使って作業を始められるように」と、データセットを列ごとに徐々に構築するように要求したことがありました。複雑なパイプラインやデータサイエンスの経験を持つデータエンジニアなら、この要求が荒唐無稽であると思うでしょう:

理由1) 部分的なデータセットは比例的な利用価値を持たない

データセットが10列中9列を含んでいる場合、その利用価値は90%なのでしょうか?それは省略された列がどれであるかによります。 - データサイエンティストがデータに基づいて予測モデルを構築しようとしているが、欠けている列が予測したいラベルまたは値である場合、そのデータセットの利用価値は0%です。 - 列がラベルと無関係なランダムなメタデータである場合、利用価値は100%かもしれません。

最も一般的なのは、列がラベルと相関する可能性があるかどうかをフィールドごとに確認することです。これはまさにデータサイエンティストが実験を通じて見つけ出したいことです。そのため、データサイエンティストは可能な限り完全なデータセットを手に入れて、実験を始め、探索し、モデルを徐々に最適化したいと思っています。部分的なデータセットを提供しても追加のフィールドが利用可能になった時点で、実験と最適化を再度行う必要があります。

理由2) パイプラインの開発時間はデータセットのサイズとは関連しない

たとえ顧客がデータセットの半分で満足するとしても、それを作り出すのに全体のデータセットを作るのに必要な時間の半分を必要とするわけではありません。データパイプラインは、それぞれが列を生成する独立したジョブで構成されているわけではありません。複数の列が同じソースから派生している場合、パイプライン構築はほぼ同じ作業量です。

理由3) データセットを構築するための時間と経済的コストはそのサイズに比例する

データセットが(行と列の両方の意味で)大きいほど、それを構築し更新するのにかかる時間も長くなります。巨大なデータベースの単一レコードを編集することは些細で早い作業ですが、通常、分析用データセットを変更することは、列の追加、列全体の変更など、何千、何百万という行の更新を含みます。データの変更を処理する方法はテーブルごと上書きしたり、該当する列や行のみ更新処理するなどいくつか手段はありますが、どちらも簡単ではありません。

結論:部分的に完成したパイプラインを本番環境にデプロイすることは無駄である

部分的に完成したパイプラインを本番環境にデプロイすることは、顧客にとって有益でなく、計算資源を無駄にし、パイプラインを構築するエンジニアの業務を複雑にします。DevOpsとアジャイルの原則をそのままパイプライン構築作業に適用して、増分的な変更と頻繁なデプロイを推奨しても、それはデータの慣性を単純に無視しているだけです。

パイプライン開発におけるフィードバックループは極めて遅い

新しい機能を迅速に作成したり、ソフトウェアのバグを修正したりするためには、開発者が書いたコードが正しく、ソフトウェアを正しい方向に進めるかどうかを早急に知らせるフィードバックが必要です。

ソフトウェア開発では通常、ユニットテストのスイートを使用して達成されます。これは開発者がローカルで実行し、ソフトウェアの各コンポーネントが(依然として)意図した通りに機能するかどうかを確認するためのものです。ユニットテストは高速であり、外部システムとは一切関連せず、状態に依存することもありません。関数、メソッド、クラスを独立してテストができます。このようにして、ソフトウェア開発者は開発中に迅速なフィードバックを得て、プルリクエストを出すときには、コードが意図した通りに機能することをかなり確信することができます。他のシステムとのインタラクションをテストする必要がある場合、ユニットテストよりは遅いですが CIパイプラインはインテグレーションテストを実行することも可能です。

データパイプラインはほとんどユニットテストされません。データパイプラインは通常、単純にデプロイすることでテストされます。通常は最初に開発環境に向けてです。これにはビルドとデプロイのステップが必要で、その後エンジニアは一定時間パイプラインを監視して、意図した通りに機能するかどうかを確認しなければなりません。もしパイプラインが冪等でないなら、再デプロイする前に、前回のデプロイが残した状態をリセットするための手動の介入が最初に必要かもしれません。このフィードバックサイクルは、ユニットテストを実行することと比較して非常に遅いです。

では、なぜ単にユニットテストを書かないのでしょうか?

理由1) ユニットテストできない側面でデータパイプラインは失敗する

データパイプラインにおいてユニットテストできる自己完結型のロジックは通常、限られています。ほとんどのコードはシステム間のグルーとダクトテープのようなもので、ほぼ全ての失敗は、システム間の不適切なインターフェースや予期しないデータがパイプラインに入った結果発生します。

システム間のインターフェースはユニットテストすることができません。なぜなら、これらのテストは孤立して実行することができないからです。外部システムをモック化することは可能ですが、これはデータエンジニアが理解する範囲でしか外部システムの動作を保証できません。

データを生成するシステムは多くの場合、一貫性のある品質の高いデータを提供してくれません。データの予期せぬ内容や構造はパイプラインを壊すか、少なくとも不正確な結果を生み出します。低品質なデータソースに対抗するための一般的な戦略は、読み取り時にスキーマを検証することです。しかし、これはデータの誤った内容や微妙な「データバグ」を防ぐことはできません。例えば、時系列は夏時間を正しく扱っていますか?期待されるパターンに適合しない列内の文字列はありますか?測定値を表す列は実際に意味のある値ですか?これらの質問はいずれもユニットテストできるパイプラインロジックに関連していません。

理由2) ユニットテストはパイプラインロジックよりも複雑

ユニットテストが書けたとしても、テストコードはパイプラインロジックよりも複雑です。なぜなら、それは開発者が代表的なテストデータ、さらには期待される出力データを作成しなければならないからです。

さらに、テスト目的である「この関数は意図した通りに機能しますか?」という問いから「このテストデータは私の本物のデータを適切に代表していますか?」という問いに関心が変わってしまいます。一般的なユニットテストは理想的には入力パラメータの良い部分集合をカバーするべきですが、データセットを変換する関数では例えばデータフレームで入力が表現され、パラメータ空間は非常に大きなものとなってしまいます。

結論:パイプラインの開発は遅い

データパイプラインに対する信頼性のあるフィードバックを得る最善の方法は、それをデプロイして実行することです。これはローカルでユニットテストを実行するより遅く、フィードバックを得るのに時間がかかります。その結果、パイプラインの開発、特にデバッグの段階では、面倒くさく遅いということになります。

全体のパイプラインを実行するよりも早い統合テストを考えることもできますが、ローカル環境では関連するソースシステムへの直接アクセスを持っておらず、このテストはパイプラインと同じ環境でしか実行することができず、デプロイを必要とします。これは高速なフィードバックを得るためのテストを書くという主旨を大幅に逸脱します。

「データ契約(Data Contracts)」は今、無謀なデータ生成者への対処法として大流行しています。パイプラインに入るデータに対して信頼を持つことができれば、パイプラインの開発から多くの不確定性を取り除き、壊れにくくできます。しかしデータ生成者にはこれを遵守するインセンティブはなく、この契約を適用することは難しいようです。

さらに、組織は公開APIからデータを引き出して利用したいと思うでしょう。外部パーティとデータ契約を交渉する場合、幸運を祈るしかありません。

パイプライン開発は並列化できない

データパイプラインは、フィードバックサイクルが長くて開発が遅い、単一のユーザーストーリーであることを見てきました。

パイプラインは複数のタスクで構成されているため、一部のマネージャーはプロセスをスピードアップするために、それらを複数の開発者で分担させようとします。残念ながらこれはうまくいきません。パイプライン内でデータを処理するタスクは順序があります。第二段階を構築するためには、第一段階の出力が安定していなければなりません。第二段階を構築することで得られた洞察は、第一段階の改善にフィードバックされます。したがって、パイプラインは特定の開発者が反復して機能改善していかなければなりません。

一部のマネージャーは、これは単にパイプラインが最初から十分に計画されていないのだと反論します。最初にデータがあり、最後に出力されるべきデータは明確です。それなら中間で何を構築する必要があるかは事前に計画できるのではないでしょうか?

データソースの特性が事前に分かっていない限り、パイプライン全体を最初から計画することはできません。契約や文書化がない場合、データエンジニアはデータの特性を発見するために試行錯誤するしかありません。この発見プロセスはパイプラインのアーキテクチャを形成します。ある意味でこれはアジャイルです。ただし、ビジネスステークホルダーが考えるアジャイルの方法とは異なります。

結論と推奨事項

データパイプラインはソフトウェアですが、ソフトウェア製品ではありません。これは、例えるなら顧客が要求した車を製造するための工場です。目的のための手段であり、取り扱いが難しいデータソースから簡単に利用可能なデータセットを作成するための自動化レシピであり、相互に通信するように設計されていないシステム間のダクトテープであり、醜く壊れやすくて高価な解決策です。

データは本質的にソフトウェアとは異なります。これらの違いを認識せず、ソフトウェアチームで機能したからといってデータチームでアジャイルプロセスを強制することは、逆効果になるだけです。

データチームを成功させ、生産性を高めるために何ができるでしょうか?

  • データパイプラインプロジェクトでは、軽量なウォーターフォール(通常、ソフトウェアエンジニアリングでは悪と考えられている)が避けられないと認識してください。開発を始める前に、望ましいデータセットについての要件を明確にし、データ生成者との知識を共有してソースシステムに接続するための多くの会話を顧客と行う必要があります。顧客とデータ生成者の両方が解決策に同意するまで、あまりパイプライン開発に時間を費やさないでください。パイプラインが一度リリースされると、変更費用が高くなってしまいます。
  • データエンジニアにデータソースで実験する時間を与えてください。それまではデータセットが利用可能になる時期についての見積もりは間違っていることを認識してください。
  • パイプラインを複数の開発者に分割しないでください。代わりに、2人以上の開発者が同時にパイプラインに取り組むことを許可してください。ペアプログラミング・エクストリームプログラミング・グループプログラミングとトランクベースの開発を行い、gitブランチの地獄・プルリクエスト・コードレビューを避けることで最大の生産性を確保します。常に二人がかりでコードを検査することで、早期に問題を発見できます。これは特に、フィードバックループが遅いパイプライン開発では特に価値があります。