Skip to content
All roles

Data & AI

Data Engineer

Comprehensive guide covering SQL & Database Fundamentals, Python for DE, ETL/ELT Pipelines, Data Warehousing, Big Data Technologies (Spark, Kafka, Airflow), Cloud Platforms, and System Design.

300 questionsUpdated 2026-02-03BeginnerIntermediateAdvanced

What you will be asked about

SQL & Database FundamentalsPython for Data EngineeringETL/ELT ProcessesData WarehousingBig Data TechnologiesCloud PlatformsNoSQL DatabasesData Quality & TestingDevOps & CI/CDData Modeling & ArchitecturePerformance OptimizationReal-time & StreamingScenarioBehavioral

How to prepare

  • Go through the topic list above and mark every one you cannot explain for five minutes unprepared. Those are your gaps.
  • Pair every concept with a story from your own work — interviewers probe depth, and depth comes from having actually done it.
  • Do the DSA rounds anyway. Almost every role in this list still screens with coding.
  • Prepare two projects you can whiteboard end to end, including what you would change now.

Also do

Data Engineer interview questions300

300 of 300 questions

SQL & Database Fundamentals30

The WHERE clause is used to filter individual rows before any groupings are made. The HAVING clause is used to filter groups created by the GROUP BY clause, typically involving aggregate functions like SUM, COUNT, or AVG. Example: 'WHERE salary > 50000' filters rows; 'GROUP BY dept HAVING COUNT(*) > 10' filters departments with more than 10 employees.

INNER JOIN: Returns records with matching values in both tables. LEFT JOIN: All records from the left table + matching records from the right. RIGHT JOIN: All records from the right table + matching records from the left. FULL OUTER JOIN: All records when there is a match in either table. CROSS JOIN: Produces a Cartesian product (every row from table A joined with every row from table B).

Window functions perform calculations across a set of table rows related to the current row without grouping them into a single output row. Use cases: 1) Calculating running totals using SUM() OVER(), 2) Ranking data using RANK() or DENSE_RANK(), 3) Accessing previous/next row values using LAG() or LEAD() for time-series analysis.

Optimization involves: 1) Analyzing the Execution Plan to find bottlenecks. 2) Indexing columns used in JOINs and WHERE clauses. 3) Avoiding 'SELECT *' to reduce I/O. 4) Using Partition Pruning. 5) Replacing correlated subqueries with JOINs or CTEs. 6) Ensuring statistics are up-to-date for the query optimizer.

DELETE is a DML command that removes specific rows (can be filtered with WHERE and rolled back). TRUNCATE is a DDL command that removes all rows from a table but keeps the structure (faster, not logged per row). DROP is a DDL command that deletes the entire table structure and data from the database.

A clustered index determines the physical order of data in a table (only one allowed per table, usually the Primary Key). A non-clustered index is a separate structure from the data rows that contains pointers to the actual data (multiple allowed). Think of a clustered index as the phone book itself (sorted by name) and a non-clustered index as the index at the back of a textbook.

A query execution plan is the sequence of operations the database engine performs to run a query. You read it to find 'Table Scans' (expensive) versus 'Index Seeks' (efficient). Using 'EXPLAIN' before your query shows the cost, join types (Hash Join, Nested Loop), and the order of operation execution.

CTEs (Common Table Expressions) are temporary result sets defined using the 'WITH' clause. They differ from subqueries because: 1) They are more readable and can be reused within the same query. 2) They support recursion (Recursive CTEs). 3) They help break down complex logic into modular steps.

Partitioning involves dividing a large table into smaller, more manageable pieces (partitions) based on a key (e.g., Date or Region). This improves performance through 'Partition Pruning,' where the database only scans the relevant partition instead of the whole table.

Sharding is a horizontal scaling technique where data is split across multiple independent database instances (shards). Unlike partitioning (which happens on one server), sharding distributes the load across different physical servers to handle massive traffic and data volume.

OLTP (Online Transactional Processing) is optimized for many small, fast transactions (e.g., ATM, e-commerce orders). It uses normalized schemas. OLAP (Online Analytical Processing) is optimized for complex queries and data analysis. It uses denormalized schemas (Star/Snowflake) and columnar storage.

ACID ensures reliable transaction processing: Atomicity (All or nothing), Consistency (Data remains valid), Isolation (Transactions don't interfere), and Durability (Committed data is permanent even after a crash).

A materialized view is a database object that contains the results of a query and persists them physically. Unlike a standard view (which runs the query every time), a materialized view provides much faster read performance for complex aggregations but requires a refresh strategy when underlying data changes.

A deadlock occurs when two transactions wait for each other to release locks. Handling strategies: 1) Consistent ordering of resource access. 2) Keeping transactions short. 3) Using appropriate isolation levels. 4) The DB engine usually detects deadlocks and kills one transaction (the 'victim'), which must be retried by the application.

Replication is copying data across multiple servers for high availability and load balancing. Types: 1) Snapshot (Full copy). 2) Transactional (Incremental updates). 3) Merge (Bi-directional). 4) Synchronous (Guarantees zero data loss but slower) vs. Asynchronous (Faster but risk of slight lag).

CAP theorem states that a distributed system can only provide two out of three: Consistency (Every read receives the most recent write), Availability (Every request receives a response), and Partition Tolerance (The system operates despite network failures). In the real world, network partitions are inevitable, so systems must choose between CP or AP.

Normalization reduces data redundancy by splitting tables (used in OLTP). Denormalization improves read performance by combining tables and introducing redundant data (used in OLAP/Data Warehousing).

1NF: Atomic values, no repeating groups. 2NF: In 1NF + no partial dependencies on the PK. 3NF: In 2NF + no transitive dependencies. BCNF: A stricter version of 3NF where every determinant must be a candidate key.

1) Requirement gathering (Entities/Attributes). 2) Conceptual design (ERD). 3) Logical design (Normalization). 4) Physical design (Indexing/Partitioning). 5) Selection of the right database type (Relational vs. NoSQL) based on the access patterns.

Star Schema: Central fact table connected to denormalized dimension tables (simpler, faster joins). Snowflake Schema: Dimension tables are normalized into further tables (saves space, more complex joins).

Fact tables contain quantitative metrics (numbers/facts like Price, Quantity) and foreign keys to dimensions. Dimension tables contain descriptive attributes (context like Product Name, Date, Store Location).

SCD 1: Overwrite old data (No history). SCD 2: Add a new row with a version/date (Full history). SCD 3: Add a new column to store the previous value (Partial history). SCD 2 is the industry standard for data warehousing.

NULL represents unknown data. Handle using 'IS NULL' / 'IS NOT NULL' for filtering. Use functions like 'COALESCE()' to provide default values or 'IFNULL()' / 'NVL()' depending on the dialect.

UNION combines results and removes duplicates (expensive because it performs a sort/distinct). UNION ALL combines results including duplicates (much faster).

1) Indexing FKs for join performance. 2) Indexing columns in WHERE/ORDER BY. 3) Using Composite Indexes for queries filtering on multiple columns. 4) Avoiding over-indexing which slows down writes.

Composite Key: A primary key made of two or more columns. Surrogate Key: A system-generated artificial key (like an auto-incrementing ID or UUID) that has no business meaning, used to simplify joins.

Incremental loads fetch only new or changed data. Methods: 1) Using a 'Last_Updated' timestamp column. 2) Change Data Capture (CDC). 3) Using an auto-incrementing ID. 4) Comparing source and target hashes (less efficient).

A transaction is a unit of work. Isolation levels define how transactions see each other's changes: Read Uncommitted (Dirty reads), Read Committed (Standard), Repeatable Read (Prevents non-repeatable reads), and Serializable (Highest, prevents phantoms).

Optimistic: Assumes no conflict; checks for changes before committing (using versioning). Better for high-concurrency. Pessimistic: Locks the data when it's read so no one else can modify it. Better when conflicts are frequent.

Connection pooling maintains a cache of database connections that can be reused for future requests. This avoids the high overhead of opening/closing a new connection for every single query.

Python for Data Engineering25

1) Pandas/Polars (Data manipulation). 2) PySpark (Big data). 3) SQLAlchemy (DB interaction). 4) Boto3 (AWS). 5) Airflow (Orchestration). 6) Requests (APIs). 7) Great Expectations (Data quality).

The GIL is a mutex in CPython that allows only one thread to execute Python bytecode at a time. This makes Python's memory management thread-safe but limits CPU-bound multi-threading. For CPU-intensive DE tasks, we use Multiprocessing or Spark instead.

Multithreading shares the same memory space and is affected by the GIL, making it best for I/O-bound tasks (e.g., waiting for API responses). Multiprocessing creates separate memory spaces and processes, bypassing the GIL, which is ideal for CPU-bound tasks (e.g., heavy data transformations/parsing).

