This page requires JavaScript to display.
A
Unpacking...
← Data Engineering
Data Engineering

Enterprise Data Architect

Architecture styles, table formats, compatibility, governance, privacy, security and lineage across AWS, Azure, Google Cloud, Snowflake and Databricks, and how to implement lakes, warehouses, lakehouses and data mesh and modernise legacy systems.

Contents
1. What a Data Architect Does 2. The Platform at a Glance 3. Architecture Styles 4. Medallion Architecture and Data Layers 5. Edge, Ingestion and the Landing Zone 6. Choosing an Open Table Format 7. Data Integration Patterns 8. Compatibility and Schema Evolution 9. Reliability and Consistency Patterns 10. Scalability, Performance and Workload Isolation 11. Event-Driven Architecture 12. Enterprise Modelling and the Semantic Layer 13. Challenges in Data Modelling 14. Joining Two Fact Tables 15. Data Governance 16. Metadata and Catalogs 17. Data Classification 18. Data Privacy 19. Data Security and How to Implement It 20. Data Lineage 21. Building Enterprise Lineage Across Systems 22. Data Quality 23. Data Quality KPIs at Enterprise Level 24. Master Data Management (MDM) 25. Observability and Operations 26. Resilience, Lifecycle and Cost 27. Data Architecture for AI 28. Choosing Technology 29. Implementing an Enterprise Data Lake 30. Implementing an Enterprise Data Warehouse 31. Implementing a Lakehouse on Delta Lake 32. Implementing a Data Mesh 33. Modernising Legacy Systems 34. Putting It Together: The Retailer's Plan 35. Common Mistakes 36. Common Interview Questions

1. What a Data Architect Does

A data architect designs how an organisation's data is collected, stored, connected, protected and used, so that many teams can build on it safely. Data engineers build pipelines; the architect decides the rules and the structure those pipelines fit into.

RoleMain questionTypical output
Data engineerHow do I move and transform this data?Pipelines, jobs, tables
Data modelerHow should this data be structured?Logical and physical models
Data architectHow does all our data fit together, safely, at scale?Platform design, standards, policies, decisions
Data governance leadWho owns the data, and which rules apply?Policies, roles, glossary

This card continues the online retailer from the Data Modelling and dbt cards. The sales mart works. But the company has grown to 40 pipelines and three teams, it now holds customer data from India and Europe, and an auditor has asked a simple question: "Who can see customer emails, and where does that data go?" Nobody can answer it quickly. Answering it properly is the architect's job, and each section below solves part of it.

ResponsibilityCovered in
Overall platform designThe platform at a glance
Who owns and defines dataData governance, metadata and catalogs
Which data is sensitiveData classification
Legal duties for personal dataData privacy
Who can access whatData security
Where data comes from and goesData lineage
Whether the data is rightData quality
Recovering from failure, controlling costResilience, lifecycle and cost

Thinking across the whole enterprise

An enterprise data architect is not tied to one product. A large company usually runs several platforms at once: an ERP on Oracle or SAP, a CRM in the cloud, a warehouse on Snowflake or BigQuery, a lakehouse on Databricks, Kafka for events and Power BI or Tableau for reporting. The architect's job is to make them work together under one set of rules, and to choose new technology for good reasons rather than habit.

Skill areaWhat to know
PlatformsAWS, Azure and Google Cloud data services; Databricks, Snowflake; on-premise databases
Architecture stylesWarehouse, lake, lakehouse, data mesh, data fabric, federation
IntegrationBatch, CDC, streaming, APIs, replication and virtualisation
Governance and securityCatalogs, classification, access models, encryption, audit, privacy law
Data managementModelling, quality, MDM, metadata, lineage, lifecycle
DeliveryCost, migration, vendor selection, roadmaps, stakeholder management

Frameworks worth knowing

FrameworkWhat it gives you
TOGAFAn enterprise architecture method (the ADM cycle), where data architecture is one of four domains
Zachman FrameworkA grid for describing an enterprise from different viewpoints
DAMA-DMBOKThe body of knowledge for data management, with governance at the centre
DCAM (EDM Council)A capability model for assessing data management maturity
Cloud well-architected frameworksAWS, Azure and Google Cloud guidance on reliability, security, cost and performance

You rarely follow a framework rigidly, but interviewers expect you to know them, and they give a shared vocabulary with enterprise architects and auditors.

2. The Platform at a Glance

An architect first draws the whole platform, because every later decision has to fit into it. The layers in the middle carry data; the band underneath applies to all of them, which is why governance and security cannot be added to one pipeline at a time.

 SOURCES        Orders DB   CRM    ERP    Clickstream (Kafka)   Partner APIs
                    │        │      │          │                    │
 INGESTION      CDC, batch connectors, streaming (Kafka, Kinesis, Event Hubs)
                    │
 STORAGE        Bronze (raw)  ─►  Silver (clean)  ─►  Gold (business)
                Open table formats on object storage, or a cloud warehouse
                    │
 PROCESSING     Spark, SQL warehouses, dbt, stream processors
                    │
 SERVING        BI tools, ML models, data sharing, APIs, reverse ETL
 ───────────────────────────────────────────────────────────────────────
 ACROSS ALL     Governance │ Catalog │ Security │ Lineage │ Quality │ Cost

The data movement patterns behind this picture, such as Lambda, Kappa and medallion, are covered in the Data Architecture Patterns card.

The same layers on every platform

The layers stay the same whichever vendor you choose; only the product names change. This map is the most useful single table for an architect working across clouds.

LayerAWSAzureGoogle Cloud
Object storageS3ADLS Gen2, OneLakeCloud Storage
WarehouseRedshiftFabric Warehouse, SynapseBigQuery
Spark and lakehouseEMR, GlueFabric Spark, Azure DatabricksDataproc
Batch integrationGlue, AppFlowData FactoryDataflow, Data Fusion
Change data captureDMSData Factory, Fabric mirroringDatastream
StreamingKinesis, MSKEvent HubsPub/Sub
OrchestrationMWAA (Airflow), Step FunctionsData FactoryCloud Composer (Airflow)
Catalog and governanceGlue Data Catalog, Lake Formation, SageMaker CatalogMicrosoft PurviewKnowledge Catalog (formerly Dataplex)
IdentityIAM, IAM Identity CenterMicrosoft Entra IDCloud IAM
Keys and secretsKMS, Secrets ManagerKey VaultCloud KMS, Secret Manager
Sensitive data discoveryMaciePurviewSensitive Data Protection
AuditCloudTrailAzure Monitor, activity logsCloud Audit Logs
BIQuickSightPower BILooker
Machine learningSageMakerAzure Machine LearningVertex AI

Databricks and Snowflake run on all three clouds, and Kafka (or Confluent), Airflow, dbt and Tableau run almost anywhere. These cross-cloud tools are often chosen precisely to avoid being tied to one cloud.

Cloud product names change often. For example, Google renamed Dataplex to Knowledge Catalog in April 2026, and AWS has moved DataZone into SageMaker. Check current names when writing a design document.

The decisions an architect records

Big choices should be written down with their reasons, as an architecture decision record (ADR). When someone asks in two years why the company chose Delta over Iceberg, the answer is in the record, not in a person's memory.

ADR-007: Use Unity Catalog as the single governance layer

Status:   Accepted (2026-03-14)
Context:  Three teams manage access separately; an audit could not
          list who can read customer emails.
Decision: Register every table in Unity Catalog. Grant access to
          groups, never users. Tag personal data with governed tags.
Options:  Per-workspace Hive metastore (rejected: no central audit),
          external catalog only (rejected: no enforcement).
Impact:   All new tables must be Unity Catalog managed tables.
          Existing Hive tables are migrated by June.

Non-functional requirements

Most architecture is driven by requirements that are not features. Agree on these numbers early, because they decide cost and design.

RequirementQuestionRetailer example
FreshnessHow old can the data be?Sales by 7 a.m.; stock every 5 minutes
AvailabilityHow much downtime is acceptable?99.5% for dashboards
Recovery (RPO)How much data can we lose?1 hour
Recovery (RTO)How fast must we recover?4 hours
RetentionHow long do we keep data?Orders 8 years; logs 90 days
ComplianceWhich laws apply?DPDP Act, GDPR, PCI DSS
ScaleHow big will it get?5 TB now, 40 TB in 3 years
CostWhat is the budget?Cost per team visible each month

3. Architecture Styles

Before choosing products, an architect chooses a style: where data is stored, who owns it, and how it is accessed. Most enterprises combine several.

StyleIdeaStrengthsWeaknesses
Data warehouseStructured, modelled data in one SQL engineFast, trusted reporting; mature toolingPoor for raw, unstructured or ML data
Data lakeStore everything as files in cheap storageCheap, flexible, any formatBecomes a "data swamp" without governance
LakehouseLake storage with warehouse features through open table formatsOne copy for BI and ML; ACID on filesNeeds discipline in layout and governance
Data meshDomains own and publish their data as productsScales ownership across a large companyNeeds mature teams and shared standards
Data fabricA metadata-driven layer connecting data wherever it livesUnified access and governance across systemsDepends on rich metadata; vendor-heavy
FederationQuery data in place (virtualisation), without copyingFast to deliver, no duplicationSlower queries; load on source systems

Operational and analytical data

AspectOperational (OLTP)Analytical (OLAP)
PurposeRun the business: place ordersUnderstand the business: revenue trends
QueriesMany small reads and writesFew large scans and aggregations
DesignNormalised, row-orientedDimensional or wide, column-oriented
ExamplesPostgreSQL, Oracle, SQL Server, DynamoDBSnowflake, BigQuery, Redshift, Databricks SQL

Keeping them separate protects the order system from heavy reports. Data moves from operational to analytical systems through the integration patterns in the next section.

Open table formats: the key to avoiding lock-in

Delta Lake, Apache Iceberg and Apache Hudi add transactions, schema evolution and time travel to Parquet files in your own storage. Because the format is open, several engines can read the same data: Spark, Trino, Snowflake, BigQuery, Athena, Fabric and Databricks all read Iceberg or Delta to some degree. Catalogs that speak the Iceberg REST protocol, such as Apache Polaris, Unity Catalog and Snowflake's catalog, let engines share one table definition. For an architect, this separates the choice of storage from the choice of engine. The next section explains which format to choose.

Compare the formats in the Delta Lake, Apache Iceberg and Apache Hudi cards. Interoperability features change quickly, so check current support for each engine before committing.

4. Medallion Architecture and Data Layers

The medallion architecture organises a lake or lakehouse into layers of increasing quality: bronze (raw), silver (cleaned and conformed) and gold (business-ready). Databricks popularised the name, but the idea is older and platform-neutral: warehouses have long used staging, integration and mart layers, and Snowflake, Fabric, BigQuery and AWS lakes use the same layering under other names.

It is a design pattern, not a product. Each layer has a clear contract: what it contains, who writes it, who may read it, and what quality it guarantees. That contract is what makes data traceable, replayable and trustworthy.

SOURCES  ->  LANDING  ->  BRONZE  ->  SILVER  ->  GOLD  ->  SERVING (BI, APIs, ML, AI)

         SOURCES          LANDING          BRONZE           SILVER           GOLD
         databases, SaaS, files exactly    raw history,     cleaned, typed,  business models,
         devices, events, as delivered,    append-only,     deduplicated,    KPIs, star
         files, APIs      short-lived      + load metadata  conformed keys   schemas

quality  unknown          unknown          as the source    validated        certified
users    source teams     ingestion jobs   engineers        engineers, DS    analysts, BI

Every layer at a glance

LayerPurposeTypical contentsWrite patternRetention
Edge / sourceWhere data is produced and first collectedOLTP databases, SaaS apps, IoT devices, logs, partner filesOwned by source teamsSource-defined
LandingA safe drop zone for files exactly as deliveredCSV, JSON, XML, Avro, Parquet, zip files, CDC batchesWrite once by ingestion tools; never editedDays to weeks, then deleted or archived
Bronze (raw)An immutable, queryable copy of every sourceSource columns, often as strings, plus load metadata and rescued bad recordsAppend-onlyLong: it is the replay point
Silver (cleaned, conformed)One trusted, deduplicated version of each business entityTyped, validated, deduplicated tables; SCD history; conformed keys; personal data protectedMERGE / upsertLong
Gold (curated)Answers to business questionsStar schemas, aggregates, KPIs, wide feature tables, data productsOverwrite or MERGE per partitionAs the business needs
Serving (optional)Fast delivery to tools and applicationsSemantic models, metric views, caches, low-latency stores, vector indexesRefreshed from goldRebuildable

Why each layer matters

Landing: decouples delivery from processing
Sources can deliver at any time, in any format, even when pipelines are down. Nothing is lost if a job fails, and files can be checked (checksums, manifests, virus scans) before anyone reads them.
Bronze: the system of record for replay
Because bronze keeps everything exactly as received, a bug in silver logic is fixed by reprocessing bronze, not by asking the source to resend months of data. It is also the audit evidence of what the source actually sent.
Silver: one version of the truth per entity
Data quality, deduplication, type casting, standardisation and joins across sources happen once here, so every consumer inherits the same rules instead of reinventing them.
Gold: speed and meaning for the business
Modelled for the question, pre-aggregated, documented and certified. BI stays fast and metrics mean the same in every report.

What each layer does, in detail

ConcernBronzeSilverGold
SchemaSource schema; schema evolution allowed; often everything as stringEnforced, typed, documentedBusiness-friendly names, stable contract
QualityNone applied: keep bad records (rescued or corrupt column)Expectations: not null, ranges, referential checks; failures quarantinedReconciled totals, certified metrics
DuplicatesKept, because they are what the source sentRemoved: latest version per keyNone
HistoryEvery delivery appendedSCD Type 1 or 2 per attributeSnapshots and facts as modelled
Organised bySource systemBusiness entity (customer, order, product)Business use or domain (sales, finance)
Personal dataRestricted access; encryptedMasked, tokenised or separatedOnly what the use case needs
Who readsData engineersEngineers, data scientists, power usersAnalysts, BI, apps, executives
Typical modellingNone3NF, Data Vault or cleaned source shapeKimball star schemas, wide tables, aggregates

Tools used in each layer

LayerDatabricksSnowflakeOpen source
LandingUnity Catalog volumes, cloud storageInternal or external stagesMinIO, HDFS
Into bronzeAuto Loader, Lakeflow Connect, COPY INTOSnowpipe, Snowpipe Streaming, COPY INTO, OpenflowKafka Connect, NiFi, Airbyte, Debezium
Bronze to silverLakeflow Declarative Pipelines, Spark jobs, MERGEDynamic Tables, Streams and Tasks, SnowparkSpark, Flink, dbt
Silver to goldDeclarative Pipelines, dbt, Databricks SQL, materialized viewsDynamic Tables, dbtdbt, Trino, Spark
ServingDatabricks SQL, metric views, Lakebase, Vector SearchSemantic views, Cortex SearchSuperset, Cube
QualityDeclarative Pipelines expectations, Lakehouse MonitoringData metric functionsGreat Expectations, Soda, dbt tests
OrchestrationLakeflow JobsTasksAirflow, Dagster

The same layers on the cloud platforms

LayerAWSAzure / FabricGoogle Cloud
LandingS3ADLS Gen2, OneLake FilesCloud Storage
Into bronzeGlue, Firehose, DMS, AppFlowData Factory, Fabric pipelines, Mirroring, EventstreamDatastream, Dataflow, BigQuery Data Transfer Service
Bronze to silverGlue, EMRDatabricks, Fabric Spark, Data Factory data flowsDataproc, Dataflow, BigQuery SQL, Dataform
Silver to goldRedshift, Glue, dbtFabric Warehouse, Synapse, dbtBigQuery, Dataform, dbt
ServingRedshift, Athena, QuickSight, OpenSearchPower BI semantic models (Direct Lake)BigQuery BI Engine, Looker
QualityGlue Data Quality, DeequPurview data qualityDataplex data quality
OrchestrationStep Functions, MWAAData Factory, Fabric pipelinesCloud Composer, Workflows

Product names move quickly: Databricks renamed Delta Live Tables to Lakeflow Declarative Pipelines and Workflows to Lakeflow Jobs in 2025. Check the current name before an interview, but explain the role of the tool, which does not change.

Tested: a medallion pipeline end to end

We dropped two JSON files into a landing folder. The second file contained a re-sent duplicate, an amount of "abc", a truncated line, and a later update to order 1. Then we built each layer with PySpark and Delta Lake:

from pyspark.sql import functions as F, Window

# BRONZE: read everything as strings, keep bad lines, add load metadata
bronze = (spark.read
    .schema("order_id STRING, customer STRING, amount STRING, "
            "status STRING, updated_at STRING, _corrupt_record STRING")
    .option("columnNameOfCorruptRecord", "_corrupt_record")
    .option("recursiveFileLookup", "true")  # landing/orders/2026-11-07/*.json
    .json("/lake/landing/orders")
    .withColumn("_source_file", F.col("_metadata.file_name"))
    .withColumn("_ingested_at", F.current_timestamp()))
bronze.write.format("delta").mode("append").save("/lake/bronze/orders")

# SILVER: type, validate, quarantine, keep the latest version per key
br = spark.read.format("delta").load("/lake/bronze/orders")
typed = (br.filter("_corrupt_record IS NULL")
    .withColumn("order_id", F.col("order_id").cast("int"))
    .withColumn("amount", F.expr("try_cast(amount AS DECIMAL(10,2))"))
    .withColumn("updated_at", F.to_timestamp("updated_at")))

bad = typed.filter("amount IS NULL OR amount < 0")
bad.write.format("delta").mode("append").save("/lake/quarantine/orders")

w = Window.partitionBy("order_id").orderBy(F.col("updated_at").desc())
silver = (typed.filter("amount IS NOT NULL AND amount >= 0")
    .withColumn("rn", F.row_number().over(w)).filter("rn = 1")
    .drop("rn", "_corrupt_record"))
silver.write.format("delta").mode("overwrite").save("/lake/silver/orders")

# GOLD: answer a business question
gold = (silver.groupBy("customer")
    .agg(F.count("*").alias("orders"), F.sum("amount").alias("revenue")))
gold.write.format("delta").mode("overwrite").save("/lake/gold/customer_revenue")
LayerRowsWhat happened
Landing2 files, 8 linesStored exactly as delivered
Bronze8Every line kept, including the duplicate and the truncated line, which sits in _corrupt_record with its source file name
Quarantine1Order 5 with amount "abc": try_cast returned NULL instead of failing the job
Silver4Duplicate of order 2 removed; order 1 shows its latest state, SHIPPED at 100.50
Gold3 customersC1: 2 orders, 145.50; C2: 1 order, 80.00; C3: 1 order, 300.00

A gotcha we hit: without recursiveFileLookup, reading /lake/landing/orders returned 0 rows with no error, because the files were in a date subfolder that was not named key=value. Spark only discovers subfolders as partitions in that form. Always check row counts after a landing read.

On Databricks: declarative pipelines with expectations

On Databricks the same flow is usually written as a Lakeflow Declarative Pipeline. Auto Loader picks up only new files, and expectations record, drop or fail on bad rows, with the results shown in the pipeline's event log:

import dlt  # also: from pyspark import pipelines as dp
from pyspark.sql import functions as F

@dlt.table(comment="Raw orders, exactly as landed")
def bronze_orders():
    return (spark.readStream.format("cloudFiles")  # Auto Loader
        .option("cloudFiles.format", "json")
        .option("cloudFiles.schemaLocation", "/Volumes/prod/landing/_schemas/orders")
        .option("cloudFiles.schemaEvolutionMode", "rescue")  # unknown fields -> _rescued_data
        .load("/Volumes/prod/landing/orders/")
        .select("*", F.col("_metadata.file_path").alias("_source_file"),
                F.current_timestamp().alias("_ingested_at")))

