Belajar Data Engineer - Data Quality & Testing
Episode 14 of 28

Belajar Data Engineer - Data Quality & Testing

Membangun budaya data quality yang terukur: empat dimensi inti (freshness, volume, schema, dan content), implementasi dengan dbt tests, Great Expectations, dan Soda, serta praktik merancang data quality framework yang berjalan di pipeline

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Di episode 13 pipeline kalian sudah bisa menunggu, gagal dengan benar, dan pulih. Tapi ada satu pertanyaan yang belum dijawab: bagaimana kalian tahu data yang dikirim ke konsumen itu benar? Pipeline yang "berhasil" dengan data kosong, data ganda, atau schema yang berubah diam-diam adalah kegagalan yang paling berbahaya — karena tidak ada yang menyadarinya.

Episode ini membangun data quality framework: empat dimensi yang bisa diukur, tool yang mengotomatisasi pengecekannya (dbt tests, Great Expectations, Soda), dan cara menempatkannya sebagai quality gate di pipeline — bukan sekadar laporan yang dibaca kalau sempat.

Empat Dimensi Data Quality

Semua masalah kualitas data bisa dikelompokkan ke empat dimensi yang terukur:

DimensiPertanyaanContoh Check
FreshnessApakah data terbarui sesuai jadwal?max(created_at) masih di bawah 12 jam
VolumeApakah jumlah data masuk akal?Baris hari ini vs rata-rata 7 hari (±20%)
SchemaApakah struktur masih sesuai kontrak?Kolom yang diharapkan ada, tipe data benar
ContentApakah isi data valid?Tidak ada NULL pada key, nilai dalam rentang wajar

Empat dimensi ini adalah minimum viable data quality. Tanpa keempatnya, data "berhasil dimuat" belum berarti apa-apa.

Freshness dan Volume Check

Kedua dimensi ini paling sering diabaikan, padahal paling cepat mendeteksi kegagalan sumber. Freshness menjawab: sumber mati tanpa kita sadari? Volume menjawab: sumber mengirim separuh data tanpa kita sadari?

Freshness & volume check (SQL murni)
-- Freshness: data terakhir kapan?
SELECT max(ingested_at) AS last_data
FROM raw_orders;
 
-- Volume: perbandingan vs kemarin
WITH today AS (
    SELECT COUNT(*) AS n FROM raw_orders WHERE ingested_at::date = CURRENT_DATE
), yesterday AS (
    SELECT COUNT(*) AS n FROM raw_orders WHERE ingested_at::date = CURRENT_DATE - 1
)
SELECT
    today.n AS today,
    yesterday.n AS yesterday,
    CASE WHEN yesterday.n = 0 THEN NULL
         ELSE ROUND((today.n - yesterday.n)::numeric / yesterday.n, 3)
    END AS delta_pct
FROM today, yesterday;

Di dbt, keduanya sudah diformalkan — freshness lewat sources.yml (episode 7), volume lewat test kustom:

tests/assert_volume_not_drop.sql
-- Gagal jika volume hari ini turun > 30% dibanding kemarin
WITH vol AS (
    SELECT
        COUNT(*) FILTER (WHERE DATE(created_at) = CURRENT_DATE) AS today,
        COUNT(*) FILTER (WHERE DATE(created_at) = CURRENT_DATE - 1) AS yesterday
    FROM {{ ref('stg_orders') }}
)
SELECT * FROM vol
WHERE today < 0.7 * yesterday;

dbt Test: Quality Menempel pada Model

dbt tests (episode 7) adalah fondasi: test generik (unique, not_null, accepted_values, relationships) dan test kustom sebagai query yang harus kosong. Kuncinya adalah severity — tidak semua kegagalan harus menghentikan pipeline:

models/marts/_marts.yml
models:
  - name: fct_orders
    tests:
      - dbt_utils.accepted_range:
          column_name: total_amount
          min_value: 0
          severity: error       # blokir jika negatif
  - name: stg_orders
    tests:
      - dbt_utils.expression_is_true:
          expression: "length(CAST(order_id AS TEXT)) > 0"
          severity: warn        # catat tapi jangan blokir

Strategi severity yang sehat:

  • error untuk pelanggaran critical path: key NULL, amount negatif, referensi rusak.
  • warn untuk anomali yang perlu diketahui tapi tidak memblokir: outlier volume, nilai jarang.

Tip

