[{"data":1,"prerenderedAt":1089},["ShallowReactive",2],{"\u002Fblog\u002Faccidentally-built-modern-data-platform":3},{"id":4,"title":5,"body":6,"date":1075,"description":1076,"extension":1077,"image":1078,"meta":1079,"navigation":505,"path":1080,"seo":1081,"stem":1082,"tags":1083,"__hash__":1088},"blog\u002Fblog\u002Faccidentally-built-modern-data-platform.md","I Accidentally Built a Modern Data Platform Years Before It Became a Trend",{"type":7,"value":8,"toc":1054},"minimark",[9,14,21,68,71,76,79,82,84,88,91,104,107,124,127,144,147,150,155,158,160,164,167,170,173,222,232,234,238,242,245,248,253,256,260,284,287,291,294,305,308,312,315,321,324,335,338,342,345,359,362,364,368,371,879,882,885,888,890,894,897,900,903,907,911,918,932,935,937,941,944,949,952,955,958,963,966,968,972,1005,1007,1011,1014,1034,1037,1039,1042,1050],[10,11,13],"h3",{"id":12},"કેડ-માં-છોકરું-ને-ગામ-માં-ઢંઢેરો","\"કેડ માં છોકરું, ને ગામ માં ઢંઢેરો\"",[15,16,17],"p",{},[18,19,20],"em",{},"Basically: built something quietly, realised later it was a big deal.",[22,23,24,50],"blockquote",{},[15,25,26,30,31,34,35,38,39,34,42,45,46,49],{},[27,28,29],"strong",{},"TL;DR"," — Years ago, I built a simple pricing system: raw tables in, a stored procedure in the middle, and one final \"correct price\" table out. At the time, I didn't know anything about terms like ",[27,32,33],{},"data products",", ",[27,36,37],{},"Foundational Data Products (FDPs)",", a ",[27,40,41],{},"Derived Data Product (DDP)",[27,43,44],{},"data lineage",", or ",[27,47,48],{},"data contracts",". I was just trying to make the website show the right price.",[15,51,52,53,56,57,34,60,63,64,67],{},"Much later, when I started learning ",[27,54,55],{},"modern data engineering",", I realised I had already built many of those concepts without knowing their names. This post is me ",[27,58,59],{},"looking back",[27,61,62],{},"connecting the dots",", and understanding those ideas through something I actually built in ",[27,65,66],{},"real life",".",[69,70],"hr",{},[72,73,75],"h2",{"id":74},"why-im-writing-this","Why I'm Writing This",[15,77,78],{},"I'm writing this mainly for myself.\nI've been trying to understand modern data engineering concepts, and sometimes the wording makes everything sound more complex than it is. When I step back and compare the concepts to my real experience, I realise I actually lived through many of them already — I just didn't have the terminology.",[15,80,81],{},"This blog helps me learn by linking theory to something real I built. If someone else learns from it too, great.",[69,83],{},[72,85,87],{"id":86},"the-system-i-built-before-i-had-the-words","The System I Built (Before I Had the Words)",[15,89,90],{},"Years ago, I built a pricing system for an e‑commerce site. The business side (marketing\u002Fadmin) would enter:",[92,93,94,98,101],"ul",{},[95,96,97],"li",{},"Product‑level prices",[95,99,100],{},"Variant‑level prices",[95,102,103],{},"Start and end dates",[15,105,106],{},"I stored that data in two simple raw tables. Then I wrote a stored procedure that:",[92,108,109,112,115,118,121],{},[95,110,111],{},"cleaned and normalised the input,",[95,113,114],{},"applied pricing rules,",[95,116,117],{},"merged everything into one consistent truth,",[95,119,120],{},"fixed date issues,",[95,122,123],{},"and finally wrote out one table that the frontend could trust.",[15,125,126],{},"The rules were practical and simple:",[92,128,129,132,135,138,141],{},[95,130,131],{},"If a variant had its own price, use it.",[95,133,134],{},"Otherwise, use the product price.",[95,136,137],{},"Respect time ranges.",[95,139,140],{},"Don't leave gaps or overlaps.",[95,142,143],{},"Don't produce duplicates.",[15,145,146],{},"I didn't think about \"architecture.\" I wasn't trying to implement patterns. I just wanted a clean and reliable answer for the frontend.",[15,148,149],{},"Back then, I called it:",[22,151,152],{},[15,153,154],{},"\"the final price table.\"",[15,156,157],{},"Nothing more.",[69,159],{},[72,161,163],{"id":162},"the-same-system-with-modern-labels","The Same System, With Modern Labels",[15,165,166],{},"Later, when I started exploring modern data engineering — especially the vocabulary — things clicked.",[15,168,169],{},"My old setup actually lined up with many concepts people talk about today.",[15,171,172],{},"Here's the mapping using generic table names:",[92,174,175,188,201,210,216],{},[95,176,177,180,181,34,185],{},[27,178,179],{},"Raw inputs:"," ",[182,183,184],"code",{},"price_raw_product",[182,186,187],{},"price_raw_variant",[95,189,190,193,194,34,197,200],{},[27,191,192],{},"Foundational Data Products (FDPs):"," cleaned versions of the raw inputs (",[182,195,196],{},"price_fdp_product",[182,198,199],{},"price_fdp_variant",")",[95,202,203,206,207,200],{},[27,204,205],{},"Derived Data Product (DDP):"," the final truth table (",[182,208,209],{},"price_ddp_effective",[95,211,212,215],{},[27,213,214],{},"Orchestration:"," my stored procedure",[95,217,218,221],{},[27,219,220],{},"Consumers:"," frontend, checkout, reports, etc.",[223,224,229],"pre",{"className":225,"code":227,"language":228},[226],"language-text","Admin \u002F Marketing (price inputs)\n           |\n           v\nRaw Input Tables\n┌───────────────────────┐\n│ price_raw_product     │\n│ price_raw_variant     │\n└──────────┬────────────┘\n           v\nFoundational Data Products (FDP)\n┌───────────────────────────────┐\n│ price_fdp_product             │\n│ price_fdp_variant             │\n└──────────┬────────────────────┘\n           v\nOrchestration (Stored Procedure)\n┌───────────────────────────────┐\n│ joins, precedence, date logic │\n└──────────┬────────────────────┘\n           v\nDerived Data Product (DDP)\n┌───────────────────────────────┐\n│ price_ddp_effective           │\n└──────────┬────────────────────┘\n           v\nFrontend \u002F APIs \u002F Checkout\n","text",[182,230,227],{"__ignoreMap":231},"",[69,233],{},[72,235,237],{"id":236},"mapping-my-experience-to-data-engineering-concepts","Mapping My Experience to Data Engineering Concepts",[10,239,241],{"id":240},"_1-data-product","1) Data Product",[15,243,244],{},"A data product is basically a dataset with a clear purpose, rules, and an owner.",[15,246,247],{},"That final table I produced fulfilled exactly that purpose:",[22,249,250],{},[15,251,252],{},"\"Give me the correct price for any product or variant, at any time.\"",[15,254,255],{},"It had a consistent schema, the whole system depended on it, and I was the owner. So yes — it was a data product even if I never used the term.",[10,257,259],{"id":258},"_2-foundational-vs-derived-data-products","2) Foundational vs. Derived Data Products",[92,261,262,274],{},[95,263,264,265,34,267,269,270,273],{},"My cleaned tables (",[182,266,196],{},[182,268,199],{},") were ",[27,271,272],{},"FDPs"," — stable inputs.",[95,275,276,277,279,280,283],{},"My final table (",[182,278,209],{},") was the ",[27,281,282],{},"DDP"," — the business‑logic‑applied truth.",[15,285,286],{},"I didn't design it like that intentionally. But the structure naturally appeared because it was practical.",[10,288,290],{"id":289},"_3-orchestration","3) Orchestration",[15,292,293],{},"Today people use workflow tools, but my orchestration was simply:",[92,295,296,299,302],{},[95,297,298],{},"A stored procedure,",[95,300,301],{},"Triggered by a cron job,",[95,303,304],{},"Running steps in a controlled order.",[15,306,307],{},"It cleaned, merged, validated, and published the final result. Simple. And dependable.",[10,309,311],{"id":310},"_4-data-lineage","4) Data Lineage",[15,313,314],{},"I didn't draw lineage diagrams back then, but the flow was clear.",[223,316,319],{"className":317,"code":318,"language":228},[226],"price_raw_* → price_fdp_* → price_ddp_effective → frontend\n",[182,320,318],{"__ignoreMap":231},[15,322,323],{},"When something was wrong, I always knew where to look:",[92,325,326,329,332],{},[95,327,328],{},"If it was a business logic issue → the DDP.",[95,330,331],{},"If it was messy input → the raw tables.",[95,333,334],{},"If it was formatting → the FDP layer.",[15,336,337],{},"That's lineage in practice.",[10,339,341],{"id":340},"_5-data-contract","5) Data Contract",[15,343,344],{},"No one told me to write a \"data contract.\" But I still had an internal understanding:",[92,346,347,350,353,356],{},[95,348,349],{},"the columns needed to exist,",[95,351,352],{},"dates had to be valid,",[95,354,355],{},"variant overrides product,",[95,357,358],{},"the table needed to refresh with cron.",[15,360,361],{},"It wasn't formal, but the \"contract\" existed through consistent behaviour.",[69,363],{},[72,365,367],{"id":366},"the-core-business-logic","The Core Business Logic",[15,369,370],{},"Here's the simplified idea of what the stored procedure did, without the exact code.",[223,372,376],{"className":373,"code":374,"language":375,"meta":231,"style":231},"language-sql shiki shiki-themes github-light github-dark","-- Normalize raw inputs into FDPs\nCREATE TABLE price_fdp_product AS\nSELECT product_id,\n       price,\n       GREATEST(start_date, DATE '2000-01-01') AS start_date,\n       COALESCE(end_date, DATE '2999-12-31')   AS end_date\nFROM price_raw_product\nWHERE price IS NOT NULL;\n\nCREATE TABLE price_fdp_variant AS\nSELECT product_id, variant_id,\n       price,\n       GREATEST(start_date, DATE '2000-01-01') AS start_date,\n       COALESCE(end_date, DATE '2999-12-31')   AS end_date\nFROM price_raw_variant\nWHERE price IS NOT NULL;\n\n-- Materialize the DDP: variant price overrides product price\nCREATE TABLE price_ddp_effective AS\nWITH unioned AS (\n  SELECT product_id, variant_id, price, start_date, end_date, 2 AS precedence\n  FROM price_fdp_variant\n  UNION ALL\n  SELECT product_id, NULL AS variant_id, price, start_date, end_date, 1 AS precedence\n  FROM price_fdp_product\n),\nresolved AS (\n  SELECT *\n  FROM unioned\n  QUALIFY ROW_NUMBER() OVER (\n    PARTITION BY product_id, COALESCE(variant_id, -1), start_date, end_date\n    ORDER BY precedence DESC\n  ) = 1\n)\nSELECT product_id, variant_id,\n       price AS effective_price,\n       start_date, end_date,\n       CASE WHEN variant_id IS NOT NULL THEN 'variant' ELSE 'product' END AS price_source\nFROM resolved;\n","sql",[182,377,378,387,404,414,420,454,476,485,500,507,519,527,532,555,572,580,591,596,602,614,628,651,660,666,693,701,707,717,725,733,750,780,792,804,810,817,828,837,871],{"__ignoreMap":231},[379,380,383],"span",{"class":381,"line":382},"line",1,[379,384,386],{"class":385},"sJ8bj","-- Normalize raw inputs into FDPs\n",[379,388,390,394,397,401],{"class":381,"line":389},2,[379,391,393],{"class":392},"szBVR","CREATE",[379,395,396],{"class":392}," TABLE",[379,398,400],{"class":399},"sScJk"," price_fdp_product",[379,402,403],{"class":392}," AS\n",[379,405,407,410],{"class":381,"line":406},3,[379,408,409],{"class":392},"SELECT",[379,411,413],{"class":412},"sVt8B"," product_id,\n",[379,415,417],{"class":381,"line":416},4,[379,418,419],{"class":412},"       price,\n",[379,421,423,427,430,433,435,438,442,445,448,451],{"class":381,"line":422},5,[379,424,426],{"class":425},"sj4cs","       GREATEST",[379,428,429],{"class":412},"(",[379,431,432],{"class":392},"start_date",[379,434,34],{"class":412},[379,436,437],{"class":392},"DATE",[379,439,441],{"class":440},"sZZnC"," '2000-01-01'",[379,443,444],{"class":412},") ",[379,446,447],{"class":392},"AS",[379,449,450],{"class":392}," start_date",[379,452,453],{"class":412},",\n",[379,455,457,460,463,465,468,471,473],{"class":381,"line":456},6,[379,458,459],{"class":425},"       COALESCE",[379,461,462],{"class":412},"(end_date, ",[379,464,437],{"class":392},[379,466,467],{"class":440}," '2999-12-31'",[379,469,470],{"class":412},")   ",[379,472,447],{"class":392},[379,474,475],{"class":412}," end_date\n",[379,477,479,482],{"class":381,"line":478},7,[379,480,481],{"class":392},"FROM",[379,483,484],{"class":412}," price_raw_product\n",[379,486,488,491,494,497],{"class":381,"line":487},8,[379,489,490],{"class":392},"WHERE",[379,492,493],{"class":412}," price ",[379,495,496],{"class":392},"IS NOT NULL",[379,498,499],{"class":412},";\n",[379,501,503],{"class":381,"line":502},9,[379,504,506],{"emptyLinePlaceholder":505},true,"\n",[379,508,510,512,514,517],{"class":381,"line":509},10,[379,511,393],{"class":392},[379,513,396],{"class":392},[379,515,516],{"class":399}," price_fdp_variant",[379,518,403],{"class":392},[379,520,522,524],{"class":381,"line":521},11,[379,523,409],{"class":392},[379,525,526],{"class":412}," product_id, variant_id,\n",[379,528,530],{"class":381,"line":529},12,[379,531,419],{"class":412},[379,533,535,537,539,541,543,545,547,549,551,553],{"class":381,"line":534},13,[379,536,426],{"class":425},[379,538,429],{"class":412},[379,540,432],{"class":392},[379,542,34],{"class":412},[379,544,437],{"class":392},[379,546,441],{"class":440},[379,548,444],{"class":412},[379,550,447],{"class":392},[379,552,450],{"class":392},[379,554,453],{"class":412},[379,556,558,560,562,564,566,568,570],{"class":381,"line":557},14,[379,559,459],{"class":425},[379,561,462],{"class":412},[379,563,437],{"class":392},[379,565,467],{"class":440},[379,567,470],{"class":412},[379,569,447],{"class":392},[379,571,475],{"class":412},[379,573,575,577],{"class":381,"line":574},15,[379,576,481],{"class":392},[379,578,579],{"class":412}," price_raw_variant\n",[379,581,583,585,587,589],{"class":381,"line":582},16,[379,584,490],{"class":392},[379,586,493],{"class":412},[379,588,496],{"class":392},[379,590,499],{"class":412},[379,592,594],{"class":381,"line":593},17,[379,595,506],{"emptyLinePlaceholder":505},[379,597,599],{"class":381,"line":598},18,[379,600,601],{"class":385},"-- Materialize the DDP: variant price overrides product price\n",[379,603,605,607,609,612],{"class":381,"line":604},19,[379,606,393],{"class":392},[379,608,396],{"class":392},[379,610,611],{"class":399}," price_ddp_effective",[379,613,403],{"class":392},[379,615,617,620,623,625],{"class":381,"line":616},20,[379,618,619],{"class":392},"WITH",[379,621,622],{"class":412}," unioned ",[379,624,447],{"class":392},[379,626,627],{"class":412}," (\n",[379,629,631,634,637,639,642,645,648],{"class":381,"line":630},21,[379,632,633],{"class":392},"  SELECT",[379,635,636],{"class":412}," product_id, variant_id, price, ",[379,638,432],{"class":392},[379,640,641],{"class":412},", end_date, ",[379,643,644],{"class":425},"2",[379,646,647],{"class":392}," AS",[379,649,650],{"class":412}," precedence\n",[379,652,654,657],{"class":381,"line":653},22,[379,655,656],{"class":392},"  FROM",[379,658,659],{"class":412}," price_fdp_variant\n",[379,661,663],{"class":381,"line":662},23,[379,664,665],{"class":392},"  UNION ALL\n",[379,667,669,671,674,677,679,682,684,686,689,691],{"class":381,"line":668},24,[379,670,633],{"class":392},[379,672,673],{"class":412}," product_id, ",[379,675,676],{"class":392},"NULL",[379,678,647],{"class":392},[379,680,681],{"class":412}," variant_id, price, ",[379,683,432],{"class":392},[379,685,641],{"class":412},[379,687,688],{"class":425},"1",[379,690,647],{"class":392},[379,692,650],{"class":412},[379,694,696,698],{"class":381,"line":695},25,[379,697,656],{"class":392},[379,699,700],{"class":412}," price_fdp_product\n",[379,702,704],{"class":381,"line":703},26,[379,705,706],{"class":412},"),\n",[379,708,710,713,715],{"class":381,"line":709},27,[379,711,712],{"class":412},"resolved ",[379,714,447],{"class":392},[379,716,627],{"class":412},[379,718,720,722],{"class":381,"line":719},28,[379,721,633],{"class":392},[379,723,724],{"class":392}," *\n",[379,726,728,730],{"class":381,"line":727},29,[379,729,656],{"class":392},[379,731,732],{"class":412}," unioned\n",[379,734,736,739,742,745,748],{"class":381,"line":735},30,[379,737,738],{"class":412},"  QUALIFY ",[379,740,741],{"class":425},"ROW_NUMBER",[379,743,744],{"class":412},"() ",[379,746,747],{"class":392},"OVER",[379,749,627],{"class":412},[379,751,753,756,759,761,764,767,770,772,775,777],{"class":381,"line":752},31,[379,754,755],{"class":392},"    PARTITION",[379,757,758],{"class":392}," BY",[379,760,673],{"class":412},[379,762,763],{"class":425},"COALESCE",[379,765,766],{"class":412},"(variant_id, ",[379,768,769],{"class":392},"-",[379,771,688],{"class":425},[379,773,774],{"class":412},"), ",[379,776,432],{"class":392},[379,778,779],{"class":412},", end_date\n",[379,781,783,786,789],{"class":381,"line":782},32,[379,784,785],{"class":392},"    ORDER BY",[379,787,788],{"class":412}," precedence ",[379,790,791],{"class":392},"DESC\n",[379,793,795,798,801],{"class":381,"line":794},33,[379,796,797],{"class":412},"  ) ",[379,799,800],{"class":392},"=",[379,802,803],{"class":425}," 1\n",[379,805,807],{"class":381,"line":806},34,[379,808,809],{"class":412},")\n",[379,811,813,815],{"class":381,"line":812},35,[379,814,409],{"class":392},[379,816,526],{"class":412},[379,818,820,823,825],{"class":381,"line":819},36,[379,821,822],{"class":412},"       price ",[379,824,447],{"class":392},[379,826,827],{"class":412}," effective_price,\n",[379,829,831,834],{"class":381,"line":830},37,[379,832,833],{"class":392},"       start_date",[379,835,836],{"class":412},", end_date,\n",[379,838,840,843,846,849,851,854,857,860,863,866,868],{"class":381,"line":839},38,[379,841,842],{"class":392},"       CASE",[379,844,845],{"class":392}," WHEN",[379,847,848],{"class":412}," variant_id ",[379,850,496],{"class":392},[379,852,853],{"class":392}," THEN",[379,855,856],{"class":440}," 'variant'",[379,858,859],{"class":392}," ELSE",[379,861,862],{"class":440}," 'product'",[379,864,865],{"class":392}," END",[379,867,647],{"class":392},[379,869,870],{"class":412}," price_source\n",[379,872,874,876],{"class":381,"line":873},39,[379,875,481],{"class":392},[379,877,878],{"class":412}," resolved;\n",[15,880,881],{},"Not fancy.",[15,883,884],{},"Just practical.",[15,886,887],{},"In reality, I also handled edge cases (date overlaps, gaps, rounding rules, and invalid inputs).",[69,889],{},[72,891,893],{"id":892},"scheduling-freshness","Scheduling & Freshness",[15,895,896],{},"The entire pipeline refreshed through a cron job triggered via command line function and some database triggers. This could have been improved but when simple things solves the purpose, no need make it complicated with modern flows.",[15,898,899],{},"No special tools.",[15,901,902],{},"No workflow orchestrators.",[72,904,906],{"id":905},"and-honestly-it-worked-extremely-well","And honestly, it worked extremely well.",[72,908,910],{"id":909},"why-this-whole-thing-worked","Why This Whole Thing Worked",[15,912,913,914,917],{},"Looking back, the success wasn't about technology. It was about ",[27,915,916],{},"clarity",":",[92,919,920,923,926,929],{},[95,921,922],{},"Inputs were clean.",[95,924,925],{},"Logic lived in one place.",[95,927,928],{},"Output was predictable.",[95,930,931],{},"Everyone trusted the result.",[15,933,934],{},"That trust mattered more than the fancy names.",[69,936],{},[72,938,940],{"id":939},"how-i-would-describe-it-today","How I Would Describe It Today",[15,942,943],{},"If I had to describe the same system in “modern terms,” I’d say:",[22,945,946],{},[15,947,948],{},"“This is a pricing data product built from foundational inputs, transformed through an orchestration step into a reliable derived output used across the platform.”",[72,950,951],{"id":231},"🤯",[15,953,954],{},"But honestly?",[15,956,957],{},"The simpler explanation still wins:",[22,959,960],{},[15,961,962],{},"“It’s one table that always had the right price.”",[15,964,965],{},"Both are true.\nJust different levels of vocabulary.",[69,967],{},[72,969,971],{"id":970},"what-i-learned-from-this","What I Learned From This",[973,974,975,980,985,990,995,1000],"ol",{},[95,976,977],{},[27,978,979],{},"You can build something modern without knowing the terminology.",[95,981,982],{},[27,983,984],{},"Words can make things sound complex — the concepts are often simple.",[95,986,987],{},[27,988,989],{},"If systems are predictable and trusted, you're doing it right.",[95,991,992],{},[27,993,994],{},"Data engineering becomes clearer when tied to real experiences.",[95,996,997],{},[27,998,999],{},"Practical work teaches faster than reading definitions.",[95,1001,1002],{},[27,1003,1004],{},"Sometimes you understand what you built only years later.",[69,1006],{},[72,1008,1010],{"id":1009},"if-youre-building-something-similar","If You’re Building Something Similar",[15,1012,1013],{},"My advice is simple:",[92,1015,1016,1019,1022,1025,1028,1031],{},[95,1017,1018],{},"Clean your inputs.",[95,1020,1021],{},"Name your outputs clearly.",[95,1023,1024],{},"Keep the logic centralised.",[95,1026,1027],{},"Don’t overcomplicate orchestration.",[95,1029,1030],{},"Make your data trustworthy.",[95,1032,1033],{},"Let the fancy terms come after the architecture makes sense.",[15,1035,1036],{},"Understanding follows experience — not the other way around.",[69,1038],{},[15,1040,1041],{},"Thanks for reading. Writing this helped me understand these concepts in my own language.",[22,1043,1044],{},[15,1045,1046,1049],{},[18,1047,1048],{},"P.S. The idea, the story, and the experience are 100% mine — the only thing AI helped with was turning my brain dump into readable English. So if anything sounds unusually polished, blame the robot, not me."," 😄",[1051,1052,1053],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":231,"searchDepth":389,"depth":389,"links":1055},[1056,1057,1058,1059,1060,1067,1068,1069,1070,1071,1072,1073,1074],{"id":12,"depth":406,"text":13},{"id":74,"depth":389,"text":75},{"id":86,"depth":389,"text":87},{"id":162,"depth":389,"text":163},{"id":236,"depth":389,"text":237,"children":1061},[1062,1063,1064,1065,1066],{"id":240,"depth":406,"text":241},{"id":258,"depth":406,"text":259},{"id":289,"depth":406,"text":290},{"id":310,"depth":406,"text":311},{"id":340,"depth":406,"text":341},{"id":366,"depth":389,"text":367},{"id":892,"depth":389,"text":893},{"id":905,"depth":389,"text":906},{"id":909,"depth":389,"text":910},{"id":939,"depth":389,"text":940},{"id":231,"depth":389,"text":951},{"id":970,"depth":389,"text":971},{"id":1009,"depth":389,"text":1010},"2026-02-24","Years ago, I built a simple pricing system: raw tables in, a stored procedure in the middle, one final table out. Much later I realised I had already built data products, FDPs, DDPs, lineage, and contracts — without knowing the names.","md","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1558494949-ef010cbdcc31?auto=format&fit=crop&w=1600&q=80",{},"\u002Fblog\u002Faccidentally-built-modern-data-platform",{"title":5,"description":1076},"blog\u002Faccidentally-built-modern-data-platform",[1084,1085,1086,1087],"Data Engineering","Data Products","Data Architecture","ETL","MomWGg5Nbz0WKnULPiOKMqSfQ3R1L0epVDOjqWp0E_o",1777082696553]