@dlt.table(comment="Clean orders")
@dlt.expect_or_drop("valid_amount", "amount IS NOT NULL AND amount >= 0")
@dlt.expect("has_customer", "customer IS NOT NULL")  # record only
def silver_orders_clean():
    return (dlt.read_stream("bronze_orders")
        .withColumn("amount", F.expr("try_cast(amount AS DECIMAL(10,2))")))

dlt.create_streaming_table("silver_orders")
# create_auto_cdc_flow was called apply_changes before 2025
dlt.create_auto_cdc_flow(target="silver_orders", source="silver_orders_clean",
                  keys=["order_id"], sequence_by=F.col("updated_at"),
                  stored_as_scd_type=1)

@dlt.table(comment="Revenue per customer")
def gold_customer_revenue():
    return (dlt.read("silver_orders").groupBy("customer")
        .agg(F.count("*").alias("orders"), F.sum("amount").alias("revenue")))

On Snowflake: stages, Snowpipe and Dynamic Tables

-- landing: an external stage over cloud storage
CREATE STAGE landing.orders_stage URL = 's3://acme-landing/orders/'
  STORAGE_INTEGRATION = s3_int FILE_FORMAT = (TYPE = JSON);

-- bronze: Snowpipe loads each new file as it arrives
CREATE TABLE bronze.orders (raw VARIANT, _source_file STRING, _ingested_at TIMESTAMP_LTZ);
CREATE PIPE bronze.orders_pipe AUTO_INGEST = TRUE AS
  COPY INTO bronze.orders
  FROM (SELECT $1, METADATA$FILENAME, CURRENT_TIMESTAMP() FROM @landing.orders_stage);

-- silver: a Dynamic Table keeps itself up to date
CREATE DYNAMIC TABLE silver.orders TARGET_LAG = '15 minutes' WAREHOUSE = transform_wh AS
SELECT raw:order_id::INT AS order_id, raw:customer::STRING AS customer,
       TRY_TO_DECIMAL(raw:amount::STRING, 10, 2) AS amount,
       raw:updated_at::TIMESTAMP_NTZ AS updated_at
FROM bronze.orders
QUALIFY ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY updated_at DESC) = 1;

-- gold
CREATE DYNAMIC TABLE gold.customer_revenue TARGET_LAG = '1 hour' WAREHOUSE = transform_wh AS
SELECT customer, COUNT(*) AS orders, SUM(amount) AS revenue FROM silver.orders GROUP BY customer;

How to lay out the layers

OptionExampleGood for
Catalog or database per layerprod_bronze.crm.customers, prod_silver.sales.ordersStrict isolation and permissions per layer; central platform teams
Schema per layer inside a domain catalogsales.bronze_orders, sales.silver.orders, sales.gold.daily_revenueData mesh: each domain owns its own three layers
Storage account or bucket per layeracme-prod-bronze, acme-prod-silverSeparate keys, lifecycle rules and network rules per layer

Always separate environments (dev, test, prod) first and layers second. Grant users read access to gold by default, silver on request, and bronze to engineers only.

Variations you will meet

VariationWhat it means
Other namesRaw / enriched / curated; staging / core / mart; raw / cleansed / conformed / presentation. Same idea.
Fewer layersSmall or simple sources may go landing to silver directly, or skip a separate landing zone when Auto Loader or Snowpipe reads the delivery location. Keep bronze if you ever need replay or audit.
More layersA platinum or serving layer for semantic models, feature stores or APIs; a sandbox for experiments; a quarantine table beside silver.
Streaming medallionEvery layer is a streaming table, so data flows from bronze to gold in seconds or minutes, with checkpoints providing exactly-once processing.
Medallion and modellingMedallion says where data sits; Kimball, Inmon and Data Vault say how it is modelled. A common enterprise combination is Data Vault or 3NF in silver and Kimball stars in gold.

Common mistakes

MistakeWhy it hurtsBetter
Cleaning data in bronzeThe original is lost, so a bad rule cannot be undoneBronze stays raw; clean in silver
Silver tables that mirror each sourceEvery consumer still has to join and reconcile sourcesConform to business entities with shared keys
Reports reading silver or bronzeLogic is duplicated and numbers disagreeBI reads gold or the semantic layer only
One gold table per report requestHundreds of near-duplicate tablesReusable facts and dimensions, metrics in a semantic layer
Copying every layer in full every runCost and runtime grow with historyIncremental processing: Auto Loader, streams, MERGE, Change Data Feed
Treating layers as quality by name"Gold" means nothing without checksExplicit tests and owners per table, shown in the catalog

5. Edge, Ingestion and the Landing Zone

Before the medallion layers begin, data has to travel from where it is produced, the edge, into the platform. This is where most real-world failures start: missing files, duplicates, half-written files, schema surprises, firewalls, and sources that cannot take extra load.

What the edge layer is

The edge is everything outside the central platform that produces or first collects data: factory sensors and gateways, shop tills, mobile apps, branch servers, on-premise databases, partner SFTP servers and SaaS applications. In IoT architectures the edge also has its own compute, an edge gateway, which does useful work before sending anything:

Edge toolPlatform
AWS IoT Greengrass, IoT Core, IoT SiteWiseAWS
Azure IoT Edge, IoT Hub, IoT OperationsAzure
Partner solutions (Google Cloud IoT Core was retired in 2023), then Pub/SubGoogle Cloud
MQTT brokers (Mosquitto, EMQX, HiveMQ), Apache NiFi MiNiFi, Kafka at the edgeOpen source and vendor-neutral

Two meanings of "landing zone"

TermWhat it isContains
Data landing zoneThe first storage area where incoming data is dropped exactly as delivered, before bronzeFiles and CDC batches in their original format
Cloud landing zoneThe secure, pre-built cloud foundation that everything runs onAccounts or subscriptions per environment, network, identity, encryption keys, logging, guardrail policies

Both matter in an interview. Cloud landing zones are built with AWS Control Tower, the Azure landing zone accelerators (and Cloud Adoption Framework data landing zones), or the Google Cloud landing zone blueprints, usually as Terraform. Build it before any data arrives, because moving networks and permissions later is painful.

How data lands: the ingestion patterns

PatternHow it worksTools
File drop (push)The source sends files to an SFTP server or storage bucketAWS Transfer Family, Azure Blob SFTP, partner SFTP, AzCopy, gsutil and gcloud storage
File pullThe platform fetches files or extracts on a scheduleData Factory, Glue, Storage Transfer Service, DataSync, NiFi
Database CDCChanges are read from the transaction log, with no load on source tablesDebezium, AWS DMS, Datastream, Qlik Replicate, Oracle GoldenGate, Fivetran HVR, Lakeflow Connect, Fabric Mirroring, Snowflake Openflow
Bulk database extractFull or incremental queries by watermark columnSpark JDBC, Data Factory copy, Sqoop (legacy), Airbyte
Event streamingApplications publish events continuouslyKafka, Confluent, Amazon Kinesis and MSK, Azure Event Hubs, Google Pub/Sub
SaaS APIsManaged connectors call the vendor API incrementallyFivetran, Airbyte, AppFlow, Lakeflow Connect (Salesforce, Workday and others), Data Factory connectors
Offline transferPetabytes shipped on physical devicesAWS Snowball, Azure Data Box, Google Transfer Appliance

Connecting the edge to the cloud

NeedAWSAzureGoogle Cloud
Private dedicated link from a data centreDirect ConnectExpressRouteCloud Interconnect
Encrypted tunnel over the internetSite-to-Site VPNVPN GatewayCloud VPN
Agent inside the private networkDMS replication instance, DataSync agentSelf-hosted integration runtime, on-premises data gatewayDatastream private connectivity
Private access to storageVPC endpoints, PrivateLinkPrivate endpointsPrivate Service Connect

How data should arrive in the landing zone

1. Agree an ingestion contract per source. Owner, format, schema, delivery frequency, file naming, expected volume, classification and retention. No contract, no onboarding.

2. Use a predictable path. landing/<source>/<dataset>/<yyyy-mm-dd>/<file>_<timestamp>.json, so files are easy to find, replay and expire.

3. Make "complete" explicit. Write to a temporary name and rename, or send a _SUCCESS marker or manifest listing files, row counts and checksums. Never process a file still being written.

4. Make it write-once. Ingestion roles may add files but not change or delete them; object lock or immutability policies where audit requires it.

5. Validate on arrival. Checksum, row count against the manifest, file format, malware scan for external files. Move failures to a rejected folder and alert the source owner.

6. Load into bronze incrementally and idempotently. Auto Loader, Snowpipe or COPY INTO track which files were already loaded, so a re-run never loads a file twice.

7. Expire the landing zone. Once a file is safely in bronze, lifecycle rules delete or archive it after days or weeks. Landing is a buffer, not a store.

Tested: what happens when a load runs twice

Jobs get re-run after failures, so loading from landing into bronze must be idempotent: a re-run must not load the same file again. We put two files of 5 rows each in a landing folder and compared a plain batch load with a streaming load that keeps a checkpoint:

# plain batch: no memory of what was loaded
spark.read.schema(schema).json("/lake/landing/orders")      .write.format("delta").mode("append").save("/lake/bronze/orders")

# incremental: the checkpoint records which files were processed
(spark.readStream.schema(schema).json("/lake/landing/orders")
     .writeStream.format("delta")
     .option("checkpointLocation", "/lake/_checkpoints/bronze_orders")
     .trigger(availableNow=True)  # process what is there, then stop
     .start("/lake/bronze/orders"))
RunBatch appendStream with checkpoint
First run (10 rows landed)1010
Same job again, no new files20: every row duplicated10: nothing reloaded
After a third file with 3 rows-13: only the new file loaded

Managed tools give the same guarantee: Auto Loader (a streaming read with file tracking that scales to millions of files), Databricks and Snowflake COPY INTO, which skip files they have already loaded unless forced, and Snowpipe, which keeps load history per pipe:

COPY INTO prod_bronze.shop.orders
FROM '/Volumes/prod/landing/shop/orders/'
FILEFORMAT = JSON
FORMAT_OPTIONS ('rescuedDataColumn' = '_rescued_data')
COPY_OPTIONS ('mergeSchema' = 'true');     -- 'force' = 'true' would reload everything

Typical enterprise flow, end to end

 Factory sensors -> IoT Edge gateway -(MQTT/HTTPS)-> IoT Hub -> Event Hubs --+
 Oracle ERP ---- CDC (GoldenGate / Debezium) -----------------> Kafka ------+-> bronze (streaming)
 Salesforce ---- Fivetran / Lakeflow Connect ---------------------------------> bronze
 Partner CSVs -- SFTP (Transfer Family) -> s3://landing/partner/ -- Auto Loader -> bronze
                                              |
                       checksum, manifest, schema check, lifecycle expiry
 bronze -> silver (quality, dedupe, conform) -> gold (marts, KPIs) -> Power BI / APIs / ML

6. Choosing an Open Table Format

An open table format turns a folder of Parquet files into a real table: transactions, schema evolution, time travel and efficient updates, while the data stays in your own storage. The choice matters because it decides which engines can read and write your data for years. There is no single winner; there is a right answer for each situation.

Delta LakeIcebergHudiPaimon
OriginDatabricks; now Linux FoundationNetflix; ApacheUber; ApacheFlink Table Store; Apache
FocusSimple, fast batch and streaming on SparkEngine-neutral tables at very large scaleFrequent upserts and incremental processingStreaming-first on Flink
FeaturesDeletion vectors, liquid clustering, change data feed, UniFormHidden partitioning, partition evolution, column IDs, REST catalog standardMerge-on-read, record-level index, incremental queries, built-in table servicesLSM-tree storage for high-rate streaming updates
EnginesDatabricks, Spark, FabricSnowflake, BigQuery, Athena, Trino, Spark, FlinkSpark, FlinkFlink
CatalogsUnity Catalog, HiveIceberg REST catalogs (Polaris, Glue, Unity, Horizon), NessieHive, GluePaimon catalog, Hive

Which one, in which scenario

ScenarioUsual choiceWhy
Databricks is the main platformDeltaDeepest integration: liquid clustering, predictive optimization, Unity Catalog; UniForm or managed Iceberg for other readers
Microsoft FabricDeltaOneLake stores tables as Delta; Iceberg can be added through virtualisation
Several engines share one copy (Snowflake, Spark, Trino, Athena)IcebergThe widest engine support and an open catalog protocol
AWS-native lake (Athena, Glue, EMR, Redshift)IcebergFirst-class support across AWS analytics services
Snowflake or BigQuery with external tablesIcebergBoth support Iceberg tables natively
Migrating Hive tables in placeIcebergIn-place migration, and partition evolution avoids rewrites later
Heavy CDC upserts with frequent small updatesHudi (or Delta/Iceberg with deletion vectors)Merge-on-read and record-level indexes keep writes cheap
Incremental pipelines reading only changesHudi, or Delta change data feedBuilt-in incremental queries
Flink streaming lakehousePaimon or HudiDesigned for continuous streaming writes
Small, append-only data read by one enginePlain ParquetA table format adds little
Which engines must WRITE the tables?
  ├─ Mostly Databricks or Fabric ............... Delta (+ UniForm for readers)
  ├─ Many engines, or Snowflake / BigQuery ..... Iceberg
  └─ Flink-heavy streaming ..................... Paimon or Hudi
Then check the workload:
  ├─ Update-heavy CDC with tight latency ....... consider Hudi or MoR
  └─ Mostly append and large scans ............. any; follow the engines

You do not always have to choose just one

ToolWhat it doesLimit
Delta UniFormWrites Iceberg (and Hudi) metadata for Delta tables, so Iceberg engines can read themIceberg side is read-only; writes go through Delta
Apache XTable (incubating)Translates metadata between Delta, Iceberg and Hudi over one copy of dataSync job to run and monitor; feature gaps
Managed Iceberg in Unity Catalog or SnowflakeNative Iceberg tables inside the platformFeature parity varies by platform

Format versions are a compatibility decision

Formats keep adding features that older readers cannot understand. Delta tracks this with reader and writer protocol features; Iceberg with format versions. Iceberg v3, which adds deletion vectors, row lineage and the VARIANT type, became generally available in Snowflake in May 2026 and was in public preview on Databricks in April 2026. A v3 table is unreadable by engines that only support v2, so upgrade a format version only after every reader supports it.

Details of each format are in the Delta Lake, Apache Iceberg and Apache Hudi cards. Feature support moves quickly; check each engine's documentation for the exact version you run.

7. Data Integration Patterns

Moving data between systems is where most enterprise complexity lives. The right pattern depends on how fresh the data must be and how much load the source can take.

PatternHow it worksFreshnessUse when
Batch ETLExtract, transform on a separate engine, then loadHoursLegacy warehouses, heavy cleansing before load
Batch ELTLoad raw data, transform inside the platformHoursModern warehouses and lakehouses
Change data captureRead the database transaction log for inserts, updates and deletesSeconds to minutesKeeping copies of operational databases in sync
Event streamingApplications publish events to a log such as KafkaSecondsClickstreams, IoT, event-driven systems
APIsCall a service for data on requestReal timeSaaS sources, small lookups
Replication and mirroringCopy whole tables or databases continuouslyNear real timeMoving operational data into analytics
VirtualisationQuery in place across systemsLiveQuick access without new pipelines
Reverse ETLPush analytics results back into business appsHoursSending scores or segments to a CRM

Tools by pattern

PatternCommercial and cloudOpen source
ETL and ELTInformatica, Talend, Fivetran, Matillion, Azure Data Factory, AWS GlueAirbyte, Apache NiFi, Spark, dbt
CDCOracle GoldenGate, Qlik Replicate, AWS DMS, Datastream, FivetranDebezium with Kafka Connect
StreamingConfluent, Amazon MSK and Kinesis, Event Hubs, Pub/SubApache Kafka, Apache Flink, Spark Structured Streaming
VirtualisationDenodo, Starburst, DremioTrino, Presto
Reverse ETLHightouch, CensusCustom jobs
OrchestrationDatabricks Jobs, Azure Data Factory, Step FunctionsApache Airflow, Dagster, Prefect

How the retailer combines them

CDC captures deletes, which matters for privacy: when a customer is deleted at the source, the delete flows to the analytics copy. Plain batch snapshots miss deletes unless you compare full extracts.

8. Compatibility and Schema Evolution

Data outlives the code that writes it. A schema change that is harmless for the producer can break ten consumers, so an architect defines which changes are allowed, how they are checked, and how breaking changes are rolled out.

The four kinds of compatibility

ModeMeaningUpgrade order
BackwardConsumers using the new schema can read data written with the old oneUpgrade consumers first
ForwardConsumers using the old schema can read data written with the new oneUpgrade producers first
FullBoth directionsAny order
NoneNo guaranteeCoordinated release, everyone at once

Schema registries add transitive variants: the new schema must be compatible with all earlier versions, not only the previous one. This matters when consumers can replay old data, such as a Kafka topic kept for months.

Tested: which changes break readers

We wrote Avro records with one schema and read them with another, in both directions:

ChangeNew reader, old data (backward)Old reader, new data (forward)
Add a field with a defaultOKOK
Add a field without a defaultFailsOK
Remove a field that had no defaultOKFails
Remove a field that had a defaultOKOK
Widen int to longOKFails
Narrow long to intFailsOK
Rename without an aliasFailsFails
Rename with an aliasOKFails
Make a field optional (null by default)OKOK while values are present

The pattern to remember: defaults make changes safe, renames are almost never free, and an alias only helps the new reader. That is why many teams default to full compatibility for shared topics: add optional fields with defaults, and never rename or retype in place.

Schema registries

RegistryWhere
Confluent Schema RegistryKafka and Confluent; Avro, Protobuf, JSON Schema
AWS Glue Schema RegistryMSK, Kinesis
Azure Schema RegistryEvent Hubs
Apicurio RegistryOpen source

A registry stores every version of a schema and rejects incompatible versions when the producer registers them, so a breaking change fails at deploy time rather than in a consumer at 3 a.m. Protobuf follows similar rules through field numbers: never reuse or renumber a field.

Tables evolve differently

ChangeDelta LakeIcebergPlain Parquet
Add a columnYesYesYes, but readers may miss it
Rename a columnNeeds column mappingYes, columns tracked by IDBreaks readers
Drop a columnNeeds column mappingYesBreaks readers
Widen a typeType widening on newer versionsSafe promotions, such as int to longRisky
Change partitioningRewrite, or use liquid clusteringPartition evolution without rewriteRewrite

Tested in the other cards: on Delta Lake 3.2, a rename and a drop both failed until column mapping was enabled, and changing INT to BIGINT failed. Appending an extra column to plain Parquet succeeded, but the new column was invisible when reading the folder.

Rolling out a breaking change: expand and contract

1. Expand. Add the new column or field alongside the old one, for example email_address next to email.

2. Dual-write. Producers fill both, so old and new consumers keep working.

3. Migrate consumers. Lineage lists every consumer; each moves to the new field, tracked to completion.

4. Deprecate. Announce a removal date in the catalog and data contract.

5. Contract. Remove the old field once nothing reads it, which lineage and query logs can prove.

Other compatibility an architect manages

AreaRule of thumb
Data products and APIsSemantic versioning: a breaking change means a new major version, such as /v2, with the old one kept for an agreed period
SQL dialectsPrefer ANSI SQL in shared logic; translate with tools such as SQLGlot when migrating engines
File formatsParquet for analytics, Avro or Protobuf for messages, JSON only at the edges
TimeStore timestamps in UTC; convert at display time
Codes and encodingsUTF-8 text; ISO 8601 dates, ISO 3166 country codes, ISO 4217 currencies
NumbersDECIMAL for money, never floating point
Client librariesPin versions; test upgrades in a staging environment against real data