Quality gate bukan berarti pipeline harus berhenti di setiap anomali. Pipeline yang sehat membedakan "gagal segera" (kesalahan permanen) dari "catat dulu" (anomali sementara). Menyetel semua test sebagai error akan membuat tim mati rasa terhadap alarm.

Great Expectations dan Soda: Validation Framework

dbt menguji hasil model SQL. Untuk validasi file/data di luar SQL — data frame Python, file landing, sebelum masuk ke database — gunakan framework validasi khusus.

Great Expectations

Framework validasi data dengan Expectations: deklarasi assert seperti expect_column_values_to_not_be_null. Biasanya dijalankan setelah data landing sebelum di-load:

ge_validate.py
import great_expectations as gx
 
context = gx.get_context()
 
suite = context.add_expectation_suite("orders_suite")
suite.add_expectation(
    gx.expectations.ExpectColumnValuesToNotBeNull,
    column="order_id",
)
suite.add_expectation(
    gx.expectations.ExpectColumnValuesToBeBetween,
    column="amount",
    min_value=0,
)
suite.add_expectation(
    gx.expectations.ExpectColumnMeanToBeBetween,
    column="amount",
    min_value=10,
    max_value=100000,
)
 
df = gx.read_pandas(pd.read_csv("landing/orders.csv"))
result = df.validate(suite)
if not result["success"]:
    raise SystemExit("VALIDATION FAILED: data tidak lolos quality gate")
print("OK: semua expectations lolos")

Keunggulan Great Expectations: validasi bisa dijalankan sebelum data masuk database — menghentikan data buruk di pintu, bukan setelah mencemari tabel.

Soda

Soda berfokus pada data warehouse dengan konfigurasi YAML yang ringkas dan integrasi alerting yang baik:

checks/orders.yml
checks for raw_orders:
  - freshness(ingested_at) < 12h
  - row_count > 0
  - missing_count(amount) = 0
  - invalid_count(amount) < 10:
      valid min: 0
      valid max: 100000000

freshness(ingested_at) &lt; 12h menyatakan batch check dalam satu file yang dibaca orang: kondisi, bukan kode. Soda bisa dijalankan di Airflow dan mengirim hasil ke Slack — sangat cocok sebagai quality gate harian.

Praktik: Data Quality Framework

Rangkai semuanya menjadi framework yang berjalan di pipeline Airflow:

  1. Validasi landing — Great Expectations memeriksa file/API response sebelum masuk DB.
  2. dbt build — transformasi menghasilkan silver/gold.
  3. dbt test — test generik + kustom menempel di model.
  4. Soda scan — batch check freshness/volume pada tabel final.
  5. Gate — jika severity error gagal, pipeline berhenti dan notify; jika warn, lanjut tapi tercatat di observability.
Sequence quality gate di pipeline
python ge_validate.py &&
dbt build --select state:modified &&
dbt test --select severity:error &&
soda scan -d de_lab -c soda/configuration.yml soda/checks/orders.yml

Urutan && memastikan: data tidak maju ke tahap berikutnya jika gagal di tahap ini — fail fast, fail loudly.

Kesalahan Umum (Common Pitfalls)

  1. Hanya menguji content, lupa freshness & volume. Sumber mati tak terdeteksi sampai berhari-hari. Empat dimensi wajib lengkap.

  2. Semua test severity: error. Alarm yang selalu berbunyi membuat tim abai. Pisahkan blokir vs peringatan.

  3. Validasi setelah data mencemari warehouse. Validasi di landing (sebelum DB) jauh lebih murah daripada bersih-bersih setelahnya.

  4. Quality check tanpa pemilik. Check yang tidak ada yang bertanggung jawab akan mati sendiri. Tetapkan pemilik tiap dataset.

Penutup

Di episode 14 ini kalian telah membangun fondasi data quality:

  • Empat dimensi: freshness, volume, schema, content — semua terukur dan bisa diotomasi.
  • dbt test menempel di model dengan severity error vs warn yang disengaja.
  • Great Expectations memvalidasi data sebelum masuk database; Soda memeriksa warehouse dengan YAML ringkas.
  • Framework quality gate: landing validation → build → test → scan → gate, fail fast & loudly.

Di episode 15 selanjutnya kita membahas tata kelola: data governance & lineage — data catalog, lineage, access control, dan metadata management dengan DataHub & OpenMetadata, plus praktik setup catalog & lineage. Sampai jumpa di episode 15!

Belajar Data Engineer - Data Quality & Testing | Belajar Data Engineer