Avoid reading the entire file into memory. Use 'chunking' (e.g., `pd.read_csv(chunksize=1000)`) or use generators to process line by line. For extremely large datasets, libraries like Polars or Dask are preferred as they use lazy evaluation and parallel processing.

Generators are functions that return an iterator using the `yield` keyword. They are memory-efficient because they produce one item at a time only when requested, rather than storing the entire sequence in RAM. This is crucial when processing billion-row datasets.

Decorators are functions that modify the behavior of another function without changing its source code. In Data Engineering, they are commonly used for logging, measuring execution time, or implementing retry logic for failing API calls/DB connections.

List comprehension `[x for x in data]` creates a full list in memory immediately. A generator expression `(x for x in data)` creates a generator object that produces items on the fly. Use the latter for large-scale data processing to save memory.

Using `try`, `except`, `else`, and `finally` blocks. In pipelines, it's best practice to catch specific exceptions (e.g., `sqlalchemy.exc.OperationalError`) rather than a general `Exception`, and log the error context before deciding whether to halt or skip the record.

A context manager ensures resources are properly acquired and released (e.g., closing a file or DB connection) even if an error occurs. The `with` statement simplifies this by automatically calling `__enter__` and `__exit__` methods.

`*args` allows a function to accept any number of positional arguments as a tuple. `**kwargs` allows it to accept any number of keyword arguments as a dictionary. They are useful for creating flexible wrapper functions in ETL frameworks.

1. Use built-in functions (written in C). 2. Use Vectorization (NumPy/Pandas) instead of loops. 3. Use Multiprocessing for CPU tasks. 4. Profile code using `cProfile` to find bottlenecks. 5. Choose the right data structures (Sets for lookups, Lists for ordered data).

A shallow copy creates a new object but stores references to the original nested objects. A deep copy creates a new object and recursively copies all nested objects. Use deep copy when you need to modify a complex configuration dictionary without affecting the original.

Python primarily uses Reference Counting to deallocate objects when their reference count drops to zero. It also has a Cyclic Garbage Collector to handle reference cycles (where two objects refer to each other).

Async/await are used for asynchronous programming. They allow a program to handle multiple tasks concurrently without blocking the main thread. This is highly effective for high-performance data ingestion from multiple webhooks or APIs.

Using the `requests` or `httpx` library. Standard flow: 1. Send GET/POST request. 2. Handle status codes (e.g., 200, 429 for rate limits). 3. Parse JSON response using `.json()`. 4. Flatten the JSON into a tabular format using `pd.json_normalize()`.

`@classmethod` takes `cls` as the first argument and can access class-level variables. `@staticmethod` takes no implicit first argument and behaves like a regular function, used when no class state is needed.

Using the `logging` module. In production, we set different levels: DEBUG (local dev), INFO (pipeline progress), WARNING (non-critical issues), and ERROR (pipeline failures). Logs should ideally be sent to a central sink like ELK or CloudWatch.

Pickle is used for serializing and de-serializing Python objects. While useful for saving model states, it is not recommended for long-term data storage because it's Python-specific and potentially insecure if loading data from untrusted sources.

Lambda functions are anonymous, one-line functions. `map` applies a function to all items in an input. `filter` creates a list of items for which a function returns true. `reduce` (from functools) performs a rolling computation to return a single value.

By deleting large objects using `del`, using `__slots__` in classes to reduce memory footprint, using generators for streaming data, and monitoring usage with `sys.getsizeof()` or memory profilers.

Introduced in Python 3.7, `@dataclass` provides a concise way to create classes that primarily store data. It automatically generates methods like `__init__`, `__repr__`, and `__eq__`, making code cleaner for data structures like schema definitions.

Using the `unittest` or `pytest` library. For data engineering, we 'mock' external dependencies like databases or APIs using `unittest.mock` to ensure we are only testing the transformation logic.

A virtual environment (venv/conda) is an isolated space to install dependencies for a specific project. It prevents 'dependency hell' where different projects require different versions of the same library (e.g., Airflow needing an older version of Pandas).

Type hints (`def func(name: str) -> bool:`) make code more readable and allow static analysis tools (like MyPy) to catch type errors before execution. They are highly recommended for large-scale data engineering projects.

Use `cProfile` for overall execution statistics, `line_profiler` to see time spent per line, or `memory_profiler` to track RAM usage. This helps determine if a slow pipeline is due to heavy computation or inefficient memory management.

ETL/ELT Processes25

ETL (Extract, Transform, Load) transforms data before loading it into the warehouse (best for sensitive data/on-prem). ELT (Extract, Load, Transform) loads raw data into the warehouse and uses the warehouse's compute to transform (best for cloud/modern data stacks).

1. Extraction: Fetching from APIs/DBs. 2. Staging: Saving raw data to S3. 3. Validation: Checking schemas/nulls. 4. Transformation: Cleaning, joining, and aggregating. 5. Loading: Inserting into the final Dimension/Fact tables. 6. Monitoring: Sending success/failure alerts.

Standard tools include Airflow (Orchestration), dbt (Transformation in ELT), AWS Glue (Managed Spark), Informatica, or Fivetran/Stitch for automated data ingestion.

Full load truncates and reloads everything (simple but slow). Incremental load uses 'Watermarking' (comparing a timestamp or ID) to fetch only the records created since the last successful run (faster, more complex).

Data lineage tracks the flow of data from its origin to its destination. It helps in debugging, impact analysis (what happens if I change this source column?), and auditing for compliance (GDPR/SOX).

I use a multi-layered approach: 1) Validation at the source (Schema check), 2) Try-Except blocks in transformation code, 3) Dead Letter Queues (DLQ) for malformed records to prevent the entire pipeline from failing, and 4) Automated retries with exponential backoff for transient network issues. All errors are logged to a central dashboard for observability.

An idempotent pipeline is one that produces the same result no matter how many times it is executed with the same input. This is critical for fault tolerance; if a job fails halfway, you should be able to re-run it without creating duplicate records or corrupted data. This is typically achieved using 'UPSERT' logic or overwrite partitions.

I implement SCD Type 2 for historical tracking by adding 'start_date', 'end_date', and an 'is_active' flag. For SCD Type 1, I simply overwrite the existing value when changes occur. The choice depends on whether the business needs to report on historical states (e.g., tracking a customer's address changes over time).

CDC is a technique that identifies and captures changes (Insert, Update, Delete) made to a source database. Instead of bulk-loading data, CDC reads the database transaction logs (like Binlog in MySQL) to stream changes to the target system in near real-time, significantly reducing the load on the source DB.

1) Log-based: Reading DB engine logs (fastest, least intrusive). 2) Query-based: Using 'updated_at' timestamps (higher load on source). 3) Trigger-based: Using DB triggers to record changes in a separate table (slows down writes). 4) Difference-based: Comparing full source and target snapshots (slowest, only for small tables).

I implement Data Quality (DQ) checks at three stages: 1) Ingestion (Null checks, Type checks), 2) Transformation (Join integrity, business logic validation), and 3) Post-load (Row counts, distribution checks). Tools like 'Great Expectations' or 'dbt tests' are used to automate these assertions.

Data validation ensures data meets defined standards before processing. I implement 'Check-Sum' validation to ensure no data loss during movement and 'Schema validation' to ensure source changes don't break downstream models. It is best implemented in a 'Staging' area before the data hits production tables.

I use the `ROW_NUMBER()` window function in SQL to rank records by a timestamp and keep only the latest one (`Rank = 1`). In Spark, I use `dropDuplicates()`. To prevent duplicates from entering, I use unique constraints or merge keys during the 'Load' phase.

Data transformation is the process of converting data from its raw format into a format usable for analysis. Examples: 1) Formatting (Date strings to Timestamps), 2) Cleaning (Removing whitespace), 3) Aggregation (Summing daily sales), 4) Enrichment (Joining user IDs with demographic data).

Optimization techniques include: 1) Parallel processing to handle multiple chunks simultaneously, 2) Partitioning to reduce data scanned, 3) Using 'Push-down' optimization to let the database handle heavy lifting, 4) Incremental loading instead of full loads, and 5) Optimizing join strategies (e.g., Broadcast joins).

Parallel processing is the simultaneous execution of multiple tasks. In ETL, this means processing multiple files at once or splitting a single large file into chunks that different CPU cores or Spark executors can process at the same time, significantly reducing 'wall-clock' time.

I use 'Schema Evolution' or 'Schema Registry' (in Kafka). For batch pipelines, I implement 'Schema Drift' detection that alerts the team when new columns appear or types change. Using 'Late Binding' (JSON/AVRO) allows pipelines to remain flexible to minor source additions without failing.

