データ基盤の理想と現実 〜数百店舗のPOSデータと戦った話〜
登場人物
のびしろ太郎 ── データ収集プロジェクトの現場を任された若手エンジニア。勢いと楽観で突き進み、だいたい痛い目を見る。Laravelが好き。成功体験に引きずられがち。
現場キリ子 ── 太郎の先輩社員。冷静で鋭い観察眼を持つ。修羅場をいくつもくぐり抜けてきた歴戦のエンジニア。ハイボールに切り替えるタイミングが絶妙。
──ある日の退勤後。二人はプロジェクトの振り返りを兼ねて、駅前の居酒屋にいた。
「とりあえず生で」。太郎が言い終わる前に、キリ子はすでにメニューを開いていた。「枝豆と、たこわさ」。手慣れた注文。二人がこの店に来るのは、もう何度目だろう。カウンターの奥では店主が黙々と焼き鳥を焼いていて、炭の匂いが店内に漂っている。金曜の夜、サラリーマンたちの喧騒がBGMのように響く中、生ビールのジョッキが二つ、テーブルに置かれた。
プロローグ:回想 ── 太郎の入社1年目
──話は少し遡る。
太郎:あ、本題に入る前にちょっとだけ前置きいいすか。このプロジェクトの前の話。
キリ子:どうぞ。
太郎:僕ね、このプロジェクトにアサインされる前は、社内向けの小さなWebアプリを作ってたんですよ。LaravelとRDSで。ユーザー数50人くらいの業務管理ツール。
キリ子:ふーん。
太郎:それがめちゃくちゃうまくいったんです。Eloquentでモデル定義して、リレーション張って、Bladeでフロント書いて。3ヶ月で完成して、現場からも「使いやすい」って好評で。上司にも「いい仕事したね」って言われて。
キリ子:新人にしては上出来だったのね。
太郎:いや、もう天狗ですよ。「俺、Laravelの使い方わかってきたな」って。Eloquentのリレーション定義とか、マイグレーションファイルの書き方とか、全部手に馴染んできて。**「この道具があれば、なんでもできる」**って本気で思ってた。
キリ子:……それで「この成功体験を、次のプロジェクトでも」って思った。
太郎:……はい。50人向けの小さなアプリでうまくいった方法が、数百店舗のPOSデータ統合でもうまくいくと思ってしまった。スケールの違いを完全に見誤った。 自転車でうまく走れたから、次は自転車でF1に出ようとしたようなもんです。
キリ子:(枝豆をつまみながら)……まあ、誰しも最初はそういう時期があるわよ。問題は、そこから何を学ぶかだけど。
太郎:手厳しいっすね。
キリ子:事実を述べてるだけよ。……で、本題に入りましょうか。
第1話:「RDSにぶち込めばいけるやろ」
キリ子:改めて聞きたいんだけど、あのプロジェクトの最初って、どんな感じだったの?私が合流したのは途中からだったから、伝説の「ORM時代」のことは噂でしか知らないのよね。
太郎:ああ、あれっすか。聞きます? 長くなりますよ。
キリ子:いいわよ。今日は時間あるし。
──太郎はジョッキの生ビールを一口飲んで、少し遠い目をした。
太郎:最初に与えられたミッションは「グループ企業が全国に展開する数百店舗のPOSデータを統合したい」ってやつだったんすよ。POSデータっていうのは、まあレジで商品をピッてやるたびに記録される販売データですね。何が、いつ、何個売れたか。それが数百店舗分ある。
キリ子:それで?
太郎:で、それを聞いたときに僕が最初に考えたのが――
キリ子:まさか。
太郎:「RDSにぶち込めばいけるやろ」 です。
キリ子:……やっぱり。
太郎:いやいや、当時の僕なりにちゃんと考えたんすよ! 各店舗のPOSデータベースって、約100テーブルあるんです。で、まずその100テーブルの関連を全部洗い出して、カラム名の統一方針を決めて、LaravelのEloquent ORMでモデルとして全部定義したんです。
キリ子:Eloquent? データ基盤に?
太郎:はい。ORMってデータベースのテーブルをPHPのオブジェクトとして扱える仕組みじゃないですか。Laravelなら使い慣れてるし、さっき話した50人向けアプリの成功体験もあるし。関連性さえ定義しておけば、各店舗から送られてくる差分のJSONデータをモデルに投げつけるだけで――
- 正規化されていないテーブルデータが、きちんとテーブルごとに分かれる
- サロゲートキー(データベースが自動的に振る連番のID)が付与される
- カラム名もいい感じにリネームされる
- Amazon RDS(AWSが提供するマネージドなデータベース)にUPSERT / DELETEされていく
太郎:完璧じゃないですか? スライドにアーキテクチャ図も描いたんすよ。矢印がきれいに並んで、データの流れが一目瞭然で。上司に見せたら「おお、いいね!」って。あの瞬間の僕のドヤ顔、思い出すだけで顔から火が出ますけど。
キリ子:(箸を置いて)……ちょっと待って。整理させて。100テーブルのリレーションをEloquentで定義して、差分JSONを投げてUPSERT/DELETEまでやる。それってつまり……。
太郎:はい?
キリ子:ORMにETLエンジンの役割を全部背負わせたってこと?
太郎:そうっす。「JSONを投げるだけで、データの変換から格納まで全自動」。 我ながら賢い設計だなーって。
キリ子:それ、包丁でネジを回そうとしてるようなものよ。切れるかもしれないけど、ネジは回らない。道具にはそれぞれ適切な用途があるの。ORMはアプリケーションがデータベースと会話するための道具であって、大量データの変換・加工のために作られたものじゃない。
太郎:……今ならわかります。
キリ子:(生ビールを一口飲んで)続けて。
キリ子のテクニカルノート Vol.1「ORM・ETL・RDS・UPSERT ── 道具は正しく使いなさい」
ORM(Object-Relational Mapping) っていうのは、データベースのテーブルをプログラムのオブジェクト(かたまり)として扱えるようにする仕組みのこと。たとえばLaravelのEloquentっていうORMを使えば、SQLを直接書かなくても User::find(1) みたいにPHPのコードでデータを取り出せる。Webアプリケーション開発ではすごく便利な道具よ。
ETL は「Extract(抽出)→ Transform(変換)→ Load(格納)」の略。いろんな場所からデータを集めて、きれいに整えて、保管先に入れる一連の流れのこと。データ基盤の世界では専用のETLツール(AWS GlueやDatabricksなど)が存在するの。
RDS(Relational Database Service) はAWSが提供するマネージドデータベースサービス。PostgreSQLやMySQLなどのデータベースを、サーバーの管理なしで使える。Webアプリのバックエンドとしては優秀だけど、大量データの分析用途には向いていないわ。
UPSERT は「UPDATE(更新)+ INSERT(挿入)」を合わせた造語。データがすでにあれば更新し、なければ新規挿入する処理のこと。差分データを取り込むときによく使うわ。
太郎くんがやったのは、Webアプリ向けのORMに、データ基盤向けのETL処理を全部やらせたってこと。包丁は料理には最高の道具だけど、ネジを回すのには使わない。道具にはそれぞれ得意分野がある。 甘いわね、そんなことも知らずにプロジェクト始めるなんて。でも、失敗から学んだなら許してあげる。一発だけよ。
第2話:息をするように壊れる生データたち
太郎:で、意気揚々とデータを流し込み始めたんですけど。その前にまず前提を話させてください。収集元の店舗システム、仕様書がメンテナンスされてないんですよ。
キリ子:……え?
太郎:100テーブルあるシステムの仕様を支えていたのは、たった一人の超ベテランエンジニア。歴戦の神みたいな人で、その人に聞けばなんでもわかる。逆に言えば、その人しかわからない。 仕様書の代わりに、生きた人間が一人仕様書として存在していたんです。
キリ子:属人性の極み。
太郎:しかもその人、めちゃくちゃ忙しいんですよ。質問をSlackで投げると、返事が来るのが早くて翌日、遅いと1週間。でも返事が来ると完璧な回答なんです。「このテーブルは2015年の改修で追加されたもので、元々はこういう意図で……」って、もう考古学の論文みたいな精度で。
キリ子:生きたWikiね。ただしレスポンスタイムがひどい。
太郎:人間にSLA求めるわけにもいかないですし。しかもね、後から知ったんですけど、その神ですら元々の開発者じゃなかったんです。初期に作った人はもういなくて、引き継いだコードから仕様を必死に紐解いていただけだった。
キリ子:……つまり、正確な仕様はこの世に存在しない。
太郎:そういうことです。考古学の発掘現場みたいなもんですよ。地層を一枚一枚めくっていくと、過去の開発者の思考の化石が出てくるんです。ただし解読不能。そのシステムのデータを集めるっていうのが、このプロジェクトでした。……で、そんな状態のデータを流し込み始めたらどうなったか。
キリ子:崩壊したんでしょ。
太郎:まあ、そうなんですけど……聞いてくださいよ! まずスキーマの問題。一応、命名規則はあったんです。カラム名自体はちゃんと規則に沿ってる。
キリ子:じゃあ何が問題だったの?
太郎:カラムの中身が、名前と違う用途で使われてるんすよ。あっちの店舗ではこのカラムを本来の用途で使ってるけど、そっちの店舗では「使ってないカラムだから」って別の用途に流用してる。それが常態化してたんです。
キリ子:つまり、カラム名だけ見たら正しく見えるのに、中のデータが嘘をついてるってこと。
太郎:そうっす。あるとき売上レポートの数字がどう見てもおかしくて。合計がマイナスになるんですよ。売上なのに。半日かけて調べたら、ある店舗が sales_amount に返品金額をマイナスで入れてた……んじゃなくて、全然関係ない「棚卸し差異」を入れてたんです。実在庫と帳簿在庫のズレ。棚減りはマイナスで記録するから、合計がマイナスになってた。
キリ子:……在庫管理の数字を売上カラムに?
太郎:「在庫の差異を記録する場所がなくて、使ってないカラムだからいいかなって」。現場の回答です。名前を信じてマッピングしたら、見た目は通るけど中身がめちゃくちゃになる。 これが一番厄介でした。名前がバラバラなら気づけるんですよ。名前が正しいのに中身が違うから、発見が遅れる。
キリ子:……それは怖いわね。集計ロジックが全部狂うじゃない。
太郎:狂いました。Eloquentのモデルって、カラムの中身が正しい前提でキャストやバリデーションをかけるじゃないですか。でも実際には、数値のはずのカラムに想定外の値が入ってたり、NULLの入り方も店舗ごとにバラバラで、集計するたびに結果が合わない。
キリ子:他には?
太郎:謎のレコードっすね。金額がマイナスの売上、日付が1970年1月1日のトランザクション、商品コードが「TEST」のレコード。現場に問い合わせたら「ああ、それはたぶんテストデータですね」「昔の担当者が入れたやつかもしれません」って。
キリ子:「たぶん」。
太郎:「たぶん」じゃないんですよ。 こっちは「たぶん」のデータを本番のデータ基盤に入れるかどうかの判断を迫られてるんです。「たぶんテストデータ」を除外して、もし本物の売上データだったら? 逆に含めて、ゴミが混ざったまま経営判断に使われたら? 「たぶん」が一番怖い。
キリ子:……それは、データエンジニアなら誰でも共感する悪夢ね。
太郎:あと地味にきつかったのが、他の店舗のデータが混ざってるパターン。店舗Aのデータを取り込んだはずなのに、なぜか店舗Bの売上レコードが紛れ込んでるんです。
キリ子:なぜそうなるの?
太郎:原因は主に2つで。一つは、新店舗のセットアップ時に既存店舗のDBをコピーして流用してて、元のデータを消し忘れてるパターン。もう一つは、ある店舗が閉店して別の店舗に変わるときに、前の店舗のデータを**「バックアップだから」って謎の理由でそのまま残してた**パターン。どちらもデータ上は同じテーブルに入ってるから、店舗コードで絞り込まないと気づけない。
キリ子:それは……仕組みの問題と運用の問題が絡み合ってるわね。
太郎:ファイル転送で偶発的にCSVが壊れるのはまだ対処しやすいんです。リトライすればいいから。でもDB自体に最初から混ざってるのは、正解データがどれか判断できないから本当に厄介で。
キリ子:存在しないデータを求められるやつもあったんでしょ?
太郎:あった! 「閉店した店舗の過去データも分析に含めたいんですが」って言われて。でも閉店と同時にサーバーも撤去されてて、物理的にデータが存在しないんです。ないものはない。
キリ子:「データはどこかにあるはずです」って言う人、いるのよね。
太郎:「クラウドに残ってませんか?」って聞かれたこともあります。オンプレですよ。サーバーごと撤去されてるのに、どこのクラウドに残るんですか。
キリ子:そこは同情するわ。
──太郎が枝豆をつまむ。キリ子は二杯目の生ビールを注文した。隣の席では、営業マンらしき男たちが「今月の数字が」と愚痴っている。
キリ子:……彼らは「数字が合わない」って言ってるけど、その数字がどこから来てるか知ったら卒倒するわよね。
太郎:やめてくださいよ。他人事じゃないんで。
キリ子のテクニカルノート Vol.2「スキーマ・属人性・データ品質 ── 信じられるのは自分の目だけ」
スキーマ(Schema) っていうのは、データベースの「設計図」のこと。テーブルにどんなカラム(列)があって、それぞれどんな型(数字なのか文字列なのか)で、どういうルールで使うのかを定義したもの。スキーマが揃っていれば、データの扱い方が統一される。逆にスキーマがバラバラだと、同じ「売上金額」という名前のカラムでも、税込みだったり税抜きだったり、意味が違ってくる。
属人性(ぞくじんせい) は、特定の人しかわからない・できない状態のこと。「あの人に聞けば全部わかる」は一見頼もしいけど、その人がいなくなった瞬間に全てが崩壊する。ドキュメント(設計書や仕様書)がないシステムは、実質的に「人間が仕様書」。これは時限爆弾と同じよ。
データ品質 の問題は、大きく分けてこんなパターンがあるわ。
| パターン | 具体例 | 怖さ |
|---|---|---|
| カラム名と中身の不一致 |
sales_amount に棚卸し差異が入っている |
見た目は正常、中身がウソ。発見が遅れる |
| テストデータの混入 | 商品コード「TEST」、金額マイナス | 集計結果を静かに狂わせる |
| 他店舗データの混入 | DB流用時の消し忘れ、店舗変更時の残置 | 同じテーブルに混在し、店舗コードで絞らないと気づけない |
| データの欠損 | 閉店店舗のデータ消失 | ないものはない。物理的に復旧不可 |
| ファイル破損 | 転送途中で切れたCSV | リトライで対処可能だが、原因が回線側だと再発する |
甘いわね。「データは正しいはず」なんて思い込み、一番危険よ。データは嘘をつく。しかも、見た目は正直そうな顔をしてね。 信じられるのは、自分の目で確認した事実だけ。それがデータエンジニアの鉄則よ。
第3話:ネ申Excelと固定IPの罠
太郎:あとね、キリ子さん。データの差分送信って、各店舗端末の固定IPで認証してたんですよ。
キリ子:固定IP認証ね。で、そのIPの管理は?
太郎:ネ申Excel。
キリ子:…………。
太郎:セル結合と色分けと手入力コメントで構成された、人類の叡智の対極に位置するスプレッドシートに、数百店舗分のIP情報が埋もれてました。検索もフィルタもまともに機能しない。
キリ子:……A1にタイトルが書いてあって、B3あたりからデータが始まるやつ?
太郎:よくわかりますね。で、問題はこのExcelがまったくメンテされてないこと。最終更新日を見たら1年以上前。その間に閉店した店舗はそのまま残ってるし、新しくオープンした店舗は載ってない。IPアドレスが変わった店舗も反映されてない。
キリ子:つまり、現状と一致してるかどうか誰も保証できない台帳ってこと。
太郎:そうっす。しかもセル結合が激しくて検索もフィルタもまともに効かないから、1行ずつ目視で確認するしかない。数百行を。
キリ子:……もう一杯頼んでいい?
太郎:手で転記しましたよ。全店舗分、手作業です。 あのExcelから1行ずつ読み取って、IPアドレスを抜き出して、設定ファイルに書き写す。コピペすら危険なんですよ。セル結合のせいで、コピーすると隣のセルの値まで巻き込まれるから。
キリ子:……。
太郎:しかも「全店舗は固定IP」って前提だったはずが、例外的にIP固定できない店舗が出てきて。固定IPを前提に設計した認証の仕組みが根底から揺らぐ。例外のための例外処理。カオスの上にカオスが積み重なっていく……。
キリ子:……IPの話で思い出したけど、回線の話もあったわよね。
太郎:ああ、あれね! 最初はセキュリティの観点から、データ転送は社内ネットワーク経由でやってたんですよ。で、途中でネットワーク構成を詳しく調べたら、とんでもないことがわかった。
キリ子:聞いてるだけで嫌な予感がするわ。
太郎:各店舗の社内回線が100Mbpsのベストエフォート。まあこれはまだいい。問題はここから。回線契約の都合で、全店舗の過半数がデータセンターのルーター1台にVPNで集約されてて、そこから社内ネットワークへの出口が100Mbpsベストエフォート1本。
キリ子:……ちょっと待って。数百店舗のトラフィックが100Mbps1本に集約されてるの?
太郎:そうなんです。 数百店舗で100Mbps共有。計算してみたんすよ。100Mbpsを仮に300店舗で共有するとして、1店舗あたり約0.33Mbps。330kbps。
キリ子:ISDNかな?
太郎:令和ですよキリ子さん! 令和のISDN。新しいジャンルですよ。そこにデータの差分送信を流し込んだらどうなるか。
キリ子:すぐ溢れて死ぬ。
太郎:すぐ溢れて死にます。 転送途中でCSVが壊れて届くことがあるって話したじゃないですか。あれ、回線の問題も原因の一つだったんすよ。
キリ子:で、どうしたの?
太郎:認証の通信だけ社内ネットワーク経由に残して、実際のデータ転送は暗号化通信でインターネット側から送る方式に切り替えました。社内回線のボトルネックを避けつつ、セキュリティも担保する形です。
キリ子:よくそれ見つけて切り替えたわね。
太郎:見つけたというか、死にまくったから調べたら判明したというか……。発見のきっかけは、常に障害。 データエンジニアあるあるかもしれません。
──焼き鳥の追加注文が来た。ねぎまと砂肝、塩で。太郎はビールをおかわりした。
キリ子のテクニカルノート Vol.3「固定IP認証・VPN・ベストエフォート ── ネットワークは裏切る」
固定IP認証 は、「このIPアドレスから来た通信だけ許可する」というセキュリティの仕組み。IPアドレスはインターネット上の住所みたいなもので、固定IPなら「いつも同じ住所」。だから「この住所からの手紙だけ受け取る」ってルールが作れる。ただし、引っ越し(IP変更)が起きると一切通信できなくなるリスクがあるわ。
VPN(Virtual Private Network) は、インターネット上に「仮想の専用トンネル」を作る技術。公共のネットワークを使いながら、あたかも社内ネットワークに直接つながっているかのように通信できる。セキュリティは高いけど、トンネルの出口が1本だと、全員がそこに殺到してボトルネック(渋滞ポイント)になるの。
ベストエフォート は「最大限努力します(でも保証はしません)」という意味の通信契約。「100Mbpsベストエフォート」は「最大100Mbps出せるけど、実際にどれくらい出るかはそのときの混み具合次第」ってこと。家庭用の光回線もほとんどがこの方式よ。
この話のポイントは、数百店舗のVPNトラフィックが100Mbpsベストエフォート1本に集約されていたこと。単純計算で1店舗あたりの帯域は1Mbps以下。テキストメールならまだしも、大量のデータを流すには完全に力不足。しかもベストエフォートだから、混雑時はさらに遅くなる。
ネットワーク設計は「理論上の最大速度」じゃなくて「最悪のケースで何Mbps出るか」で考えなさい。甘いわね、回線の太さを確認せずにデータを流し込むなんて。 水道管の太さを確認せずにプールの水を流し込むようなものよ。
第4話:膨れ上がるカオスの塊
キリ子:それで、そういう問題を一個一個潰していったわけよね。
太郎:そうです。ある店舗のデータで謎のレコードが見つかる。取り込み処理にif文を足す。別の店舗で別パターンの謎データが出てくる。またif文を足す。スキーマが違う店舗に対応するために変換ロジックを追加する。例外処理を追加する。さらに例外の例外が見つかる。
キリ子:if文の地層ね。
太郎:最初は50行くらいの取り込みスクリプトだったんですよ。それが100行になり、500行になり、気がつけば3000行の単一ファイルになってた。しかもif文のネストが最大で12段。
キリ子:12段。それはもうコードじゃなくて迷路ね。
太郎:自分で書いたのに、3日前の自分が何を考えてこのif文を入れたのかわからないんですよ。コメントも「// 店舗Aの特殊対応」とか「// なぜか動く、触るな」とか。
キリ子:「触るな」はフラグよ。触るなと書いてあるコードには、必ず触らなきゃいけない日が来る。
太郎:来ました。しっかり来ました。 「触るな」のif文をいじったら、5店舗分のデータ取り込みが壊れました。
キリ子:……。
太郎:気がつけば、取り込み処理は非常にドロドロかつ重たい変換ロジックのカオス集合体と化していました。 100テーブルの関連を定義して「JSONを投げるだけで全自動」のはずだった美しい設計が、数百店舗分の特殊対応を吸収し続けた結果、誰も全体像を把握できない巨大な変換エンジンに。
キリ子:新しい店舗を足すたびに……。
太郎:「今度は何が壊れるんだ」って身構える。修正するたびに別の店舗の取り込みが壊れないか祈る。デプロイボタンを押すとき、毎回手が震えてましたよ。
キリ子:物理DELETEの話も聞いてるわよ。
太郎:あれはマジで鬼門でしたね……。ORMでリレーションを定義してるってことは、削除も関連テーブルに連鎖するじゃないですか。ある店舗のデータ差分で削除が発生すると、100テーブルにまたがる関連レコードを根こそぎ消しに行く。一度に大量のテーブルにDELETEが走って、ディスクI/Oがとんでもないことになる。
キリ子:DBが詰まるのよね。
太郎:詰まります。ある金曜の夜ですよ。もう帰ろうと思ってコート着てたときに、Slackに通知が来たんです。「RDSのCPU使用率が90%を超えました」。見たら、ある店舗の差分データで大量削除が走ってて、100テーブルにカスケードDELETEが連鎖してた。
キリ子:金曜の夜。
太郎:結局、DELETE処理が終わるまでCloudWatchのメトリクスをずっと眺めてましたね。CPU使用率のグラフがゆっくり下がっていくのを見守って。他の店舗の取り込みも巻き添えで遅延して。「JSONを投げるだけ」の裏側で、RDSは毎回悲鳴を上げてました。
キリ子:……。
太郎:……それでもやるしかなかったんで、一つ一つ対応して、一つ一つ動作確認して、愚直に前に進むしか。
キリ子:(静かにうなずく)
──太郎が焼き鳥の追加を注文する。「塩で」と言ってから、「やっぱりタレで」と変えた。
キリ子:優柔不断ね。
太郎:仕事では一度決めたら簡単に変えられないじゃないですか。せめてプライベートでは迷う自由を……。
キリ子:(笑わずに)次の話、いく?
キリ子のテクニカルノート Vol.4「カスケードDELETE・技術的負債 ── 借金は膨れ上がる」
カスケードDELETE は、あるテーブルのレコードを削除すると、関連する別テーブルのレコードも芋づる式に削除される仕組みのこと。「注文」を消したら、その注文に紐づく「注文明細」も自動で消える、みたいなイメージ。少量なら便利だけど、100テーブルにまたがるリレーションで大量削除が走ると、データベースが悲鳴を上げるわ。ディスクI/O(データの読み書き)が爆発して、他の処理も巻き添えで止まる。
技術的負債(Technical Debt) は、「今は動くけど、将来ツケが回ってくるコード」のこと。お金の借金と同じで、少額なら問題ないけど、返さずに放置すると利息(追加の修正コスト)がどんどん膨れ上がる。
太郎くんのケースでは、店舗ごとの例外対応を if文 で追加し続けた結果、コードが巨大な「if文の地層」になった。最初は50行だったコードが3000行に膨れ上がるのは、まさに技術的負債が利息ごと積み重なった状態ね。
技術的負債が怖いのは、目に見えないこと。機能は動いている。テストも通る(テストがあればの話だけど)。でも中身はカオスで、新しい変更を入れるたびに「別の場所が壊れないか」祈るしかない。
対策はシンプル。定期的にリファクタリング(コードの整理整頓)をすること。 「動いてるから触らない」は借金を無視してるのと同じ。甘いわね。借金は早めに返しなさい。利息が膨れる前にね。
第5話:WinMergeとの死闘
太郎:そしてね、収集の最終関門が待ってたんですよ。
キリ子:整合性の担保でしょ。
太郎:はい。「集めたデータが、現場の生DBと本当に完全一致しているか」 を証明しなきゃいけない。数百店舗のPOSデータですよ。1件でもズレがあれば売上集計が狂って、経営判断を誤らせる可能性がある。品質事故は絶対に許されない。
キリ子:自動テストは書かなかったの?
太郎:もっとスマートな方法があったかもしれません。でも、スキーマも揃わず、謎のレコードが混入し、ファイルが壊れて届くようなデータですよ? 自動化の前提となる「データの一貫性」そのものが信用できない状態で、最後に信じられるのは自分の目だけだと思ったんです。
キリ子:で、何を使ったの?
太郎:WinMerge。
キリ子:テキスト差分比較ツールで、数百店舗分のデータを?
太郎:狂気でしょ? やったことはシンプルです。
- 数百店舗分の生データベースから、巨大なバックアップをエクスポートする
- 収集したデータを、店舗の生DBと同じ形式に逆算成形する
- その2つをWinMergeで突き合わせる
キリ子:……力技にもほどがある。
太郎:この作業、まるまる1ヶ月やったんです。毎日、朝イチでWinMergeを開くところから一日が始まる。
キリ子:RPGの最終ダンジョンね。毎朝入り口に立つところから。
太郎:しかもセーブポイントがないやつ。開いた瞬間、画面が真っ赤に染まりましたよ。差分、差分、差分。スクロールしても、スクロールしても、赤い行が終わらない。
キリ子:原因は?
太郎:もう多岐にわたりました。正直、全部は覚えてないです。ただ、一つ潰したと思ったら別の種類の差分が出てくる。「これで最後だろう」が何回あったか。 とにかく、データの世界って目に見えないところで微妙に食い違ってるものが山ほどあるんですよ。
太郎:1つ差分を見つけるたびに原因を特定して、変換スクリプトを直して、もう一度データを流し直して、もう一度WinMergeで突き合わせる。その繰り返し。何百回やったかわかりません。
キリ子:心折れなかった?
太郎:正直、2週目の終わりくらいで一度折れかけました。「これ永遠に終わらないんじゃないか」って。月曜の朝、WinMergeのアイコンをダブルクリックする手が止まるんですよ。あの赤い画面を見るのが怖くて。
キリ子:……。
太郎:でもね、差分の数が少しずつ減ってることに気づいたんです。最初の日は画面全体が赤だった。1週間後には赤い部分が半分になった。2週間後には、赤い行を数えられるようになった。「終わりがある」ってわかった瞬間、もう一度立ち上がれたんです。
──キリ子のグラスの結露が、静かにテーブルに流れ落ちた。店内の喧騒が、なぜか少し遠くに聞こえる。
太郎:で、ある日。いつものようにWinMerge開いて、覚悟決めて比較ボタン押したんです。
キリ子:……。
太郎:画面が表示されました。
(間)
太郎:真っ白だったんですよ。
キリ子:……!
太郎:……最初、バグかと思ったんです。表示が壊れてるのかと思って、別の店舗でも試した。白。また別の店舗。白。全部白い。
キリ子:……。
太郎:気がついたら椅子の背もたれに身体を預けて、天井見てました。何も考えられなくて。1ヶ月分の緊張が一気に解けて。
キリ子:……泣いたの?
太郎:泣いてないっすよ。……たぶん。いや……目が潤んだのは認めます。差分ゼロ。数百店舗分のデータが、1レコードの狂いもなく完全に一致してた。あの赤い海を渡り切ったんだって。WinMergeの真っ白な画面は、どんなダッシュボードよりも美しかったです。
キリ子:……いい話じゃない。
太郎:あの瞬間のスクリーンショット、今でもデスクトップに保存してます。お守りみたいに。
キリ子:(小さく微笑んで)……バカね。
太郎:こうして、数百店舗のPOSデータの収集は完了しました。 ……でもここからが本当の地獄の始まりだったんすけどね。
キリ子のテクニカルノート Vol.5「データ整合性・文字コード・改行コード ── 悪魔は細部に宿る」
データ整合性(Data Integrity) は、「データが正確で、矛盾がなく、信頼できる状態」のこと。数百店舗のPOSデータを集めて「このデータは正しい」と言い切るためには、元のデータと完全に一致していることを証明しなければならない。1件でもズレがあれば、売上集計が狂い、経営判断を誤らせる可能性がある。
データの突合で差分が出る原因は、本当に多岐にわたるの。文字コード、改行コード、数値の丸め誤差、NULLの扱い……挙げ始めたらキリがない。しかも厄介なのは、目に見えない差分が大半だということ。画面上は同じに見えるのに、バイナリレベルでは違う。こういうのが数百店舗分のデータに無数に紛れ込んでいるの。
甘いわね。「目に見えるデータ」だけ確認して安心するなんて。本当の敵は、目に見えない場所に潜んでいる。 こういう「見えない差分」を一つ残らず潰す根気こそが、データエンジニアの真価よ。
幕間:店長の一言
太郎:あ、でもね。一つだけ、あの時期に嬉しかったことがあるんです。
キリ子:何?
太郎:ある店舗の店長さんから、直接メールが来たんですよ。普段は現場の人から連絡が来ることなんてないんですけど。「今まで月末にならないと売上がわからなかったのに、毎日見られるようになって助かってます」って。
キリ子:……。
太郎:たった一通のメールなんですけど。あの1ヶ月のWinMerge地獄が、全部報われた気がして。データって、最終的には人のためにあるんだなって。机の上でアーキテクチャ図を描いてるだけじゃ、絶対にわからない感覚でした。
キリ子:……それは、大事な経験ね。
太郎:そのメール、スクショして保存してあります。WinMergeの白い画面と一緒に。
キリ子:……だからこそ、次の話が余計に辛いわよね。
太郎:……はい。
──気がつけば、テーブルには空のジョッキが4つ並んでいた。隣の席の営業マンたちはいつの間にかいなくなり、代わりに大学生らしきグループが賑やかに入ってきた。
「ハイボールに変えるわ」とキリ子が言った。話が重くなることを予感しているかのように。
第6話:大規模障害とウン十万円の請求書
キリ子:ここから先は私も現場にいたから覚えてるわよ。あの日のこと。
太郎:……ですよね。収集は完了した。RDSにはきちんとデータが入ってる。WinMergeで整合性も確認済み。さあ活用だ、と思って本番環境でアプリケーションからデータを使い始めた瞬間。
キリ子:RDSが死んだ。
太郎:あの日のこと、今でも覚えてます。午前10時。店舗が開店して、レジが動き始めた頃。最初は1店舗から「POSアプリケーションの反応が遅い」って問い合わせが来たんです。「まあ、ネットワークの一時的な問題かな」って楽観してたら、5分後には社内チャットが嵐になってて。
キリ子:「全店舗でレジが動きません」。あのメッセージが一斉に飛んできたときの空気、忘れないわよ。
太郎:CPU使用率が張り付いて、データベースが応答しなくなった。 数百店舗の売上データが詰まった巨大なRDSに、アプリケーションが一斉にクエリを投げて。しかもこのRDSは全店舗で共有してるから、障害の影響が全店舗に波及して――
キリ子:大規模障害。あのときは本当に修羅場だったわね。
太郎:僕のSlackの通知音が止まらなくなって。手が震えましたね、あれは。障害対応のミーティングが立ち上がって、偉い人たちがぞろぞろ入ってきて。あの部屋の空気の重さ、今でも夢に出ます。
キリ子:私が見てたのは、あなたの顔色がどんどん悪くなっていくところだったけどね。
太郎:で、正直に白状すると、この障害の最大の原因は、アーキテクチャ以前にまともな負荷テストをせずに本番投入したことでした。
キリ子:それは聞いた。
太郎:アホな判断です。でも当時は、あの泥沼の収集作業をようやく完遂した達成感と、「早く成果を見せなければ」って焦りの中で、テストっていう当たり前の工程を飛ばしてしまった。泥臭さを乗り越えた先で、基本中の基本を怠った。 一番の反省点です。
キリ子:WinMergeで1ヶ月かけて品質を追い込んだのに、負荷テストは飛ばした。……皮肉よね。
太郎:本当にそうです。データの品質には命を懸けたのに、システムの品質保証を怠った。品質への覚悟が、偏っていた。
キリ子:……そしてその後、請求書を見たのよね。
太郎:はい。障害対応が落ち着いた翌週、ふとAWSのコスト画面を開いたんです。何気なく。……そしたら目に飛び込んできた数字が。
キリ子:ウン十万?
太郎:数字を三度見しました。「桁、間違ってない?」って電卓まで引っ張り出して。でも間違ってなかった。月額ウン十万円。
キリ子:同期処理のスケールアップとS3のAPI呼び出しの積み重ねね。
太郎:そうっす。で、思い出してほしいんですけど、このデータ、まだ誰にも使われてないんですよ。 ただ収集してためているだけ。アプリから参照しようとしたら大規模障害を起こしたので。使ってもいないデータが保存されてるだけの仕組みに、月ウン十万円。
キリ子:まともに活用しようとしたら?
太郎:同期処理だけでRDSは性能的にほぼ限界。アプリケーションからの参照クエリが加わったら、さらにスケールアップが必要。ウン十万? ウン百万??? 天井が見えない。
キリ子:技術的な限界、大規模障害、ウン十万の請求書。
太郎:三つ揃って、RDS + Laravel方式に引導が渡されました。
キリ子のテクニカルノートVol.6「負荷テスト・スケールアップ ── 本番環境を舐めるな」
負荷テスト(Load Testing) は、システムに大量のアクセスやデータ処理を意図的に発生させて、「どこまで耐えられるか」を事前に確認するテストのこと。本番環境に近い条件で、想定されるピーク時の負荷をかけてみる。これをやらずに本番投入するのは、試乗せずに車を買うようなもの。……いえ、もっとひどいわね。ブレーキを確認せずに高速道路に乗るようなものよ。
スケールアップ は、サーバーのCPUやメモリを増強して性能を上げること。「もっと強いマシンに取り替える」イメージ。対義語は スケールアウト で、「マシンの台数を増やして負荷を分散する」方法。RDSのようなリレーショナルデータベースは基本的にスケールアップでしか対応できず、天井がある。しかもスケールアップすればするほど、料金は加速度的に上がる。
この話の教訓は2つ。
-
負荷テストは省略してはいけない。 どんなに収集作業が大変だったとしても、本番投入前のテストは「当たり前の工程」。焦りや達成感でこの工程を飛ばすと、もっと大きな代償を払うことになる。
-
「常時起動型」のコスト構造を理解すること。 RDSは使っていなくてもインスタンスが起動している限り課金される。データ基盤のように「常にアクセスするわけではない」用途では、使った分だけ課金されるサーバーレスな仕組みのほうが合理的よ。
甘いわね。「動いたから大丈夫」なんて、最も危険な思い込みよ。本番環境は、開発環境の100倍厳しい。 テストで見つかるバグは幸運。本番で見つかるバグは事故。覚えておきなさい。
第7話:アーキテクチャを根本から考え直す
キリ子:それで、私が呼ばれたのよね。「ちょっと根本的にまずいことになってる」って。
太郎:はい。藁にもすがる気持ちでした。
キリ子:正直に言うと、合流して最初にコードベースを見たとき、「これは……」って思ったわ。
太郎:3000行のif文の塊を?
キリ子:それもだけど。「// なぜか動く、触るな」「// 店舗Aの特殊対応(理由不明)」「// TODO: あとでちゃんとやる」が並んでるのを見たときは、さすがに目を閉じたわ。
太郎:……すいません。
キリ子:でもね。あなたが一人でここまで持ってきたこと自体は、すごいと思ったわよ。……言わなかったけど。
太郎:初めて聞きましたよそれ。
キリ子:今言ったからもういいでしょう。……で、あの失敗を経て見えた要件を整理してみて。
太郎:まず、使わなければ費用がかからないこと。データは安価なストレージにためておくだけのデータレイクに置く。RDBみたいに常時起動してる必要はない。「誰も使ってないのにウン十万」の二の舞は絶対に避ける。
キリ子:次は?
太郎:プロダクトごとに独立してデータを取り出すこと。費用も影響範囲もプロダクト単位で分離する。プロダクトAの障害がプロダクトBに波及しない。あの大規模障害の教訓です。RDS時代のアーキテクチャは、全員が同じ部屋に住んでるシェアハウスだったんです。一人が夜中に暴れたら全員起きる。新しい設計は、マンションに近い。共有部分はあるけど、各部屋は独立してる。
キリ子:いい比喩ね。最後は?
太郎:事業撤退時はその部分だけを抹殺できること。他のプロダクトやデータに一切影響を与えずに、きれいに切り離せる。
キリ子:いい要件ね。全部、痛い目を見た経験から出てきてる。机上の空論じゃない。で、候補は?
太郎:色々調べて、候補に上がったのがこのあたりです。
| 候補 | 特徴 |
|---|---|
| Athena + dbt | S3上のデータをSQLで分析。dbtでデータ変換を管理 |
| AWS Glue | AWSネイティブのETLサービス。S3との相性が良く、サーバーレスで使った分だけ課金 |
| Databricks | Apache Sparkベースの統合データプラットフォーム。メダリオンアーキテクチャとの親和性が高い |
| Snowflake | クラウドネイティブなデータウェアハウス。コンピュートとストレージが完全分離 |
キリ子:Athena + dbtは落ちたのよね。
太郎:はい。dbtはSQLベースの変換ツールとしては優秀なんですけど、今回のデータはカオスすぎて。Pythonでゴリゴリ書ける方が複雑な変換ロジックに柔軟に対応できるという判断です。あと、Athena……というかS3のスロットリング制限ですね。大量のリクエストを投げると制限に引っかかるので、数百店舗分のデータを日常的に処理する用途には厳しかった。
キリ子:Snowflakeも候補にはあったわよね。
太郎:はい。性能は申し分ないし、プロダクトとしては魅力的でした。ただ、イニシャルコストが高かった。それに、Snowflakeってデータウェアハウスとしてフル活用して初めてコストメリットが出る設計じゃないですか。ETLだけ乗せても旨味が薄い。うちの規模と用途だと、ちょっとオーバースペックだったんです。
キリ子:道具の性能だけじゃなくて、コスト構造が自分たちに合うかどうか。ウン十万の教訓が効いてるわね。
太郎:あれで懲りましたからね。結局、「Pythonでゴリゴリ書ける自由度」と「コスト面での現実的さ」がDatabricksを選んだ理由でした。あの3000行のif文は愚かだったけど、「複雑な変換ロジックをコードで書く必要がある」という本質は正しかった。 問題は道具の選び方だったんです。
キリ子:それは良い振り返りね。
太郎:データガバナンスの観点から、データは自社AWSアカウントのS3上に保持して、Databricksのコンピューティングリソースだけを借りる形にしました。使わなければコンピューティング費用がかからない。 データはS3に安価に眠らせておいて、必要なときだけクラスターを起動する。RDS時代の「常時課金」とは根本的に違う設計思想です。
キリ子:メダリオンアーキテクチャも採用したわね。
太郎:はい。データの変換処理を段階的にやる仕組みです。
- Bronze層(生データ): ソースシステムからそのまま取り込んだ生データ
- Silver層(クレンジング済み): 型変換、重複排除、バリデーションを通したデータ
- Gold層(ビジネス利用可能): 分析やレポーティングにすぐ使える形に加工されたデータ
プロダクトごとにGold層を分けることで、事業単位での独立性も実現できる。事業が撤退するなら、そのプロダクトのGold層だけを消せばいい。
キリ子:IaCとCI/CDも一緒に組んだわよね。
太郎:AWSリソースはCloudFormation、Databricks側はDatabricks Asset Bundles(DAB)とTerraformを組み合わせて、GitHub Actionsで自動デプロイ。コードをpushすればインフラからジョブ定義まで全部自動。
キリ子:RDSで痛い目を見た分、今度こそ隙のない設計ができたわね。
太郎:あの泥沼を知ってるからこそ、この設計には自信がありました。スライドに描いたアーキテクチャ図は本当に美しかったし、レビューでも好評で。
キリ子:美しいアーキテクチャ図ね。……でも、人間の問題は美しい図じゃ解決しないのよ。
太郎:……はい。
キリ子のテクニカルノート Vol.7「データレイク・メダリオンアーキテクチャ・IaC ── モダンな武器の使い方」
データレイク(Data Lake) は、あらゆる種類のデータを「そのままの形で」ためておく保管場所のこと。RDB(リレーショナルデータベース)が「きちんと整理された本棚」だとすれば、データレイクは「とりあえず全部放り込んでおく倉庫」。整理は後からやる。AWSだとS3(Simple Storage Service)という安価なストレージがよく使われるわ。
メダリオンアーキテクチャ は、データの変換処理を3段階に分けて管理する設計パターン。
| 層 | 役割 | 例え |
|---|---|---|
| Bronze(ブロンズ) | 生データそのまま | 市場で買ってきた野菜(土付き) |
| Silver(シルバー) | 洗浄・整形済み | 洗って皮をむいた野菜 |
| Gold(ゴールド) | すぐ使える状態 | 料理として盛り付けられた一品 |
この3層構造のメリットは、どこで問題が起きたか特定しやすいこと。Gold層のデータがおかしければ、Silver層まで遡って原因を調べられる。Bronze層には生データがそのまま残っているから、いつでもやり直しがきく。
IaC(Infrastructure as Code) は、サーバーやネットワークなどのインフラをコードで定義・管理する手法。手作業でポチポチ設定するのではなく、コードとして書いておけば、同じ環境を何度でも正確に再現できる。AWS CloudFormationやTerraformが代表的なツールよ。
甘いわね。道具がモダンになっても、使う人間がポンコツなら意味がない。でも、正しい道具を正しく選べるようになったのは成長の証。 失敗から学んだ武器選びは、机上の空論より100倍強い。……まあ、一発で正解を選べなかったのは減点だけどね。
幕間:キリ子の独白
──太郎がトイレに立った。キリ子は手元のハイボールのグラスを見つめながら、小さくつぶやいた。
キリ子:……まったく。あの3000行のif文を見たとき、「こんなの作り直しだ」って思ったわよ。当然。プロの目から見れば、あのコードは設計も何もあったもんじゃない。
(グラスの氷をからんと鳴らす)
キリ子:でもね。あの泥臭いコードの一行一行に、現場のリアルが詰まってた。「// 店舗Bだけ日付フォーマットが違う」「// 閉店済み店舗は除外」「// ここでNULLが来ると死ぬ」……。机の上で「こうあるべき」って描いた設計図には絶対に出てこない、生のデータとの格闘の痕跡。あれは、失敗の記録であると同時に、現場の真実のアーカイブでもあったのよ。
(ハイボールを一口)
キリ子:……作り直しは必要だった。でも、あの経験がなかったら、今の設計は生まれなかった。「使わなければ費用がかからない」なんて要件は、ウン十万の痛みを知らなきゃ出てこない。「プロダクトごとに独立」なんて思想は、全店舗を巻き込む大規模障害を経験しなきゃ腹落ちしない。
キリ子:……あの子は自分で思ってるより、ずっと成長してるわよ。言わないけど。
──太郎が戻ってくる。キリ子は何事もなかったように顔を上げた。
キリ子:遅いわよ。次の話。
太郎:すいません、レジ混んでて。次は……ああ、あの話っすね。AIドリーマーたち。
第8話:暴走する「AIドリーマー」を止めるお仕事
太郎:データ基盤がある程度形になってくると、社内の空気が変わり始めたんですよ。
キリ子:……ああ、あの流れね。
太郎:「データレイクができたら、AIで需要予測できますよね?」「機械学習で自動発注できるようになるんですよね?」みたいな声が、あちこちから聞こえてくるようになって。
キリ子:「データを集めれば、AIが魔法のように何でも解決してくれる」と本気で信じてる人たち。
太郎:僕は心の中で 「AIドリーマー」 って呼んでました。直接僕のところに来るわけじゃないんですけど、プロジェクト全体にそういう期待がじわじわ乗っかってくるのは感じてて。「データさえ集まればあとはAI」。でもそのデータがどれだけ汚いかには誰も興味がない。
キリ子:AIっていう高級な鍋を手に入れたら、いい料理ができると思ってるのよね。
太郎:そうそう! でも実際は、いい鍋を使っても食材の下ごしらえをサボったら、出来上がるのはまずい料理です。鍋が高級であればあるほど、食材の質が問われる。
キリ子:伝え聞いた話で一番すごかったのは?
太郎:役員プレゼンの前日に「明日の役員会で、AIによる需要予測のデモを見せたいんだけど」って言われたチームがあったらしくて。
キリ子:……前日?
太郎:「データはもうあるんでしょ? AIに食わせるだけでしょ?」って。「食わせるだけ」。その「食わせるだけ」のために、どれだけの工程を踏んでると思ってるんですか。しかもその材料が、パッケージに「豚肉」って書いてあるのに中身が鶏肉だったり、消費期限が1970年1月1日だったりするわけですよ。
キリ子:で、どうなったの?
太郎:データの現状をまとめた資料を出して、「今の品質ではAIに投入しても信頼できる結果は出ない」って丁寧に説明したらしいです。で、反応が……「わかりました。で、いつからAI使えるんですか?」
キリ子:……。
太郎:データ基盤やってると、こういう話はどこでも聞きますね。
キリ子:GIGO。Garbage In, Garbage Out。ゴミを入れたらゴミが出てくる。AI時代になっても変わらない鉄則よ。
太郎:むしろAIの性能が上がれば上がるほど、入力データの品質がモロに結果に効いてくる。データパイプラインを作るより、データの現状を理解してもらうほうがずっと大変だっていう。
キリ子:データエンジニアの仕事って、結局コードを書くことだけじゃないのよ。
太郎:今はそう思います。「データを正しく届けること」が仕事で、コードはそのための手段の一つでしかない。……当時はわかってなかったですけど。
キリ子:……悪くない成長ね。
キリ子のテクニカルノート Vol.8「GIGO・機械学習の前提条件 ── AIは魔法じゃない」
GIGO(Garbage In, Garbage Out) は、「ゴミを入れればゴミが出てくる」という意味の格言。コンピュータサイエンスの世界で昔から言われている鉄則よ。どんなに高性能なAIモデルでも、入力データが汚ければ、出力もゴミ。逆に言えば、AIの精度を決めるのは、アルゴリズムよりもデータの品質。
機械学習(Machine Learning) でまともな結果を得るための前提条件を整理するわね。
- 十分な量のデータがあること — 少量では統計的に意味のあるパターンを見つけられない
- データの品質が担保されていること — 欠損値、異常値、重複が適切に処理されている
-
データの意味が正しいこと —
sales_amountに売上以外のデータが入っていたら、需要予測なんてできるわけがない - 継続的にデータが更新されること — 古いデータで学習したモデルは、時間とともに精度が劣化する
「AIドリーマー」たちの問題は、上記の前提条件を理解せずに「データ + AI = 魔法」と信じてしまうこと。AIは魔法じゃない。AIは「きれいなデータから統計的なパターンを見つける道具」。道具の性能は、材料の品質で決まるの。
甘いわね。「AIで解決」なんて、一番楽な道に見えて、一番険しい道よ。本当に必要なのは、AIの前にデータをきれいにする地味な仕事。 そこを飛ばして成功した人なんて、あたしは見たことがないわ。
エピローグ
──二人の前のジョッキは、いつの間にか空になっていた。テーブルには焼き鳥の串と枝豆の殻が散らばり、時計は22時を回っていた。店内はラストオーダーの声が響き始めている。
キリ子:振り返ると、学んだことは何だった?
太郎:いくつかあります。まず、「正しいアプローチ」は失敗の先にしかないってこと。RDSにぶち込んでLaravelで触ろうとした日々は、一見無駄に見えます。でもあの失敗がなければ、データのカオスさを知らないまま机上の空論でアーキテクチャを描いてた。「使わなければ費用がかからない」「事業単位で切り離せる」って要件が腹落ちしたのは、RDS時代にその逆の地獄を体験したからです。
キリ子:失敗は、正解への最短ルート。 ……それは本当にそう思うわ。
太郎:あと、データ基盤は「2割の技術」と「8割の泥臭さ」でできているってこと。美しいアーキテクチャ図を描くのは楽しいし、最新の技術スタックを選定するのもワクワクする。でも実際に手を動かしてる時間の大半は、データのクレンジング、スキーマの統一、ステークホルダーとの対話、果てしない整合性確認に費やされてた。
キリ子:モダンなデータスタックは「泥臭い仕事を効率的にやるための道具」であって、泥臭い仕事そのものをなくしてくれるわけじゃない。
太郎:そうっす。そしてもう一つ。最後に品質を決めるのは「覚悟」だってこと。WinMergeで数百店舗分の差分を潰し続けたあの日々は、お世辞にもスマートとは言えません。でもあの経験を通じて、「このデータは絶対に正しい」と胸を張って言える基盤ができた。
キリ子:データエンジニアリングの本質って何だと思う?
太郎:「データを正しく届けること」 だと思います。カッコいい技術でもなく、華やかなダッシュボードでもなく、信頼できるデータを、必要な人に、正しい形で届けること。そのためには、時に泥臭い方法を選ぶ覚悟も必要で。
キリ子:……悪くない答えね。
太郎:キリ子さん、それ褒めてます?
キリ子:さあ。……もう一杯飲む?
太郎:飲みます。
(間)
キリ子:……ねえ、太郎くん。
太郎:はい?
キリ子:あなたがあのプロジェクトを投げ出さなかったこと。あの3000行のif文も、WinMergeの1ヶ月も、全部。……それは、誰にでもできることじゃないわよ。
太郎:……キリ子さんが合流してくれたからですよ。一人だったら、たぶんどこかで折れてた。
キリ子:(ハイボールのグラスを傾けながら、小さく笑って)……そういうとこよ。
ヨーコの百発百中テクニカルノート「データエンジニアって?」
データエンジニアは、近年急速に注目されている職種よ。でも、華やかなイメージとは裏腹に、仕事の実態はこの物語で描かれた通り、とにかく泥臭い。 その泥臭さを楽しめるかどうかが、この職種に向いているかの分かれ目ね。
データエンジニアのキャリアパスは、大きく分けてこんな方向性があるわ。
1. データプラットフォームエンジニア
データ基盤そのものを設計・構築・運用する道。この物語の太郎くんが歩んでいる方向ね。AWS、GCP、Azureなどのクラウドサービスと、Databricks、Snowflakeなどのデータプラットフォームの知識が求められる。
2. アナリティクスエンジニア
LookerやTableau、Power BIなどのBIツールを使って、ダッシュボードやレポートを作る専門家。ビジネス側が求める数字を、わかりやすい形で届ける。技術とビジネスの橋渡し役。
3. MLエンジニア(機械学習エンジニア)
機械学習モデルをプロダクションで動かすためのパイプラインを構築する。データエンジニアリングの経験があると、「データの品質がモデルの精度を決める」ことを身体で理解しているから強いわよ。
4. データマネジメント・ガバナンス
データの品質管理、セキュリティ、コンプライアンスを統括する役割。組織が大きくなるほど重要になる。
どの道を選ぶにしても、共通して大事なことが一つ。現場の泥臭さを知っていること。 きれいなアーキテクチャ図を描くだけのエンジニアは、信用されない。手を動かして、データの汚さに向き合った経験がある人こそ、本当に頼りになるエンジニアよ。
甘いわね、なんて今回は言わない。ここまで読んだあなたは、もうデータの泥臭さを知っている。あとは一歩踏み出すだけ。——一発必中で仕留めなさい。
──居酒屋の喧騒は変わらない。サラリーマンたちの笑い声、グラスのぶつかる音。その片隅で、二人は黙ってビールとハイボールを飲んでいた。
──データ基盤は、まだ完成していない。たぶん、永遠に完成しない。
──でも、それでいい。データを正しく届けるために、明日もまた泥臭い仕事が待っている。
これからデータ基盤の構築に挑むあなたへ。
アーキテクチャ図を美しく書く前に、まずは一度、実際の生データ・現場のネットワーク構成を覗いてみてください。きっと、想像もしなかった現実がそこにあります。
Excelのセル結合に絶望し、WinMergeの赤い画面に心が折れそうになる日が来るかもしれません。
でも大丈夫。その泥臭さの先に、WinMergeの画面が真っ白になる瞬間が待っています。
あなたが届けるデータで、誰かが正しい判断をする。その価値は、どんなに美しいアーキテクチャ図よりも重い。
だから、泥臭くいきましょう。
その先にある達成感は、何ものにも代えがたいものですから。
📜 この物語から学ぶ10の教訓
これらの掟は、太郎くんとキリ子さんの実体験から生まれたものである。
血と汗と生ビールの結晶として、ここに記す。
第一条:ORMはORMの仕事をさせろ。包丁でネジは回すな。
道具には得意分野がある。Webアプリ用のORMにデータ基盤のETL処理を任せるのは、包丁でネジを回すのと同じ。切れるかもしれないが、ネジは回らない。適材適所を見極めよ。
第二条:仕様書のない設計図は、地図のない航海である。
「あの人に聞けばわかる」は仕様書ではない。人間はいつかいなくなる。ドキュメントは、未来の自分と仲間への最大の贈り物だと心得よ。
第三条:データは嘘をつく。しかも、誠実な顔をして。
カラム名が sales_amount だからといって、中身が売上金額とは限らない。名前を信じるな、中身を確認せよ。最も厄介なバグは、一見正しく見えるデータの中に潜んでいる。
第四条:例外処理の例外処理は、設計の敗北宣言である。
if文を足すのは簡単だ。しかし100個のif文は、もはやコードではなくパッチワークの絨毯。根本設計を見直す勇気を持て。
第五条:ネットワークの帯域は、最も細い管で決まる。
理論上の最大速度は幻想である。実際に流れるデータ量と、最も細いボトルネックを把握せよ。令和のISDNを生み出すな。
第六条:テストを省いた者は、本番で10倍の代償を払う。
「早く成果を見せたい」という焦りで負荷テストを飛ばすと、大規模障害という形で帰ってくる。テストは保険ではない。義務である。
第七条:使わないデータに金を払うな。
誰も使っていないデータが毎月ウン十万円を食い続ける。コスト構造を理解し、「使わなければ費用がかからない」設計を選べ。常時起動は最後の手段。
第八条:美しいアーキテクチャ図は、泥臭い経験の上にしか描けない。
机上の空論で描いた設計図は、生データに触れた瞬間に崩壊する。まず手を動かせ。失敗から学んだ設計こそ、本当に強い設計である。
第九条:AIは魔法ではない。ゴミを入れればゴミが出る。
「データさえあればAIが解決する」は幻想。データ品質が担保されていなければ、どんな高性能なモデルも正しい答えは出せない。GIGOを忘れるな。
第十条:エンジニアの仕事の半分は、人と話すことである。
コードを書く時間より、ステークホルダーの期待値を調整する時間のほうが長い。技術力とコミュニケーション力は車の両輪。片方だけでは走れない。
──以上、十の掟。これを守ればデータ基盤プロジェクトは成功する……とは言わない。
だが、これを知らずに始めれば、確実に痛い目を見る。
先人の屍を無駄にするな。
Discussion