9. Reliability and Consistency Patterns

Pipelines fail: networks drop, jobs are retried, data arrives late or twice. Reliable architecture does not try to prevent every failure; it makes failure safe to repeat.

PatternProblem it solvesHow
Idempotent writesA retried job duplicates dataMERGE on a business key, or overwrite a whole partition, instead of plain append
At-least-once plus dedupeStreams can redeliver messagesDeduplicate on an event ID within a time window
Exactly-onceDuplicates are unacceptable, such as paymentsCheckpointed source offsets plus a transactional or idempotent sink
Retries with backoffTemporary failuresRetry with increasing waits; give up after a limit and alert
Dead-letter queueBad records block the streamDivert failures to a separate table or topic for review
Replay and backfillLogic changed or data was wrongKeep raw data, make jobs parameterised by date, rerun a range
WatermarksLate-arriving eventsWait a bounded time, then finalise; route later data separately
Write-audit-publishBad data reaching consumersWrite to a hidden area, test it, then publish atomically

Write-audit-publish on different platforms

PlatformHow to stage, test and publish
IcebergWrite to a branch, validate, then fast-forward main
Delta / DatabricksWrite to a staging table, test, then swap or MERGE into production
SnowflakeZero-copy clone, load and test the clone, then swap tables
Any lakeGit-like branching with lakeFS or Project Nessie
dbtdbt build: tests run after each model and failures stop downstream models

Consistency across systems

A single Delta, Iceberg or Snowflake table gives ACID transactions. Across systems there is no shared transaction: a row can be in the orders database but not yet in the lakehouse. Architects design for eventual consistency and make the delay explicit, with a freshness SLA per dataset.

The idempotency patterns are tested in the Data Modelling and dbt cards, where MERGE reruns created no duplicates while plain append did.

10. Scalability, Performance and Workload Isolation

Modern platforms separate storage from compute: data sits once in cheap object storage or a warehouse's storage layer, and any number of compute clusters read it. This is what lets an architect scale and isolate workloads independently.

LeverWhat it doesExamples
Scale upA bigger cluster or warehouse for one heavy jobLarger warehouse size, bigger instances
Scale outMore clusters for more concurrent usersSnowflake multi-cluster warehouses, Databricks SQL warehouse scaling, Redshift concurrency scaling
Workload isolationSeparate compute so ETL never slows dashboardsOne warehouse or cluster per workload; BigQuery reservations
Data layoutRead less dataPartitioning, clustering, Z-order, sort keys, file sizing
Pre-computationAnswer common questions in advanceMaterialised views, aggregate tables, BI extracts
CachingAvoid repeating workResult caches, local SSD caches, semantic-layer caches

Isolation by workload

                 one copy of the data
                          │
   ┌──────────────┬───────┴──────┬──────────────┐
   │ ETL compute   │ BI warehouse │ Data science  │ Ad hoc SQL
   │ scheduled,    │ always on,   │ GPU or large  │ small, auto-
   │ job clusters  │ auto-scaling │ memory        │ suspends
   └──────────────┴──────────────┴──────────────┘
     each billed and tagged to its own team

Multi-tenancy

ModelIsolationCostUse when
Separate account or workspace per tenantStrongestHighestRegulated clients, strict contracts
Separate catalog or databaseStrongMediumBusiness units, data residency
Separate schemaModerateLowTeams inside one unit
Shared tables with row-level securityLogical onlyLowestMany small tenants, SaaS analytics

Capacity planning

Estimate from data volume, growth rate, concurrency and freshness: for example, "5 TB growing 60% a year, 200 dashboard users at 9 a.m., hourly refresh". Load-test with realistic data before go-live, because skew, small files and concurrency show up only at real scale. Then set budgets and alerts, since elastic platforms scale costs as easily as performance.

11. Event-Driven Architecture

Instead of systems asking each other for data, an event-driven architecture has systems publish facts, such as OrderPlaced, to a durable log that many consumers read independently.

ConceptMeaning
Event vs commandAn event states something happened (OrderPlaced); a command asks for something (ShipOrder)
Topic and partitionEvents with the same key go to one partition, which preserves their order
Retention and compactionKeep events for a period, or keep only the latest value per key
Event sourcingStore every change as an event; current state is rebuilt by replaying them
CQRSSeparate models for writing and for reading, kept in sync by events
Exactly-once in KafkaIdempotent producers and transactions within Kafka; end to end needs an idempotent sink too

Design decisions

12. Enterprise Modelling and the Semantic Layer

Physical models were covered in the Data Modelling card. At enterprise level the architect also standardises meaning, so that "revenue" or "active customer" is the same number in every tool.

ConceptPurpose
Enterprise data modelA high-level map of the main business entities and their relationships across all systems
Canonical modelA shared format for data exchanged between systems, so each system maps to one model rather than to every other system
Conformed dimensionsOne customer, product and date dimension reused by every fact table
Reference dataShared code lists: countries, currencies, status codes
Semantic layerBusiness metrics defined once, used by every BI and AI tool

Semantic layer options

ToolNotes
dbt Semantic Layer (MetricFlow)Metrics defined in the dbt project
Looker (LookML)Semantic modelling inside Looker
Power BI semantic modelsShared datasets in Microsoft Fabric and Power BI
Databricks metric viewsMetrics defined in Unity Catalog
Snowflake semantic viewsMetrics and relationships defined in Snowflake
Cube, AtScaleIndependent semantic layers over many sources

A semantic layer also matters for AI: natural-language tools such as chat-with-data assistants answer far more reliably when metrics and relationships are defined explicitly rather than guessed from table names.

Standards worth fixing early

13. Challenges in Data Modelling

Drawing a star schema is easy. The hard part is everything that happens around it: unclear requirements, messy sources, changing history, data that arrives late and tables that grow to billions of rows. Interviewers ask about these challenges because they show whether you have modelled real data or only read about it.

The challenges at a glance

AreaChallengeWhat goes wrongHow architects handle it
RequirementsUnclear or changing business questionsModel built for one report, rebuilt for the nextModel business processes, not reports; use a bus matrix; agree definitions in a glossary
GrainUndefined or mixed grainDouble counting, totals that never matchWrite the grain as one sentence before any column: "one row per order line"
KeysSame entity, different IDs in each sourceOne customer counted three timesSurrogate keys, a key-mapping table, MDM golden record
HistoryAttributes change over timeOld sales reported under the new regionSCD Type 1 or 2 per attribute, point-in-time joins
Late dataFacts arrive before their dimension rowsRows dropped by inner joins or keyed to NULLInferred (placeholder) members, updated when the real row arrives
Late factsFacts arrive days after the eventClosed periods change silentlyPartition by event date, reprocess a rolling window, restatement rules
RelationshipsMany-to-many: one account, many ownersFan-out and inflated totalsBridge tables with allocation weights
HierarchiesRagged or changing hierarchiesBroken roll-ups in org charts and product treesFlattened level columns, parent-child with closure table
ConformanceEach team builds its own customer dimensionReports disagree; facts cannot be comparedConformed dimensions owned once and reused
Semantics"Revenue" means different things to Sales and FinanceTwo dashboards, two numbers, no trustMetrics defined once in a semantic layer
DeletesSources hard-delete rowsThe model keeps ghostsCDC with delete flags, soft deletes, periodic reconciliation
Schema driftSources add, rename or retype columnsPipelines break or load wrong valuesData contracts, schema evolution rules, alerting
ScaleFacts with billions of rowsSlow queries, expensive joinsPartitioning, clustering, aggregates, avoid fact-to-fact joins
UnitsMixed currencies, time zones, unitsWrong sums and wrong day boundariesStore UTC plus local date; amount plus currency; convert in one place
PrivacyPII spread across many tablesHard to mask or delete on requestKeep PII in few dimensions, tag it, mask by policy
Wide vs starOne big table or star schema?Wide tables duplicate logic; stars need joinsStar as the source of truth; wide tables as derived, rebuildable outputs

Grain: the challenge behind most wrong numbers

The grain is what one row means. If a table mixes order headers and order lines, or a join quietly changes one row per order into one row per order line, every sum after that is wrong. Most "the dashboard is wrong" incidents trace back to grain.

History: joining to the right version

With SCD Type 2 a customer has one row per version. Joining facts to it on the business key alone matches every version, which duplicates rows. Join on the version valid on the event date, or store the version's surrogate key in the fact at load time.

-- dim_customer_scd2: C1 lived in Pune until 2026-06-30, then Mumbai
SELECT o.order_id, d.customer_sk, d.city, o.amount
FROM orders o
JOIN dim_customer_scd2 d
  ON  o.customer_id = d.customer_id
  AND o.order_date BETWEEN d.valid_from AND d.valid_to;
JoinRows returned (2 orders)Result
On customer_id only4Every order appears once per city: wrong
On customer_id and date range2O1 in Pune (key 1), O2 in Mumbai (key 2): right

Tested on PySpark 3.5. A date range join cannot use a plain equi-join alone, so on very large tables resolve the surrogate key once at load time and store it in the fact; queries then join on a simple key.

Late-arriving dimensions: inferred members

An order for customer C9 arrives before the CRM sends C9. An inner join drops the order; a left join leaves a NULL key. The standard fix is an inferred member: insert a placeholder row for C9 now, key the fact to it, and overwrite the placeholder's attributes when the real row arrives. The fact never needs to be touched again.

INSERT INTO dim_customer
SELECT (SELECT MAX(customer_sk) FROM dim_customer)
         + ROW_NUMBER() OVER (ORDER BY o.customer_id) AS customer_sk,
       o.customer_id, 'Unknown (pending)' AS name, true AS is_inferred
FROM (SELECT DISTINCT customer_id FROM staged_orders) o
LEFT ANTI JOIN dim_customer d ON o.customer_id = d.customer_id;

Tested: C9 got key 3 with is_inferred = true and both orders loaded. In production, use an identity column or a hash key instead of MAX + ROW_NUMBER, which is not safe when two loads run at once.

Many-to-many: bridge tables

A joint bank account has two owners. Join the balance fact to customers directly and the balance is counted twice. A bridge table (account_id, customer_id, weight) sits between them; weights such as 0.5 and 0.5 allocate the measure so totals still add up, and queries that need "accounts per customer" can ignore the weight.

A checklist before you call a model done

Interview-ready summary

Say this in the interview

"The hardest modelling problems are rarely the diagram. They are grain, keys, history and timing."

"I start by writing the grain of every fact as one sentence and testing it for uniqueness, because mixed grain is the main cause of double counting. I use surrogate keys and conformed dimensions so the same customer is one customer everywhere, with MDM when sources disagree."

"For history I choose SCD type per attribute and join facts to the version valid at the event time. For late-arriving dimensions I insert inferred members, so no fact is dropped. Many-to-many relationships go through bridge tables with weights."

"Finally, I make definitions shared: conformed dimensions, a semantic layer for metrics, data contracts with source teams, and reconciliation against source totals, so the model stays trusted as it grows."

14. Joining Two Fact Tables

A classic interview question: "What happens when you join two fact tables, given that facts are huge?" The short answer is that you usually get wrong numbers and a very expensive query at the same time. The good answer explains why, and what to do instead.

Why fact-to-fact joins go wrong

Two fact tables usually share only dimensions (customer, product, date), not a one-to-one key. Joining on a shared dimension is a many-to-many join: every sales row for a customer pairs with every return row for that customer. Kimball calls the result a fan trap (or chasm trap): rows multiply and measures are counted many times.

fact_sales (grain: order line)          fact_returns (grain: return line)
customer  line  sales                   customer  return  amount
C1        L1    100                     C1        R1      40
C1        L2     50                     C1        R2      30
C1        L3     30
C2        L4     80

JOIN ON customer_id  ->  C1: 3 sales rows x 2 return rows = 6 rows
                         C2: no returns, so an inner join drops C2 entirely
SELECT s.customer_id,
       COUNT(*)             AS joined_rows,
       SUM(s.sales_amount)  AS total_sales,
       SUM(r.return_amount) AS total_returns
FROM fact_sales s
JOIN fact_returns r ON s.customer_id = r.customer_id
GROUP BY s.customer_id;
customerjoined_rowstotal_salestotal_returnsTruth
C16360 (doubled)210 (tripled)Sales 180, returns 70
C2missingmissingmissingSales 80, returns 0

Tested on PySpark 3.5. Each sales row is repeated once per matching return row and each return row once per sales row. No error is raised: the query looks fine and the numbers are silently wrong.

What happens to the engine at huge volume

The output size of a many-to-many join is the sum over every key of (rows on the left) × (rows on the right). With billions of rows that number becomes astronomical, and the engine has to do all the work below before it can return anything:

StageWhat happensSymptom
ShuffleNeither fact fits in memory, so broadcast is impossible. Both sides are hash-partitioned on the join key and sent across the networkTerabytes of shuffle read and write
Sort-merge or hash joinSpark chooses a sort-merge join; warehouses use a distributed hash join. Each partition pairs every left row with every right row for the same keyLong stages, high CPU
Row explosionOutput rows = Σ (left rows × right rows) per key, often 100× or 1,000× the inputsSnowflake Query Profile flags an "exploding join"; Spark shows output rows far above input rows
SkewHot keys, such as a marketplace seller or an "unknown" customer, land on one taskOne task runs for hours while the others finish in seconds
Spill and failurePartitions exceed memory and spill to diskDisk spill, out-of-memory errors, executor loss, retries
CostWarehouse credits or cluster hours scale with the exploded rows, not the inputsOne dashboard query costs more than the nightly load

A tested example at small scale: 300,000 sales rows and 30,000 return rows over 1,000 customers.

ApproachRows producedTotal sales reported
Naive join on customer_id9,000,000 (27× the 330,000 inputs)450,161,414: 30× the truth
Drill-across (below)1,00015,005,380: correct

Scale the same shape to 3 billion sales rows and the naive join would produce trillions of rows. That is why the query either runs for hours or fails, and why the answer would be wrong even if it finished.

Estimate the damage before you join

Counting the rows per key on each side tells you how big a join will be, using only cheap aggregations:

SELECT SUM(s.n * r.n) AS rows_the_join_will_produce,
       MAX(s.n * r.n) AS rows_from_worst_key
FROM (SELECT customer_id, COUNT(*) AS n FROM fact_sales   GROUP BY customer_id) s
JOIN (SELECT customer_id, COUNT(*) AS n FROM fact_returns GROUP BY customer_id) r
  ON s.customer_id = r.customer_id;

Tested: it predicted 9,000,000 rows and a worst key of 9,000, matching the real join exactly. If the result is far above the larger input, the join is many-to-many.

The right patterns

1. Drill-across: aggregate first, then join

Bring each fact to the same grain on conformed dimensions, then join the small results. This is Kimball's drill-across, and it is what BI tools such as Power BI and Looker do behind the scenes when one report uses two facts through shared dimensions.

WITH s AS (SELECT customer_id, SUM(sales_amount) AS total_sales
           FROM fact_sales GROUP BY customer_id),
     r AS (SELECT customer_id, SUM(return_amount) AS total_returns
           FROM fact_returns GROUP BY customer_id)
SELECT COALESCE(s.customer_id, r.customer_id) AS customer_id,
       COALESCE(s.total_sales, 0)             AS total_sales,
       COALESCE(r.total_returns, 0)           AS total_returns
FROM s FULL OUTER JOIN r ON s.customer_id = r.customer_id;

Result: C1 180 and 70, C2 80 and 0. Use a full outer join so customers that appear in only one fact are kept. The aggregations are cheap because engines aggregate partially before the shuffle, and the join is now one-to-one on far fewer rows.

2. Union, then aggregate (no join at all)

SELECT customer_id,
       SUM(sales_amount)  AS total_sales,
       SUM(return_amount) AS total_returns
FROM (
  SELECT customer_id, sales_amount, 0 AS return_amount FROM fact_sales
  UNION ALL
  SELECT customer_id, 0, return_amount FROM fact_returns
) t
GROUP BY customer_id;

Same correct result. Stacking the facts and grouping once avoids the join and its skew entirely, and scales well because a group-by can combine rows partially before the shuffle, unlike a join.

3. Join at the true shared grain

Sometimes two facts really are related row to row: a return points to the order line it reverses. Then join on that key (often a degenerate dimension such as order_line_id), after aggregating the side that can have several rows per key:

SELECT s.order_line_id, s.sales_amount,
       COALESCE(r.returned, 0)                  AS returned,
       s.sales_amount - COALESCE(r.returned, 0) AS net_amount
FROM fact_sales s
LEFT JOIN (SELECT order_line_id, SUM(return_amount) AS returned
           FROM fact_returns GROUP BY order_line_id) r
  ON s.order_line_id = r.order_line_id;

Tested: L1 100 − 40 = 60, L3 30 − 30 = 0, L2 and L4 unchanged. The join is now one-to-one, so no row multiplies.

4. Model it once: consolidated and accumulating facts

PatternWhen to use itExample
Consolidated factTwo facts are always analysed together at one grainfact_sales_and_returns_daily by customer, product, day
Accumulating snapshotA process with milestonesOne row per order with ordered, shipped, delivered and returned dates
Aggregate table or materialized viewCommon dashboard queriesDaily revenue and returns per store, refreshed incrementally
Semantic layer metricsMany tools need the same cross-fact metricNet revenue = sales − returns, defined once

When you really must join two large facts

TechniqueWhy it helps
Filter and prune firstRestrict dates so partition and file pruning skip most data; select only needed columns
Pre-aggregate the many sideTurns many-to-many into one-to-many before the join
Co-locate on the join keyLiquid clustering or Z-order in Delta, clustering keys in Snowflake, clustering in BigQuery, bucketing or storage-partitioned joins in Iceberg and Hive tables reduce or remove the shuffle
Adaptive Query ExecutionSpark 3.2+ enables AQE by default: it splits skewed partitions and switches to broadcast when a side turns out small after filtering
Salt hot keysSpreads one huge key across many tasks (below)
Process incrementallyJoin only new or changed partitions instead of the full history every run
# Salting: spread a hot key across 16 tasks
from pyspark.sql import functions as F
SALT = 16
fact = big_fact.withColumn("salt", (F.rand() * SALT).cast("int"))
other = small_side.crossJoin(spark.range(SALT).withColumnRenamed("id", "salt"))
joined = fact.join(other, ["customer_id", "salt"])

Tested: a key holding 150,000 of 200,000 rows was split into 16 pieces of about 9,300 rows each, and the salted join returned exactly the same count and sum as the plain join. Salt the big side randomly and copy the other side once per salt value; it only makes sense when the other side is small enough to copy.

== physical plan of the naive join, with broadcast disabled ==
SortMergeJoin [customer_id], [customer_id], Inner
:- Sort [customer_id ASC]
:  +- Exchange hashpartitioning(customer_id, 8)     <- shuffle of fact 1
+- Sort [customer_id ASC]
   +- Exchange hashpartitioning(customer_id, 8)     <- shuffle of fact 2

Simplified from the tested plan. Two Exchange nodes mean both facts cross the network; that is the cost you pay before a single row is matched.

Common mistakes

MistakeEffectFix
Joining facts on a shared dimension keyFan-out, inflated totalsDrill-across or union then aggregate
Inner join between factsMembers missing from one fact disappearFull outer join of the aggregates
Using COUNT(DISTINCT) or AVG to hide fan-outSome measures look right, sums stay wrongFix the grain, not the symptom
Joining full history every runCost grows every dayIncremental processing and partition filters
Ignoring skewOne task runs for hoursAQE skew handling, salting, filter out unknown keys