Data reconciliation is the process of verifying that data has been migrated or transformed accurately. I compare row counts and aggregate totals (like `SUM(total_amount)`) between the source and target systems to ensure nothing was lost or altered incorrectly during the ETL run.

I use the built-in 'Retries' feature in orchestrators like Airflow. I configure specific settings: `retries: 3`, `retry_delay: 5 minutes`, and `retry_exponential_backoff: True`. This ensures that temporary blips (API timeouts) are handled automatically without manual intervention.

Backfilling is the process of running a pipeline for historical dates where data is missing or needs re-processing due to a logic change. In Airflow, this is done using the CLI `airflow dags backfill` command, ensuring that the 'execution_date' context is correctly applied to each historical run.

I use a combination of: 1) Orchestrator dashboards (Airflow/Dagster), 2) Logging (ELK Stack/CloudWatch), 3) Slack/Email alerts for failures, and 4) Custom 'Audit' tables that track the start time, end time, row counts, and status of every pipeline run.

SLA (Service Level Agreement) is the agreed-upon time by which data must be available in the target system. For example, 'Executive dashboards must be updated by 8:00 AM daily.' Monitoring SLAs involves setting alerts that fire if a job takes longer than expected, even if it hasn't failed yet.

The industry standard is to store all data in UTC in the warehouse. Conversions to local timezones (like IST or PST) should only happen at the final 'Reporting' or 'Visualization' layer. This prevents complex logic errors when joining data from different regions.

Metadata management involves documenting the 'data about the data' (Schema, Source, Ownership, Transformation logic). I use tools like 'DataHub' or 'Amundsen' to maintain a searchable catalog, helping users understand where a column comes from and how it's calculated.

I use Git. Every transformation (SQL in dbt or Python in Spark) is kept in a repository. Changes are made via feature branches, peer-reviewed via Pull Requests, and deployed using CI/CD pipelines (Jenkins/GitHub Actions) to ensure that code in Production is always tested and stable.

Data Warehousing20

A data warehouse is a central repository designed specifically for analytical reporting (OLAP). It integrates data from various transactional systems (OLTP), transforms it into a structured format (usually Star Schema), and stores historical data for long-term trend analysis.

Data Warehouse: Structured data for business users. Data Lake: Raw, unstructured/structured data for data scientists (Storage-first). Data Mart: A subset of a warehouse focused on a specific department (e.g., Marketing or Finance).

Dimensional modeling is a data design technique used to make databases simple for end-users to query and fast for the database to process. It organizes data into 'Facts' (measurements) and 'Dimensions' (contextual attributes like Product, Time, or Geography).

Kimball (Bottom-up): Focuses on building Data Marts first and joining them via 'Conformed Dimensions' (Agile, user-centric). Inmon (Top-down): Focuses on building a centralized, normalized Enterprise Data Warehouse (EDW) first (Robust, but slower and more expensive).

Fact Tables: Store quantitative metrics and FKs. They are usually 'tall and thin' (millions of rows). Dimension Tables: Store descriptive text attributes. They are usually 'short and wide' (fewer rows but many columns). A join between the two provides meaningful business insights.

A factless fact table contains only foreign keys and no numeric metrics. It is used to record events or 'coverage.' Examples: Recording student attendance (who attended which class) or tracking which products were on promotion even if they didn't sell.

A conformed dimension is a dimension that has exactly the same meaning and content across multiple fact tables (e.g., a 'Date' or 'Customer' dimension used by both Sales and Inventory). This allows for 'Drill-across' reporting between different business processes.

Late-arriving facts are records that reach the warehouse days or weeks after the event occurred. To handle them, I look up the dimension keys that were valid *at the time of the event* (using the historical SCD 2 records) rather than using the current version of the dimension.

A bridge table is used to handle many-to-many relationships in a star schema. For example, if one 'Account' can have multiple 'Customers' and one 'Customer' can have multiple 'Accounts,' a bridge table sits between the fact and the dimension to resolve the relationship.

Grain is the level of detail stored in a fact table. For example, is a row in the Sales table one transaction, one line item, or a daily total? Defining the grain is the most important step in modeling, as it determines what questions the warehouse can answer.

An aggregate table is a summary table that stores pre-calculated totals (e.g., monthly sales instead of individual transactions). You use it to improve query performance for dashboards; if a user only needs to see yearly trends, it is much faster to query 12 rows than 12 million.

I implement SCD Type 2 by adding administrative columns: `effective_start_date`, `effective_end_date`, and a `current_flag`. When a change occurs (e.g., a customer moves), I update the `end_date` and `flag` of the old record and insert a new row with the updated info and a new `start_date`.

A junk dimension is a single table used to store a collection of miscellaneous low-cardinality flags and indicators (like 'Yes/No' flags for 'Is_Member', 'Is_Verified', etc.) that don't belong in their own dimension. This prevents the fact table from being cluttered with too many foreign keys.

A role-playing dimension is a single physical dimension table that is referenced multiple times by the same fact table for different purposes. The most common example is a 'Date' dimension acting as 'Order Date,' 'Shipping Date,' and 'Delivery Date' in a single Sales fact table.

Optimization includes: 1) Using Columnar Storage (like Parquet or Snowflake) to read only necessary columns. 2) Implementing Materialized Views for heavy joins. 3) Clustering keys to physically sort data. 4) Partition Pruning to skip irrelevant data files. 5) Using Result Caching for repeated queries.

Partition pruning is an optimization where the query engine skips scanning entire folders or partitions of data that do not match the WHERE clause. For example, if a table is partitioned by 'Year' and you query 'Year = 2024', the engine ignores all other years, saving massive I/O.

Row storage (OLTP) stores all columns of a single record together, making it fast for single-row lookups. Columnar storage (OLAP) stores all values of a single column together. This is ideal for analytical queries that perform aggregates (like SUM or AVG) on specific columns across millions of rows.

Data Vault is a modeling methodology designed to be highly scalable and provide full auditing. It uses: 1) Hubs (Unique business keys), 2) Links (Relationships between hubs), and 3) Satellites (Contextual data and history). It is excellent for handling rapidly changing sources in large enterprises.

History is handled via: 1) Periodic snapshots (storing the state of the world at the end of every month). 2) SCD Type 2 (tracking change-by-change). 3) Time-travel features (found in tools like Snowflake or Delta Lake) that allow querying data as it looked at a specific point in the past.

It is a data design pattern in a Lakehouse: 1) Bronze (Raw): Unfiltered data from sources. 2) Silver (Cleansed): Filtered, joined, and standardized data. 3) Gold (Business): Aggregated data ready for BI and ML.

Big Data Technologies30

Apache Spark is a distributed computing framework for fast processing of large-scale data. Its architecture consists of a Driver Program (orchestrator) and multiple Executors (workers) running on nodes. It uses a Cluster Manager (YARN, K8s) to manage resources and perform in-memory computations.

RDD (Resilient Distributed Dataset) is the basic low-level abstraction (no schema). DataFrame is a distributed collection of data organized into named columns (like a table). Dataset is a type-safe version of DataFrame (available in Java/Scala). Most DE work today is done using DataFrames.

Transformations (e.g., `map`, `filter`, `groupBy`) create a new RDD/DataFrame from an existing one and are 'Lazy.' Actions (e.g., `count`, `collect`, `saveAsTextFile`) trigger the actual execution of the transformations to produce a result or write to disk.

Lazy evaluation means Spark doesn't execute transformations immediately. Instead, it builds a Logical Plan (DAG). Execution only happens when an Action is called. This allows Spark to optimize the entire execution plan (e.g., combining filters) before starting the work.

Optimization includes: 1) Persisting/Caching DataFrames used multiple times. 2) Using Broadcast Joins for small tables. 3) Reducing Shuffling by using appropriate partition keys. 4) Avoiding 'UDFs' (User Defined Functions) in favor of built-in Spark functions. 5) Tuning memory (Executor/Driver memory).

Data skewness occurs when data is not evenly distributed across partitions (e.g., one partition has 90% of the data). This causes some executors to work much longer than others. Handle it by: 1) Salting (adding a random prefix to keys), 2) Re-partitioning, or 3) Using 'Adaptive Query Execution' (AQE) in Spark 3.x.

Partitioning splits data into folders based on a column (ideal for high-level filtering). Bucketing splits data within those partitions into a fixed number of files based on a hash of a column. Bucketing is highly effective for optimizing JOINs on large tables as it avoids expensive shuffling.

Hadoop is a framework for distributed storage and processing. HDFS (Hadoop Distributed File System) is the storage layer. It stores data across many machines in 'Blocks' (default 128MB) and replicates them (usually 3 times) to ensure data is never lost even if a server fails.

