<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~files/feed-premium.xsl"?>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:feedpress="https://feed.press/xmlns" xmlns:podcast="https://podcastindex.org/namespace/1.0" version="2.0">
  <channel>
    <feedpress:locale>en</feedpress:locale>
    <atom:link rel="self" href="https://feeds.dzone.com/big-data"/>
    <atom:link rel="hub" href="https://feedpress.superfeedr.com/"/>
    <title>DZone Big Data Zone</title>
    <link>https://dzone.com/big-data</link>
    <description>Recent posts in Big Data on DZone.com</description>
    <item>
      <title>How We Cut PyFlink Pipeline p99 Latency from 3-5 Seconds to ~500ms</title>
      <link>https://dzone.com/articles/how-we-got-pyflink-under-half-a-second-of-end-to-e</link>
      <description><![CDATA[<h2 data-sourcepos="5:1-5:40;241-280" dir="ltr">The Problem: Our p99 Was 3-5 Seconds</h2>
<p data-sourcepos="7:1-7:310;282-591" dir="ltr">Our PyFlink pipeline was missing its latency SLO by seconds. The pipeline itself was straightforward: consume events from Kafka, transform them, serialize them as Protobuf, and write the results to downstream systems. Yet under production load, p99 end-to-end latency was consistently in the 3-5 second range.</p>
<p data-sourcepos="9:1-9:364;593-956" dir="ltr">Profiling pointed us to an unexpected bottleneck: we were deserializing Protobuf messages in <a href="https://dzone.com/articles/python-tutorial-for-beginners-a-comprehensive-guid">Python</a>, even though the Flink runtime processing our stream was JVM-based. Every record that entered the Python path had to cross the JVM-to-Python process boundary, get parsed by a Python UDF, and then cross back. The business logic wasn't the problem. The doorway was.</p>]]></description>
      <pubDate>Fri, 07 Aug 2026 16:00:05 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3666511</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19120907&amp;w=600"/>
      <dc:creator>Arjun Shah</dc:creator>
    </item>
    <item>
      <title>TensorFlow vs PyTorch: The Real Difference Isn’t Accuracy</title>
      <link>https://dzone.com/articles/tensorflow-vs-pytorch</link>
      <description><![CDATA[<p data-selectable-paragraph="">A few days ago, I set out to build a simple image classification model using convolutional neural networks (CNNs). The task itself wasn’t particularly complex, but choosing the right framework proved more challenging than expected. I found myself choosing between TensorFlow and PyTorch, two powerful frameworks for building high-performance CNNs.</p>
<p data-selectable-paragraph="">To explore this, I implemented the same <a href="https://dzone.com/articles/cnn-convolutional-neural-networks-guide">CNN</a> in both frameworks under identical conditions and compared them across key aspects like learning curve, flexibility, debugging, and performance.</p>]]></description>
      <pubDate>Wed, 05 Aug 2026 17:00:04 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3656409</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19116379&amp;w=600"/>
      <dc:creator>Rakshath Naik</dc:creator>
    </item>
    <item>
      <title>Why LLM Pipelines Fail in Production and How Temporal and Kafka Fix Them</title>
      <link>https://dzone.com/articles/llm-pipeline-failures</link>
      <description><![CDATA[<p>A production LLM pipeline is rarely just a prompt and a response. It typically combines retrieval, prompt rendering, model inference, output shaping, validation, persistence, and downstream actions. That broader shape is why many systems look stable in a demo and then become fragile under live traffic. The model call is only one component; the operational problem is the workflow around it. Provider APIs impose rate limits, structured outputs still need application-level checks, and external calls introduce failure ambiguity that ordinary request-response code does not handle well.</p>
<h2>Where the Breakage Starts</h2>
<p>Most production failures happen between steps, not inside the prompt. A request enters an API, context is loaded, a model call is sent, the response is parsed, a downstream action is triggered, and a record is written. If the provider generated output but the network dropped before the caller saw it, the system no longer has a clean answer to whether the operation should be retried or treated as complete. <a href="https://dzone.com/articles/kafka-powerhouse-messaging">Kafka’s</a> default delivery model is at least once, and Temporal’s documentation is explicit that activities may be retried and therefore should be idempotent. That combination makes duplicate side effects the default risk unless the pipeline is designed around durable state and idempotent writes.&nbsp;</p>]]></description>
      <pubDate>Wed, 05 Aug 2026 15:00:01 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3664907</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19116375&amp;w=600"/>
      <dc:creator>Akhil Madineni</dc:creator>
    </item>
    <item>
      <title>Why Enterprise AI Agents Fail: A Runtime Data Governance Pattern for Reliable Answers</title>
      <link>https://dzone.com/articles/why-enterprise-ai-agents-fai</link>
      <description><![CDATA[<h2><strong>The Failure You Have Probably Already Seen</strong></h2>
<p>An enterprise AI agent is deployed against production data. It answers the first ten questions confidently and correctly. Then, on the eleventh question, it produces an answer that looks reasonable but is completely wrong. The team investigates. The model is fine. The prompt is fine. The tool integrations are fine. The problem is buried in the data itself. A field the agent relied on has drifted. A join it assumed existed no longer holds. A quality signal that used to be reliable has silently degraded.</p>
<p>This is not a rare edge case. It is becoming one of the most common failure patterns in enterprise AI systems moving from prototype to production. And it points to a simple, uncomfortable truth: most enterprise data infrastructure was built for a consumer we no longer have.</p>]]></description>
      <pubDate>Mon, 03 Aug 2026 14:00:05 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3665341</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19101297&amp;w=600"/>
      <dc:creator>Avinash Maddineni</dc:creator>
    </item>
    <item>
      <title>Compliance Reporting Without Losing the Spreadsheet or the Control</title>
      <link>https://dzone.com/articles/compliance-reporting-control</link>
      <description><![CDATA[<p dir="ltr">Compliance-reporting teams keep spreadsheets in the loop for a practical reason: a workbook lets domain experts inspect assumptions, formulas, source rows, and intermediate values without reading a line of application code. That transparency is genuinely useful, and it's a big part of why replacing Excel outright so often fails to stick.</p>
<p dir="ltr">The trouble starts once that workbook becomes part of a repeatable, audited reporting process — a regulatory filing, an IFRS report, a periodic compliance submission. At that point, a shared Excel file isn't enough on its own. What's actually needed is version control, validation, an audit trail, a review step, and a reliable way to connect the spreadsheet's logic to the systems downstream.</p>]]></description>
      <pubDate>Tue, 14 Jul 2026 17:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3665047</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19087555&amp;w=600"/>
      <dc:creator>Hawk Chen</dc:creator>
    </item>
    <item>
      <title>AWS Glue ETL Design Principles for Production PySpark Pipelines</title>
      <link>https://dzone.com/articles/aws-glue-pyspark-pipelines</link>
      <description><![CDATA[<p>AWS Glue makes it easy to get a PySpark pipeline running quickly. It is significantly harder to build one that stays maintainable as logic grows, performs reliably at scale, and does not quietly accumulate operational debt over time.</p>
<p>Most Glue pipelines start simple and become difficult to manage gradually — formulas get hardcoded, modules grow without boundaries, output files proliferate, and before long a single job is doing too many things in ways that are hard to test, hard to debug, and expensive to change.</p>]]></description>
      <pubDate>Tue, 14 Jul 2026 14:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3658541</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19087504&amp;w=600"/>
      <dc:creator>Janani Annur Thiruvengadam</dc:creator>
    </item>
    <item>
      <title>Top 10 Best Places to Prepare for Your Next Data Engineer Interview</title>
      <link>https://dzone.com/articles/data-engineer-interview-prep</link>
      <description><![CDATA[<p dir="ltr">Landing a data engineering role means clearing a gauntlet that no other software discipline has to face all at once: airtight SQL, production-grade Python, data modeling instincts, distributed-compute fluency (Spark, warehouses, ETL), and system design that has to survive real data volume. Generic coding prep barely scratches the surface, and "just grind LeetCode" advice falls apart the moment an interviewer asks you to model a slowly changing dimension or reason about a skewed join.</p>
<p dir="ltr">So we did the work. We evaluated the resources <a href="https://dzone.com/articles/what-is-data-engineering-data-engineering-skills-a" rel="noopener noreferrer" target="_blank">data engineers</a> actually use, judged on five things that matter: relevance to the DE interview loop, depth of practice, realism of the questions, feedback quality, and price. Below is the ranked list.</p>]]></description>
      <pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3660846</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19085087&amp;w=600"/>
      <dc:creator>Rahul Han</dc:creator>
    </item>
    <item>
      <title>Building Production-Grade Delta Lake Pipelines With Apache Spark on Databricks</title>
      <link>https://dzone.com/articles/production-grade-data-late-pipelines</link>
      <description><![CDATA[<h2>Why Delta Lake?</h2>
<p>Apache Parquet on cloud storage was a great first step for <a href="https://dzone.com/articles/data-lake-warehouse-or-lakehouse">data lakes</a> — but it left engineers dealing with a painful set of problems in production:</p>
<ul>
 <li><strong>No ACID transactions</strong> — concurrent reads/writes could corrupt data silently</li>
 <li><strong>Schema drift</strong> — nothing stopped upstream systems from changing column types</li>
 <li><strong>No deletes or updates</strong> — GDPR compliance meant rewriting entire partitions</li>
 <li><strong>Painful failure recovery</strong> — half-written data after a job crash became your problem</li>
</ul>
<p>Delta Lake solves all of this by sitting on top of Parquet and adding a transaction log (<code>_delta_log/</code>) that records every operation atomically. On Databricks, Delta is the default table format, deeply integrated with Apache Spark, Auto Optimize, and the Photon execution engine.</p>]]></description>
      <pubDate>Wed, 08 Jul 2026 14:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3662913</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19081615&amp;w=600"/>
      <dc:creator>Jubin Soni, FBCS</dc:creator>
    </item>
    <item>
      <title>Azure Databricks for Scalable MLOps and Feature Engineering With Apache Spark, Delta Lake, and MLflow</title>
      <link>https://dzone.com/articles/azure-databricks-mlops</link>
      <description><![CDATA[<p>Raw data doesn't win model competitions. Features do. And when your raw data is tens of billions of rows sitting across multiple sources, you can't afford to run pandas in a notebook and call it a day.</p>
<p>In this tutorial, I'll walk through building a production-grade feature engineering pipeline on <a href="https://dzone.com/articles/azure-databricks-best-practices-for-a-developer">Azure Databricks</a> using:</p>]]></description>
      <pubDate>Mon, 06 Jul 2026 14:00:03 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3663565</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19076762&amp;w=600"/>
      <dc:creator>Jubin Soni, FBCS</dc:creator>
    </item>
    <item>
      <title>From Polling to PubSub: Building an Asynchronous OPC UA Stack in Python</title>
      <link>https://dzone.com/articles/async-opc-ua-python</link>
      <description><![CDATA[<p data-path-to-node="8">Industrial control systems are generating more data than ever before, but the Python tooling used to process this telemetry often encounters severe performance constraints. Traditional OPC UA libraries are built around synchronous, polling-based Client and Server architectures. When industrial networks scale to thousands of sensors broadcasting high-frequency data, these synchronous Python implementations choke. To handle this modern many-to-many topology, developers need a native Publisher and Subscriber solution that does not block the execution thread while waiting for network packets.</p>
<p data-path-to-node="9">For Python developers unfamiliar with industrial protocols, OPC UA PubSub (IEC 62541-14) is a standard that decouples data producers from consumers by allowing devices to broadcast telemetry via stateless middleware like UDP Multicast. For industrial engineers new to Python concurrency, <code data-index-in-node="288" data-path-to-node="9">asyncio</code> is a standard library that uses an event loop to handle thousands of simultaneous network operations concurrently without the heavy overhead of traditional threading.</p>]]></description>
      <pubDate>Fri, 03 Jul 2026 18:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3663572</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19077404&amp;w=600"/>
      <dc:creator>Harshith Narasimhan Srivatsa</dc:creator>
    </item>
    <item>
      <title>Real-Time AI Feature Engineering With Spark Structured Streaming and Databricks Feature Store</title>
      <link>https://dzone.com/articles/real-time-ai-features-spark-databricks</link>
      <description><![CDATA[<h2>The Feature Engineering Problem</h2>
<p>Feature engineering is where most ML projects silently fail in production. Not because the model is wrong — but because the features the model sees at training time are different from the features it sees at inference time. This is called training-serving skew, and it's the #1 silent killer of ML systems.</p>
<p>Three specific failure modes cause it:</p>]]></description>
      <pubDate>Thu, 02 Jul 2026 18:00:05 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3662916</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19077381&amp;w=600"/>
      <dc:creator>Jubin Soni, FBCS</dc:creator>
    </item>
    <item>
      <title>Dead Letter Queue Patterns in Apache Flink: Handling Poison Messages Without Stopping Your Stream</title>
      <link>https://dzone.com/articles/flink-dlq-patterns</link>
      <description><![CDATA[<div data-theme="light">
 <p>Streaming systems usually fail in one of two ways:</p>
 <ul>
  <li><strong>Loudly</strong>, when infrastructure breaks</li>
  <li><strong>Quietly</strong>, when one bad record keeps replaying until the pipeline is effectively dead</li>
 </ul>
 <p>The second failure mode is more dangerous because it often starts with something small: malformed JSON, an unexpected schema change, a missing required field, or a downstream timeout that was never handled correctly.</p>]]></description>
      <pubDate>Thu, 02 Jul 2026 13:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3659522</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19049222&amp;w=600"/>
      <dc:creator>Rohit Muthyala</dc:creator>
    </item>
    <item>
      <title>Apache Spark Query Optimization on Databricks: Catalyst, AQE, and Photon Engine</title>
      <link>https://dzone.com/articles/spark-query-optimization</link>
      <description><![CDATA[<h2>Why Query Optimization Matters</h2>
<p>A Spark query written by a human and a Spark query executed by the engine are often very different things. The gap between them — the <em>optimization</em> — is what separates a job that runs in 3 minutes from one that runs in 3 hours on identical hardware.</p>
<p>Databricks compounds Spark's native Catalyst optimizer with two additional layers:</p>]]></description>
      <pubDate>Thu, 02 Jul 2026 12:00:07 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3662915</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19075064&amp;w=600"/>
      <dc:creator>Jubin Soni, FBCS</dc:creator>
    </item>
    <item>
      <title>Fine-Tuning LLMs at Scale With Databricks MLflow and Spark</title>
      <link>https://dzone.com/articles/llm-finetuning-databricks</link>
      <description><![CDATA[<h2>Why Fine-Tune on Databricks?</h2>
<p>General-purpose LLMs like Llama 3, Mistral, or Falcon are impressive out of the box — but they underperform on domain-specific tasks: medical coding, legal clause extraction, internal support ticket classification, and financial report summarization. Fine-tuning adapts a pre-trained model's weights to your domain using your proprietary labeled data.</p>
<p>Doing this <em>at scale</em> introduces real engineering challenges:</p>]]></description>
      <pubDate>Tue, 30 Jun 2026 17:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3662914</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19073096&amp;w=600"/>
      <dc:creator>Jubin Soni, FBCS</dc:creator>
    </item>
    <item>
      <title>Data Pipeline Observability: Why Your AI Model Fails in Production</title>
      <link>https://dzone.com/articles/why-ai-model-fails-in-production</link>
      <description><![CDATA[<h2 data-anchor="the3amincidentthatchangedeverything" data-slate-node="element" data-slug="the3amincidentthatchangedeverything3"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The 3:00 AM Incident That Changed Everything</span></span></span></h2>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">It was a Tuesday morning when the alerts started firing. Our recommendation engine, the one that drives 30% of our revenue, had tanked. Accuracy dropped from 94% to 58% overnight. The data science team immediately blamed the model. They started tweaking hyperparameters, re-training on new data, and running diagnostics. Nothing worked.&nbsp;</span></span></span>
</div>
<div data-slate-node="element">
 <br>
</div>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">I got pulled into the war room at 3:00 AM. The first thing I asked wasn't "What's wrong with the model?" It was "What changed in the data pipeline?"</span></span></span>
</div>
<div data-slate-node="element">
 <br>
</div>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Turns out, everything.</span></span></span>
 <br>
 <br>
</div>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">A vendor had pushed a schema change upstream. A field that used to be required became optional. Null values started flowing through our pipeline. Our feature engineering code didn't handle nulls gracefully; it just propagated them downstream. By the time the data reached the model, 40% of our feature vectors were corrupted.</span></span></span>
 <br>
 <br>
</div>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The model wasn't broken. The data was.</span></span></span>
 <br>
 <br>
</div>
<div data-slate-node="element">
 <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">We spent six hours manually rolling back the schema change, re-running the pipeline, and restoring service. The incident report was brutal: "Lack of data validation caught a breaking change too late."</span></span></span>
</div>
<div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjAzJTIwQU0lMjBJbmNpZGVudCUyMFRoYXQlMjBDaGFuZ2VkJTIwRXZlcnl0aGluZyUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIySXQlMjB3YXMlMjBhJTIwVHVlc2RheSUyMG1vcm5pbmclMjB3aGVuJTIwdGhlJTIwYWxlcnRzJTIwc3RhcnRlZCUyMGZpcmluZy4lMjBPdXIlMjByZWNvbW1lbmRhdGlvbiUyMGVuZ2luZSVFMiU4MCU5NHRoZSUyMG9uZSUyMHRoYXQlMjBkcml2ZXMlMjAzMCUyNSUyMG9mJTIwb3VyJTIwcmV2ZW51ZSVFMiU4MCU5NGhhZCUyMHRhbmtlZC4lMjBBY2N1cmFjeSUyMGRyb3BwZWQlMjBmcm9tJTIwOTQlMjUlMjB0byUyMDU4JTI1JTIwb3Zlcm5pZ2h0LiUyMFRoZSUyMGRhdGElMjBzY2llbmNlJTIwdGVhbSUyMGltbWVkaWF0ZWx5JTIwYmxhbWVkJTIwdGhlJTIwbW9kZWwuJTIwVGhleSUyMHN0YXJ0ZWQlMjB0d2Vha2luZyUyMGh5cGVycGFyYW1ldGVycyUyQyUyMHJlLXRyYWluaW5nJTIwb24lMjBuZXclMjBkYXRhJTJDJTIwcnVubmluZyUyMGRpYWdub3N0aWNzLiUyME5vdGhpbmclMjB3b3JrZWQuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJJJTIwZ290JTIwcHVsbGVkJTIwaW50byUyMHRoZSUyMHdhciUyMHJvb20lMjBhdCUyMDMlMjBBTS4lMjBUaGUlMjBmaXJzdCUyMHRoaW5nJTIwSSUyMGFza2VkJTIwd2Fzbid0JTIwJTVDJTIyV2hhdCdzJTIwd3JvbmclMjB3aXRoJTIwdGhlJTIwbW9kZWwlM0YlNUMlMjIlMjBJdCUyMHdhcyUyMCU1QyUyMldoYXQlMjBjaGFuZ2VkJTIwaW4lMjB0aGUlMjBkYXRhJTIwcGlwZWxpbmUlM0YlNUMlMjIlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlR1cm5zJTIwb3V0JTJDJTIwZXZlcnl0aGluZy4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkElMjB2ZW5kb3IlMjBoYWQlMjBwdXNoZWQlMjBhJTIwc2NoZW1hJTIwY2hhbmdlJTIwdXBzdHJlYW0uJTIwQSUyMGZpZWxkJTIwdGhhdCUyMHVzZWQlMjB0byUyMGJlJTIwcmVxdWlyZWQlMjBiZWNhbWUlMjBvcHRpb25hbC4lMjBOdWxsJTIwdmFsdWVzJTIwc3RhcnRlZCUyMGZsb3dpbmclMjB0aHJvdWdoJTIwb3VyJTIwcGlwZWxpbmUuJTIwT3VyJTIwZmVhdHVyZSUyMGVuZ2luZWVyaW5nJTIwY29kZSUyMGRpZG4ndCUyMGhhbmRsZSUyMG51bGxzJTIwZ3JhY2VmdWxseSVFMiU4MCU5NGl0JTIwanVzdCUyMHByb3BhZ2F0ZWQlMjB0aGVtJTIwZG93bnN0cmVhbS4lMjBCeSUyMHRoZSUyMHRpbWUlMjB0aGUlMjBkYXRhJTIwcmVhY2hlZCUyMHRoZSUyMG1vZGVsJTJDJTIwNDAlMjUlMjBvZiUyMG91ciUyMGZlYXR1cmUlMjB2ZWN0b3JzJTIwd2VyZSUyMGNvcnJ1cHRlZC4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRoZSUyMG1vZGVsJTIwd2Fzbid0JTIwYnJva2VuLiUyMFRoZSUyMGRhdGElMjB3YXMuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJXZSUyMHNwZW50JTIwc2l4JTIwaG91cnMlMjBtYW51YWxseSUyMHJvbGxpbmclMjBiYWNrJTIwdGhlJTIwc2NoZW1hJTIwY2hhbmdlJTJDJTIwcmUtcnVubmluZyUyMHRoZSUyMHBpcGVsaW5lJTJDJTIwYW5kJTIwcmVzdG9yaW5nJTIwc2VydmljZS4lMjBUaGUlMjBpbmNpZGVudCUyMHJlcG9ydCUyMHdhcyUyMGJydXRhbCUzQSUyMCU1QyUyMkxhY2slMjBvZiUyMGRhdGElMjB2YWxpZGF0aW9uJTIwY2F1Z2h0JTIwYSUyMGJyZWFraW5nJTIwY2hhbmdlJTIwdG9vJTIwbGF0ZS4lNUMlMjIlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRoYXQncyUyMHdoZW4lMjBJJTIwcmVhbGl6ZWQlMjB3ZSUyMG5lZWRlZCUyMG9ic2VydmFiaWxpdHklMjBpbiUyMG91ciUyMGRhdGElMjBwaXBlbGluZSUyQyUyMG5vdCUyMGp1c3QlMjBpbiUyMG91ciUyMG1vZGVscy4lMjIlN0QlNUQlN0QlNUQ=">
 <div data-slate-node="element">
  <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">That's when I realized we needed&nbsp;</span></span></span><a href="https://dzone.com/articles/introduction-to-the-four-pillars-of-observability"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">observability</span></span></span></a><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;in our data pipeline, not just in our models.</span></span></span>
  <br>
  <br>
 </div>
 <h2 data-anchor="theproblem%3Adataqualityisinvisibleuntilitbreaks" data-slate-node="element" data-slug="theproblem%3Adataqualityisinvisibleuntilitbreaks12"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The Problem: Data Quality is Invisible Until It Breaks</span></span></span></h2>
 <div data-slate-node="element">
  <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Here's the uncomfortable truth about data pipelines: they fail silently.</span></span></span>
  <br>
  <br>
 </div>
 <div data-slate-node="element">
  <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Your ETL job completes successfully. Your Spark cluster finishes transformations. Your data warehouse loads without errors. Everything looks green in the monitoring dashboard. But the data itself? Garbage in, garbage out.</span></span></span>
  <br>
  <br>
 </div>
 <div data-slate-node="element">
  <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">There are three categories of failures that break AI models in production:</span></span></span>
  <br>
  <br>
 </div>
 <ul>
  <li>
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>Missing Values:</strong></span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>&nbsp;</strong>A source system stops populating a field. Your pipeline doesn't validate it. The model gets NaN values it never saw during training. Predictions become random noise.</span></span></span>
   </div></li>
  <li>
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>Schema Changes:</strong></span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>&nbsp;</strong>An upstream team adds a new column, renames an existing one, or changes data types. Your pipeline doesn't expect these changes. Either it crashes, or worse, it silently maps data to the wrong columns.</span></span></span>
   </div></li>
  <li>
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>Distribution Shifts:</strong></span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><strong>&nbsp;</strong>The statistical properties of your data change. A field that was always between 0 and 100 suddenly has values of 50,000. Your model's scaling assumptions break. Predictions become nonsensical.</span></span></span>
   </div></li>
 </ul>
 <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBQcm9ibGVtJTNBJTIwRGF0YSUyMFF1YWxpdHklMjBpcyUyMEludmlzaWJsZSUyMFVudGlsJTIwSXQlMjBCcmVha3MlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkhlcmUncyUyMHRoZSUyMHVuY29tZm9ydGFibGUlMjB0cnV0aCUyMGFib3V0JTIwZGF0YSUyMHBpcGVsaW5lcyUzQSUyMHRoZXklMjBmYWlsJTIwc2lsZW50bHkuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJZb3VyJTIwRVRMJTIwam9iJTIwY29tcGxldGVzJTIwc3VjY2Vzc2Z1bGx5LiUyMFlvdXIlMjBTcGFyayUyMGNsdXN0ZXIlMjBmaW5pc2hlcyUyMHRyYW5zZm9ybWF0aW9ucy4lMjBZb3VyJTIwZGF0YSUyMHdhcmVob3VzZSUyMGxvYWRzJTIwd2l0aG91dCUyMGVycm9ycy4lMjBFdmVyeXRoaW5nJTIwbG9va3MlMjBncmVlbiUyMGluJTIwdGhlJTIwbW9uaXRvcmluZyUyMGRhc2hib2FyZC4lMjBCdXQlMjB0aGUlMjBkYXRhJTIwaXRzZWxmJTNGJTIwR2FyYmFnZSUyMGluJTJDJTIwZ2FyYmFnZSUyMG91dC4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRoZXJlJTIwYXJlJTIwdGhyZWUlMjBjYXRlZ29yaWVzJTIwb2YlMjBmYWlsdXJlcyUyMHRoYXQlMjBicmVhayUyMEFJJTIwbW9kZWxzJTIwaW4lMjBwcm9kdWN0aW9uJTNBJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJNaXNzaW5nJTIwVmFsdWVzJTNBJTIyJTJDJTIyYm9sZCUyMiUzQXRydWUlN0QlMkMlN0IlMjJ0ZXh0JTIyJTNBJTIyJTIwQSUyMHNvdXJjZSUyMHN5c3RlbSUyMHN0b3BzJTIwcG9wdWxhdGluZyUyMGElMjBmaWVsZC4lMjBZb3VyJTIwcGlwZWxpbmUlMjBkb2Vzbid0JTIwdmFsaWRhdGUlMjBpdC4lMjBUaGUlMjBtb2RlbCUyMGdldHMlMjBOYU4lMjB2YWx1ZXMlMjBpdCUyMG5ldmVyJTIwc2F3JTIwZHVyaW5nJTIwdHJhaW5pbmcuJTIwUHJlZGljdGlvbnMlMjBiZWNvbWUlMjByYW5kb20lMjBub2lzZS4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlNjaGVtYSUyMENoYW5nZXMlM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBBbiUyMHVwc3RyZWFtJTIwdGVhbSUyMGFkZHMlMjBhJTIwbmV3JTIwY29sdW1uJTJDJTIwcmVuYW1lcyUyMGFuJTIwZXhpc3RpbmclMjBvbmUlMkMlMjBvciUyMGNoYW5nZXMlMjBkYXRhJTIwdHlwZXMuJTIwWW91ciUyMHBpcGVsaW5lJTIwZG9lc24ndCUyMGV4cGVjdCUyMHRoZXNlJTIwY2hhbmdlcy4lMjBFaXRoZXIlMjBpdCUyMGNyYXNoZXMlMkMlMjBvciUyMHdvcnNlJTJDJTIwaXQlMjBzaWxlbnRseSUyMG1hcHMlMjBkYXRhJTIwdG8lMjB0aGUlMjB3cm9uZyUyMGNvbHVtbnMuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJEaXN0cmlidXRpb24lMjBTaGlmdHMlM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBUaGUlMjBzdGF0aXN0aWNhbCUyMHByb3BlcnRpZXMlMjBvZiUyMHlvdXIlMjBkYXRhJTIwY2hhbmdlLiUyMEElMjBmaWVsZCUyMHRoYXQlMjB3YXMlMjBhbHdheXMlMjBiZXR3ZWVuJTIwMCUyMGFuZCUyMDEwMCUyMHN1ZGRlbmx5JTIwaGFzJTIwdmFsdWVzJTIwb2YlMjA1MCUyQzAwMC4lMjBZb3VyJTIwbW9kZWwncyUyMHNjYWxpbmclMjBhc3N1bXB0aW9ucyUyMGJyZWFrLiUyMFByZWRpY3Rpb25zJTIwYmVjb21lJTIwbm9uc2Vuc2ljYWwuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJOb25lJTIwb2YlMjB0aGVzZSUyMHNob3clMjB1cCUyMGluJTIwdHJhZGl0aW9uYWwlMjBpbmZyYXN0cnVjdHVyZSUyMG1vbml0b3JpbmcuJTIwWW91ciUyMENQVSUyMGlzJTIwZmluZS4lMjBNZW1vcnklMjBpcyUyMGZpbmUuJTIwTmV0d29yayUyMGlzJTIwZmluZS4lMjBCdXQlMjB5b3VyJTIwZGF0YSUyMGlzJTIwb24lMjBmaXJlLiUyMiU3RCU1RCU3RCU1RA==">
  <div data-slate-node="element">
   <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">None of these show up in traditional infrastructure monitoring. Your CPU is fine. Memory is fine. Network is fine. But your data is on fire.</span></span></span>
  </div>
  <h2 data-anchor="thesolution%3Aobservabilityateverylayer" data-slate-node="element" data-slug="thesolution%3Aobservabilityateverylayer21"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The Solution: Observability at Every Layer</span></span></span></h2>
  <div data-slate-node="element">
   <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">I started building a three-layer observability framework using dbt, Great Expectations, and custom validation logic. The goal was simple: catch data quality issues before they reach the model.</span></span></span>
  </div>
  <h3 data-anchor="layer1%3Adbttests(thefirstlineofdefense)" data-slate-node="element" data-slug="layer1%3Adbttests(thefirstlineofdefense)23"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Layer 1: dbt Tests (The First Line of Defense)</span></span></span></h3>
  <div data-slate-node="element">
   <a href="https://dzone.com/articles/dbt-false-failures"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">dbt tests</span></span></span></a><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;are your cheapest, fastest way to catch obvious data quality issues. They run after every transformation and fail the entire pipeline if something's wrong.</span></span></span>
  </div>
  <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBTb2x1dGlvbiUzQSUyME9ic2VydmFiaWxpdHklMjBhdCUyMEV2ZXJ5JTIwTGF5ZXIlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkklMjBzdGFydGVkJTIwYnVpbGRpbmclMjBhJTIwdGhyZWUtbGF5ZXIlMjBvYnNlcnZhYmlsaXR5JTIwZnJhbWV3b3JrJTIwdXNpbmclMjBkYnQlMkMlMjBHcmVhdCUyMEV4cGVjdGF0aW9ucyUyQyUyMGFuZCUyMGN1c3RvbSUyMHZhbGlkYXRpb24lMjBsb2dpYy4lMjBUaGUlMjBnb2FsJTIwd2FzJTIwc2ltcGxlJTNBJTIwY2F0Y2glMjBkYXRhJTIwcXVhbGl0eSUyMGlzc3VlcyUyMGJlZm9yZSUyMHRoZXklMjByZWFjaCUyMHRoZSUyMG1vZGVsLiUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJoZWFkaW5nJTIyJTJDJTIybGV2ZWwlMjIlM0EzJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyTGF5ZXIlMjAxJTNBJTIwZGJ0JTIwVGVzdHMlMjAoVGhlJTIwRmlyc3QlMjBMaW5lJTIwb2YlMjBEZWZlbnNlKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyZGJ0JTIwdGVzdHMlMjBhcmUlMjB5b3VyJTIwY2hlYXBlc3QlMkMlMjBmYXN0ZXN0JTIwd2F5JTIwdG8lMjBjYXRjaCUyMG9idmlvdXMlMjBkYXRhJTIwcXVhbGl0eSUyMGlzc3Vlcy4lMjBUaGV5JTIwcnVuJTIwYWZ0ZXIlMjBldmVyeSUyMHRyYW5zZm9ybWF0aW9uJTIwYW5kJTIwZmFpbCUyMHRoZSUyMGVudGlyZSUyMHBpcGVsaW5lJTIwaWYlMjBzb21ldGhpbmcncyUyMHdyb25nLiUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIySGVyZSdzJTIwd2hhdCUyMHdlJTIwaW1wbGVtZW50ZWQlM0ElMjIlN0QlNUQlN0QlNUQ=">
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Here's what we implemented:</span></span></span>
   </div>
   <div class="codeMirror-wrapper" contenteditable="false">
    <div contenteditable="false">
     <div class="codeHeader">
      <div class="nameLanguage">
       SQL
      </div><i class="icon-cancel-circled-1 cm-remove">&nbsp;</i>
     </div>
     <div class="codeMirror-code--wrapper" data-code="-- models/staging/stg_user_events.yml
version: 2

models:
  - name: stg_user_events
    columns:
      - name: user_id
        tests:
          - not_null
          - unique
      - name: event_timestamp
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: &quot;event_timestamp <= current_timestamp()&quot;
      - name: event_value
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: &quot;event_value > 0&quot;" data-lang="text/x-sql">
      <pre><code lang="text/x-sql">-- models/staging/stg_user_events.yml
version: 2

models:
  - name: stg_user_events
    columns:
      - name: user_id
        tests:
          - not_null
          - unique
      - name: event_timestamp
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: "event_timestamp &lt;= current_timestamp()"
      - name: event_value
        tests:
          - not_null
          - dbt_utils.expression_is_true:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expression: "event_value &gt; 0"</code></pre>
     </div>
    </div>
   </div>
   <div data-slate-node="element">
    <br>
   </div>
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">These tests are simple but powerful. They catch:</span></span></span>
   </div>
   <ul>
    <li>Missing required fields (<code>not_null</code>)</li>
    <li>Duplicate records (unique)</li>
    <li>Impossible values (<code>event_timestamp</code> in the future)</li>
    <li>Out-of-range values (negative prices)</li>
   </ul>
   <div data-slate-node="element">
    <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">We run these tests on every dbt run. If any test fails, the pipeline stops. No data reaches the model. No silent corruption.</span></span></span>
    <br>
    <br>
   </div>
   <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGVzZSUyMHRlc3RzJTIwYXJlJTIwc2ltcGxlJTIwYnV0JTIwcG93ZXJmdWwuJTIwVGhleSUyMGNhdGNoJTNBJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyTWlzc2luZyUyMHJlcXVpcmVkJTIwZmllbGRzJTIwKG5vdF9udWxsKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkR1cGxpY2F0ZSUyMHJlY29yZHMlMjAodW5pcXVlKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkltcG9zc2libGUlMjB2YWx1ZXMlMjAoZXZlbnRfdGltZXN0YW1wJTIwaW4lMjB0aGUlMjBmdXR1cmUpJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyT3V0LW9mLXJhbmdlJTIwdmFsdWVzJTIwKG5lZ2F0aXZlJTIwcHJpY2VzKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyV2UlMjBydW4lMjB0aGVzZSUyMHRlc3RzJTIwb24lMjBldmVyeSUyMGRidCUyMHJ1bi4lMjBJZiUyMGFueSUyMHRlc3QlMjBmYWlscyUyQyUyMHRoZSUyMHBpcGVsaW5lJTIwc3RvcHMuJTIwTm8lMjBkYXRhJTIwcmVhY2hlcyUyMHRoZSUyMG1vZGVsLiUyME5vJTIwc2lsZW50JTIwY29ycnVwdGlvbi4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRoZSUyMGJlYXV0eSUyMG9mJTIwZGJ0JTIwdGVzdHMlMjBpcyUyMHRoYXQlMjB0aGV5J3JlJTIwdmVyc2lvbi1jb250cm9sbGVkJTJDJTIwZG9jdW1lbnRlZCUyQyUyMGFuZCUyMHBhcnQlMjBvZiUyMHlvdXIlMjB0cmFuc2Zvcm1hdGlvbiUyMGNvZGUuJTIwV2hlbiUyMGElMjBzY2hlbWElMjBjaGFuZ2UlMjBoYXBwZW5zJTJDJTIweW91JTIwdXBkYXRlJTIwdGhlJTIwdGVzdCUyQyUyMGNvbW1pdCUyMGl0JTJDJTIwYW5kJTIwZXZlcnlvbmUlMjBrbm93cyUyMHdoYXQlMjBjaGFuZ2VkLiUyMiU3RCU1RCU3RCU1RA==">
    <div data-slate-node="element">
     <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The beauty of dbt tests is that they're version-controlled, documented, and part of your transformation code. When a schema change happens, you update the test, commit it, and everyone knows what changed.</span></span></span>
     <br>
     <br>
    </div>
    <h3 data-anchor="layer2%3Agreatexpectations(thestatisticalvalidator)" data-slate-node="element" data-slug="layer2%3Agreatexpectations(thestatisticalvalidator)34"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Layer 2: Great Expectations (The Statistical Validator)</span></span></span></h3>
    <div data-slate-node="element">
     <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">dbt tests catch structural issues. Great Expectations catches statistical anomalies, the subtle shifts that break models.</span></span></span>
     <br>
     <br>
    </div>
    <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTMlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJMYXllciUyMDIlM0ElMjBHcmVhdCUyMEV4cGVjdGF0aW9ucyUyMChUaGUlMjBTdGF0aXN0aWNhbCUyMFZhbGlkYXRvciklMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMmRidCUyMHRlc3RzJTIwY2F0Y2glMjBzdHJ1Y3R1cmFsJTIwaXNzdWVzLiUyMEdyZWF0JTIwRXhwZWN0YXRpb25zJTIwY2F0Y2hlcyUyMHN0YXRpc3RpY2FsJTIwYW5vbWFsaWVzJUUyJTgwJTk0dGhlJTIwc3VidGxlJTIwc2hpZnRzJTIwdGhhdCUyMGJyZWFrJTIwbW9kZWxzLiUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIySGVyZSdzJTIwYSUyMHJlYWwlMjBzY2VuYXJpbyUzQSUyMG91ciUyMHVzZXJfYWdlJTIwY29sdW1uJTIwaGFkJTIwYSUyMGRpc3RyaWJ1dGlvbiUyMG9mJTIwMTgtNjUlMjBmb3IlMjB0d28lMjB5ZWFycy4lMjBUaGVuJTIwb25lJTIwZGF5JTJDJTIwd2UlMjBzdGFydGVkJTIwZ2V0dGluZyUyMGFnZXMlMjBvZiUyMDIwMCUyQyUyMDUwMCUyQyUyMDEwMDAuJTIwQSUyMGRhdGElMjBlbnRyeSUyMGJ1ZyUyMHVwc3RyZWFtLiUyMGRidCUyMHRlc3RzJTIwd291bGRuJ3QlMjBjYXRjaCUyMHRoaXMlMjBiZWNhdXNlJTIwdGhlJTIwdmFsdWVzJTIwYXJlJTIwdGVjaG5pY2FsbHklMjB2YWxpZCUyMGludGVnZXJzLiUyMEJ1dCUyMEdyZWF0JTIwRXhwZWN0YXRpb25zJTIwd291bGQuJTIyJTdEJTVEJTdEJTVE">
     <div data-slate-node="element">
      <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Here's a real scenario: our <code>user_age</code> column had a distribution of 18-65 for two years. Then one day, we started getting ages of 200, 500, 1000. A data entry bug upstream. dbt tests wouldn't catch this because the values are technically valid integers. But Great Expectations would.</span></span></span>
     </div>
     <div class="codeMirror-wrapper" contenteditable="false">
      <div contenteditable="false">
       <div class="codeHeader">
        <div class="nameLanguage">
         Python
        </div><i class="icon-cancel-circled-1 cm-remove">&nbsp;</i>
       </div>
       <div class="codeMirror-code--wrapper" data-code="# great_expectations/expectations/user_events_expectations.py
from great_expectations.core.batch import RuntimeBatchRequest
from great_expectations.data_context import DataContext

context = DataContext()
suite = context.create_expectation_suite(
    expectation_suite_name=&quot;user_events_suite&quot;,
    overwrite_existing=True
)

validator = context.get_validator(
    batch_request=RuntimeBatchRequest(
        datasource_name=&quot;my_spark_datasource&quot;,
        data_connector_name=&quot;default_runtime_data_connector&quot;,
        data_asset_name=&quot;user_events&quot;
    ),
    expectation_suite_name=&quot;user_events_suite&quot;
)

# Expect user_age to be between 18 and 120
validator.expect_column_values_to_be_between(
    column=&quot;user_age&quot;,
    min_value=18,
    max_value=120
)

# Expect event_value to have a mean between 50 and 200
validator.expect_column_mean_to_be_between(
    column=&quot;event_value&quot;,
    min_value=50,
    max_value=200
)

# Expect less than 5% missing values in critical columns
validator.expect_column_values_to_not_be_null(
    column=&quot;user_id&quot;,
    mostly=0.95
)

# Expect the distribution to match historical patterns
validator.expect_column_kl_divergence_from_list(
    column=&quot;event_type&quot;,
    partition_object={&quot;event_type&quot;: [&quot;click&quot;, &quot;view&quot;, &quot;purchase&quot;]},
    threshold=0.1
)

validator.save_expectation_suite(discard_failed_expectations=False)" data-lang="text/x-python">
        <pre><code lang="text/x-python"># great_expectations/expectations/user_events_expectations.py
from great_expectations.core.batch import RuntimeBatchRequest
from great_expectations.data_context import DataContext

context = DataContext()
suite = context.create_expectation_suite(
    expectation_suite_name="user_events_suite",
    overwrite_existing=True
)

validator = context.get_validator(
    batch_request=RuntimeBatchRequest(
        datasource_name="my_spark_datasource",
        data_connector_name="default_runtime_data_connector",
        data_asset_name="user_events"
    ),
    expectation_suite_name="user_events_suite"
)

# Expect user_age to be between 18 and 120
validator.expect_column_values_to_be_between(
    column="user_age",
    min_value=18,
    max_value=120
)

# Expect event_value to have a mean between 50 and 200
validator.expect_column_mean_to_be_between(
    column="event_value",
    min_value=50,
    max_value=200
)

# Expect less than 5% missing values in critical columns
validator.expect_column_values_to_not_be_null(
    column="user_id",
    mostly=0.95
)

# Expect the distribution to match historical patterns
validator.expect_column_kl_divergence_from_list(
    column="event_type",
    partition_object={"event_type": ["click", "view", "purchase"]},
    threshold=0.1
)

validator.save_expectation_suite(discard_failed_expectations=False)</code></pre>
       </div>
      </div>
     </div>
     <div data-slate-node="element">
      <br>
     </div>
     <div data-slate-node="element">
      <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Great Expectations runs after dbt tests. It validates:</span></span></span>
     </div>
     <ul>
      <li>Value ranges (age between 18 and 120)</li>
      <li>Statistical properties (mean event value between 50 and 200)</li>
      <li>Null rates (less than 5% missing in critical columns)</li>
      <li>Distribution shifts (<code>event_type</code> distribution matches historical patterns)</li>
     </ul>
     <div data-slate-node="element">
      <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">If Great Expectations detects an anomaly, it alerts us. We investigate before the data reaches the model.</span></span></span>
     </div>
     <h3 data-anchor="layer3%3Acustomvalidation(thedomainexpert)" data-slate-node="element" data-slug="layer3%3Acustomvalidation(thedomainexpert)44"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Layer 3: Custom Validation (The Domain Expert)</span></span></span></h3>
     <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJHcmVhdCUyMEV4cGVjdGF0aW9ucyUyMHJ1bnMlMjBhZnRlciUyMGRidCUyMHRlc3RzLiUyMEl0JTIwdmFsaWRhdGVzJTNBJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyVmFsdWUlMjByYW5nZXMlMjAoYWdlJTIwYmV0d2VlbiUyMDE4JTIwYW5kJTIwMTIwKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlN0YXRpc3RpY2FsJTIwcHJvcGVydGllcyUyMChtZWFuJTIwZXZlbnQlMjB2YWx1ZSUyMGJldHdlZW4lMjA1MCUyMGFuZCUyMDIwMCklMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJvcmRlcmVkJTIyJTNBZmFsc2UlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJOdWxsJTIwcmF0ZXMlMjAobGVzcyUyMHRoYW4lMjA1JTI1JTIwbWlzc2luZyUyMGluJTIwY3JpdGljYWwlMjBjb2x1bW5zKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkRpc3RyaWJ1dGlvbiUyMHNoaWZ0cyUyMChldmVudF90eXBlJTIwZGlzdHJpYnV0aW9uJTIwbWF0Y2hlcyUyMGhpc3RvcmljYWwlMjBwYXR0ZXJucyklMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMklmJTIwR3JlYXQlMjBFeHBlY3RhdGlvbnMlMjBkZXRlY3RzJTIwYW4lMjBhbm9tYWx5JTJDJTIwaXQlMjBhbGVydHMlMjB1cy4lMjBXZSUyMGludmVzdGlnYXRlJTIwYmVmb3JlJTIwdGhlJTIwZGF0YSUyMHJlYWNoZXMlMjB0aGUlMjBtb2RlbC4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyaGVhZGluZyUyMiUyQyUyMmxldmVsJTIyJTNBMyUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkxheWVyJTIwMyUzQSUyMEN1c3RvbSUyMFZhbGlkYXRpb24lMjAoVGhlJTIwRG9tYWluJTIwRXhwZXJ0KSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyZGJ0JTIwYW5kJTIwR3JlYXQlMjBFeHBlY3RhdGlvbnMlMjBhcmUlMjBnZW5lcmljLiUyMFlvdXIlMjBkb21haW4lMjBpcyUyMHNwZWNpZmljLiUyMFdlJTIwYWRkZWQlMjBjdXN0b20lMjB2YWxpZGF0aW9uJTIwbG9naWMlMjB0aGF0JTIwdW5kZXJzdGFuZHMlMjBvdXIlMjBidXNpbmVzcy4lMjIlN0QlNUQlN0QlNUQ=">
      <div data-slate-node="element">
       <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">dbt and Great Expectations are generic. Your domain is specific. We added custom validation logic that understands our business.</span></span></span>
      </div>
      <div class="codeMirror-wrapper newest" contenteditable="false">
       <div contenteditable="false">
        <div class="codeHeader">
         <div class="nameLanguage">
          Python
         </div><i class="icon-cancel-circled-1 cm-remove">&nbsp;</i>
        </div>
        <div class="codeMirror-code--wrapper" data-code="# pipelines/validation/custom_validators.py
import pandas as pd
from datetime import datetime, timedelta

def validate_feature_engineering(df: pd.DataFrame) -> dict:
    &quot;&quot;&quot;
    Custom validation for features before they reach the model.
    Returns a dict of validation results.
    &quot;&quot;&quot;
    results = {}
    
    # Validate 1: Feature completeness
    # We need at least 95% of features populated
    feature_cols = [col for col in df.columns if col.startswith('feature_')]
    null_rate = df[feature_cols].isnull().sum().sum() / (len(df) * len(feature_cols))
    results['feature_completeness'] = {
        'passed': null_rate < 0.05,
        'null_rate': null_rate,
        'threshold': 0.05
    }
    
    # Validate 2: Feature scaling
    # After normalization, features should be roughly between -3 and 3 (3 sigma)
    for col in feature_cols:
        max_val = df[col].max()
        min_val = df[col].min()
        results[f'{col}_scaling'] = {
            'passed': max_val < 10 and min_val > -10,
            'max': max_val,
            'min': min_val
        }
    
    # Validate 3: Temporal consistency
    # Events should be recent (within last 30 days)
    if 'event_date' in df.columns:
        df['event_date'] = pd.to_datetime(df['event_date'])
        days_old = (datetime.now() - df['event_date'].max()).days
        results['temporal_freshness'] = {
            'passed': days_old < 30,
            'days_old': days_old,
            'threshold_days': 30
        }
    
    # Validate 4: Business logic
    # Revenue should always be positive
    if 'revenue' in df.columns:
        negative_revenue = (df['revenue'] < 0).sum()
        results['business_logic_revenue'] = {
            'passed': negative_revenue == 0,
            'negative_count': negative_revenue
        }
    
    return results

def validate_and_alert(df: pd.DataFrame, validation_results: dict) -> bool:
    &quot;&quot;&quot;
    Check all validations and alert if any fail.
    Returns True if all pass, False otherwise.
    &quot;&quot;&quot;
    all_passed = True
    
    for check_name, check_result in validation_results.items():
        if not check_result['passed']:
            all_passed = False
            print(f&quot;ALERT: {check_name} failed&quot;)
            print(f&quot;Details: {check_result}&quot;)
            # Send to monitoring system (Datadog, New Relic, etc.)
            # send_alert(check_name, check_result)
    
    return all_passed" data-lang="text/x-python">
         <pre><code lang="text/x-python"># pipelines/validation/custom_validators.py
import pandas as pd
from datetime import datetime, timedelta

def validate_feature_engineering(df: pd.DataFrame) -&gt; dict:
    """
    Custom validation for features before they reach the model.
    Returns a dict of validation results.
    """
    results = {}
    
    # Validate 1: Feature completeness
    # We need at least 95% of features populated
    feature_cols = [col for col in df.columns if col.startswith('feature_')]
    null_rate = df[feature_cols].isnull().sum().sum() / (len(df) * len(feature_cols))
    results['feature_completeness'] = {
        'passed': null_rate &lt; 0.05,
        'null_rate': null_rate,
        'threshold': 0.05
    }
    
    # Validate 2: Feature scaling
    # After normalization, features should be roughly between -3 and 3 (3 sigma)
    for col in feature_cols:
        max_val = df[col].max()
        min_val = df[col].min()
        results[f'{col}_scaling'] = {
            'passed': max_val &lt; 10 and min_val &gt; -10,
            'max': max_val,
            'min': min_val
        }
    
    # Validate 3: Temporal consistency
    # Events should be recent (within last 30 days)
    if 'event_date' in df.columns:
        df['event_date'] = pd.to_datetime(df['event_date'])
        days_old = (datetime.now() - df['event_date'].max()).days
        results['temporal_freshness'] = {
            'passed': days_old &lt; 30,
            'days_old': days_old,
            'threshold_days': 30
        }
    
    # Validate 4: Business logic
    # Revenue should always be positive
    if 'revenue' in df.columns:
        negative_revenue = (df['revenue'] &lt; 0).sum()
        results['business_logic_revenue'] = {
            'passed': negative_revenue == 0,
            'negative_count': negative_revenue
        }
    
    return results

def validate_and_alert(df: pd.DataFrame, validation_results: dict) -&gt; bool:
    """
    Check all validations and alert if any fail.
    Returns True if all pass, False otherwise.
    """
    all_passed = True
    
    for check_name, check_result in validation_results.items():
        if not check_result['passed']:
            all_passed = False
            print(f"ALERT: {check_name} failed")
            print(f"Details: {check_result}")
            # Send to monitoring system (Datadog, New Relic, etc.)
            # send_alert(check_name, check_result)
    
&nbsp; &nbsp; return all_passed</code></pre>
        </div>
       </div>
      </div>
      <div data-slate-node="element">
       <br>
      </div>
      <div data-slate-node="element">
       <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">This custom validation runs after Great Expectations. It checks:</span></span></span>
      </div>
      <ul>
       <li>Feature completeness (95% of features populated)</li>
       <li>Feature scaling (normalized features in the expected range)</li>
       <li>Temporal freshness (data is recent)</li>
       <li>Business logic (revenue is positive)</li>
      </ul>
      <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGlzJTIwY3VzdG9tJTIwdmFsaWRhdGlvbiUyMHJ1bnMlMjBhZnRlciUyMEdyZWF0JTIwRXhwZWN0YXRpb25zLiUyMEl0JTIwY2hlY2tzJTNBJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyRmVhdHVyZSUyMGNvbXBsZXRlbmVzcyUyMCg5NSUyNSUyMG9mJTIwZmVhdHVyZXMlMjBwb3B1bGF0ZWQpJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyRmVhdHVyZSUyMHNjYWxpbmclMjAobm9ybWFsaXplZCUyMGZlYXR1cmVzJTIwaW4lMjBleHBlY3RlZCUyMHJhbmdlKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRlbXBvcmFsJTIwZnJlc2huZXNzJTIwKGRhdGElMjBpcyUyMHJlY2VudCklMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIybGlzdEl0ZW0lMjIlMkMlMjJvcmRlcmVkJTIyJTNBZmFsc2UlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJCdXNpbmVzcyUyMGxvZ2ljJTIwKHJldmVudWUlMjBpcyUyMHBvc2l0aXZlKSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIySWYlMjBhbnklMjBjaGVjayUyMGZhaWxzJTJDJTIwd2UlMjBibG9jayUyMHRoZSUyMHBpcGVsaW5lJTIwYW5kJTIwYWxlcnQlMjB0aGUlMjB0ZWFtLiUyMiU3RCU1RCU3RCU1RA==">
       <div data-slate-node="element">
        <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">If any check fails, we block the pipeline and alert the team.</span></span></span>
       </div>
       <h2 data-anchor="therealworldgotchaswediscovered" data-slate-node="element" data-slug="therealworldgotchaswediscovered54"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The Real-World Gotchas We Discovered</span></span></span></h2>
       <h3 data-slate-node="element"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Gotcha 1: Validation Overhead</span></span></span></h3>
       <div data-slate-node="element">
        <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Running dbt tests, Great Expectations, and custom validation on every pipeline run adds latency. We went from 15-minute runs to 25-minute runs. The trade-off was worth it (catching one data quality issue saved us more time than we lost), but you need to plan for it.</span></span></span>
       </div>
       <h3 data-slate-node="element"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Gotcha 2: False Positives</span></span></span></h3>
       <div data-slate-node="element">
        <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Great Expectations' distribution shift detection is sensitive. Legitimate business changes (a marketing campaign causing a spike in <code>user_age</code> distribution) triggered false alerts. We had to tune thresholds carefully and add context to alerts.</span></span></span>
       </div>
       <h3 data-slate-node="element"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Gotcha 3: Schema Changes Are Sneaky</span></span></span></h3>
       <div data-slate-node="element">
        <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">A vendor added a new column to an upstream table. Our pipeline didn't break; it just ignored the new column. But the data science team expected it. We added schema validation to catch new columns and alert us.</span></span></span>
       </div>
       <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBSZWFsLVdvcmxkJTIwR290Y2hhcyUyMFdlJTIwRGlzY292ZXJlZCUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyR290Y2hhJTIwMSUzQSUyMFZhbGlkYXRpb24lMjBPdmVyaGVhZCUyMiUyQyUyMmJvbGQlMjIlM0F0cnVlJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJpc0JyZWFrJTIyJTNBdHJ1ZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMiUyMiU3RCU1RCU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjJSdW5uaW5nJTIwZGJ0JTIwdGVzdHMlMkMlMjBHcmVhdCUyMEV4cGVjdGF0aW9ucyUyQyUyMGFuZCUyMGN1c3RvbSUyMHZhbGlkYXRpb24lMjBvbiUyMGV2ZXJ5JTIwcGlwZWxpbmUlMjBydW4lMjBhZGRzJTIwbGF0ZW5jeS4lMjBXZSUyMHdlbnQlMjBmcm9tJTIwMTUtbWludXRlJTIwcnVucyUyMHRvJTIwMjUtbWludXRlJTIwcnVucy4lMjBUaGUlMjB0cmFkZS1vZmYlMjB3YXMlMjB3b3J0aCUyMGl0JTIwKGNhdGNoaW5nJTIwb25lJTIwZGF0YSUyMHF1YWxpdHklMjBpc3N1ZSUyMHNhdmVkJTIwdXMlMjBtb3JlJTIwdGltZSUyMHRoYW4lMjB3ZSUyMGxvc3QpJTJDJTIwYnV0JTIweW91JTIwbmVlZCUyMHRvJTIwcGxhbiUyMGZvciUyMGl0LiUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyR290Y2hhJTIwMiUzQSUyMEZhbHNlJTIwUG9zaXRpdmVzJTIyJTJDJTIyYm9sZCUyMiUzQXRydWUlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmlzQnJlYWslMjIlM0F0cnVlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyJTIyJTdEJTVEJTdEJTJDJTdCJTIydGV4dCUyMiUzQSUyMkdyZWF0JTIwRXhwZWN0YXRpb25zJyUyMGRpc3RyaWJ1dGlvbiUyMHNoaWZ0JTIwZGV0ZWN0aW9uJTIwaXMlMjBzZW5zaXRpdmUuJTIwTGVnaXRpbWF0ZSUyMGJ1c2luZXNzJTIwY2hhbmdlcyUyMChhJTIwbWFya2V0aW5nJTIwY2FtcGFpZ24lMjBjYXVzaW5nJTIwYSUyMHNwaWtlJTIwaW4lMjB1c2VyX2FnZSUyMGRpc3RyaWJ1dGlvbiklMjB0cmlnZ2VyZWQlMjBmYWxzZSUyMGFsZXJ0cy4lMjBXZSUyMGhhZCUyMHRvJTIwdHVuZSUyMHRocmVzaG9sZHMlMjBjYXJlZnVsbHklMjBhbmQlMjBhZGQlMjBjb250ZXh0JTIwdG8lMjBhbGVydHMuJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJHb3RjaGElMjAzJTNBJTIwU2NoZW1hJTIwQ2hhbmdlcyUyMEFyZSUyMFNuZWFreSUyMiUyQyUyMmJvbGQlMjIlM0F0cnVlJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJpc0JyZWFrJTIyJTNBdHJ1ZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMiUyMiU3RCU1RCU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjJBJTIwdmVuZG9yJTIwYWRkZWQlMjBhJTIwbmV3JTIwY29sdW1uJTIwdG8lMjBhbiUyMHVwc3RyZWFtJTIwdGFibGUuJTIwT3VyJTIwcGlwZWxpbmUlMjBkaWRuJ3QlMjBicmVhayVFMiU4MCU5NGl0JTIwanVzdCUyMGlnbm9yZWQlMjB0aGUlMjBuZXclMjBjb2x1bW4uJTIwQnV0JTIwdGhlJTIwZGF0YSUyMHNjaWVuY2UlMjB0ZWFtJTIwZXhwZWN0ZWQlMjBpdC4lMjBXZSUyMGFkZGVkJTIwc2NoZW1hJTIwdmFsaWRhdGlvbiUyMHRvJTIwY2F0Y2glMjBuZXclMjBjb2x1bW5zJTIwYW5kJTIwYWxlcnQlMjB1cy4lMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkdvdGNoYSUyMDQlM0ElMjBOdWxsJTIwSGFuZGxpbmclMjBWYXJpZXMlMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyaXNCcmVhayUyMiUzQXRydWUlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjIlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0ZXh0JTIyJTNBJTIyUHl0aG9uJTIwdHJlYXRzJTIwbnVsbCUyMGFzJTIwTm9uZS4lMjBTUUwlMjB0cmVhdHMlMjBpdCUyMGFzJTIwTlVMTC4lMjBTcGFyayUyMHRyZWF0cyUyMGl0JTIwYXMlMjBudWxsLiUyMFdoZW4lMjBkYXRhJTIwZmxvd3MlMjBiZXR3ZWVuJTIwc3lzdGVtcyUyQyUyMG51bGxzJTIwZ2V0JTIwbG9zdCUyMG9yJTIwbWlzaW50ZXJwcmV0ZWQuJTIwV2UlMjBoYWQlMjB0byUyMHN0YW5kYXJkaXplJTIwbnVsbCUyMGhhbmRsaW5nJTIwYWNyb3NzJTIwdGhlJTIwZW50aXJlJTIwcGlwZWxpbmUuJTIyJTdEJTVEJTdEJTVE">
        <h3 data-slate-node="element"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Gotcha 4: Null Handling Varies</span></span></span></h3>
        <div data-slate-node="element">
         <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Python treats null as None. SQL treats it as NULL. Spark treats it as null. When data flows between systems, nulls get lost or misinterpreted. We had to standardize null handling across the entire pipeline.</span></span></span>
        </div>
        <h2 data-anchor="theframework%3Aadecisionmatrix" data-slate-node="element" data-slug="theframework%3Aadecisionmatrix60"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The Framework: A Decision Matrix</span></span></span></h2>
        <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBGcmFtZXdvcmslM0ElMjBBJTIwRGVjaXNpb24lMjBNYXRyaXglMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkhlcmUncyUyMGhvdyUyMHdlJTIwZGVjaWRlJTIwd2hpY2glMjB2YWxpZGF0aW9uJTIwbGF5ZXIlMjB0byUyMHVzZSUzQSUyMiU3RCU1RCU3RCU1RA==">
         <div data-slate-node="element">
          <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Here's how we decide which validation layer to use:</span></span></span>
         </div>
         <div class="table-responsive" style="border: none;">
          <table style="width: auto; max-width: 100%; table-layout: fixed; display: table;" width="auto">
           <tbody>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Issue Type</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Caught By</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Example</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Action</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Missing required field</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">dbt tests</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">user_id is null</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Fail pipeline immediately</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Duplicate records</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">dbt tests</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Same user_id appears twice</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Fail pipeline immediately</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Impossible values</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">dbt tests</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">event_timestamp in future</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Fail pipeline immediately</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Out-of-range values</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Great Expectations</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">age &gt; 150</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Alert, investigate, fail if severe</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Distribution shift</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Great Expectations</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">event_value mean changes 50%</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Alert, investigate, continue if acceptable</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Business logic violation</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Custom validation</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">revenue is negative</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Alert, investigate, fail</td>
            </tr>
            <tr style="overflow-wrap: break-word; width: auto;" width="auto">
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Schema change</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Custom validation</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">New column added upstream</td>
             <td style="width: auto; overflow-wrap: break-word;" width="auto">Alert, investigate, update tests</td>
            </tr>
           </tbody>
          </table>
         </div>
         <h2 data-anchor="theresults%3Afromchaostoconfidence" data-slate-node="element" data-slug="theresults%3Afromchaostoconfidence64"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The Results: From Chaos to Confidence</span></span></span></h2>
         <div data-slate-node="element">
          <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">After implementing this three-layer framework:</span></span></span>
         </div>
         <ul>
          <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Incident reduction:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;We went from 2-3 data quality incidents per month to 0 in six months.</span></span></span></li>
          <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Time to resolution:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;When issues do occur, we catch them within minutes instead of hours.</span></span></span></li>
          <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Model stability:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Model accuracy stopped fluctuating. It's now consistently 93-95%.</span></span></span></li>
          <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Team confidence:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Data scientists trust the data. Engineers trust the pipeline.</span></span></span></li>
         </ul>
         <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBSZXN1bHRzJTNBJTIwRnJvbSUyMENoYW9zJTIwdG8lMjBDb25maWRlbmNlJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJBZnRlciUyMGltcGxlbWVudGluZyUyMHRoaXMlMjB0aHJlZS1sYXllciUyMGZyYW1ld29yayUzQSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkluY2lkZW50JTIwcmVkdWN0aW9uJTNBJTIyJTJDJTIyYm9sZCUyMiUzQXRydWUlN0QlMkMlN0IlMjJ0ZXh0JTIyJTNBJTIyJTIwV2UlMjB3ZW50JTIwZnJvbSUyMDItMyUyMGRhdGElMjBxdWFsaXR5JTIwaW5jaWRlbnRzJTIwcGVyJTIwbW9udGglMjB0byUyMDAlMjBpbiUyMHNpeCUyMG1vbnRocyUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRpbWUlMjB0byUyMHJlc29sdXRpb24lM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBXaGVuJTIwaXNzdWVzJTIwZG8lMjBvY2N1ciUyQyUyMHdlJTIwY2F0Y2glMjB0aGVtJTIwd2l0aGluJTIwbWludXRlcyUyMGluc3RlYWQlMjBvZiUyMGhvdXJzJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQWZhbHNlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyTW9kZWwlMjBzdGFiaWxpdHklM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBNb2RlbCUyMGFjY3VyYWN5JTIwc3RvcHBlZCUyMGZsdWN0dWF0aW5nLiUyMEl0J3MlMjBub3clMjBjb25zaXN0ZW50bHklMjA5My05NSUyNSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0FmYWxzZSUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRlYW0lMjBjb25maWRlbmNlJTNBJTIyJTJDJTIyYm9sZCUyMiUzQXRydWUlN0QlMkMlN0IlMjJ0ZXh0JTIyJTNBJTIyJTIwRGF0YSUyMHNjaWVudGlzdHMlMjB0cnVzdCUyMHRoZSUyMGRhdGEuJTIwRW5naW5lZXJzJTIwdHJ1c3QlMjB0aGUlMjBwaXBlbGluZSUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJwJTIyJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyVGhlJTIwYmVzdCUyMHBhcnQlM0YlMjBXZSUyMGNhdWdodCUyMHRoZSUyMHNjaGVtYSUyMGNoYW5nZSUyMGluY2lkZW50JTIwYmVmb3JlJTIwaXQlMjBoYXBwZW5lZC4lMjBHcmVhdCUyMEV4cGVjdGF0aW9ucyUyMGRldGVjdGVkJTIwdGhlJTIwZGlzdHJpYnV0aW9uJTIwc2hpZnQlMkMlMjB3ZSUyMGludmVzdGlnYXRlZCUyQyUyMGZvdW5kJTIwdGhlJTIwdXBzdHJlYW0lMjBjaGFuZ2UlMkMlMjBhbmQlMjBjb29yZGluYXRlZCUyMHdpdGglMjB0aGUlMjB2ZW5kb3IlMjB0ZWFtJTIwYmVmb3JlJTIwYW55JTIwZGF0YSUyMHJlYWNoZWQlMjBwcm9kdWN0aW9uLiUyMiU3RCU1RCU3RCU1RA==">
          <div data-slate-node="element">
           <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The best part? We caught the schema change incident before it happened. Great Expectations detected the distribution shift, we investigated, found the upstream change, and coordinated with the vendor team before any data reached production.</span></span></span>
          </div>
          <h2 data-anchor="gettingstarted%3Atheminimalviableobservability" data-slate-node="element" data-slug="gettingstarted%3Atheminimalviableobservability72"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Getting Started: The Minimal Viable Observability</span></span></span></h2>
          <div data-slate-node="element">
           <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">You don't need to implement everything at once. Start here:</span></span></span>
          </div>
          <ol>
           <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Week 1:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Add dbt tests for not_null and unique on critical columns.</span></span></span></li>
           <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Week 1:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Add dbt tests for not_null and unique on critical columns.</span></span></span></li>
           <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Week 1:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Add dbt tests for not_null and unique on critical columns.</span></span></span></span></span></span></li>
           <li><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Week 4:</span></span></span><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">&nbsp;Set up alerting so you're notified when validations fail.</span></span></span></span></span></span></span></span></span></li>
          </ol>
          <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJHZXR0aW5nJTIwU3RhcnRlZCUzQSUyMFRoZSUyME1pbmltYWwlMjBWaWFibGUlMjBPYnNlcnZhYmlsaXR5JTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJZb3UlMjBkb24ndCUyMG5lZWQlMjB0byUyMGltcGxlbWVudCUyMGV2ZXJ5dGhpbmclMjBhdCUyMG9uY2UuJTIwU3RhcnQlMjBoZXJlJTNBJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmxpc3RJdGVtJTIyJTJDJTIyb3JkZXJlZCUyMiUzQXRydWUlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJXZWVrJTIwMSUzQSUyMiUyQyUyMmJvbGQlMjIlM0F0cnVlJTdEJTJDJTdCJTIydGV4dCUyMiUzQSUyMiUyMEFkZCUyMGRidCUyMHRlc3RzJTIwZm9yJTIwbm90X251bGwlMjBhbmQlMjB1bmlxdWUlMjBvbiUyMGNyaXRpY2FsJTIwY29sdW1ucyUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0F0cnVlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyV2VlayUyMDIlM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBBZGQlMjBHcmVhdCUyMEV4cGVjdGF0aW9ucyUyMGZvciUyMHZhbHVlJTIwcmFuZ2VzJTIwb24lMjBudW1lcmljJTIwY29sdW1ucyUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0F0cnVlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyV2VlayUyMDMlM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBBZGQlMjBjdXN0b20lMjB2YWxpZGF0aW9uJTIwZm9yJTIweW91ciUyMHNwZWNpZmljJTIwYnVzaW5lc3MlMjBsb2dpYyUyMiU3RCU1RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJsaXN0SXRlbSUyMiUyQyUyMm9yZGVyZWQlMjIlM0F0cnVlJTJDJTIyY2hpbGRyZW4lMjIlM0ElNUIlN0IlMjJ0ZXh0JTIyJTNBJTIyV2VlayUyMDQlM0ElMjIlMkMlMjJib2xkJTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMnRleHQlMjIlM0ElMjIlMjBTZXQlMjB1cCUyMGFsZXJ0aW5nJTIwc28lMjB5b3UncmUlMjBub3RpZmllZCUyMHdoZW4lMjB2YWxpZGF0aW9ucyUyMGZhaWwlMjIlN0QlNUQlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIycCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRoYXQncyUyMGl0LiUyMFlvdSUyMG5vdyUyMGhhdmUlMjBvYnNlcnZhYmlsaXR5JTIwaW4lMjB5b3VyJTIwZGF0YSUyMHBpcGVsaW5lLiUyMiU3RCU1RCU3RCU1RA==">
           <div data-slate-node="element">
            <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">That's it. You now have observability in your data pipeline.</span></span></span>
           </div>
          </div>
         </div>
         <h2 data-anchor="conclusion%3Aobservabilitysavesmodels" data-slate-node="element" data-slug="conclusion%3Aobservabilitysavesmodels80"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Conclusion: Observability Saves Models</span></span></span></h2>
         <div data-slate-node="element">
          <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Your AI model isn't failing because it's bad. It's failing because the data feeding it is bad. And you won't know the data is bad until you look.</span></span></span>
         </div>
         <div data-slate-node="element">
          <br>
         </div>
         <div data-slate-node="element">
          <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">The best models in the world can't save you from garbage data. But good observability can. dbt tests, Great Expectations, and custom validation aren't fun. They don't make it into conference talks. But they'll save your production system at 3:00 AM.</span></span></span>
         </div>
         <div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMmhlYWRpbmclMjIlMkMlMjJsZXZlbCUyMiUzQTIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJDb25jbHVzaW9uJTNBJTIwT2JzZXJ2YWJpbGl0eSUyMFNhdmVzJTIwTW9kZWxzJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJZb3VyJTIwQUklMjBtb2RlbCUyMGlzbid0JTIwZmFpbGluZyUyMGJlY2F1c2UlMjBpdCdzJTIwYmFkLiUyMEl0J3MlMjBmYWlsaW5nJTIwYmVjYXVzZSUyMHRoZSUyMGRhdGElMjBmZWVkaW5nJTIwaXQlMjBpcyUyMGJhZC4lMjBBbmQlMjB5b3UlMjB3b24ndCUyMGtub3clMjB0aGUlMjBkYXRhJTIwaXMlMjBiYWQlMjB1bnRpbCUyMHlvdSUyMGxvb2suJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJUaGUlMjBiZXN0JTIwbW9kZWxzJTIwaW4lMjB0aGUlMjB3b3JsZCUyMGNhbid0JTIwc2F2ZSUyMHlvdSUyMGZyb20lMjBnYXJiYWdlJTIwZGF0YS4lMjBCdXQlMjBnb29kJTIwb2JzZXJ2YWJpbGl0eSUyMGNhbi4lMjBkYnQlMjB0ZXN0cyUyQyUyMEdyZWF0JTIwRXhwZWN0YXRpb25zJTJDJTIwYW5kJTIwY3VzdG9tJTIwdmFsaWRhdGlvbiUyMGFyZW4ndCUyMHNleHkuJTIwVGhleSUyMGRvbid0JTIwbWFrZSUyMGl0JTIwaW50byUyMGNvbmZlcmVuY2UlMjB0YWxrcy4lMjBCdXQlMjB0aGV5J2xsJTIwc2F2ZSUyMHlvdXIlMjBwcm9kdWN0aW9uJTIwc3lzdGVtJTIwYXQlMjAzJTIwQU0uJTIyJTdEJTVEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMnAlMjIlMkMlMjJjaGlsZHJlbiUyMiUzQSU1QiU3QiUyMnRleHQlMjIlM0ElMjJTdGFydCUyMHNtYWxsLiUyMFRlc3QlMjBlYXJseS4lMjBWYWxpZGF0ZSUyMG9mdGVuLiUyMiU3RCU1RCU3RCU1RA==">
          <div data-slate-node="element">
           <br>
          </div>
          <div data-slate-node="element">
           <span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">Start small. Test early. Validate often.</span></span></span>
          </div>
         </div>
        </div>
       </div>
      </div>
     </div>
    </div>
   </div>
  </div>
 </div>
</div></p>]]></description>
      <pubDate>Fri, 26 Jun 2026 16:00:05 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3643570</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19061479&amp;w=600"/>
      <dc:creator>Abhilash Rao Mesala</dc:creator>
    </item>
    <item>
      <title>Implementing Asynchronous Communication Between Microservices Using Kafka and Spring Boot</title>
      <link>https://dzone.com/articles/asynchronous-microservices-communication-kafka-spring-boot</link>
      <description><![CDATA[<p>In a microservices system, that tight coupling turns a small hiccup into a cascading slowdown. Thread pools fill, retries amplify traffic, and suddenly your simple request is blocked on half the fleet. My executive summary: asynchronous messaging with Kafka helps systems keep moving when individual components inevitably slow down or fail. It does this by decoupling producers from consumers, absorbing traffic spikes, and allowing services to evolve without tying their availability directly to one another.</p>
<h2>Code Patterns in Spring Boot With Kafka</h2>
<p>Spring for Apache Kafka gives me two primitives that feel pleasantly old Spring <code>KafkaTemplate</code> for sending and <code>@KafkaListener</code> for receiving. That template/listener model is intentionally similar to other Spring integration tech, which keeps application code focused on domain logic instead of raw client plumbing.&nbsp;</p>]]></description>
      <pubDate>Wed, 24 Jun 2026 13:00:05 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3643443</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19056287&amp;w=600"/>
      <dc:creator>Mallikharjuna Manepalli</dc:creator>
    </item>
    <item>
      <title>Data Governance Checklist for AI-Driven Systems</title>
      <link>https://dzone.com/articles/ai-data-governance-checklist</link>
      <description><![CDATA[<p style="font-size: 17px;"><em>Editor’s Note: The following is an article written for and published in DZone’s 2026 Trend Report,&nbsp;</em><a href="https://dzone.com/link/2026-tr-databases-data-contributor-article" rel="noopener noreferrer" target="_blank"><em>Cognitive Databases, Intelligent Data: Unified Infrastructure for Vector Search, AI-Optimized Queries, and Hybrid Workloads</em></a>.</p>
<hr>
<p dir="ltr">Many teams find governance gaps only after a retrieval system surfaces stale or unauthorized content in production. Models, agents, and retrieval workflows all depend on enterprise data. Before any of that data reaches an AI system, teams need to know where it originates, how it’s integrated, whether it meets quality expectations, what context enriches it, who can access it, and how it changes over time.</p>]]></description>
      <pubDate>Tue, 23 Jun 2026 12:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3660855</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19052925&amp;w=600"/>
      <dc:creator>Abhishek Gupta</dc:creator>
    </item>
    <item>
      <title>The Real-Time Revolution: Why Blockchain Needs Data Stream Processing</title>
      <link>https://dzone.com/articles/why-blockchain-needs-data-stream-processing</link>
      <description><![CDATA[<p><span>Blockchain is an extremely data-driven technology because its primary function is to store, verify, and coordinate independent records in a secure, distributed data network. Without this information, no transaction, smart contract execution, or network activity would be valid, and it could jeopardize the integrity of much larger functions of trust.&nbsp;</span></p>
<p><span>T</span><span>he data coming into the blockchain affects the accuracy of the whole system. Blockchain is nothing without the data it connects to, so, as far as transparency, immutability, and safe decisions are concerned, data is the backbone of blockchain.</span></p>]]></description>
      <pubDate>Wed, 17 Jun 2026 18:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3659742</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19049426&amp;w=600"/>
      <dc:creator>Gautam Goswami</dc:creator>
    </item>
    <item>
      <title>Parallel Kafka Batch Processing With Kotlin Coroutines in Spring Boot</title>
      <link>https://dzone.com/articles/parallel-kafka-processing</link>
      <description><![CDATA[<p style="text-align: justify;">Managing high-volume message traffic in distributed architectures is crucial. Efficient use of database and CPU resources is also very important. There are structures that allow us to receive messages in batches. The default Spring Kafka "BatchMessageListener" structure addresses this need. However, the processing of these messages often goes through a sequential bottleneck.</p>
<p>This article will discuss the structure and usage of Kotlin Coroutines in detail. We will examine how to maximize Kafka message processing performance using Structured Concurrency principles and Resource Throttling techniques.</p>]]></description>
      <pubDate>Tue, 16 Jun 2026 19:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3652393</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19051329&amp;w=600"/>
      <dc:creator>Erkin Karanlık</dc:creator>
    </item>
    <item>
      <title>From ETL to Lakeflow: Shifting to a Declarative Data Paradigm</title>
      <link>https://dzone.com/articles/shifting-to-declarative-data-paradigm</link>
      <description><![CDATA[<p dir="ltr">If you've worked on a data platform for more than a few years, you've almost certainly built the same pipeline twice. First, the way the team wrote pipelines in 2019: a notebook here, a Python script there, an <a href="https://dzone.com/articles/airflow-dag-failure-detection-ai">Airflow DAG</a> to glue it all together, and a long document explaining the order things had to run in. Then the rewrite, two years later, when somebody quit, and nobody could remember why a particular task had a sleep(180) in it.&nbsp;</p>
<p dir="ltr">Lakeflow is Databricks' answer to that pattern, and the shift it's pushing for is bigger than the marketing makes it sound. It isn't a new orchestrator. It's a move from imperative pipelines, where you write the steps, to declarative pipelines, where you write the destination and let the engine figure out the steps. What follows is the practical version of that shift — what's actually different, where the gains are real, and how to migrate without ending up with a half-converted lakehouse.</p>]]></description>
      <pubDate>Mon, 15 Jun 2026 15:00:00 GMT</pubDate>
      <guid isPermaLink="false">https://dzone.com/articles/3505797</guid>
      <media:thumbnail url="https://dz2cdn1.dzone.com/thumbnail?fid=19049183&amp;w=600"/>
      <dc:creator>Seshendranath Balla Venkata</dc:creator>
    </item>
  </channel>
</rss>