Interview-ready summary

Say this in the interview

"Two fact tables normally share only dimensions, so joining them on, say, customer_id is many-to-many. Every sales row pairs with every return row for that customer, so totals are multiplied. That is the fan trap. An inner join also drops customers that exist in only one fact."

"At big-data volume it is also a performance problem. Neither side can be broadcast, so both facts are shuffled on the join key and sort-merged. The output is the sum of left rows times right rows per key, so it can be hundreds of times the input. Hot keys cause skew, then spills, out-of-memory failures and high cost."

"So I avoid joining facts directly. I use drill-across: aggregate each fact to the same grain on conformed dimensions, then full outer join the small results. Or I union the facts and aggregate once. If the facts are truly related row to row, I join on that shared key, such as order_line_id, after aggregating the many side."

"If the business always needs them together, I model it once as a consolidated fact, an accumulating snapshot or an aggregate table, with the metric in the semantic layer. When a large join is unavoidable I filter early, cluster on the join key, rely on AQE for skew, salt hot keys and process incrementally."

15. Data Governance

Data governance is the set of roles, rules and processes that decide how data is owned, defined, protected and used. Tools enforce it, but governance starts with people: if nobody owns the customer table, no tool can decide who should see it.

The three parts of governance

PartWhat it coversRetailer example
PeopleOwners, stewards and a governance councilThe CRM team owns customer data
ProcessHow access is requested, how definitions are agreed, how issues are fixedAccess requests approved by the owner within 2 days
TechnologyCatalog, access control, lineage, quality and audit toolsUnity Catalog, plus a business catalog

Roles

RoleResponsible for
Data ownerA senior business person accountable for a data domain; approves access and rules
Data stewardDay-to-day definitions, quality rules and metadata for that domain
Data custodianThe technical team that stores and secures the data
Data consumerUses data within the agreed rules
Governance councilSets company-wide policies and settles disputes

Operating models

ModelHow it worksSuits
CentralisedOne team sets and enforces all rulesSmaller or highly regulated companies
FederatedA central team sets standards; domains apply themMost large companies, and data mesh
DecentralisedEach domain decides aloneRarely works: definitions drift

Our retailer chooses the federated model. A small central team defines classification levels, naming rules and security standards. The Sales, CRM and Finance domains each own their data and apply those rules.

Policies a company needs

Many companies follow DAMA-DMBOK, the Data Management Body of Knowledge, which puts data governance at the centre of ten other knowledge areas, including architecture, modelling, security, quality and metadata.

Governance tools

ToolTypeStrengths
Databricks Unity CatalogPlatform-native governanceEnforces access, captures lineage and audit automatically
Microsoft PurviewEnterprise catalog and complianceMicrosoft and Azure estates, classification
CollibraBusiness-focused governance platformGlossary, stewardship workflows, policy management
AlationData catalogSearch, collaboration, query-log based usage
AtlanModern active metadata platformWide connectors, collaboration
Informatica CDGCEnterprise data governance and catalogLarge estates, data quality and MDM suite
DataHub, OpenMetadataOpen-source metadata platformsFree, extensible, strong lineage
Apache AtlasOpen-source, Hadoop eraHive and Hadoop ecosystems
AWS Lake Formation, Glue Data CatalogAWS-nativePermissions and catalog for AWS data lakes
Google DataplexGoogle Cloud-nativeCatalog and governance on Google Cloud

A common pattern is two layers: the platform's own catalog, such as Unity Catalog, enforces rules where the data lives, and an enterprise catalog, such as Collibra or Purview, describes data across every system for business users.

16. Metadata and Catalogs

Governance needs metadata, meaning data about data. Without it, nobody can find a table, trust a definition or tell which column holds an email address.

KindExamplesWho uses it
TechnicalSchema, data types, location, sizeEngineers
BusinessDefinitions, glossary terms, ownersAnalysts, business users
OperationalLast refresh, row counts, job runsOperations, support
GovernanceClassification tags, access rules, retentionSecurity, compliance

How Unity Catalog organises data

Metastore (one per region)
  └─ Catalog        prod_sales            often one per domain or environment
       └─ Schema    gold
            ├─ Table     fact_order_line
            ├─ View      v_customer_masked
            ├─ Volume    raw_files           non-table files
            ├─ Function  mask_email          UDFs, masks, filters
            └─ Model     churn_model         ML models

Full name: catalog.schema.object -> prod_sales.gold.fact_order_line

Each object can carry the metadata above: a comment for the business definition, an owner, and tags for classification.

COMMENT ON TABLE prod_sales.gold.dim_customer IS
  'One row per version of a customer (SCD2). Owner: CRM team.';

ALTER TABLE prod_sales.gold.dim_customer OWNER TO `crm-owners`;

ALTER TABLE prod_sales.gold.dim_customer
  ALTER COLUMN email SET TAGS ('pii' = 'email');

A business glossary

A glossary defines business terms once, such as Active Customer: placed an order in the last 90 days, and links each term to the tables and columns that implement it. This is what stops three dashboards from showing three different numbers for the same thing.

Catalogs on other platforms

PlatformTechnical catalogEnterprise or business catalog
DatabricksUnity CatalogUnity Catalog, or an external catalog
SnowflakeSnowflake Horizon (built in)Horizon, or an external catalog
AWSGlue Data CatalogSageMaker Catalog (formerly DataZone)
AzureFabric and OneLake catalogMicrosoft Purview
Google CloudBigQuery metadataKnowledge Catalog (formerly Dataplex)
Open sourceHive Metastore, Apache Polaris (Iceberg REST)DataHub, OpenMetadata, Amundsen

The architectural decision is which catalog is the source of truth for each kind of metadata. A common answer: each platform's own catalog enforces access, and one enterprise catalog collects metadata, glossary and lineage from all of them.

17. Data Classification

Before deciding how to protect data, you must know how sensitive it is. Classification gives every dataset, and ideally every column, a label that the rest of the platform can act on.

LevelMeaningRetailer exampleTypical controls
PublicSafe to publishProduct catalogueNone special
InternalFor employeesDaily sales totalsLogin required
ConfidentialLimited to certain teamsSupplier prices, marginsGroup-based access, audit
RestrictedSerious harm if leakedEmails, phone numbers, payment dataMasking, encryption, strict approval

Types of sensitive data

TermMeaningExamples
PIIPersonally identifiable informationName, email, phone, address
Sensitive personal dataHigher-risk categoriesHealth, biometrics, religion, finances
PHIProtected health information (US HIPAA)Diagnoses, prescriptions
PCI dataPayment card dataCard number, CVV
Quasi-identifiersNot identifying alone, but together they can bePostcode + birth date + gender

Classification only helps if it drives action. In Unity Catalog, a governed tag is a tag from an approved list, so it cannot be misspelled, and access policies can use it directly, as the security section shows. Databricks documents a safe default: tag every new object as unverified, block access until it is reviewed, then apply the right tags.

Finding sensitive data automatically

In a large estate you cannot tag every column by hand. Discovery tools scan data and suggest classifications, which stewards then confirm.

PlatformDiscovery tool
AWSAmazon Macie (S3)
Azure and multi-cloudMicrosoft Purview
Google CloudSensitive Data Protection
SnowflakeBuilt-in data classification
DatabricksData classification in Unity Catalog
Multi-platformBigID, Collibra, Informatica, Immuta, Privacera

18. Data Privacy

Privacy is about using personal data fairly and lawfully: collecting only what you need, using it only for the stated purpose, and respecting people's rights. Security is about protecting data from unauthorised access. A perfectly secure system can still break privacy law, for example by keeping data for too long or using it for something nobody agreed to.

Privacy principles

PrincipleMeaningArchitecture decision
Purpose limitationUse data only for the stated purposeTag tables with their allowed purpose
Data minimisationCollect and keep only what is neededDo not copy emails into gold if no report needs them
ConsentA lawful basis, often consent, for processingStore consent and filter by it
Storage limitationKeep data no longer than neededRetention rules, then delete
Rights of individualsAccess, correction and erasureBe able to find and delete one person's data
AccountabilityProve that you complyAudit logs, lineage, documentation

Laws and standards an architect meets

RegulationWhereKey points
DPDP Act 2023IndiaConsent, purpose limits and individual rights. The DPDP Rules were notified in November 2025, with most duties applying from May 2027. Penalties reach ₹250 crore
GDPREURight to erasure; breach notification within 72 hours; fines up to 4% of global turnover
CCPA / CPRACaliforniaRights to know, delete, and opt out of sale or sharing
HIPAAUS healthcareProtects health information
PCI DSSCard payments worldwideAn industry standard, not a law; strict rules for card data

This is a summary for architecture planning, not legal advice. Confirm exact obligations and dates with your legal team.

Privacy techniques

TechniqueWhat it doesReversible?Use for
MaskingHides part of a value when shownThe data is unchanged underneathShowing data to analysts
PseudonymisationReplaces an identifier with a codeYes, with the key or mappingJoining data without revealing who
TokenisationSwaps a value for a random token, kept in a vaultYes, through the vaultCard numbers
EncryptionScrambles data with a keyYes, with the keyStorage and transfer
AggregationKeeps only totalsNoReports
AnonymisationRemoves any way to identify peopleNo, if done properlySharing and research

Tested: hashing is not anonymisation

A common mistake is to hash emails with SHA-256 and call the data anonymous. We hashed two emails, then hashed a list of guessed emails and joined the two:

from pyspark.sql import functions as F

hashed = customers.select(F.sha2("email", 256).alias("email_hash"), "spend")

guesses = candidate_emails.select(
    "email", F.sha2("email", 256).alias("email_hash"))

hashed.join(guesses, "email_hash").select("email", "spend").show()
# Both customers were re-identified, with their spend.

Anyone with a list of likely emails can reverse a plain hash. A keyed hash, mixing in a secret held in a secret store, stopped the attack in our test: 0 matches. The same email still produced the same value, so joins kept working.

key = dbutils.secrets.get("privacy", "hash_key")  # never in code
pseudo = F.sha2(F.concat(F.lit(key), F.col("email")), 256)

Under laws such as GDPR, keyed hashes are still personal data, called pseudonymised data, because whoever holds the key can link them back. They lower risk; they do not remove legal duties. A dedicated keyed function such as HMAC is the stronger form of this idea.

Tested: deleting a person from a Delta table

Erasure requests are harder in a lakehouse than they look, because Delta keeps old versions for time travel. We deleted customer C200 and checked what remained:

StepResult
DELETE ... WHERE customer_id = 'C200'The current table has 2 rows
Read version 0 with time travelC200's email is still readable
VACUUM ... RETAIN 0 HOURSRefused by a safety check, unless it is turned off
VACUUM after the old files expireThe old data file is deleted
Read C200 from version 0 againFails: the data is gone
count() on version 0Still says 3, from the log's statistics, not the files
DESCRIBE HISTORYStill shows the DELETE and VACUUM, which is your evidence

So erasure is a process, not a single statement: delete from every table that holds the person, including bronze and any copies, then let VACUUM remove the old files after the retention period. Lineage tells you which tables to check.

DELETE FROM prod_sales.silver.customer WHERE customer_id = 'C200';
DELETE FROM prod_sales.gold.dim_customer WHERE customer_id = 'C200';
-- after the retention period (default 7 days), old files are removed
VACUUM prod_sales.silver.customer;
VACUUM prod_sales.gold.dim_customer;

The default retention exists to protect running readers and time travel; do not set it to zero in production. Check your retention settings against the deadline the law gives you. Raw files in bronze, exports and backups also need a deletion plan.

Erasure on other platforms

Every modern platform keeps deleted data for a while to protect against mistakes, so the Delta lesson applies everywhere: a DELETE is only the first step, and the erasure is complete once the recovery copies expire.

PlatformWhere deleted data survivesHow it is finally removed
Delta LakeOld versions for time travelVACUUM after the retention period
Apache IcebergOld snapshotsExpire snapshots, then remove orphan files
SnowflakeTime Travel (up to 90 days on Enterprise), then 7 days of Fail-safeExpires automatically; shorten Time Travel on sensitive tables
BigQueryTime travel window (up to 7 days), then fail-safeExpires automatically
S3, ADLS, GCSObject versions and soft delete, if enabledLifecycle rules for old versions
Backups and exportsSnapshots, extracts, test copiesYour own retention schedule

Document these windows in your privacy notice and erasure process, because "deleted" usually means "deleted from live use now, and from recovery copies within a known period".

Data residency and cross-border transfers

Many laws care about where personal data is stored and processed. GDPR restricts transfers outside the EU unless safeguards are in place, and India's DPDP Act lets the government restrict transfers to certain countries. For an architect this becomes concrete: choose cloud regions deliberately, keep EU customers' personal data in an EU region, and make sure backups, logs, support access and analytics copies follow the same rule. Aggregated or anonymised data can usually move more freely.

19. Data Security and How to Implement It

Security protects data from unauthorised access, change or loss. No single control is enough, so architects use defence in depth: several layers, so that one mistake does not expose everything.

LayerQuestionControls
IdentityWho are you?Single sign-on, MFA, users and groups synced from the identity provider, service principals
AccessWhat may you do?Grants to groups, least privilege, ABAC policies
Fine-grainedWhich rows and columns?Row filters, column masks, dynamic views
EncryptionCan stolen data be read?At rest, in transit (TLS), customer-managed keys
NetworkWhere can you connect from?Private connectivity, no public IPs, IP access lists
SecretsWhere are passwords kept?A secret store, never in code
MonitoringWhat happened?Audit logs, alerts, regular access reviews

Implementation steps on any platform

Whatever the vendor, security is implemented in the same order. The Databricks example below and the other platforms after it all follow these steps.

#StepDone when
1Federate identity from one identity provider (Entra ID, Okta, Ping)No local accounts; MFA everywhere
2Create role-based groups and service identitiesPeople and jobs are in groups, not granted directly
3Grant least privilege to groupsEvery grant has an owner and a reason
4Classify and tag sensitive dataPersonal data columns are tagged
5Apply masking and row-level rules, ideally by tagAnalysts cannot see raw personal data
6Encrypt at rest and in transit, with managed keys where neededKey ownership documented
7Isolate the networkNo public endpoints for data services
8Move secrets to a vaultNo passwords in code or config
9Centralise audit logs and alertsYou can answer "who read this?" in minutes
10Review access regularlyQuarterly certification by data owners

This approach is often called zero trust: never trust a request because of where it comes from, verify every identity, and give the least access needed.

Example: implementing it on Databricks

Here is how the retailer answers the auditor's question, layer by layer.

1. Connect identity. Sync users and groups from the identity provider, such as Microsoft Entra ID or Okta, with SCIM. Create groups by role, for example analysts, crm-owners and data-engineers. Production jobs run as service principals, not as people.

2. Grant to groups, with least privilege. Each group gets only what its job needs. Because grants go to groups, moving a person between teams changes their access automatically.

GRANT USE CATALOG ON CATALOG prod_sales TO `analysts`;
GRANT USE SCHEMA  ON SCHEMA  prod_sales.gold TO `analysts`;
GRANT SELECT      ON SCHEMA  prod_sales.gold TO `analysts`;

GRANT ALL PRIVILEGES ON SCHEMA prod_sales.silver TO `data-engineers`;

SHOW GRANTS ON TABLE prod_sales.gold.dim_customer;

3. Protect sensitive columns. Analysts need customer counts by city, but not emails. A column mask hides the value for everyone outside an approved group, while the table stays the same.

CREATE FUNCTION prod_sales.security.mask_email(email STRING)
RETURNS STRING
RETURN CASE
  WHEN is_account_group_member('crm-owners') THEN email
  ELSE concat(left(email, 1), '***@', substring_index(email, '@', -1))
END;

ALTER TABLE prod_sales.gold.dim_customer
  ALTER COLUMN email SET MASK prod_sales.security.mask_email;

The masking expression itself was tested in Spark: asha@example.com became a***@example.com, and a card number became ************1111.

4. Limit rows where needed. Regional teams should see only their region's orders. A row filter applies that automatically to every query.

CREATE FUNCTION prod_sales.security.region_filter(region STRING)
RETURNS BOOLEAN
RETURN is_account_group_member('sales-all-regions')
    OR (region = 'IN' AND is_account_group_member('sales-india'))
    OR (region = 'EU' AND is_account_group_member('sales-europe'));

ALTER TABLE prod_sales.gold.fact_sales
  SET ROW FILTER prod_sales.security.region_filter ON (region);

5. Scale it with tags and ABAC. Writing a mask for each table does not scale to hundreds of tables. Attribute-based access control applies one policy to every column carrying a tag, including tables created later. Databricks now recommends ABAC for this, and it became generally available in April 2026.

-- tag the column once
ALTER TABLE prod_sales.gold.dim_customer
  ALTER COLUMN email SET TAGS ('pii' = 'email');

-- one policy covers every email column in the schema
CREATE POLICY mask_emails
ON SCHEMA prod_sales.gold
COLUMN MASK prod_sales.security.mask_email
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag_value('pii', 'email') AS email_col
ON COLUMN email_col;

ABAC needs Databricks Runtime 16.4 or above, or serverless compute, and some parts are still in Beta. Row filters and column masks have limits: for example, they cannot be applied to views, and time travel and clones are not supported on tables that use them.

6. Encrypt and isolate. Data is encrypted at rest and in transit. For stricter control, use customer-managed keys in the cloud key service, so that revoking the key makes the data unreadable. Use private connectivity so traffic does not cross the public internet.

7. Keep secrets out of code. Store passwords and keys in a secret scope, or in a cloud vault behind it, and read them at runtime.

password = dbutils.secrets.get(scope="etl", key="crm_db_password")

8. Audit and review. Every access is logged. Query the audit system table to answer the auditor directly, and review grants every quarter.

-- Who read the customer table in the last 30 days?
SELECT event_time, user_identity.email, action_name
FROM system.access.audit
WHERE event_date >= current_date() - INTERVAL 30 DAYS
  AND request_params.full_name_arg = 'prod_sales.gold.dim_customer'
ORDER BY event_time DESC;

The Databricks grants, masks, filters, policies and audit queries above follow the Databricks documentation; they need a Unity Catalog workspace and were not run here. Check audit column names in your workspace before relying on the query.

The same controls on other platforms

Here is the email mask and the regional row filter from the Databricks example, on the other major platforms. The pattern is the same everywhere: a policy object, attached to a column or table, deciding per user at query time.

Snowflake uses masking policies and row access policies, both requiring Enterprise Edition or higher. A masking policy can also be attached to a tag, so every tagged column is masked.

CREATE OR REPLACE MASKING POLICY email_mask AS (val STRING) RETURNS STRING ->
  CASE
    WHEN CURRENT_ROLE() IN ('CRM_OWNER') THEN val
    ELSE '*********'
  END;

ALTER TABLE customers MODIFY COLUMN email SET MASKING POLICY email_mask;

CREATE OR REPLACE ROW ACCESS POLICY region_policy
AS (region VARCHAR) RETURNS BOOLEAN ->
  CURRENT_ROLE() = 'SALES_ALL'
  OR (region = 'IN' AND CURRENT_ROLE() = 'SALES_INDIA');

ALTER TABLE sales ADD ROW ACCESS POLICY region_policy ON (region);

Google BigQuery uses row access policies that grant rows to principals, and policy tags for column-level security and data masking. Data masking is applied on top of row-level security.

CREATE ROW ACCESS POLICY india_rows
ON retail.sales
GRANT TO ("group:sales-india@example.com")
FILTER USING (region = "IN");

CREATE ROW ACCESS POLICY all_rows
ON retail.sales
GRANT TO ("group:sales-all@example.com")
FILTER USING (TRUE);