MapReduce is a programming model for processing huge datasets. 1) Map: Filters and sorts data (e.g., grouping words). 2) Shuffle: Moves data across the network. 3) Reduce: Aggregates the results (e.g., counting the words).

YARN (Yet Another Resource Negotiator) is the operating system of Hadoop. It manages the cluster's resources (RAM, CPU) and schedules tasks. It allows different engines like Spark, Hive, and Flink to run on the same Hadoop cluster simultaneously.

Apache Hive is a data warehouse software built on top of Hadoop. It allows users to write SQL-like queries (HiveQL) which are then converted into MapReduce or Spark jobs. It is used for batch processing and summarizing large datasets stored in HDFS.

Traditional DBs (RDBMS) are designed for low-latency queries and many small transactions (Schema-on-Write). Hive is designed for high-throughput batch processing over massive data (Schema-on-Read) and has much higher latency because it involves overhead in starting distributed jobs.

Apache Kafka is a distributed event streaming platform. Use cases: 1) Real-time data pipelines (Streaming data from DBs to warehouses). 2) Log aggregation. 3) Messaging systems. 4) Stream processing (Processing events as they arrive).

Producers: Applications that send data to Kafka. Brokers: Servers that store the data and serve the clients. Consumers: Applications that read data from Kafka. A Kafka cluster consists of multiple brokers to ensure high availability and scalability.

A Topic is a category or feed name where records are published. Topics are split into Partitions. Partitions allow Kafka to scale; different consumers can read from different partitions of the same topic in parallel, increasing throughput.

Kafka uses 'Acknowledgments' (acks). `acks=0` (no guarantee), `acks=1` (leader must acknowledge), `acks=all` (leader and all replicas must acknowledge). It also uses 'Offsets' to track which messages a consumer has already read, allowing it to resume after a crash.

Apache Airflow is an open-source platform for orchestrating complex data workflows. It allows you to define tasks and their dependencies as Directed Acyclic Graphs (DAGs) using Python code, providing a powerful UI to monitor and troubleshoot pipelines.

1. Define the default arguments (owner, start_date, retries). 2. Instantiate the DAG object. 3. Define tasks using Operators (e.g., `PythonOperator`, `SnowflakeOperator`). 4. Define dependencies using bitshift operators (e.g., `task1 >> task2`).

Operators: The building blocks that perform a single task (e.g., run a script). Sensors: A special type of operator that waits for an external event (e.g., a file to arrive in S3). Hooks: Interfaces to external systems (e.g., PostgresHook) used by operators to interact with APIs or DBs.

Apache Flink is a powerful framework for 'True' stream processing. Unlike Spark Streaming (which uses micro-batches), Flink processes events one by one with very low latency. It is excellent for stateful computations and complex event processing in real-time.

Batch processing involves collecting data over a period and processing it as a single unit (e.g., daily sales reports). Stream processing involves continuous data ingestion and immediate processing (e.g., fraud detection). Batch focuses on high volume and accuracy (High Latency), while streaming focuses on low latency and speed (Real-time).

Apache Beam is a unified programming model for defining both batch and streaming data processing pipelines. It provides an abstraction layer that allows you to write your pipeline once and run it on any execution engine (Runner) like Apache Spark, Apache Flink, or Google Cloud Dataflow.

A Data Lakehouse combines the cost-effective storage of a Data Lake with the performance and ACID transaction capabilities of a Data Warehouse. It uses open-source storage layers (Delta Lake, Iceberg) to enable features like indexing, time travel, and schema enforcement directly on top of raw files (Parquet/ORC).

These are open-source storage layers that bring reliability to data lakes. They provide: 1) ACID transactions, 2) Scalable metadata handling, 3) Time Travel (querying previous versions), 4) Schema Evolution, and 5) Unified batch and streaming support. They solve the 'corrupt file' issues common in traditional S3/HDFS data lakes.

This is implemented through specialized storage formats like Delta Lake or Apache Iceberg. They use a 'Transaction Log' (Delta Log) or 'Manifest Files' (Iceberg) to track every write operation. This ensures that a reader never sees a partially written file, maintaining Atomicity and Isolation.

Parquet is a columnar storage file format. Advantages: 1) High compression (reduces storage costs), 2) Columnar storage (read only required columns, improving I/O), 3) Highly efficient for OLAP queries, 4) Stores metadata like min/max values for partition pruning.

ORC (Optimized Row Columnar) is similar to Parquet but optimized primarily for Apache Hive. It offers even better compression than Parquet and supports features like ACID transactions and indexing within the file, making it a standard for Hadoop-based systems.

Avro is a row-based storage format. It is schema-dependent and stores the schema in JSON format while the data is binary. It is ideal for Write-Heavy operations and real-time data streaming (Kafka) because it handles schema evolution very gracefully.

Use Avro for data ingestion and streaming (row-based, fast writes). Use Parquet or ORC for analytical querying and data warehousing (columnar, fast reads). Use JSON/CSV only for data landing/external sharing due to poor performance and lack of compression.

Partitioning involves grouping data into sub-directories (e.g., `s3://bucket/year=2024/month=01/`). The goal is to avoid scanning the entire dataset. A good strategy chooses a column with moderate cardinality (e.g., Date) rather than extremely high cardinality (e.g., UserID) to avoid 'too many small files' problem.

Cloud Platforms25

Modern Data Engineering often happens on AWS (S3, Redshift, Glue), Azure (ADLS, Synapse, ADF), or GCP (BigQuery, Dataflow, Cloud Storage). Proficiency in at least one ecosystem is essential for building scalable cloud-native pipelines.

S3 is an object storage service. Storage classes: 1) S3 Standard (frequent access), 2) S3 Intelligent-Tiering (automatic cost-saving), 3) S3 Standard-IA (infrequent access), 4) S3 Glacier (long-term archive). For data lakes, Standard or Intelligent-Tiering is used for active pipelines.

AWS Redshift is a fully managed, petabyte-scale data warehouse. It uses Columnar Storage and Massively Parallel Processing (MPP) to run complex SQL queries across large datasets. It also supports 'Redshift Spectrum' to query data directly from S3 without loading it.

AWS Glue is a serverless ETL service. Components: 1) Glue Data Catalog (central metadata repository), 2) Glue Crawlers (auto-discovers schemas), 3) Glue ETL Jobs (Spark/Python based transformation), 4) Glue Triggers (orchestration).

Lambda is a compute service that runs code in response to events (e.g., file upload to S3). Serverless means you don't manage the underlying servers. In DE, Lambda is used for lightweight tasks like triggering pipelines, moving files, or small API calls.

EMR (Elastic MapReduce) is a managed cluster platform that simplifies running big data frameworks like Apache Spark, Hive, and Presto. It allows you to scale the number of EC2 instances up or down based on the processing requirements of your job.

AWS Kinesis is a platform for streaming data. Use cases: Kinesis Data Streams (capturing real-time logs/events), Kinesis Data Firehose (loading streams into S3/Redshift), and Kinesis Data Analytics (processing streams with SQL).

ADF is a cloud-based data integration service (orchestrator). It uses 'Activities' (e.g., Copy data, Execute notebook) and 'Pipelines' to move and transform data. It is the Azure equivalent of a hybrid between Glue and Airflow.

Azure Databricks is a managed version of Apache Spark with added features like interactive notebooks, 'Delta Lake' support, and optimized performance. It is deeply integrated with Azure AD and Data Lake Storage (ADLS).

Synapse is an integrated analytics service that brings together data warehousing (SQL Pool), big data processing (Spark Pool), and data integration (Pipelines). It allows you to query both relational and non-relational data in one environment.

ADLS Gen2 is a highly scalable storage solution built on top of Azure Blob storage. It features a Hierarchical Namespace (HNS) which allows for efficient folder-level operations, making it suitable for big data workloads and data lakes.

BigQuery is a serverless, highly scalable cloud data warehouse. It uses a unique 'Dremel' execution engine and 'Colossus' storage. Users pay for the amount of data scanned per query, making it very cost-effective for ad-hoc analytical queries.

Dataflow is a fully managed service for executing Apache Beam pipelines. It handles the provisioning of resources and automatic scaling, making it the primary choice for unified batch and streaming processing on GCP.

GCS is the object storage service for GCP (equivalent to AWS S3). It is commonly used as a landing zone for raw data and as the storage layer for a GCP-based data lake.

Pub/Sub is an asynchronous messaging service that decouples senders and receivers. It is the GCP equivalent of Apache Kafka and is used for building real-time event-driven data pipelines.

1) Encryption at rest (KMS/Server-side encryption). 2) Encryption in transit (TLS/SSL). 3) Access control (IAM roles and policies). 4) Network security (VPC/Firewalls). 5) Data masking/Tokenization for PII data.

IAM (Identity and Access Management) allows you to securely manage access to cloud services. Best practices: Follow the 'Principle of Least Privilege,' use Roles instead of root users, and assign permissions to Groups rather than individual users.

A VPC (Virtual Private Cloud) is an isolated section of the cloud where you can launch resources in a virtual network. Security is managed via Subnets (Public/Private), Security Groups (Instance-level firewall), and NACLs (Subnet-level firewall).

1) Turning off unused resources (Dev environments). 2) Using Spot instances for non-critical jobs. 3) Implementing lifecycle policies on S3/Storage (moving to Glacier). 4) Partitioning and indexing to reduce data scan costs in warehouses (BigQuery/Athena).

1) Lift and Shift (move as-is). 2) Re-platforming (move with small changes). 3) Re-architecting (rebuilding for cloud-native features). Common tools include AWS Snowball, Azure Data Box, or online transfer services.

Managed services (e.g., AWS RDS, BigQuery) handle patching, scaling, and maintenance, allowing engineers to focus on logic. Self-hosted (e.g., running Postgres on EC2) offers full control over configuration and versions but adds significant operational overhead and manual scaling responsibilities.

I use a multi-region strategy. 1) Data Replication: Syncing S3/ADLS across regions. 2) Backups: Regular snapshots of databases. 3) Pilot Light: Minimal version of the environment always running in a second region. 4) Infrastructure as Code: Using Terraform to spin up the entire stack in minutes if the primary region fails.

Multi-cloud uses multiple providers (e.g., AWS for storage, GCP for AI/ML) to avoid vendor lock-in. Hybrid cloud combines on-premise infrastructure with cloud services, often used by banks to keep sensitive data on-site while using cloud for burst computation.

I use native tools like Amazon CloudWatch, Azure Monitor, or GCP Stackdriver. I set up 'Custom Metrics' for data-specific monitoring (e.g., tracking the size of a Kafka lag) and configure 'Alarms' to trigger webhooks that notify the on-call engineer via PagerDuty or Slack.

IaC is the practice of managing and provisioning infrastructure through machine-readable definition files (like Terraform or CloudFormation) rather than manual console clicks. It ensures environments are reproducible, version-controlled, and consistent between Dev, Staging, and Prod.

NoSQL Databases15

SQL databases are relational, use structured schemas, and are optimized for complex joins and ACID compliance. NoSQL databases are non-relational, offer flexible schemas (Document, Key-Value), and are designed for horizontal scalability and high throughput for semi-structured data.

I choose NoSQL when: 1) Data is semi-structured or unstructured (e.g., JSON logs). 2) There is a need for extreme horizontal scaling. 3) Rapid development is needed with a changing schema. 4) The application requires real-time, low-latency reads/writes (e.g., caching or session management).

1. Document (MongoDB, CouchDB). 2. Key-Value (Redis, DynamoDB). 3. Wide-Column/Column-Family (Cassandra, HBase). 4. Graph (Neo4j, Amazon Neptune).

MongoDB stores data in JSON-like documents (BSON). It is highly flexible because each document in a collection can have different fields. It supports rich indexing and powerful aggregation pipelines, making it ideal for content management and catalogs.

Cassandra is a distributed wide-column store designed to handle massive amounts of data across many servers with no single point of failure. It uses a partitioned row store and is optimized for extremely high write throughput, often used for IoT and activity logging.

Redis is an in-memory data structure store used as a database, cache, and message broker. Since data is stored in RAM, it offers sub-millisecond latency. It supports data structures like strings, hashes, lists, and sets.

Neo4j is a graph database that stores data in nodes and relationships (edges). It is optimized for traversing complex networks of data, making it the primary choice for recommendation engines, fraud detection (tracing money flow), and social networks.

Eventual consistency is a consistency model used in distributed NoSQL systems where, if no new updates are made to a data item, eventually all accesses to that item will return the last updated value. It prioritizes high availability over immediate consistency (BASE over ACID).

BASE stands for Basically Available, Soft state, and Eventual consistency. It is the counter-philosophy to ACID, designed for distributed systems where scaling and availability are more critical than providing perfectly consistent data at every millisecond.

In NoSQL, we model data based on the Access Patterns (how the data will be read) rather than normalization. We often 'embed' related data into a single document to avoid joins, which NoSQL databases typically don't support or perform poorly at.

Denormalization in NoSQL involves duplicating data across documents to ensure that a single read query can retrieve all the information it needs without needing to reference other collections. This trades storage space for read speed.

Most NoSQL DBs handle 'Atomic' updates only at the single-document level. For multi-document transactions, some (like MongoDB 4.0+) now support ACID transactions, but generally, we handle this through application logic or 'Saga patterns' for distributed consistency.

Sharding is a method for distributing data across multiple machines. A 'Shard Key' is used to determine which machine stores which piece of data. This allows NoSQL databases to scale horizontally to petabytes of data by simply adding more servers.

Choice depends on the data structure: Use MongoDB for flexible documents, DynamoDB/Redis for fast key-value lookups, Cassandra for high-volume time-series/writes, and Neo4j for relationship-heavy data.

Pros: Scalability, flexibility, performance. Cons: No native JOINs (complex code), lack of standardized query language (varies by DB), and weaker consistency guarantees which can lead to data integrity issues if not handled carefully.

Data Quality & Testing15

I use a framework approach: 1) Defining Quality Rules (e.g., column X cannot be null). 2) Profiling (analyzing source data stats). 3) Automated Testing (running dbt tests or Great Expectations). 4) Monitoring (dashboards tracking error rates over time).

1. Accuracy (Is it correct?). 2. Completeness (Are fields missing?). 3. Consistency (Does it match other systems?). 4. Timeliness (Is it up to date?). 5. Validity (Does it follow the format?). 6. Uniqueness (Are there duplicates?).

I implement validation at the 'Gatekeeper' stage. Before data is moved from Staging to Production, I run SQL scripts to check for zero-row counts, check that primary keys are unique, and ensure that date formats are within expected ranges.

Data profiling is the statistical analysis of a dataset to understand its structure, content, and relationships. It involves checking min/max values, frequency of values, null counts, and data distributions to identify anomalies before building pipelines.

1. Unit Testing: Testing small transformation functions in isolation. 2. Integration Testing: Ensuring components (Source -> Spark -> S3) work together. 3. End-to-End Testing: Running a sample dataset through the entire flow and verifying the output in the final table.

Unit testing involves testing a single Python function or SQL snippet using mocked data. Integration testing checks the 'plumbing'—ensuring that the Airflow task can connect to the DB and move data correctly between services.

I create a 'Reconciliation Job' that runs after the ETL. It calculates a hash or a sum (e.g., Total Sales Amount) from the Source and compares it with the target Warehouse. If the delta is above 0%, it flags an alert for manual review.

I track the 'Data Health Score,' which includes: % of nulls in mandatory columns, # of failed dbt tests per day, and the 'Data Freshness' (latency between event occurrence and warehouse availability).

Non-critical issues are logged and reported. Critical issues (like schema mismatch) trigger an immediate 'Stop' of the pipeline. Malformed records are redirected to a 'Bad Records' table for inspection, while valid records continue to the target.

Schema validation is the process of ensuring that incoming data matches the expected structure (columns and types). I use JSON Schema or AVRO Schema Registry to reject messages that don't comply, preventing downstream pipeline crashes.

I use a combination of heartbeats and validation checks. Heartbeats ensure the pipeline is running, while validation checks ensure the data being moved is accurate. I integrate these with tools like Prometheus/Grafana or Datadog to visualize trends in data volume, null rates, and processing latency.

Data observability is the broad ability to understand the state of data in your system. It goes beyond simple 'up/down' alerts to include monitoring for data drift, lineage tracking, and performance bottlenecks, helping teams answer 'Why is the data wrong?' rather than just 'Is it wrong?'

I set up tiered alerting. P0 (Critical) alerts for job failures or schema breaks are sent via PagerDuty/SMS. P1 (Warning) alerts for minor data quality anomalies or SLA near-misses are sent to a dedicated Slack channel for investigation during business hours.

Regression testing ensures that new code changes (e.g., a new transformation logic) do not break existing functionality or change the output for historical data. I run the new pipeline code on a 'Golden Dataset' and compare the results with the previous production output.

I implement frameworks like 'Great Expectations' or 'Deequ' (for Spark). These allow us to define expectations (assertions) as code, which are then automatically executed as part of the pipeline. If the data fails an expectation, the framework can stop the pipeline or quarantine the data.