In BigQuery, creating the first row access policy hides all rows from anyone not granted, which is why the second policy gives full access back to the right group.

AWS Lake Formation controls access to data in S3 through the Glue Data Catalog. Data filters provide column, row and cell-level security when granting SELECT, and are enforced in Athena, Redshift Spectrum, EMR and Glue. LF-Tags give tag-based access control: a user whose granted tags match a resource's tags can access it.

Microsoft offers controls in several places. Fabric's OneLake security defines roles with row-level rules, written as SQL predicates, and column-level rules on Delta tables. Azure SQL and SQL Server provide dynamic data masking and row-level security:

-- SQL Server / Azure SQL: mask a column
ALTER TABLE dbo.Customers
  ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');

-- Row-level security: a predicate function plus a security policy
CREATE FUNCTION security.fn_region(@region NVARCHAR(10))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN SELECT 1 AS allowed
WHERE IS_MEMBER('sales-all') = 1
   OR (@region = 'IN' AND IS_MEMBER('sales-india') = 1);

CREATE SECURITY POLICY security.RegionFilter
  ADD FILTER PREDICATE security.fn_region(Region) ON dbo.Sales
  WITH (STATE = ON);

PostgreSQL has row-level security built in:

ALTER TABLE sales ENABLE ROW LEVEL SECURITY;

CREATE POLICY region_access ON sales
  USING (region = current_setting('app.region'));

These examples follow each vendor's documentation and were not run here. Masking in SQL Server is a display-level control: users with ad hoc query access can sometimes infer values, so combine it with permissions.

Cross-platform security comparison

ControlDatabricksSnowflakeBigQueryAWSMicrosoft
GrantsUnity Catalog GRANTRoles and GRANTIAM rolesLake Formation, IAMWorkspace roles, SQL GRANT
Column masksColumn masksMasking policiesPolicy tags, maskingData filtersOneLake CLS, DDM
Row filtersRow filtersRow access policiesRow access policiesData filtersOneLake RLS, SQL RLS
Tag-based policiesABAC with governed tagsTag-based maskingPolicy tagsLF-TagsPurview policies
Auditsystem.access.auditACCESS_HISTORYCloud Audit LogsCloudTrailPurview audit, Monitor

When data lives on several platforms, the risk is inconsistent rules: an email masked in Snowflake but visible in a copy on S3. Mitigations are to tag sensitive data consistently everywhere, generate policies from one definition (in code, or with a cross-platform tool such as Immuta or Privacera), and use lineage to find unprotected copies.

20. Data Lineage

Data lineage shows where data comes from, how it is transformed and where it goes. It answers the second half of the auditor's question: where does customer email data end up?

UseQuestion lineage answers
Impact analysisIf I change this column, which tables and dashboards break?
Root causeThis dashboard is wrong; which upstream job caused it?
ComplianceWhere does personal data flow, and which copies must be erased?
TrustIs this number built from approved sources?
Clean-upWhich tables does nothing read any more?

Levels of lineage

LevelShowsExample
Job or processWhich job, notebook or task ranload_fact_order_line ran at 06:00
Dataset or tableWhich tables feed whichsilver.orders → gold.fact_order_line
ColumnWhich columns feed whichorders.amount → rpt_daily_sales.revenue

How lineage is captured

MethodHow it worksTrade-off
Runtime captureThe engine reports what each query read and wroteAccurate; needs support in each engine
Parsing SQL or codeA scanner reads queries and scriptsCovers many systems; misses dynamic code
Query logsReads the warehouse's query historyNo code changes; only SQL
Manual registrationPeople add links by hand or APIFills gaps; goes out of date

Example: lineage in Databricks

Unity Catalog captures lineage automatically for queries run on Databricks through Spark DataFrames or Databricks SQL. Here is what happens when the retailer's daily job runs:

1. The job runs as usual. Nothing in the code mentions lineage.

spark.sql("""
  CREATE OR REPLACE TABLE prod_sales.gold.rpt_daily_sales AS
  SELECT f.order_date, c.city, SUM(f.net_amount) AS revenue
  FROM prod_sales.gold.fact_order_line f
  JOIN prod_sales.gold.dim_customer c ON f.customer_sk = c.customer_sk
  GROUP BY f.order_date, c.city
""")

2. Unity Catalog records the links. From the query plan it learns that rpt_daily_sales reads fact_order_line and dim_customer, that revenue comes from net_amount, and which job or notebook ran it.

3. View it in Catalog Explorer. Open the table, choose the Lineage tab, then See Lineage Graph. Click a column to see its upstream and downstream columns, and filter for the jobs and dashboards that use the table.

4. Query it with SQL. Lineage is also in system tables, so it can drive reports and automation.

-- What reads dim_customer?
SELECT DISTINCT target_table_full_name, entity_type
FROM system.access.table_lineage
WHERE source_table_full_name = 'prod_sales.gold.dim_customer'
  AND event_date >= current_date() - INTERVAL 90 DAYS;

-- Where does the email column flow?
SELECT DISTINCT target_table_full_name, target_column_name
FROM system.access.column_lineage
WHERE source_table_full_name = 'prod_sales.silver.customer'
  AND source_column_name = 'email';
Unity Catalog lineage facts
CapturesTables, views, columns, notebooks, jobs, pipelines, dashboards, models
ScopeAcross all workspaces on one metastore
RetentionCatalog Explorer keeps it indefinitely (data since September 2024); system tables keep 395 days by default
Does not captureRDDs, global temp views, renamed objects, column lineage for path-based tables, column mappings inside UDFs
External systemsAdded manually or by API as external metadata, such as a Kafka topic or a Tableau report

To get full lineage, register tables in Unity Catalog and refer to them by name, not by path, and avoid RDDs. These rules are worth writing into your coding standards.

Example: lineage outside Databricks with OpenLineage

OpenLineage is an open standard for lineage events. A Spark listener sends an event when a job starts and finishes, listing the datasets it read and wrote. Marquez is its reference server, and DataHub, Purview and others can receive the same events.

spark = (SparkSession.builder
  .config("spark.jars.packages",
          "io.openlineage:openlineage-spark_2.12:1.40.1")
  .config("spark.extraListeners",
          "io.openlineage.spark.agent.OpenLineageSparkListener")
  .config("spark.openlineage.transport.type", "http")
  .config("spark.openlineage.transport.url", "http://marquez:5000")
  .config("spark.openlineage.namespace", "retail")
  .getOrCreate())

We ran this on local Spark 3.5, sending events to a file instead of a server. The result was a useful lesson:

OperationWhat OpenLineage recorded
Writing the raw orders and customers filesEach output dataset, with its schema
Joining them and writing a Delta tableBoth inputs, but no output dataset
Joining them and writing ParquetNo lineage event at all

So lineage coverage depends on the engine version, the command and the table format, and no tool catches everything. Test lineage capture for your real job types before promising an auditor complete coverage. We saw the same on Databricks' list of limitations above.

Lineage on other platforms

PlatformNative lineageNotes
DatabricksUnity Catalog, automaticSystem tables keep 395 days
SnowflakeACCESS_HISTORY view, plus a lineage graph in SnowsightColumn lineage needs Enterprise Edition; 365 days of history
Google CloudKnowledge Catalog data lineageAutomatic for BigQuery jobs; kept 30 days; accepts OpenLineage events
AzureMicrosoft PurviewScans many sources; Databricks via Unity Catalog scans
AWSSageMaker Catalog and partner toolsOften filled in with OpenLineage-based tools
Open-source SparkOpenLineage listener, SplineSends events to Marquez, DataHub or others
dbtBuilt-in model graphModel-level; dbt platform adds column-level

In Snowflake, column lineage is recorded in the objects_modified field of ACCESS_HISTORY, and can be flattened into source-to-target pairs:

SELECT ah.query_start_time,
       om.value:"objectName"::STRING    AS target_table,
       col.value:"columnName"::STRING   AS target_column,
       src.value:"objectName"::STRING   AS source_table,
       src.value:"columnName"::STRING   AS source_column
FROM snowflake.account_usage.access_history ah,
     LATERAL FLATTEN(ah.objects_modified) om,
     LATERAL FLATTEN(om.value:"columns") col,
     LATERAL FLATTEN(col.value:"directSources") src
WHERE ah.query_start_time >= DATEADD(day, -30, CURRENT_TIMESTAMP());

The Snowflake query follows the structure in Snowflake's documentation and was not run here. Retention differs widely between platforms, from 30 days to over a year, so copy lineage into your own store if audits need longer history.

Other lineage tools

ToolHow it gets lineage
dbtBuilds a model-level graph from ref() and source(), shown in dbt docs
Microsoft PurviewScans sources; Unity Catalog lineage through scans, and Spark through OpenLineage
CollibraTechnical lineage scanners, including one for Unity Catalog
DataHub, OpenMetadataConnectors for databases, Databricks, Kafka and Airflow, plus a Spark listener
MarquezReference store for OpenLineage events
Apache AtlasHooks for Hive and other Hadoop tools
AirflowThe OpenLineage provider reports what each task reads and writes

21. Building Enterprise Lineage Across Systems

Unity Catalog covers what runs on Databricks. A real company also has source databases, APIs, Kafka, other Spark clusters and Airflow, so the architect has to join them into one picture. These seven steps build on each other.

 Source DBs    APIs      Kafka       Spark jobs      Airflow DAGs
     │          │          │              │                │
 scanners    manual    schema reg.   OpenLineage    OpenLineage
 (JDBC)      / API     + topics      listener       provider
     └──────────┴──────────┴──────┬───────┴────────────────┘
                                  ▼
                  CENTRAL METADATA PLATFORM
              (Collibra, DataHub, Atlas, Purview)
       technical metadata + glossary + owners + lineage graph
                                  │
              impact analysis • visualisation • audit

1. Collect metadata from every source. Each kind of system needs a different collector:

SourceHow to collectWhat you get
Source databasesCatalog scanners connect over JDBC and read the system catalogTables, columns, types, keys
APIsRegister the API and its schema (for example OpenAPI) as a dataset, by hand or APIEndpoints and fields
KafkaRead topics and the schema registryTopics, message schemas
SparkThe OpenLineage listener, or Unity Catalog on DatabricksRuntime inputs and outputs
AirflowThe OpenLineage provider (Airflow 2.7+)Task runs and the datasets they touch

2. Capture all three levels. Job-level lineage says which run produced the data. Dataset-level lineage links tables. Column-level lineage links fields, which is what you need to trace personal data and judge the impact of a column change.

3. Use OpenLineage where it fits. Choose the open standard for anything you control, such as Spark and Airflow, so that you are not tied to one vendor. Use native connectors where they are richer, such as Unity Catalog inside Databricks. Keep dataset names consistent: OpenLineage identifies a dataset by namespace and name, so the same table must get the same name from every tool, or the graph splits.

4. Store it centrally. Send everything to one metadata platform. Collibra is strongest for business governance and stewardship workflows; DataHub is open source with strong lineage and many connectors; Apache Atlas suits Hadoop-based estates. Purview is the natural choice in Microsoft-heavy companies.

5. Connect technical and business metadata. Link each table and column to a glossary term and an owner. Then the lineage graph can say "the Revenue KPI comes from CRM and Orders data, owned by the Sales domain", not only "table A feeds table B".

6. Build impact analysis. Walk the graph upstream to find causes and downstream to find what breaks. Run it automatically: when a pull request changes a column, list the affected tables, dashboards and owners, and notify them before merging.

7. Provide visualisation and audit. Users explore the graph in the catalog. Auditors get evidence: lineage for personal data, access logs, and the history of who changed what. Keep lineage history for as long as your audit period requires.

The hard part is rarely the tools. It is coverage, naming and ownership, so measure them: the percentage of tables with an owner, and the percentage of pipelines sending lineage.

Lineage interview questions with answers

Q1. What is the difference between data lineage and data provenance?
Lineage is the map of how data flows: which sources, jobs and transformations lead to a dataset, and where it goes next. Provenance is the origin and history of specific data: where a record came from, when, from which source version, and every change made to it, often used as evidence. Lineage says "revenue comes from orders through this job"; provenance says "this row came from file orders_0612.csv, loaded at 06:02 by run 8841, and was corrected on 14 June". Delta table history and audit logs provide provenance; lineage graphs provide lineage.

Q2. What is column-level lineage?
It links each column to the source columns and transformation that produce it. For example, rpt_daily_sales.revenue comes from SUM(fact_order_line.net_amount), which comes from orders.quantity × orders.unit_price. It is needed to trace personal data such as email through every copy, to judge the impact of changing one column, and to explain a metric. Tools build it from the query plan or by parsing SQL. It breaks down inside UDFs and path-based tables, which hide the column mapping.

Q3. How do you capture lineage across 100 Spark pipelines?
Never instrument each pipeline by hand; capture lineage at the platform level so new pipelines are covered automatically. On Databricks, Unity Catalog does this, provided every pipeline uses Unity Catalog tables by name, avoids RDDs, and runs on supported compute. Elsewhere, add the OpenLineage listener once, in the cluster defaults, an init script or a shared job template, with one namespace convention. Then enforce it: a CI check rejects path-based writes and missing table registration, and a coverage report lists pipelines that sent no lineage in the last week. Finally, test each job type: in our test, OpenLineage recorded a Delta write's inputs but not its output.

Q4. How do you capture lineage from Kafka to Delta Lake?
A Structured Streaming job reads the topic and writes a bronze Delta table. On Databricks, Unity Catalog records the Delta tables, but a Kafka topic is not a Unity Catalog object, so register it as external metadata (system type Kafka, entity type Topic) and link it to the bronze table with the External Lineage API, including column mappings. That external lineage shows in Catalog Explorer, but not in the lineage system tables. With OpenLineage, the Spark listener reports the Kafka topic as an input dataset, provided your version supports the source. Also collect topic schemas from the schema registry so the columns can be matched.

Q5. How do you identify downstream impact when a source column changes?
Use column-level lineage to walk downstream from the column, then expand to the jobs, dashboards and owners attached to each result. On Databricks, query system.access.column_lineage repeatedly, starting from the changed column, and join to table_lineage for jobs and dashboards. In dbt, dbt ls --select model+ lists downstream models. Automate it in CI so a pull request that drops or renames a column shows the impact list and notifies owners, and use data contracts so breaking changes need agreement first.

# Walk downstream from one column, level by level
frontier = {("prod_sales.silver.customer", "email")}
seen = set()
while frontier:
    pairs = " OR ".join(
        f"(source_table_full_name = '{t}' AND source_column_name = '{c}')"
        for t, c in frontier)
    rows = spark.sql(f"""
        SELECT DISTINCT target_table_full_name AS t, target_column_name AS c
        FROM system.access.column_lineage
        WHERE ({pairs}) AND target_table_full_name IS NOT NULL""").collect()
    frontier = {(r.t, r.c) for r in rows} - seen
    seen |= frontier
print(sorted(seen))   # every downstream column that carries email

Q6. What is the difference between technical lineage and business lineage?
Technical lineage is system-level detail: tables, columns, jobs and SQL, for engineers. Business lineage is a simplified view in business terms: "the Monthly Revenue KPI is calculated from paid orders in the Order system, owned by Finance", for analysts, auditors and managers. Business lineage is built by linking technical assets to glossary terms and owners, and hiding intermediate steps.

Q7. How does Unity Catalog capture lineage?
Automatically, at runtime. When a query runs on Databricks compute through Spark DataFrames or Databricks SQL, Unity Catalog records which tables and columns were read and written, and which notebook, job, pipeline or dashboard ran it. It aggregates this across all workspaces on the metastore. You see it in Catalog Explorer and query it in system.access.table_lineage and column_lineage, kept 395 days by default. Tables must be registered in Unity Catalog. RDDs, global temp views, renamed objects and path-based column lineage are not captured, and external systems are added through external metadata.

Q8. What is OpenLineage, and how does it integrate with Airflow?
OpenLineage is an open standard for lineage events. Each event describes a run of a job with its input and output datasets, plus extra details called facets, such as schemas or column lineage. Airflow 2.7 and later integrate through the apache-airflow-providers-openlineage package. A listener fires when tasks start, complete or fail, and gets lineage from the operator in this order: a custom extractor, the operator's own OpenLineage methods, hook-level lineage, then manual inlets and outlets. You configure a transport, such as HTTP to Marquez or DataHub, or the console for testing. Where the operator supports it, Airflow passes parent run details to Spark, so a task links to the Spark job it launched.

Q9. How would you integrate Collibra with Databricks?
Collibra connects to Unity Catalog through an Edge site or a Collibra Cloud site. You create a Databricks Workspace connection, add the Technical Lineage for Databricks Unity Catalog capability, and synchronise manually or on a schedule, then review the ingestion report. It brings in databases, schemas, tables and columns with their lineage, with volumes and notebooks in preview, but not workflows. Use a service principal with only USE CATALOG, USE SCHEMA and SELECT; Collibra recommends a dedicated catalog of lineage views rather than direct access to query history tables. Then link the ingested assets to Collibra's glossary terms, owners and policies, so that business users see governed data with lineage.

Q10. How would you maintain lineage across multiple cloud platforms?
Choose one central metadata platform, such as DataHub, Collibra or Purview, and one standard, OpenLineage, for custom pipelines. Harvest native lineage from each platform: Unity Catalog per metastore, which means per region, AWS Glue, and Microsoft Fabric or Purview. Use a single naming convention for datasets, because the graph only joins up when the same dataset has the same identity everywhere. Register shared data, such as Delta Sharing recipients or cross-cloud copies, as explicit links. Run the collectors on a schedule, measure coverage per platform, and keep ownership and glossary terms in the central catalog, so the business view stays the same whichever cloud the data is in.

22. Data Quality

Governed, secure, traceable data is still useless if it is wrong. Data quality is measured on a few standard dimensions:

DimensionQuestionExample check
CompletenessIs anything missing?customer_id is never NULL
ValidityDoes it follow the rules?Email matches a pattern; quantity > 0
UniquenessAre there duplicates?One row per order line
ConsistencyDoes it agree across systems?Fact total equals report total
AccuracyDoes it match reality?Stock matches the warehouse count
TimelinessIs it fresh enough?Loaded before 7 a.m.

Where to check, and with what

WhereToolExample
In Databricks pipelinesLakeflow Declarative Pipelines expectationsDrop rows where amount is negative
In dbtData tests and contractsunique, not_null, relationships
In any Spark jobGreat Expectations, Soda, DeequA suite of checks after each load
On tables over timeLakehouse MonitoringDrift and freshness alerts
In the table itselfDelta NOT NULL and CHECK constraintsReject bad writes
-- Lakeflow Declarative Pipelines: rules live with the table
CREATE OR REFRESH STREAMING TABLE silver_orders (
  CONSTRAINT valid_amount EXPECT (amount > 0) ON VIOLATION DROP ROW,
  CONSTRAINT has_customer EXPECT (customer_id IS NOT NULL)
    ON VIOLATION FAIL UPDATE
) AS SELECT * FROM STREAM(bronze_orders);

Data contracts

A data contract is an agreement between the producer and consumers of a dataset: its schema, meaning, quality rules, freshness and owner. Breaking changes need notice. The dbt card shows one in practice: a model contract stops a build when a column's type changes.

Master data management

Customers often exist in several systems under different IDs, such as the CRM, the website and the loyalty app. Master data management (MDM) matches those records and creates one trusted golden record per customer, product or supplier. Tools include Informatica MDM, Reltio and Profisee, or matching rules built in Spark.

23. Data Quality KPIs at Enterprise Level