DevOps & CI/CD15

Continuous Integration (CI) involves automatically testing code changes (SQL/Python) every time they are pushed to Git. Continuous Deployment (CD) is the automated process of deploying that tested code to the production orchestrator (e.g., updating an Airflow DAG in S3).

I primarily use Git (GitHub/GitLab/Bitbucket). It is the foundation of 'DataOps,' allowing us to track changes, collaborate through Pull Requests, and revert to stable versions if a pipeline update causes issues in production.

I follow the 'Git Flow' or 'GitHub Flow' model: 1. Create a feature branch. 2. Write transformation code/tests. 3. Submit a PR. 4. Automated CI runs tests. 5. Peer review. 6. Merge to `main`. 7. CD triggers deployment to the production environment.

Docker packages the code, libraries, and dependencies into a single 'container' image. This ensures that the data pipeline runs exactly the same way on a developer's laptop as it does on a production server, eliminating 'it works on my machine' errors.

Kubernetes (K8s) manages the deployment, scaling, and operation of Docker containers. For Data Engineering, K8s is used to scale Spark executors dynamically or to run Airflow workers that can handle thousands of parallel tasks.

I use GitHub Actions or Jenkins to trigger a suite of PyTest (for Python) and dbt-test (for SQL) scripts whenever a PR is created. This prevents buggy logic or syntax errors from reaching the production branch.

IaC allows us to define our data infrastructure (Redshift clusters, S3 buckets, IAM roles) in text files. Tools like Terraform then provision these resources automatically, ensuring our infrastructure is version-controlled and easily reproducible across regions.

I use environment variables and 'Secret Managers' (like AWS Secrets Manager). I never hardcode credentials. I use config files (YAML/JSON) to define environment-specific settings like DB hostnames or S3 bucket paths for Dev, Staging, and Prod.

It is a strategy to reduce downtime. Two identical environments (Blue and Green) exist. You deploy the new pipeline version to 'Green.' Once verified, you switch the traffic (or the scheduler) from 'Blue' to 'Green.' If there's an issue, you instantly switch back.

I use the ELK stack (Elasticsearch, Logstash, Kibana) or Splunk. All pipeline logs include a `run_id` and `timestamp`. Monitoring is done via dashboards that track 'Success Rate,' 'Average Duration,' and 'Resource Utilization (CPU/RAM).'

These are automation servers used for CI/CD. They listen for Git events (like a push) and execute a 'Pipeline' (a script) that builds the Docker image, runs tests, and pushes the code to the cloud environment.

I use specialized tools like HashiCorp Vault or AWS Secrets Manager. Secrets are encrypted at rest and injected into the pipeline at runtime as environment variables, ensuring that no sensitive passwords ever appear in the code or logs.

My strategy usually involves a 'Rolling Update.' For Airflow DAGs, I deploy to a staging instance first, run a 'Dry Run,' and then deploy to the production folder. For Spark jobs, I update the container image version in the K8s manifest.

Since code is in Git, rolling back is as simple as reverting to the previous commit. For data, I use 'Time Travel' (if using Delta Lake/Snowflake) to restore tables to their state before the faulty deployment ran.

Observability is the holistic monitoring of three pillars: 1. Pipeline Health (Is it running?). 2. Data Health (Is the data correct?). 3. Infrastructure Health (Are resources sufficient?). It allows for rapid root-cause analysis when things go wrong.

Data Modeling & Architecture15

1. Understand business requirements (the 'Why'). 2. Identify entities and relationships. 3. Define the grain. 4. Choose a schema type (Star/Snowflake/Vault). 5. Create the physical model (DDL) considering partition and distribution keys.

1. Use consistent naming conventions. 2. Define Primary/Foreign keys (even if only for documentation). 3. Prefer Star Schemas for analytical performance. 4. Use surrogate keys to decouple from source business keys. 5. Document the data dictionary clearly.

An ERD is a visual representation of the tables and their relationships. It uses symbols to show if relationships are one-to-one, one-to-many, or many-to-many. It is the 'blueprint' of the database before any code is written.

I resolve many-to-many relationships by introducing an 'Associative' or 'Junction' table (also called a Bridge table in Warehousing). This table contains foreign keys to both entities, converting the many-to-many relationship into two one-to-many relationships.

Analytical modeling focuses on read performance. Unlike transactional modeling (which focuses on write integrity), we use denormalization, Star Schemas, and Columnar storage to ensure that aggregations over millions of rows are returned in seconds.

I design for horizontal scaling (adding more nodes). Key strategies include: 1. Data Partitioning. 2. Distributed processing (Spark/Kafka). 3. Shared-nothing architecture. 4. Decoupling storage (S3) from compute (Snowflake/BigQuery).

Lambda architecture handles both batch and real-time data processing in two parallel layers: 1. Batch Layer (High accuracy, high latency). 2. Speed Layer (Real-time, lower accuracy). 3. Serving Layer (Merges both for the end user).

Kappa architecture is a simplification of Lambda. It removes the batch layer and treats everything as a stream. All data processing (including historical re-runs) is handled by the streaming engine (like Flink or Spark Streaming) reading from a log (like Kafka).

I use a message broker (Kafka/PubSub), a stream processing engine (Flink/Spark Streaming), and a low-latency sink (Redis/Cassandra/Druid). I prioritize 'At-least-once' or 'Exactly-once' delivery guarantees based on the use case.

It involves breaking down a large data monolithic into small, independent data services. Each service owns its own data store and communicates via APIs or message buses, allowing teams to scale and update parts of the pipeline independently.

I implement data governance through a combination of policy and technology. This includes: 1) Data Cataloging (using DataHub/Amundsen) for discovery, 2) Access Control (RBAC/ABAC), 3) Data Lineage (tracking flow from source to sink), and 4) Data Quality Standards. It ensures data is discoverable, secure, and trustworthy across the organization.

MDM is the practice of creating a single, consistent 'Golden Record' for critical business entities like 'Customer' or 'Product' that are spread across multiple systems. It involves deduplication, data cleansing, and synchronization to ensure every department uses the same source of truth.

I handle privacy by: 1) Data Masking/Anonymization of PII (Personally Identifiable Information) in the Silver layer, 2) Implementing 'Right to be Forgotten' by building pipelines that can selectively delete user data from HDFS/S3, and 3) Using encryption for sensitive fields at rest and in transit.

A data catalog is a searchable inventory of data assets. Metadata management involves storing technical metadata (schemas, file paths) and business metadata (descriptions, owners). It reduces 'data discovery' time and helps engineers understand the impact of schema changes.

For high availability (HA), I ensure no single point of failure. This means: 1) Deploying services across multiple Availability Zones, 2) Using managed services with built-in failover (like Amazon RDS/Redshift), 3) Implementing load balancing, and 4) Designing idempotent pipelines so they can recover gracefully from infrastructure restarts.

Performance Optimization15

I optimize queries by: 1) Using EXPLAIN to find full table scans, 2) Creating appropriate B-tree or Hash indexes, 3) Using materialized views for complex aggregates, 4) Avoiding wildcards (`%`) at the start of strings, and 5) Rewriting subqueries into JOINs where the optimizer performs better.

Advanced techniques include: 1) Predicate Pushdown (filtering data at the source), 2) Columnar Pruning (reading only needed columns), 3) Partition Pruning, 4) Using Hinting to force specific join types (like Hash Join), and 5) Statistics Updating so the query optimizer has accurate data distribution info.

I use a 'Divide and Conquer' approach. I leverage distributed frameworks like Spark to parallelize the workload across a cluster of machines. I ensure data is partitioned correctly to avoid network shuffling and use memory-efficient file formats like Parquet.

Data compression (Gzip, Snappy, Zstd) reduces the size of files to save storage and I/O. Use Snappy for Spark/Big Data because it's fast and splittable. Use Gzip for long-term cold storage where compression ratio is more important than CPU speed.

1. Broadcast small tables during joins to avoid shuffling. 2. Filter data early (Predicate Pushdown). 3. Adjust `spark.sql.shuffle.partitions`. 4. Use `coalesce()` instead of `repartition()` when reducing partitions to minimize shuffling. 5. Cache/Persist DataFrames that are reused multiple times.

Partition Pruning skips entire folders based on the partition key (e.g., skip all folders except `year=2024`). Predicate Pushdown moves the filter logic (e.g., `WHERE price > 100`) directly to the storage layer, so the engine only reads the relevant rows into memory.

I look for bottlenecks using profiling tools. I optimize by: 1) Increasing parallelism, 2) Using incremental instead of full loads, 3) Tuning batch sizes for DB writes, and 4) Moving transformations to the database layer (ELT) if the DB compute is more efficient than the middleware.

Caching involves storing results in memory (RAM) for fast access. In Spark, I use `.cache()`. In APIs, I use Redis. In Data Warehouses, I leverage result-set caching. The goal is to avoid re-computing or re-reading the same data repeatedly during a workflow.

In Spark, I tune the `executor.memory` and `memory.fraction`. In Python, I use generators and `del` to free up objects. I also choose data types carefully (e.g., using `float32` instead of `float64` in large arrays) to reduce the memory footprint by 50%.

A broadcast join sends a copy of a small table to all worker nodes. This allows the workers to perform a join locally without moving the large table across the network (shuffling). It is one of the most effective ways to speed up Spark SQL queries involving a large fact and a small dimension.

1) Implementing S3 Lifecycle policies to move old data to Glacier. 2) Using high-compression formats like Zstd. 3) Deleting intermediate staging files after the job completes. 4) Using 'Cold' storage for historical audit logs that are rarely queried.

Data tiering categorizes data into: 1) Hot (frequently accessed, high-performance SSD), 2) Warm (regular access, standard HDD), and 3) Cold (archival, cheap object storage). This balances performance and budget by keeping only 'active' data on expensive hardware.

In Python, I use the `multiprocessing` library to utilize multiple CPU cores. In Spark, I increase the number of executors and ensure the data is partitioned enough so that every executor has a task to perform, avoiding idle resources.

When writing to a DB or sending API requests, I tune the batch size. Too small, and you're limited by network overhead; too large, and you risk memory overflows or timeout errors. For most SQL databases, batches of 1,000 to 10,000 records are usually the 'sweet spot.'

I use the Spark UI to find 'Stragglers' (tasks that take way longer than others) or 'Shuffles' that involve too much data. I use `cProfile` in Python and `EXPLAIN ANALYZE` in SQL to pinpoint the exact line or operation causing the delay.

Real-time & Streaming15

Stream processing is the practice of ingesting, transforming, and acting on data as it is generated in real-time. Unlike batch processing which waits for data to accumulate, streaming allows for immediate responses to events (e.g., adjusting prices based on live inventory).

Batch processes data in 'Chunks' (High Latency, High Throughput). Streaming processes data as 'Events' (Low Latency, continuous). Batch is for reporting; Streaming is for real-time alerts and reactive systems.

Kafka Streaming (or Kafka Streams) is a library that allows you to process data stored in Kafka. It uses 'Topologies' where data is consumed from an input topic, transformed (filtered, joined, or aggregated), and written back to an output topic in near real-time.

An architecture where the flow of the program is determined by events (e.g., a user purchase, a sensor reading). Services communicate by publishing and subscribing to events via a message broker, making the system highly decoupled and scalable.

In streaming, I use Watermarking. A watermark is a threshold that tells the engine how long to wait for late events before closing the 'time window' and finalizing the calculation. Events arriving after the watermark are either dropped or handled in a separate 'late-data' side-output.

Windowing splits a continuous stream into finite 'buckets' of time for aggregation. Types: 1) Tumbling (Fixed, non-overlapping), 2) Sliding (Overlapping), 3) Session (Based on user activity/inactivity).

Exactly-once ensures that even if a system fails, the end result is as if each message was processed exactly one time. This is achieved through atomic commits between the streaming engine (like Flink) and the sink (like Kafka) using two-phase commits.

I use a stack like Kafka -> Apache Flink -> Druid/ClickHouse. Flink performs the real-time aggregations, and Druid provides sub-second SQL querying capabilities for dashboards to show live metrics to users.

Stateless: Each event is processed independently (e.g., converting temperature from F to C). Stateful: The engine remembers previous events (e.g., calculating a running total or detecting if a user logged in from two different countries in 5 minutes).

Backpressure occurs when the consumer cannot keep up with the producer. I handle it by: 1) Scaling out consumers, 2) Using a buffer/queue (Kafka) to absorb spikes, or 3) Letting the streaming engine (like Flink) signal the producer to slow down.

A stream-table join enriches a live stream of events (e.g., transactions) with metadata from a reference table (e.g., user profiles). In Kafka Streams, this is done by joining a KStream with a KTable. The 'stream' provides the continuous activity, while the 'table' provides the latest state/context.

Event sourcing involves storing every change to the application state as a sequence of events in an append-only log (like Kafka). Instead of just storing the current balance in a DB, you store every 'Deposit' and 'Withdrawal' event. This provides a perfect audit trail and allows you to 'replay' events to reconstruct the state at any point in time.

CQRS (Command Query Responsibility Segregation) separates the models for writing data (Commands) from the models for reading data (Queries). In Data Engineering, this often means using a relational DB for transactions and a NoSQL/Search index (like Elasticsearch) for fast analytical reads, synchronized via a streaming pipeline.

I use 'In-line' validation. As events pass through the stream, a microservice or Flink job checks them against a schema. Invalid events are routed to a 'Dead Letter Topic' for manual inspection, while valid ones proceed to the sink. I also monitor 'Lag'—if lag increases, it often indicates a data format issue causing processing retries.

A typical architecture consists of: 1) Ingestion (Kafka/Kinesis), 2) Processing (Spark Streaming/Flink), 3) Storage (Delta Lake/Iceberg for history, Redis/Cassandra for hot reads), and 4) Visualization (Real-time dashboards).

Scenario30

I would use a Kappa Architecture: 1) Kafka as the entry point to buffer events. 2) Spark Streaming or Flink for windowed aggregations. 3) Parquet files on S3/ADLS for the long-term storage (Data Lake). 4) Snowflake or BigQuery as the warehouse for BI tools. I'd ensure the pipeline is idempotent and uses auto-scaling executors to handle traffic spikes.

I follow a phased approach: 1) Strategy (Assessment of tools). 2) Pilot (Moving a non-critical pipeline). 3) Data Migration (Using AWS Snowball or Azure Data Box for initial bulk). 4) Cutover (Setting up CDC to keep cloud in sync with on-prem until final switch). 5) Optimization (Refactoring jobs to use cloud-native tools like Glue/Lambda).

Capture user clicks in Kafka. Use Flink to calculate 'trending' products in 5-minute windows. Store these results in Redis for sub-millisecond lookups. The frontend app queries Redis to show 'People also bought' instantly. Historical data goes to S3 to retrain the ML model periodically.

1. Check the logs to identify the root cause (Network, Schema, or Logic). 2. If it's a data issue, quarantine the bad records. 3. Fix the code/config. 4. Leverage the pipeline's idempotency to re-run the job for the failed period. 5. Perform data reconciliation to ensure no duplicates were created.

1. Gather requirements. 2. Identify source systems. 3. Design a Star Schema with Conformed Dimensions. 4. Choose a cloud warehouse (Snowflake/Redshift). 5. Build ETL/ELT using dbt/Airflow. 6. Implement SCD Type 2 for history. 7. Set up monitoring and DQ checks.

Check Spark UI for 'Data Skew.' If one partition is huge, I'd apply 'Salting.' I'd check for 'OOM' (Out of Memory) errors and tune executor memory. I'd also ensure I'm using Broadcast joins for small tables and that the file format is columnar (Parquet).

At the ingestion layer, I'd use Kafka's idempotent producer. At the transformation layer, I'd use `ROW_NUMBER() OVER(PARTITION BY id ORDER BY timestamp DESC)` to select only the latest record. For the final load, I'd use a 'Merge' or 'Upsert' statement based on a unique business key.

I'd use a Medallion approach: 1) Bronze (Raw S3). 2) Silver (Cleaned Parquet). 3) Gold (Business aggregates). I'd use Delta Lake to provide ACID transactions and a Data Catalog (Glue) so users can query the files using SQL (Athena/Presto).

Use Debezium (running on Kafka Connect) to read MySQL Binlogs. Stream the changes as JSON/Avro into Kafka. Use a consumer (like Snowflake Kafka Connector or a Spark job) to 'Merge' those changes into the target warehouse table using the Primary Key.

I'd use an Avro/Protobuf schema registry. When a source field is added, the registry ensures backward compatibility. In the ETL, I use 'Schema Drift' handling (like in Spark or Glue) which can dynamically include new columns in the target table without manual code changes.

Stream transactions into Kafka. Use Flink to run 'Pattern Matching' (e.g., 3 transactions in 3 different cities within 1 hour). If a match is found, publish an event to an 'Alerts' topic that triggers a Lambda to block the card and notify the user.

I'd set up S3 Lifecycle policies. Data older than 90 days moves to 'IA' (Infrequent Access). Data older than 1 year moves to 'Glacier' (Archival). I'd also build a 'Purge' script that deletes PII data for users who have requested account deletion to comply with GDPR.