Checks catch bad rows; KPIs tell leadership whether data quality is improving across the whole company. A good enterprise scorecard measures three things: the data itself, the reliability of delivery, and how well the organisation responds when something breaks.

1. KPIs on the data (quality dimensions)

KPIFormulaTypical target for critical data
Completeness rateNon-null required values ÷ required values≥ 99.5%
Validity rateValues passing format and range rules ÷ values checked≥ 99%
Uniqueness rate1 − duplicate rows on the grain ÷ total rows100% on primary keys
Consistency rateRecords agreeing across systems ÷ records compared≥ 99%
Accuracy rateRecords matching a trusted source or audit sample ÷ records sampled≥ 98%
Referential integrityFacts with a valid dimension key ÷ facts100%; unknown members tracked separately
Reconciliation variance|platform total − source total| ÷ source total< 0.1% for financial data

2. KPIs on delivery (reliability)

KPIFormulaWhy it matters
Freshness SLA attainmentLoads landed before the SLA time ÷ scheduled loadsLate data is wrong data for a 7 a.m. dashboard
Pipeline success rateSuccessful runs ÷ total runsShows platform stability
Data downtimeΣ (time to detect + time to resolve) per incidentOne number for how long data was wrong or missing
MTTDAverage time from the issue starting to being detectedMeasures monitoring coverage
MTTRAverage time from detection to resolutionMeasures response and ownership
Schema-change breakagesUnannounced breaking changes per monthMeasures how well data contracts work

3. KPIs on the programme (governance and impact)

KPIFormulaWhat it shows
Critical data element coverageCDEs with active quality rules ÷ all CDEsWhether the important data is actually monitored
Ownership coverageCertified datasets with a named owner and steward ÷ certified datasetsAccountability
Issues found by consumersIncidents reported by users ÷ all incidentsLower is better: you find problems before the business does
Issue backlog and ageingOpen DQ issues, and how many are older than 30 daysWhether problems get fixed, not just logged
Certified dataset usageQueries on certified (gold) data ÷ all analytical queriesTrust and adoption
Cost of poor qualityRework hours, failed reports, regulatory findings, revenue lostThe business case, in money

Rolling checks up into a score

Store every check result in one table, then roll it up by domain, table and dimension. Weight rules by severity so one failed critical rule counts more than many failed optional ones.

SELECT domain,
       ROUND(100 * SUM(rows_passed) / SUM(rows_checked), 2) AS row_pass_pct,
       ROUND(100 * SUM(w * rows_passed / rows_checked) / SUM(w), 2) AS dq_score
FROM (SELECT *, CASE severity WHEN 'critical' THEN 4 WHEN 'high' THEN 3
                              WHEN 'medium' THEN 2 ELSE 1 END AS w
      FROM dq_check_results WHERE run_date = '2026-11-07') t
GROUP BY domain;
domainrow_pass_pctdq_scoreReading
crm85.0088.33Optional phone numbers drag it down; the critical rules are not the problem
sales99.9399.94Healthy: all critical rules near 100%
SELECT COUNT(*) AS incidents,
       ROUND(AVG((unix_timestamp(detected_at, 'yyyy-MM-dd HH:mm')
                - unix_timestamp(started_at,  'yyyy-MM-dd HH:mm')) / 3600), 2) AS mttd_hours,
       ROUND(AVG((unix_timestamp(resolved_at, 'yyyy-MM-dd HH:mm')
                - unix_timestamp(detected_at, 'yyyy-MM-dd HH:mm')) / 3600), 2) AS mttr_hours,
       ROUND(SUM((unix_timestamp(resolved_at, 'yyyy-MM-dd HH:mm')
                - unix_timestamp(started_at,  'yyyy-MM-dd HH:mm')) / 3600), 2) AS data_downtime_hours,
       ROUND(100 * AVG(CASE WHEN found_by = 'consumer' THEN 1 ELSE 0 END), 0) AS pct_found_by_consumers
FROM dq_incidents;

Tested on PySpark 3.5 with two incidents: MTTD 1.75 h, MTTR 3 h, data downtime 9.5 h, 50% found by consumers. A freshness query over the same sample gave 50% SLA attainment for gold.fact_sales (one late load out of two).

Making KPIs work in practice

Interview-ready summary

Say this in the interview

"At enterprise level I measure data quality on three layers. First, the data: completeness, validity, uniqueness, consistency, accuracy, referential integrity and reconciliation to source, with targets per tier."

"Second, delivery reliability: freshness SLA attainment, pipeline success rate, MTTD, MTTR and data downtime. Data that is correct but late is still a failure for the business."

"Third, the programme: coverage of critical data elements, named owners, the share of issues found by consumers rather than by us, backlog ageing, and the cost of poor quality."

"I collect every check result in one table, weight rules by severity, roll scores up by domain and publish a trending scorecard with an owner for every red number. That turns quality from a list of failed tests into something leadership can manage."

24. Master Data Management (MDM)

Master data is the small set of core business entities that many systems share: customers, products, suppliers, employees, locations and accounts. Master data management (MDM) is the discipline, process and tooling that creates one trusted version of each of those entities, the golden record, and keeps every system linked to it.

The problem it solves: the same customer exists in the CRM as "Asha Rao", in the web shop as "asha rao" with another city, and in the loyalty app as "A. Rao" with only a phone number. Without MDM, reports count three customers, marketing sends three emails and compliance cannot answer "what do we hold about this person?"

Three kinds of data

TypeWhat it isExamplesChangesManaged by
Master dataCore entities the business runs onCustomer, product, supplier, employeeSlowlyMDM
Reference dataFixed code lists that classify other dataCountry, currency, status codes, unitsRarelyReference data management, often part of MDM
Transactional dataEvents that happen to master dataOrders, payments, clicks, shipmentsConstantly, high volumeOLTP applications, then the warehouse

MDM vs a regular database vs a data warehouse

Operational database (OLTP)Data warehouse / lakehouseMDM hub
Main purposeRun one application: take orders, store accountsAnalyse history across the businessCreate and govern one trusted version of each core entity
Data heldThat application's own records, including transactionsIntegrated history of transactions and dimensionsMaster entities only, plus links to every source record
ScopeOne systemMany sources, read-only copiesMany sources, and it can feed corrections back
DuplicatesUnaware of the same customer in other systemsInherits duplicates unless someone resolves themMatches and merges duplicates: its core job
Key outputRows for the appFacts, dimensions, reports, ML featuresGolden record, cross-reference (source ID → master ID), hierarchies
Data flowUsers and apps write to itOne way: sources → warehouse → BITwo way: sources → hub → back to sources and to the warehouse
HistoryUsually current state onlyFull history (SCD, snapshots)Current golden record plus an audit trail of every merge and edit
VolumeHigh transaction rateVery large: billions of rowsSmall: millions of entities, not billions of events
PeopleApp usersAnalysts, data scientistsData stewards who review matches and approve changes
ExamplesPostgreSQL, Oracle, SQL Server behind an appSnowflake, Databricks, BigQuery, FabricInformatica MDM, Reltio, SAP MDG, Profisee, Stibo STEP, Semarchy

The key point: a database stores data for one application and a warehouse analyses data from many, but neither decides which record is the real customer. MDM is the system that decides, and both of the others then use its answer. In the warehouse, the MDM master ID becomes the business key of the conformed dim_customer.

  CRM        Web shop     Loyalty app      ERP
   |            |             |             |
   +------------+------+------+-------------+
                       v
              MDM HUB  (match -> merge -> survivorship -> steward review)
              golden records + cross-reference + hierarchies
                       |
         +-------------+--------------+
         v                            v
  back to source apps         data warehouse / lakehouse
  (corrected address,         dim_customer keyed by master_id,
   master ID stamped)         so every fact joins to one customer

How MDM works: the core steps

StepWhat happensExample
1. StandardiseClean and normalise values before comparing themLower-case and trim emails; keep the last 10 digits of phones; parse addresses
2. MatchFind records that are the same entity, using exact (deterministic) and fuzzy (probabilistic or ML) rulesSame email, or same phone, or similar name in the same city
3. MergeGroup matched records under one master IDCRM-1, WEB-7 and LOY-3 become master CRM-1
4. SurvivorshipChoose the best value of each attribute from the groupName from the most trusted source; email from the most recent update
5. Steward reviewPeople resolve uncertain matches and conflicts"Ravi Kumar" vs "Ravi Kumarr": same person?
6. Publish and syncShare golden records and the cross-reference with every consumerAPIs or events to apps; tables to the warehouse

A tested match, merge and survivorship example

Five customer records from three systems. Step 1 standardises the values and gives each source a trust rank:

CREATE OR REPLACE TEMP VIEW std AS
SELECT *,
       NULLIF(LOWER(TRIM(email)), '')                         AS email_n,
       RIGHT(REGEXP_REPLACE(phone, '[^0-9]', ''), 10)         AS phone_n,
       CASE source WHEN 'CRM' THEN 1 WHEN 'LOY' THEN 2 ELSE 3 END AS trust
FROM customer_sources;

Steps 2 and 3 match on email, then on phone, so records linked through either one end up under the same master ID:

CREATE OR REPLACE TEMP VIEW matched AS
WITH pass1 AS (
  SELECT *, MIN(source_id) OVER (PARTITION BY COALESCE(email_n, source_id)) AS g1
  FROM std),
pass2 AS (
  SELECT *, MIN(g1) OVER (PARTITION BY COALESCE(phone_n, source_id)) AS g2
  FROM pass1)
SELECT *, MIN(g2) OVER (PARTITION BY g1) AS master_id
FROM pass2;
source_idemail (standardised)phone (standardised)master_id
CRM-1asha.rao@mail.com9845012345CRM-1
WEB-7asha.rao@mail.com (was " ASHA.RAO@MAIL.COM ")—CRM-1 (same email)
LOY-3—9845012345 (was "98450-12345")CRM-1 (same phone)
CRM-2ravi@mail.com9000000001CRM-2
WEB-9ravi.k@other.com9000000002WEB-9

Step 4 builds the golden record with survivorship rules, one per attribute:

SELECT master_id,
       MIN_BY(name, trust)                                                AS name,
       MAX_BY(email_n, CASE WHEN email_n IS NOT NULL THEN updated_at END) AS email,
       MIN_BY(phone_n, CASE WHEN phone_n IS NOT NULL THEN trust END)      AS phone,
       MAX_BY(city, updated_at)                                           AS city,
       COUNT(*)                                                           AS source_records
FROM matched
GROUP BY master_id;
master_idnameemailphonecitysource_records
CRM-1Asha Rao (CRM is most trusted)asha.rao@mail.com9845012345Mumbai (latest update)3
CRM-2Ravi Kumarravi@mail.com9000000001Delhi1
WEB-9Ravi Kumarrravi.k@other.com9000000002Delhi1

Step 5 sends uncertain pairs to a data steward instead of merging them automatically:

SELECT a.master_id AS record_a, b.master_id AS record_b, a.name AS name_a, b.name AS name_b,
       LEVENSHTEIN(LOWER(a.name), LOWER(b.name)) AS name_distance
FROM matched a JOIN matched b
  ON a.master_id < b.master_id AND a.city = b.city
WHERE LEVENSHTEIN(LOWER(a.name), LOWER(b.name)) <= 2;

Tested on PySpark 3.5: five source records became three golden records, and one pair (CRM-2 and WEB-9, "Ravi Kumar" vs "Ravi Kumarr", distance 1) went to the review queue. The two-pass match is a teaching simplification: real MDM tools and libraries such as Splink or Zingg use blocking, weighted probabilistic scores and graph connected components, so chains of links of any length are merged.

The cross-reference table

MDM never throws the source IDs away. A cross-reference (xref) table maps every source record to its master ID, which is what lets the warehouse, the apps and auditors trace any golden record back to where it came from:

master_idsourcesource_id
CRM-1CRMCRM-1
CRM-1LOYLOY-3
CRM-1WEBWEB-7
CRM-2CRMCRM-2
WEB-9WEBWEB-9

MDM implementation styles

StyleHow it worksGolden record livesWhen to use it
RegistryHub stores only match links and IDs; data stays in the sourcesAssembled on readQuick start, read-only analytics, least disruption
ConsolidationHub copies and merges source data; sources are not updatedIn the hub, for analyticsBetter reporting and a single customer view in the warehouse
CoexistenceHub merges data and syncs corrections back to sourcesIn the hub and the sourcesSeveral systems keep editing, but should agree
Centralised (transaction hub)Master data is created and edited only in the hubIn the hub onlyStrict control, such as product or supplier onboarding

Tools

CategoryExamples
Enterprise MDM platformsInformatica MDM (IDMC), Reltio, SAP Master Data Governance, Profisee (integrates with Microsoft Purview and Fabric), Stibo Systems STEP, Semarchy, TIBCO EBX
Customer data platforms with identity resolutionSegment, mParticle, Salesforce Data Cloud: identity stitching for marketing, lighter on governance
Open-source entity resolution on SparkSplink (probabilistic record linkage), Zingg (ML-based matching), often run on Databricks
Cloud servicesAWS Entity Resolution

Common challenges

ChallengeHow to handle it
Over-matching: two people merged into oneConservative automatic thresholds, steward review for the grey zone, and an unmerge capability
Under-matching: duplicates remainBetter standardisation, more match attributes, fuzzy and ML matching
No business ownershipNamed data owners and stewards per domain; MDM is a governance programme, not just a tool
Trying to master everything at onceStart with one domain, usually customer or product, and a few critical attributes
PrivacyGolden records concentrate personal data: classify, mask and apply consent and deletion across the xref
Keeping sources in syncEvent-driven updates and stamping the master ID back into source systems

Types of MDM

"Types of MDM" can mean four different things, so a strong answer names the angle first. The two most often asked are by domain and by usage.

Classified byTypesWhat it means
Domain (which entity is mastered)Customer or party, product, supplier or vendor, employee, location or site, asset, financial (chart of accounts, cost centres), reference dataSingle-domain MDM masters one entity; multidomain MDM masters several on one platform and links them, such as customer ↔ product ↔ location
UsageOperational, analytical, collaborativeOperational feeds golden records to live applications in real time; analytical feeds the warehouse and BI; collaborative runs workflows where several teams create and approve master data, such as new product or supplier onboarding
Implementation styleRegistry, consolidation, coexistence, centralisedWhere the golden record lives and whether sources are updated (table above)
DeploymentOn-premises, cloud SaaS, hybridReltio and Informatica IDMC are cloud-native; SAP MDG often runs with SAP S/4HANA
DomainTypical attributesMain consumers
Customer / partyName, identifiers, addresses, contacts, consent, segment, householdCRM, marketing, service, risk, analytics
ProductSKU, description, category, attributes, pricing, packagingE-commerce, ERP, supply chain (often with a PIM tool)
Supplier / vendorLegal name, tax ID, bank details, certifications, risk ratingProcurement, payables, compliance
EmployeePerson, role, org unit, manager, locationHR, IT access, payroll
LocationSites, stores, warehouses, geocodesLogistics, sales territories
AssetEquipment, serial numbers, maintenance hierarchyOperations, maintenance
FinancialChart of accounts, legal entities, cost centresFinance, consolidation, regulatory reporting

Interview-ready summary

Say this in the interview

"MDM, master data management, creates one trusted golden record for core shared entities such as customer, product and supplier, which otherwise exist as different, conflicting copies in many systems."

"It works in steps: standardise, match with exact and fuzzy rules, merge under a master ID, apply survivorship rules to pick the best value of each attribute, send uncertain matches to data stewards, then publish the golden record and a cross-reference of source IDs to master IDs."

"It differs from an operational database, which stores data for one application and does not know about duplicates elsewhere, and from a data warehouse, which integrates high-volume transactions for analysis but does not decide which record is the real customer. MDM handles a small volume of master data, is governed by stewards, and often flows two ways, sending corrections back to source systems."

"The three work together: MDM decides who the customer is, the master ID becomes the business key of the conformed customer dimension, and the warehouse joins every fact to it, so reports count each customer once."

Follow-up interview questions

Interviewers rarely stop at "what is MDM?". These are the follow-ups that usually come next, with answers at the depth a senior or architect role expects.

Q1. What is the difference between MDM and data governance?

Data governance is the framework for all data: policies, standards, roles (owners, stewards), decision rights and quality rules. MDM is one programme that applies that framework to master data, with its own processes and tools.

Data governanceMDM
ScopeAll data: master, transactional, analytical, AIMaster and reference data only
OutputPolicies, glossary, ownership, quality standards, access rulesGolden records, cross-reference, hierarchies
NatureOrganisational: people and decisionsOperational: processes and a platform
ToolsCollibra, Purview, Unity Catalog, AlationInformatica MDM, Reltio, SAP MDG, Profisee

They depend on each other: governance decides who owns customer data, which attributes are critical and which source is trusted; MDM enforces those decisions in its match and survivorship rules. MDM without governance becomes an expensive duplicate-matching tool that nobody trusts.

Q2. What is a golden record?

The golden record (also "best version of truth") is the single, most accurate version of an entity, built by matching records from several sources and choosing the best value for each attribute. It usually does not equal any one source record: in the example above, Asha's golden record takes the name from the CRM, the email from the web shop and the latest city.

Q3. What is the difference between MDM and a Customer 360 platform?

MDM answers "who is this customer?": a governed, high-precision identity with core attributes. A Customer 360 answers "everything about this customer": the identity plus orders, payments, service cases, marketing interactions, web behaviour and scores, usually for front-office users.

MDMCustomer 360 / CDP
FocusIdentity and core attributesFull profile: identity + transactions + interactions + behaviour
MatchingHigh precision, steward-reviewed; false merges are costlyOften probabilistic identity stitching of cookies and devices, tolerant of some error
UsersOperational systems, data teams, complianceSales, service, marketing
GovernanceStrong: owners, stewards, auditLighter, driven by activation needs
ExamplesInformatica MDM, Reltio, SAP MDGSalesforce Data Cloud, Segment, Adobe Real-Time CDP

Best practice: build the Customer 360 on top of MDM. The master ID is the spine that every transaction and interaction is attached to. A 360 without MDM shows three half-profiles of the same person.

Q4. What are matching and survivorship rules?

Matching rules decide whether two records are the same entity. Survivorship rules decide which value wins for each attribute once they are matched.

Matching approachHow it worksExample
DeterministicExact match on standardised strong identifiersSame tax ID, LEI, passport number or verified email
ProbabilisticWeighted score across many attributes (Fellegi-Sunter model); fuzzy measures such as Jaro-Winkler, edit distance, phonetic codesName 0.92 similar + same date of birth + same postcode → score 0.95
ML-basedA model trained on steward decisions predicts match probabilityZingg, Reltio and Informatica use ML matching

Scores fall into three bands: auto-merge above an upper threshold, steward review in the grey zone, no match below. Blocking compares only records sharing a cheap key, such as postcode or the first letters of a surname, to avoid comparing every record with every other.

Survivorship rulePicksTypical use
Source trustValue from the most trusted source for that attributeLegal name from the KYC system
Most recentLatest updated valueEmail, phone, address
Most completeLongest or most populated valueFull name over an initial
Most frequentValue most sources agree onDate of birth
ValidatedValue that passed external verificationPostal-verified address
Steward overrideA human decision, which always winsResolved conflicts

Two golden rules: never let a NULL overwrite a real value, and define survivorship per attribute, not per record.

Q5. What is the difference between registry and consolidation MDM?

RegistryConsolidation
StoresOnly IDs, match links and the cross-referenceA physical copy of the cleansed, merged golden records
Golden viewAssembled at read time from the sources (virtual)Pre-built and stored in the hub
Source systemsUntouchedUntouched: corrections do not flow back
Data qualitySources stay as they are; view is only as good as runtime rulesCleansed and enriched in the hub
PerformanceDepends on calls to every sourceFast reads from the hub
Effort and riskLowest; quickest to startModerate
Best forQuick single view, many systems you cannot change, data that must stay in placeAnalytics, reporting and a trusted dim_customer in the warehouse