Use AWS S3 Cross-Region Replication for the data files. For databases, use 'Global Datastore' (DynamoDB) or 'Cross-Region Read Replicas' (RDS). I'd use Route 53 to failover the ingestion API to the secondary region if the primary goes down.

I'd implement a 'Circuit Breaker' pattern. If the error rate in a batch exceeds 5%, the job fails and alerts the team. I'd use a 'Staging-to-Production' check where the data is only moved to the final table if it passes all Great Expectations assertions.

I'd use a Lakehouse architecture (Delta Lake). Streaming data is appended to the Delta table in real-time. Batch jobs run periodically to perform heavy 'Gold' layer aggregations on the same Delta table. This unified storage layer handles the concurrency automatically.

I'd use 'Infrastructure as Code' (Terraform) to ensure I can spin up the entire stack in a new region. I'd perform regular 'Game Day' exercises to test the RTO (Recovery Time Objective) and RPO (Recovery Point Objective) by simulating a region failure.

Implement 'Column-Level Encryption' for fields like Email and SSN. Use a 'Hashing' or 'Tokenization' strategy for joins so engineers can analyze behavior without seeing actual PII. Access to the decryption keys would be strictly controlled via IAM and audited.

1. Use Spot instances for Spark batch jobs. 2. Auto-scale clusters to zero when idle. 3. Use 'S3 Intelligent-Tiering.' 4. Replace expensive always-on servers with serverless components (Lambda/Glue) where possible. 5. Monitor and kill 'Zombie' instances/volumes.

Sensors send data to AWS IoT Core. It's routed to Kinesis Data Streams. A Flink job performs 'Edge Case' detection (e.g., overheating). Raw data is saved to S3 (Data Lake) using Kinesis Firehose in Parquet format for long-term predictive maintenance analysis.

I'd use Delta Lake's 'Time Travel' feature, which keeps a history of file versions. This allows me to query `VERSION AS OF 10` or `TIMESTAMP AS OF '2025-01-01'`. For code, I use Git; for data schemas, I use a schema registry.

Assign users to 'Control' or 'Treatment' groups at the ingestion layer and store this in a 'User_Segments' table. In the warehouse, join user activities with the segment table. Use a dbt model to calculate the lift in conversion rate and statistical significance.

Convert all incoming data to UTC at the ingestion point. Store everything in UTC in the warehouse. Add a 'Local_Timezone' column to the 'Stores' or 'Users' dimension so BI analysts can convert to local time during reporting if needed.

I'd use a 'Push-Gateway' to send metrics from Spark/Airflow to Prometheus. I'd build Grafana dashboards to track: 1. Job success/fail rates. 2. Data volume trends. 3. Processing latency. 4. Resource usage. Alerts are sent to Slack via Alertmanager.

I'd use a 'Watermark' table in the DB that stores the `max(updated_at)` for each source. Every time the job runs, it reads the watermark, fetches only records `> watermark`, and updates the table after a successful load. For Data Lakes, I'd use Delta Lake's `MERGE` feature.

If a fact arrives and the dimension key is missing (e.g., sale for a product not yet in the system), I'd route it to a 'Holding' table. Once the dimension is updated, a 'Late-Arriving Handler' job re-processes these facts into the main fact table.

Integrate data from CRM (Salesforce), Website (Analytics), and Product (DB). Use a 'Master Data Management' (MDM) approach to link these records using a common Email or Phone ID. Create a unified 'Gold' table that aggregates all interactions into one view.

Capture web events via a tracking API. Send to Kafka. Use Spark Streaming to sessionize the data (grouping clicks by user and time). Store the sessions in Snowflake for path analysis (e.g., 'What steps lead to a purchase?').

Implement 'Checkpointing' in streaming and 'Retries with Backoff' in batch. Use 'Alerting' to notify humans only if the automated retries fail. Ensure the pipeline is 'Restartable' from the last successful offset or date without data corruption.

Keep data localized (e.g., EU data stays in EU region) to comply with sovereignty laws. Only move 'Anonymized' or 'Aggregated' data to a central Global region for corporate reporting. Use 'Data Residency' tags to audit the location of every dataset.

I'd use dbt's built-in lineage docs or an automated tool like OpenLineage/DataHub. These tools 'parse' the SQL and Spark code to automatically build a graph showing how data moves from Table A to Table B, making impact analysis easy.

Behavioral25

Focus on: 1. The Scale (TB/PB). 2. The Complexity (Real-time + Batch). 3. The Challenges (Data Quality, Shuffling). 4. The Business Impact (e.g., 'Reduced report latency by 70%').

I stay calm, prioritize 'Mitigation' (getting the system back up) over 'Investigation.' Once stable, I perform a 'Post-Mortem' (RCA) to identify why it happened and what automated tests can prevent it from recurring.

I once noticed a Spark job taking 4 hours due to a join. I realized the dimension table was small and converted it to a 'Broadcast Join.' This reduced the runtime to 15 minutes and saved $500/month in compute costs.

I follow tech blogs (Netflix, Uber, Airbnb), listen to podcasts like 'Data Engineering Podcast,' and contribute to open-source projects or build side-projects with new tools like Polars or Dagster.

I bring them together, show them the data trade-offs (e.g., 'Real-time costs 10x more than Hourly'), and help them agree on a 'Minimum Viable Product' (MVP) that meets the core business need.

I'm used to 2-week sprints, daily standups, and planning. I break large data tasks (like 'Build Warehouse') into small, deliverable stories (like 'Ingest Store Data,' 'Build Product Dimension').

I use the 'Impact vs. Effort' matrix. I prioritize 'Quick Wins' (High impact, low effort) and 'Strategic Projects.' I also maintain a clear 'Backlog' and communicate timelines transparently with stakeholders.

I once accidentally dropped a production table during a manual fix. I owned up immediately, restored it from a snapshot, and then automated that fix so no one would ever have to do it manually again.

I use 'Self-Documenting' code (dbt YAMLs, clear variable names), maintain a README in Git for architecture, and keep a 'Data Dictionary' in the company wiki for the end-users.

I do pair programming, detailed code reviews (focusing on 'Why' not just 'What'), and I encourage them to take ownership of a small end-to-end pipeline to build their confidence.

I check for: 1. Logic correctness. 2. Performance (e.g., no loops in Spark). 3. Test coverage. 4. Readability and documentation. I provide constructive feedback and always highlight what was done well.

I track it in the backlog. Every sprint, I try to dedicate 10-20% of the time to 'Refactoring' or 'Cleanup' to ensure the codebase remains maintainable and doesn't slow us down in the long run.

I once disagreed with using a Monolith. I presented a PoC of a Microservice to show how much faster we could deploy. We compromised by keeping the core but moving the new features to separate services.

By implementing: 1. Automated testing (CI). 2. Idempotency. 3. Monitoring/Alerting. 4. SLA tracking. 5. Regular data quality checks. Reliability is a combination of good code and good operations.

1. Unit Tests (functions). 2. Integration Tests (connections). 3. Data Quality Tests (assertions). 4. E2E Tests (full flow with sample data). I strive for 'Test-Driven Data Engineering.'

I ensure my pipelines have clear 'Runbooks' so whoever is on-call knows how to fix common issues. I prioritize fixing the 'Root Cause' so I'm not paged for the same thing twice.

I build. If I want to learn Flink, I'll set up a local cluster and try to build a real-time word count or fraud detector. Hands-on experience is the only way to understand the 'gotchas.'

I use analogies. I explain a 'Data Warehouse' like a library where books are organized, and a 'Data Lake' like a massive storage room where we keep everything until we need to read it.

I realized we had duplicate customer IDs. I implemented a 'Deduplication' step in the Silver layer and added a 'Unique Constraint' test in dbt. This increased our marketing accuracy by 15%.

I communicate early, manage expectations, and focus on the 'Critical Path.' I'd rather deliver a perfect 'Small' pipeline than a broken 'Big' one.

I work closely with Data Scientists to understand their feature needs and with Backend Engineers to ensure the data we receive is clean and has the right schema.

I follow the '80/20' rule for initial delivery (speed), but I never compromise on 'Idempotency' or 'Testing' (quality) because fixing data in production takes 10x longer than doing it right first.

1. Understand (The 'What'). 2. Break down (Small steps). 3. Research (Existing solutions). 4. Implement (PoC). 5. Refine (Production ready). I always aim for the simplest solution that works.

I ask questions until they're not ambiguous. I build a 'Mockup' or a small sample dataset and show it to the stakeholder to see if it's what they had in mind before building the whole thing.

I love building systems that handle data at scale. There's a unique satisfaction in seeing a complex pipeline move millions of records accurately and knowing that I'm providing the foundation for all company decisions.

Related question banks2