Both are read-only towards the sources. If sources must also be corrected, move to coexistence; if all creation must be controlled, move to centralised. Many companies start with registry or consolidation and evolve.

Q6. How do you handle conflicting customer records from different systems?

Q7. How do you maintain historical changes in master data?

Master data needs three kinds of history: attribute history (Asha moved from Pune to Mumbai), identity history (records merged or unmerged) and an audit trail (who changed what, and which steward approved it).

Attribute history is usually kept as SCD Type 2 on the golden record. This Delta Lake MERGE closes the old version and inserts the new one in one atomic step:

MERGE INTO golden_customer_hist t
USING (
  -- every incoming record, keyed so it meets its current row
  SELECT u.master_id AS merge_key, u.* FROM golden_updates u
  UNION ALL
  -- changed records again with a NULL key, so they fall through to INSERT
  SELECT NULL AS merge_key, u.*
  FROM golden_updates u
  JOIN golden_customer_hist h
    ON h.master_id = u.master_id AND h.is_current
   AND h.attr_hash <> sha2(concat_ws('|', u.name, u.email, u.city), 256)
) s
ON t.master_id = s.merge_key AND t.is_current
WHEN MATCHED AND t.attr_hash <> sha2(concat_ws('|', s.name, s.email, s.city), 256) THEN
  UPDATE SET valid_to = CAST(s.changed_at AS TIMESTAMP), is_current = false
WHEN NOT MATCHED THEN
  INSERT (master_id, name, email, city, attr_hash, valid_from, valid_to, is_current)
  VALUES (s.master_id, s.name, s.email, s.city,
          sha2(concat_ws('|', s.name, s.email, s.city), 256),
          CAST(s.changed_at AS TIMESTAMP), NULL, true);
master_idcityvalid_fromvalid_tois_current
CRM-1Pune2026-01-102026-09-01false
CRM-1Mumbai2026-09-01NULLtrue
CRM-2Delhi2026-02-01NULLtrue (unchanged, untouched)
CRM-7Chennai2026-09-01NULLtrue (new customer)

Tested on PySpark 3.5 with Delta Lake 3.2: 1 row updated, 2 inserted. Re-running the same batch changed nothing, because the attribute hash detects that nothing changed. Asking for CRM-1's city on 2026-05-01 returned Pune.

Q8. What is the difference between MDM and a data warehouse?

A data warehouse integrates large volumes of transactional history from many systems for analysis. It is one-way and read-only towards the sources, and it inherits whatever duplicates arrive. MDM manages a small volume of core entities to resolve and govern identity: it matches, merges and applies survivorship, involves human stewards, and can push corrections back to operational systems in real time.

They are complementary, not alternatives: MDM produces the master ID and golden attributes; the warehouse uses them as the business key and attributes of conformed dimensions such as dim_customer, then adds the facts. The full comparison is in the table at the top of this section.

Q9. How do you integrate MDM with Kafka and a lakehouse?

SOURCES                     KAFKA                        MDM HUB
CRM, core banking,   CDC   crm.customer.cdc      --->   match, merge,
web, loyalty   ----------> web.customer.cdc             survivorship,
(Debezium, GoldenGate)     loyalty.customer.cdc         stewardship
                                                             |
                           mdm.customer.golden   <-----------+  compacted topic,
                           mdm.customer.xref     <-----------+  key = master_id
                              |            |
               +--------------+            +--------------------+
               v                                                v
     operational apps update                LAKEHOUSE (Structured Streaming)
     their local copies                     bronze: raw golden and xref events
                                            silver: golden_customer_hist (SCD2 MERGE)
                                                    xref_hist
                                            gold:   dim_customer, Customer 360
def upsert_golden(batch_df, batch_id):
    batch_df.createOrReplaceTempView("golden_updates")
    batch_df.sparkSession.sql(HIST_MERGE_SQL)  # the SCD2 MERGE shown above

(spark.readStream.format("kafka")
   .option("kafka.bootstrap.servers", "broker:9092")
   .option("subscribe", "mdm.customer.golden")
   .load()
   .select(F.from_json(F.col("value").cast("string"), golden_schema).alias("e"))
   .select("e.*")
   .writeStream
   .foreachBatch(upsert_golden)
   .option("checkpointLocation", "/chk/golden_customer_hist")
   .start())

The MERGE inside foreachBatch is the one tested above. The Kafka reader follows the standard Spark Structured Streaming API but was not run here, as it needs a broker. If one batch can hold several events for the same master_id, keep only the latest per key before the MERGE.

Q10. How would you implement MDM for customer and counterparty data in a global bank?

Start with why the bank needs it: regulation and risk. Credit exposure must be aggregated per counterparty and per group (BCBS 239, large-exposure rules), KYC and anti-money-laundering rules require knowing the customer and its beneficial owners, and sanctions screening needs one reliable identity. A false merge in a bank is not a marketing nuisance: it can misstate risk or leak one client's data to another.

Design areaWhat I would do
Data modelA party model: one party (person or legal entity) with many roles (customer, counterparty, guarantor, issuer, beneficial owner) and relationships, rather than separate customer and counterparty silos
IdentifiersDeterministic matching on strong IDs: LEI (ISO 17442, 20 characters), SWIFT BIC, company registration and tax numbers; national IDs for individuals, tokenised
HierarchiesLegal-entity hierarchy with direct and ultimate parent, enriched from GLEIF Level 2 "who owns whom" data and vendors such as Dun & Bradstreet or Moody's Orbis, so exposure rolls up to the group
MatchingDeterministic first, then probabilistic on name and address with legal-suffix normalisation (Ltd, Limited, GmbH) and transliteration of non-Latin scripts; conservative auto-merge thresholds and four-eyes steward approval
SurvivorshipKYC system authoritative for legal identity and date of birth; GLEIF for legal name and parent; CRM for contact preferences; steward overrides audited
StyleCentralised or coexistence for counterparty onboarding, run as a collaborative KYC workflow; consolidation first for retail customers across many legacy core systems
Data residencyRegional hubs where law requires local storage (for example EU GDPR transfer rules, India's RBI payment-data localisation, China's PIPL), with a global index holding only pseudonymised keys and links
HistoryBitemporal history and a full audit trail, kept for the regulatory retention period
IntegrationCDC from core banking, cards, lending and trading into Kafka; golden and xref events back to them; a lakehouse for risk aggregation, regulatory reporting and Customer 360
ScreeningSanctions and PEP screening (OFAC, UN, EU lists) runs on the golden record and its owners, re-run on every change
SecurityPII encrypted and tokenised, attribute-level access by role and region, consent tracked
GovernanceA client data office with owners per domain, critical data elements, data quality KPIs (duplicate rate, false-merge rate, LEI coverage, KYC completeness) and BCBS 239 evidence

Roadmap: (1) legal-entity counterparty master for risk, with the highest regulatory value; (2) hierarchies and beneficial ownership; (3) retail customer master region by region; (4) real-time operational sync and Customer 360. Each phase ships golden records that a regulator-facing report actually uses.

25. Observability and Operations

A platform in production needs the same discipline as any software system. Data observability watches the data itself, not just whether jobs succeeded, because a job can succeed while writing wrong data.

PillarQuestionSignal
FreshnessIs the data up to date?Time since last update
VolumeDid we get the expected amount?Row counts against history
SchemaDid the structure change?Added, removed or retyped columns
DistributionDo the values look normal?Null rates, ranges, category mix
LineageWhat is affected?Upstream causes, downstream impact

SLIs, SLOs and SLAs

TermMeaningExample
SLI (indicator)What you measureMinutes after 06:00 that gold sales tables were ready
SLO (objective)Internal targetReady by 07:00 on 99% of days
SLA (agreement)Promise to consumers, often with consequencesReady by 08:00 or the business is notified

Tools

NeedOptions
Data observabilityMonte Carlo, Bigeye, Soda, Elementary (dbt), Lakehouse Monitoring
Pipeline and infrastructure monitoringDatadog, Grafana, cloud monitoring services
Platform usage and costSystem tables and account usage views on each platform
IncidentsPagerDuty, Opsgenie, ServiceNow

Operating model

26. Resilience, Lifecycle and Cost

Disaster recovery

Recovery is designed from the two numbers agreed earlier. RPO, the recovery point objective, is how much data you can afford to lose. RTO, the recovery time objective, is how long you can be down.

ApproachRPO and RTOCost
Backups restored when neededHours to daysLow
Copy data to a second region on a scheduleHoursMedium
Warm standby in a second regionMinutes to an hourHigh
Active in two regionsNear zeroHighest
-- A full, independent copy of a Delta table, for backup or another region
CREATE OR REPLACE TABLE dr_sales.gold.fact_order_line
DEEP CLONE prod_sales.gold.fact_order_line;
PlatformRecovery features
DatabricksDeep clone, cloud storage replication, workspace configuration as code
SnowflakeTime Travel and Fail-safe, database replication and failover across regions
BigQueryTime travel window, cross-region dataset replication
Object storageS3 cross-region replication, Azure geo-redundant storage, GCS dual and multi-region buckets
DatabasesRead replicas, point-in-time restore, cross-region replicas

Delta time travel helps with mistakes such as a bad overwrite, but it is not a backup: the data is in the same storage, and VACUUM removes old versions. Also store infrastructure and jobs as code, so the platform itself can be rebuilt.

Data lifecycle and retention

StageRetailer rule
HotLast 13 months of orders in gold for fast queries
WarmOlder orders in a cheaper storage tier
ArchiveUp to the legal retention period, rarely read
DeleteAfter retention ends, or when the law requires it

Cost as an architecture concern

Sharing data

Instead of copying data to partners, Delta Sharing, an open protocol, gives them read access to live tables, with the same governance and audit. Data mesh takes the idea further inside a company: each domain publishes data products, which are documented, owned and quality-checked datasets that others can use. The Data Architecture Patterns card covers data mesh in more detail.

27. Data Architecture for AI

AI raises the stakes for everything above. A model trained on personal data, or a chatbot that retrieves documents a user should not see, turns a governance gap into a visible incident.

ComponentPurposeExamples
Feature storeReusable, consistent features for training and servingDatabricks Feature Store, SageMaker Feature Store, Vertex AI Feature Store, Feast
Vector databaseStores embeddings for semantic search and retrievalpgvector, OpenSearch, Azure AI Search, Mosaic AI Vector Search, Pinecone
Model registryVersioned models with lineage to data and codeMLflow, SageMaker, Vertex AI, Azure ML
MonitoringDrift, quality and safety of predictionsPlatform monitoring tools, Evidently

Governance rules for AI data

28. Choosing Technology

How to evaluate a platform

Score options against agreed criteria rather than preferences. Weight the criteria first, with stakeholders, then score each option, and record the result in an ADR.

CriterionQuestions
FitDoes it meet the non-functional requirements and main use cases?
Total cost of ownershipLicences, compute, storage, people, training and migration together
OpennessOpen formats and APIs? How hard is it to leave?
Security and complianceCertifications, regions, encryption, audit
SkillsCan the current team use it, or hire for it?
EcosystemConnectors, partners, community
VendorRoadmap, support, financial stability

Build, buy or assemble

OptionGood whenRisk
Buy a managed platformSpeed matters and needs are commonCost and lock-in
Assemble open sourceStrong engineering team, unusual needsOperating effort
Build customIt is a genuine competitive advantageMaintenance forever

29. Implementing an Enterprise Data Lake

A data lake is cheap object storage plus the rules that stop it becoming a data swamp: files nobody can find, trust or delete. The storage is the easy part; the conventions are the architecture.

Zones

ZoneContentsWho writesWho reads
LandingFiles exactly as delivered, kept brieflyIngestion toolsIngestion jobs only
Raw (bronze)Immutable copy of every source, with load metadataIngestion jobsEngineers
Curated (silver)Cleaned, typed, deduplicated, conformedPipelinesEngineers, analysts
Consumption (gold)Business-ready, modelled, documentedPipelinesEveryone with access
SandboxPersonal and team experiments, auto-expiringAnalysts, scientistsTheir owners
ArchiveOld data kept for legal reasonsLifecycle rulesRarely, on request

Storage layout

Separate by environment and zone at the bucket or container level, because that is where cloud permissions, encryption keys and lifecycle rules apply. Use a fixed path convention below that.

s3://acme-lake-prod-raw/       one bucket per environment and zone
  crm/customers/ingest_date=2026-11-07/part-0000.parquet
  orders_db/orders/ingest_date=2026-11-07/...
s3://acme-lake-prod-curated/
  sales/orders/                  table format folder (Delta or Iceberg)
s3://acme-lake-prod-gold/
  sales/fact_order_line/

pattern: <env>-<zone> / <source or domain> / <dataset> / <partition>

Implementation steps

1. Set up the landing zone. Cloud accounts or subscriptions per environment, network isolation, encryption with managed keys, central logging. Do this before any data arrives.

2. Create zones with their own permissions. Ingestion roles can write raw; only pipelines write curated and gold; analysts read gold. Deny public access everywhere.

3. Register everything in a catalog. Glue Data Catalog, Unity Catalog, Purview or Knowledge Catalog. A file that is not in the catalog does not exist for consumers.

4. Standardise ingestion. One framework, with a metadata contract per source: owner, classification, schema, frequency, retention. Reject sources without an owner.

5. Choose formats. Keep raw in the delivered format or Parquet; use an open table format from curated onwards, so you get updates, deletes and schema evolution.

6. Apply lifecycle rules. Landing files expire in days, sandbox in weeks, raw moves to cheaper storage classes after months, archive follows legal retention.

7. Add quality and lineage. Checks between zones; lineage captured automatically where possible.

8. Publish and measure. Track the share of datasets with an owner, a description and a classification. Falling numbers are the first sign of a swamp.

Building blockAWSAzureGoogle Cloud
StorageS3ADLS Gen2Cloud Storage
Catalog and permissionsGlue Data Catalog, Lake FormationPurview, storage RBAC and ACLsKnowledge Catalog, IAM
QueryAthena, Redshift SpectrumSynapse serverless, FabricBigQuery external and BigLake tables
ProcessingGlue, EMRDatabricks, Fabric SparkDataproc, Dataflow

30. Implementing an Enterprise Data Warehouse

A warehouse gives the business one trusted, modelled version of its data, optimised for SQL and BI. Three classic design approaches still shape how it is built:

ApproachIdeaStrengthsWeaknesses
Kimball (bottom-up)Build star-schema marts per business process, joined by conformed dimensionsFast value, easy for BI usersNeeds discipline to keep dimensions conformed
Inmon (top-down)A normalised enterprise warehouse first, then marts from itIntegrated, consistent coreSlow to deliver
Data Vault 2.0Hubs (keys), links (relationships) and satellites (history) in the core, marts on topAuditable, absorbs source changes, parallel loadingComplex; still needs marts for users
 Sources ─► Staging ─► Integration layer ─► Data marts ─► Semantic layer ─► BI
            (as is)    (3NF or Data Vault)   (star schemas)  (metrics)

The bus matrix

Kimball's bus matrix is the planning tool: business processes as rows, shared dimensions as columns. Each tick is a fact table that must use that conformed dimension, and the matrix becomes the delivery roadmap.

Business processDateCustomerProductStoreSupplier
Orders✓✓✓✓
Returns✓✓✓✓
Inventory✓✓✓✓
Purchasing✓✓✓

Implementation steps

1. Start from business questions. Interview stakeholders, list the key metrics and decisions, and build the bus matrix.

2. Choose the core approach and platform. Kimball for speed, Data Vault when sources change often and audit matters; then a platform from the evaluation criteria in this card.

3. Build conformed dimensions first. Date, customer and product, with surrogate keys and SCD rules agreed per attribute.

4. Deliver one process at a time. Typically the highest-value row of the bus matrix, end to end, including tests and a dashboard.

5. Load incrementally. CDC or watermarks into staging, MERGE into the core, idempotent at every step.

6. Manage workloads. Separate compute for loading, BI and ad hoc queries; materialised views for hot aggregates.

7. Secure and govern. Role-based access, masking of personal data, documented metrics in the semantic layer.

The Data Warehouse card covers star and snowflake schemas, SCDs and normal forms; the Data Modelling card builds a full mart with DDL and PySpark.

31. Implementing a Lakehouse on Delta Lake

A lakehouse combines the lake's cheap open storage with the warehouse's reliability, using an open table format. Here is an enterprise implementation on Delta Lake with Unity Catalog, followed by the Iceberg equivalent.

 Ingestion           Storage and catalog            Consumption
 ─────────           ───────────────────            ───────────
 CDC, Auto Loader ─► prod catalog               ─►  SQL warehouses, BI
 Kafka streams       ├─ bronze  raw, append-only    ML, feature store
 Batch connectors    ├─ silver  cleaned            Delta Sharing
                     └─ gold    marts, products    Semantic layer, AI
                     Unity Catalog: access, lineage, audit, tags

Step 1: lay out catalogs and schemas

Decide early how catalogs map to the organisation, because permissions and isolation follow this structure. Two common designs: one catalog per environment with schemas per layer, or one catalog per domain per environment.

CREATE CATALOG IF NOT EXISTS prod_sales
  MANAGED LOCATION 's3://acme-lakehouse-prod/sales';

CREATE SCHEMA IF NOT EXISTS prod_sales.bronze;
CREATE SCHEMA IF NOT EXISTS prod_sales.silver;
CREATE SCHEMA IF NOT EXISTS prod_sales.gold;

GRANT USE CATALOG ON CATALOG prod_sales TO `sales-analysts`;
GRANT USE SCHEMA, SELECT ON SCHEMA prod_sales.gold TO `sales-analysts`;

Step 2: set table standards

Write the defaults once and apply them to every table through templates or code review.

CREATE TABLE prod_sales.silver.orders (
  order_id     STRING NOT NULL COMMENT 'Business key from the order system',
  customer_id  STRING,
  order_ts     TIMESTAMP COMMENT 'UTC',
  amount       DECIMAL(18,2),
  _ingested_at TIMESTAMP COMMENT 'Load time, for audit'
)
CLUSTER BY (order_ts)
COMMENT 'One row per order. Owner: sales-domain'
TBLPROPERTIES (
  'delta.enableChangeDataFeed'      = 'true',
  'delta.deletedFileRetentionDuration' = 'interval 7 days',
  'delta.logRetentionDuration'      = 'interval 30 days'
);
StandardWhy
Managed tables in Unity CatalogGovernance, lineage and automatic maintenance
Liquid clustering, not partitioning, for most tablesDatabricks guidance for tables under 1 TB, and flexible later
Comments and owners on every tableDiscoverability and accountability
Retention properties set deliberatelyThey decide time travel and how long deleted personal data survives
UTC timestamps, DECIMAL money, audit columnsConsistency across domains

Step 3: ingestion and processing

Step 4: operate it

The same lakehouse on Iceberg

ConcernDelta Lake on DatabricksIceberg (open or multi-engine)
CatalogUnity CatalogGlue Data Catalog, Apache Polaris, Snowflake Horizon, Unity Catalog
EnginesDatabricks, SparkSpark, Trino, Flink, Athena, Snowflake, BigQuery
LayoutLiquid clusteringHidden partitioning, sort orders, partition evolution
CompactionOPTIMIZErewrite_data_files procedure
Removing old dataVACUUMexpire_snapshots, then remove_orphan_files
Change feedChange data feedIncremental reads between snapshots; row lineage in v3

32. Implementing a Data Mesh

Data mesh is mostly an organisational change, not a product you install. It answers a scaling problem: one central data team becomes a bottleneck when hundreds of sources and consumers depend on it.

PrincipleMeaningIn practice
Domain ownershipThe teams who know the data own it end to endSales owns sales data products, including quality and support
Data as a productData is published with the care of a productOwner, documentation, SLOs, contract, version
Self-serve platformA platform team makes publishing easyTemplates for pipelines, catalogs, CI/CD, access
Federated computational governanceShared rules, enforced automaticallyPolicies as code, tag-based security, standard checks

Is it right for you?

SignalMesh helpsMesh is premature
OrganisationMany domains, many data teamsOne small data team
BottleneckCentral team backlog of monthsDelivery is fast enough
Domain skillsDomains have or can hire engineersDomains have no technical staff
Platform maturityStrong platform and governance automationBasics such as a catalog are missing

A data product specification

Every product is described in a standard, machine-readable file, so the platform can validate it, publish it to the catalog and enforce its policies.

name: sales.orders_daily
domain: sales
owner: sales-data-team@acme.example
description: Daily orders by store and product, net of returns
output_ports:
  - type: table
    location: prod_sales.gold.orders_daily
  - type: share
    name: orders_daily_partners
schema_contract: contracts/orders_daily.yaml  # columns, types, compatibility
classification: internal
slo:
  freshness: "07:00 daily"
  completeness: ">= 99.9% of stores"
version: 2.1.0

Implementation steps

1. Pick two or three domains. Choose domains with strong teams and real consumers, not the hardest problem first.

2. Build the self-serve platform. A product template that creates storage, a catalog or schema, CI/CD, quality checks and access groups from the specification above.

3. Define global standards. Naming, classification tags, security policies, interoperability formats and SLO reporting, enforced by the platform rather than by review meetings.

4. Publish products to a marketplace. A catalog where consumers find products, read contracts and request access.

5. Share between domains without copying. Use the platform's sharing features, so domains consume each other's products in place.

6. Measure and expand. Track product usage, SLO compliance and time-to-publish; add domains as the platform matures.

PlatformDomain boundarySharing between domains
DatabricksCatalog per domain in Unity CatalogGrants across catalogs, Delta Sharing
SnowflakeDatabase or account per domainSecure data sharing, listings
AWSAccount per domainLake Formation cross-account grants, SageMaker Catalog
AzureFabric domains and workspacesOneLake shortcuts, Purview
Google CloudProject per domainBigQuery sharing (Analytics Hub), Knowledge Catalog

Common failures: renaming central teams as "domains" without giving them ownership, building no platform so each domain reinvents pipelines, and skipping governance so products are incompatible. Many companies end up with a hybrid: a central platform and core data, with domains owning their products.

33. Modernising Legacy Systems

Most enterprises do not start from scratch. They have a warehouse appliance, a Hadoop cluster, decades of ETL jobs and perhaps a mainframe. Modernisation moves these to a cloud platform without breaking the business while it happens, which is why it is a programme of waves, not a single project.

Why modernise

Typical legacy estates and where they go

LegacyCommon targetApproach and tools
Teradata, Netezza, Exadata, Oracle or SQL Server warehousesSnowflake, BigQuery, Databricks, Redshift or FabricAutomated SQL conversion (Databricks Lakebridge, Snowflake SnowConvert, BigQuery SQL translation, AWS Schema Conversion Tool), then manual fixes
Hadoop: Hive, Impala, HDFSLakehouse on Delta or IcebergCopy data to object storage, convert Parquet tables in place, port HiveQL, replace Oozie with Airflow or jobs
ETL tools: Informatica PowerCenter, DataStage, SSIS, Ab Initiodbt, Spark, Data Factory, GlueConverters where available, or rebuild using lineage to prioritise
Mainframe: DB2, IMS, VSAM, COBOL batchCloud data platform, mainframe kept as system of record at firstCDC replication of DB2; parse EBCDIC files with COBOL copybooks, for example with the Cobrix library for Spark
On-premise OLTP databasesManaged cloud databasesDatabase migration services plus CDC into analytics
Reporting: Business Objects, Cognos, MicroStrategy, SSRSPower BI, Tableau, LookerRebuild on a semantic layer; retire unused reports

Migration tools change quickly: Databricks made the BladeBridge conversion technology free as Lakebridge in 2025, and Snowflake now centres on SnowConvert AI. Always run a proof of concept on your own code, because conversion rates vary widely by source.

Modernisation patterns

PatternHow it worksWhen
Strangler figReplace the legacy system piece by piece, routing one domain or report at a time to the new platformDefault for large estates: low risk, value early
Coexistence with CDCKeep the legacy source running and stream its changes to the new platformMainframes and core databases that cannot move yet
Big bangMove everything and switch over at onceSmall, simple estates only
Lift and shift firstMove as is, modernise laterA hard data-centre exit date

The six Rs of migration

StrategyMeaningExample
Rehost (lift and shift)Move as is to cloud serversOracle on a cloud VM
ReplatformSmall changes to use managed servicesOracle to a managed database service
Refactor or re-architectRedesign for the cloudTeradata warehouse to a lakehouse
RepurchaseReplace with a SaaS productCustom CRM to Salesforce
RetireSwitch off what nobody usesOld reports found through lineage
RetainKeep on premise for nowMainframe with strict constraints

A modernisation programme, phase by phase

1. Discover. Inventory every table, job, report and user. Usage logs and lineage show what is really used; it is common to find a large share of reports and tables nobody reads, which can be retired instead of migrated.

2. Assess and plan. Score each workload by business value and migration complexity, group them into waves by domain, and build the business case on total cost, including running both systems in parallel.

3. Build the foundation. Landing zone, network, identity, catalog, security policies, CI/CD and the ingestion framework, before the first wave.

4. Migrate a pilot wave. One domain end to end: bulk-load history, keep it current with CDC, convert and test the code, and rebuild its reports.

5. Validate by reconciliation. Prove the new platform gives the same answers as the old one, at several levels (below).

6. Run in parallel, then cut over. Both systems run side by side for an agreed period; consumers switch domain by domain once results match.

7. Decommission. Switch off legacy jobs and hardware wave by wave. Savings only appear when the old system is actually turned off.

Tested: converting legacy Parquet tables in place

Hadoop estates often hold Hive tables already stored as Parquet. Delta can adopt them without rewriting any data, which makes migrating very large tables fast and cheap:

CONVERT TO DELTA parquet.`s3://legacy/warehouse/sales`
  PARTITIONED BY (region_id INT);
Check on 100,000 rows in 3 partitionsResult
Converting without PARTITIONED BYRejected: partition columns found in folder names
Converting with PARTITIONED BYSucceeded
Data filesAll 6 untouched; only a transaction log was added
Reading as DeltaAll 100,000 rows
Table historyOne CONVERT operation

Iceberg offers similar procedures, such as migrate and snapshot, which add Iceberg metadata over existing files. Test them on a copy first: an in-place conversion changes how the folder must be written from then on.

Tested: reconciling source and target

Reconciliation proves the migration is correct. We copied a table, then deliberately deleted one row and changed one amount in the target. A layered check caught both:

from pyspark.sql import functions as F

def profile(df, key, cols):
    row_hash = F.xxhash64(*[F.col(c) for c in cols])
    return df.agg(F.count("*").alias("rows"),
                  F.countDistinct(key).alias("keys"),
                  F.sum("amount").alias("sum_amount"),
                  F.sum(row_hash).alias("hash_sum")).first()

s = profile(source, "order_id", ["order_id", "region_id", "amount"])
t = profile(target, "order_id", ["order_id", "region_id", "amount"])
for k in ("rows", "keys", "sum_amount", "hash_sum"):
    print(k, "match" if s[k] == t[k] else f"DIFF {s[k]} vs {t[k]}")

missing = source.join(target, "order_id", "left_anti")
changed = source.exceptAll(target).join(target.select("order_id"), "order_id")
CheckResultWhat it found
Row count100,000 vs 99,999Something is missing
Sum of amountDiffersA value changed or is missing
Hash of all columnsDiffersAny change in any column
Missing keysorder 7The deleted row
Changed rowsorder 4242The edited amount

Use the cheap aggregate checks on every table and every load; run the row-level comparison when aggregates disagree, or on a sample. Finish with report-level checks: the top business reports must show the same numbers on both platforms.

What usually goes wrong

ProblemExamplePrevention
SQL semantics differInteger division, NULL ordering, case-insensitive comparison defaults, date functionsAutomated conversion plus targeted tests on edge cases
Data types differOracle DATE includes a time; legacy NUMBER precision; CHAR paddingExplicit type mapping document, reviewed per table
Time zonesLegacy stored local time without a zoneConvert to UTC once, at ingestion
Mainframe encodingsEBCDIC text and packed decimalsCopybook-driven parsing, validated against known totals
Hidden dependenciesA spreadsheet macro reading a legacy tableUsage logs, lineage, and asking users before switching off
Running two systems too longCost doubles and attention splitsFixed parallel-run periods and decommission dates per wave
Copying old problemsMigrating unused tables and bad models as isRetire first; redesign where it matters

34. Putting It Together: The Retailer's Plan

Back to the auditor's question. Here is how the architect turns this card into a plan, in an order where each phase makes the next one possible.

PhaseWorkAnswers
1. FoundationA central catalog per platform (Unity Catalog, Lake Formation, Purview, Horizon); identity sync; groupsWhere is all our data?
2. OwnershipOwners and stewards per domain; glossary of key termsWho is responsible?
3. ClassificationOne tag scheme on every platform; discovery scans; unverified-by-defaultWhat is sensitive?
4. ProtectionLeast-privilege grants; tag-based masks and row filters; secretsWho can see emails?
5. VisibilityNative lineage per platform, OpenLineage for custom jobs, one enterprise catalogWhere does email data go?
6. TrustQuality checks, contracts, monitoringIs the data right?
7. ComplianceRetention, erasure process, audit reports, access reviewsCan we prove it?

After phase 5, the auditor's question has a one-query answer: the grants and policies show who can see the email column unmasked, and lineage shows every table it reaches, on every platform. That is the measure of a good data architecture: hard questions get quick, provable answers.

35. Common Mistakes

MistakeWhat goes wrongInstead
Buying a tool before defining ownersAn empty catalog nobody maintainsRoles and processes first
Granting access to individualsAccess piles up as people moveGrant to groups
Treating hashing as anonymisationEmails can be re-identifiedKeyed hashing, and treat it as personal data
Deleting rows and calling it erasureOld versions and copies keep the dataDELETE everywhere, then VACUUM
Masks written table by tableNew tables are missedTags with ABAC policies
Path-based tablesWeak lineage and governanceRegister tables, use names
Secrets in notebooksLeaks through Git and logsSecret scopes or vaults
Assuming lineage is completeGaps appear in an auditTest capture and measure coverage
Time travel as a backupLost with the storage, or after VACUUMDeep clones or replication
No written decisionsThe same debates repeatArchitecture decision records

36. Common Interview Questions

QuestionShort answer
What does a data architect do?Designs how data is collected, stored, connected, secured and used across the company, and sets the standards teams build on
What is data governance?The roles, rules and processes for owning, defining, protecting and using data
Data owner vs data steward?The owner is accountable and approves access; the steward maintains definitions and quality day to day
Privacy vs security?Security protects data from unauthorised access; privacy makes sure personal data is used lawfully and fairly
Masking vs tokenisation vs encryption?Masking hides values when displayed; tokenisation swaps values for tokens kept in a vault; encryption scrambles data with a key
Is a hashed email anonymous?No. It can be guessed by hashing likely emails, and even a keyed hash is still pseudonymised personal data
How do you handle a right-to-erasure request on Delta?Find every copy through lineage, DELETE from each, then VACUUM after retention so old versions are physically removed
RBAC vs ABAC?RBAC grants by role or group; ABAC applies policies based on attributes such as tags, so they scale to new tables
How do row filters and column masks work in Unity Catalog?A SQL function attached to a table filters rows or changes a column's value per user at query time
What is defence in depth?Several security layers, identity, access, encryption, network and monitoring, so one failure does not expose everything
What is data lineage used for?Impact analysis, root cause analysis, compliance, trust and finding unused tables
What are RPO and RTO?RPO is how much data you can lose; RTO is how long recovery may take
Is Delta time travel a backup?No. It lives in the same storage and VACUUM removes old versions; use deep clones or replication
What is a data contract?An agreement on a dataset's schema, meaning, quality and freshness between producer and consumers
What is MDM?Matching records about the same entity across systems into one golden record
What is an ADR?An architecture decision record: the context, decision, options and impact, written down
How do you make governance scale?Federated ownership, governed tags, ABAC policies, automated lineage and access by group
Warehouse vs lake vs lakehouse?A warehouse holds modelled data in one SQL engine; a lake stores any files cheaply; a lakehouse adds transactions and governance to lake storage with open table formats
Data mesh vs data fabric?Mesh is an organisational model, with domains owning data products; fabric is a technology layer that uses metadata to connect and govern data across systems
How do you avoid vendor lock-in?Open table formats, open catalogs, standard SQL, infrastructure as code, and keeping data in storage you control
ETL vs ELT?ETL transforms before loading; ELT loads raw data and transforms inside the target platform
Why use CDC?It streams inserts, updates and deletes from the transaction log with little load on the source, and keeps copies current
How do you keep security consistent across Snowflake, Databricks and S3?One classification scheme, the same tags everywhere, policies generated from one definition, central audit, and lineage to find unprotected copies
How do you handle data residency?Choose regions per law, keep personal data in-region including backups and logs, and move only aggregated or anonymised data across borders
How would you migrate an on-premise warehouse to the cloud?Inventory with usage data, design the target, move data then sync with CDC, convert logic, run in parallel and reconcile, then cut over by domain and decommission
How do you choose between cloud platforms?Weighted criteria agreed with stakeholders: fit, total cost, openness, security, skills, ecosystem and vendor, recorded in an ADR
What changes for AI workloads?Permission-aware retrieval, redaction before embedding, lineage from data to model, and checking the purpose and consent of training data
What is zero trust?Never trusting a request because of its network location: verify every identity and grant least privilege
Delta, Iceberg or Hudi?Delta when Databricks or Fabric is the main engine; Iceberg when many engines share data or on AWS, Snowflake and BigQuery; Hudi or Paimon for heavy upserts and Flink streaming
Backward vs forward compatibility?Backward: new readers read old data, so upgrade consumers first. Forward: old readers read new data, so upgrade producers first
Which schema changes are safe?Adding optional fields with defaults. Renames, removing required fields and narrowing types break someone
How do you make a breaking change safely?Expand and contract: add the new field, dual-write, migrate consumers using lineage, deprecate, then remove
How do you make pipelines idempotent?MERGE on a business key or overwrite whole partitions, so a rerun produces the same result
What is write-audit-publish?Write to a hidden branch, clone or staging table, validate, then publish atomically
How do you isolate workloads?Separate compute per workload over shared storage, with each tagged and budgeted
What is a semantic layer?Business metrics defined once and reused by every BI and AI tool, so numbers agree
What are the pillars of data observability?Freshness, volume, schema, distribution and lineage
SLI vs SLO vs SLA?The measurement, the internal target, and the external promise
Why keep personal data out of Kafka topics?Retained logs are hard to erase selectively; send IDs and keep details in governed tables
How do you stop a data lake becoming a swamp?Zones with permissions, a mandatory catalog entry with owner and classification at ingestion, lifecycle rules, and quality checks between zones
Kimball, Inmon or Data Vault?Kimball for fast value with conformed star schemas; Inmon for an integrated normalised core; Data Vault for auditable history when sources change often, always with marts on top
What is a bus matrix?Business processes against conformed dimensions; it plans the warehouse and keeps dimensions shared
How do you structure catalogs in a lakehouse?By environment and layer, or by domain and environment; permissions and isolation follow that choice
What are the four principles of data mesh?Domain ownership, data as a product, a self-serve platform, and federated computational governance
When is data mesh a bad idea?Small organisations, domains without technical staff, or a platform that lacks basic governance
What is the strangler fig pattern?Replacing a legacy system piece by piece, routing one part at a time to the new platform
How do you validate a migration?Layered reconciliation: counts, aggregates, column hashes, row-level differences, then the top business reports
How do you migrate Hive tables to a lakehouse?Copy to object storage and convert Parquet tables in place to Delta or Iceberg, adding metadata without rewriting files
How do you modernise a mainframe data estate?Keep it as the system of record at first, replicate with CDC, parse files with copybooks, and move consumers gradually
What is the medallion architecture?Layers of increasing quality in a lakehouse: bronze keeps raw data, silver holds cleaned and conformed entities, gold holds business-ready models
Why keep a bronze layer?It is the replay point and audit record: fix silver logic and rebuild from bronze without asking the source to resend
What is the difference between landing and bronze?Landing holds files exactly as delivered and is short-lived; bronze is a queryable, append-only table of the same data with load metadata, kept long term
What is a landing zone?Either the first drop area for incoming data, or the secure cloud foundation of accounts, networks, identity and policies; know both meanings
What is the edge layer?Devices, sites and systems outside the platform where data is produced; edge gateways buffer, filter, translate and encrypt before sending
Where do you apply data quality in medallion?Silver: expectations validate rows and quarantine failures; bronze keeps everything; gold gets reconciliation and certification
Is medallion the same as Kimball?No. Medallion is about layers of refinement; Kimball is a modelling technique, usually used for gold
How do you avoid loading a file twice?Use tools that track loaded files, such as Auto Loader, COPY INTO or Snowpipe, or a streaming checkpoint
What are the biggest challenges in data modelling?Grain, keys, history and timing: mixed grain, inconsistent IDs across sources, changing attributes, late-arriving data, many-to-many relationships and inconsistent metric definitions
How do you handle a late-arriving dimension?Insert an inferred placeholder member, key the fact to it, and update the placeholder when the real dimension row arrives
What happens if you join two fact tables directly?They usually share only dimensions, so the join is many-to-many: rows multiply, measures are double counted, and an inner join drops members missing from one side
Why is a fact-to-fact join slow at scale?Both sides are shuffled on the join key, the output is the sum of left × right rows per key, hot keys cause skew, and partitions spill or fail
How do you combine two facts correctly?Drill-across: aggregate each fact to the same grain on conformed dimensions, then full outer join; or union the facts and aggregate once
What KPIs measure enterprise data quality?Data KPIs (completeness, validity, uniqueness, consistency, accuracy, reconciliation), delivery KPIs (freshness SLA, MTTD, MTTR, data downtime) and programme KPIs (CDE coverage, ownership, issues found by consumers)
What is data downtime?The total time data was wrong, missing or late: the sum of time to detect plus time to resolve across incidents
What is a golden record?The single best version of an entity, built by matching source records and choosing the best value per attribute, with a stable master ID and lineage
What is the difference between MDM and data governance?Governance sets policies, ownership and standards for all data; MDM applies them to master data to produce governed golden records
What are the types of MDM?By domain (customer, product, supplier, employee, location, asset, financial), by usage (operational, analytical, collaborative) and by style (registry, consolidation, coexistence, centralised)
What is MDM?Master data management: the process and tools that create one trusted golden record for core entities such as customer and product across all systems
How is MDM different from a data warehouse?A warehouse integrates high-volume transactional history for analysis; MDM resolves and governs a small set of core entities and can sync corrections back to source systems. The warehouse uses MDM master IDs as dimension keys
What is survivorship in MDM?Rules that choose the best value of each attribute from matched records, such as the most trusted source, the most recent update or the most complete value
What are the MDM implementation styles?Registry, consolidation, coexistence and centralised (transaction hub), from least to most intrusive