Scenario-Based Decision Guide

Quote

“Plan to throw one away; you will, anyhow.”

Fred Brooks, The Mythical Man-Month (1975)

Scenario 1: Daily Batch Pipeline — Ingest, Transform, Serve

Business Need

“We receive market data daily from an API. We need to clean it, calculate analytics, and serve it to a BI dashboard.”

This is the most common data engineering pattern. A source produces data on a schedule, you need to land it, clean it, enrich it, and make it available for consumption. The key constraint is that data arrives in batches (daily, hourly, or on a schedule), not as a continuous stream.

ComponentTechnologyWhy
IngestionPython + requests library → GCS landing zoneSimple, testable, handles auth and retries natively
Storage (processing)SQL Server (bronze/silver/gold schemas)ACID transactions, stored procedures, sub-second lookups
Storage (analytics)BigQuery (export from gold)BI tools connect natively, handles ad-hoc analyst queries
TransformationSQL (dbt or stored procedures)Declarative, testable, version-controlled
OrchestrationAirflow (self-hosted) or Cloud Scheduler + Cloud RunDAG-based dependency management with retries
ServingBigQuery → Looker/Tableau or SQL Server → Blazor/GrafanaDepends on consumer: analysts get BigQuery, apps get SQL Server
InfrastructureTerraformReproducible, auditable, peer-reviewed infra changes
MonitoringGCP Cloud Monitoring + custom freshness metricsFree tier covers most needs; freshness alerts catch silent failures

Architecture — Daily Batch Pipeline

graph LR
    API[Market Data API] --> CR[Cloud Run Job<br/>Python + requests]
    CR --> GCS[GCS Landing Zone<br/>Raw JSON/CSV]
    GCS --> Bronze[SQL Server<br/>bronze schema]
    Bronze --> Silver[SQL Server<br/>silver schema]
    Silver --> Gold[SQL Server<br/>gold schema]
    Gold --> BQ[BigQuery<br/>Analytics Export]
    BQ --> Dashboard[BI Dashboard<br/>Looker / Tableau]

    AF[Airflow DAG] -.->|schedules & monitors| CR
    AF -.->|triggers transforms| Bronze
    AF -.->|triggers transforms| Silver
    AF -.->|triggers export| Gold

    Mon[Cloud Monitoring] -.->|alerts on failure| AF

Key Decisions Explained

Why SQL Server over BigQuery for transforms?

SQL Server provides ACID transactions, stored procedures, and sub-second point lookups. If your transforms need to update individual rows, enforce referential integrity, or run complex procedural logic, SQL Server is the right choice. BigQuery is optimized for analytical scans, not transactional writes. See medallion-architecture for the bronze/silver/gold pattern in SQL Server.

Why BigQuery for serving to BI tools?

BI tools like Looker and Tableau have native BigQuery connectors with query pushdown. Analysts can also write ad-hoc SQL directly. BigQuery handles concurrent analytical queries without affecting your transactional SQL Server workload. See querying-and-cost-optimization for cost controls.

Why Cloud Run over a VM-based cron script?

Cloud Run scales to zero when not running — no idle compute cost. Each job runs in a Docker container, making it reproducible and isolated. If the job takes 5 minutes daily, you pay for 5 minutes, not 24 hours of VM time. See cloud-run-jobs-vs-services for the jobs vs. services distinction.

Why Airflow over Cloud Scheduler alone?

Cloud Scheduler can trigger a single Cloud Run job, but Airflow manages multi-step DAGs with dependencies, retries, SLA monitoring, and backfills. If your pipeline has more than 2-3 steps, Airflow pays for itself in operational clarity. See airflow-core-concepts for DAG design.

Implementation Checklist

medallion-architecture | idempotent-pipeline-design | rest-api-design-and-consumption | dbt-transformation-layer | cloud-run-jobs-vs-services | airflow-core-concepts | gcs-buckets-and-lifecycle | bronze-layer-loading | silver-transforms | gold-transforms


Scenario 2: Real-Time Streaming Pipeline

Streaming Pipeline — Business Need

“We need to process events as they arrive (sub-minute latency) and make them queryable immediately.”

This scenario applies when batch processing is too slow. Examples include real-time dashboards, fraud detection, live pricing feeds, IoT sensor data, and operational alerting. The defining characteristic is that you cannot wait for a daily or hourly batch — data must be processed within seconds or minutes of arrival.

ComponentTechnologyWhy
IngestionPub/Sub (push or pull subscription)Fully managed, GCP-native, at-least-once delivery, no cluster ops
Stream ProcessingDataflow (Apache Beam)Built-in windowing, exactly-once semantics, auto-scaling
Hot StorageFirestore (real-time reads) or Bigtable (time-series at scale)Sub-10ms reads for dashboards and APIs
Cold StorageBigQuery (streaming insert or batch load)Long-term analytical queries over historical data
Real-Time ServingFirestore → Dashboard or REST APIReal-time listeners push updates to clients
MonitoringCloud Monitoring + custom latency metricsTrack end-to-end latency, backlog depth, error rates

Architecture — Real-Time Streaming Pipeline

graph LR
    P1[Event Producer A] --> PS[Pub/Sub Topic]
    P2[Event Producer B] --> PS
    P3[Event Producer C] --> PS

    PS --> DF[Dataflow Pipeline<br/>Apache Beam]

    DF --> FS[Firestore<br/>Hot Storage]
    DF --> BQ[BigQuery<br/>Cold Storage]
    DF --> DLQ[Dead Letter Queue<br/>Pub/Sub]

    FS --> RTD[Real-Time Dashboard]
    FS --> API[Serving API<br/>FastAPI + Cloud Run]
    BQ --> Analytics[Analytical Queries<br/>Ad-hoc / Scheduled]

    CM[Cloud Monitoring] -.->|backlog alerts| PS
    CM -.->|latency alerts| DF

Key Decisions Explained

Why Pub/Sub over Kafka?

Pub/Sub is fully managed — no brokers to provision, no ZooKeeper, no cluster sizing. It auto-scales to handle traffic spikes and you pay per message. Choose Kafka if you need message replay beyond 7 days, strict ordering guarantees across partitions, or you already run Kafka on-prem. See pubsub-messaging and pubsub-topics-and-subscriptions.

Why Dataflow over Cloud Run with a Pub/Sub trigger?

Cloud Run can process Pub/Sub messages, but it processes them individually — no windowing, no state, no exactly-once guarantees. Dataflow (Apache Beam) provides tumbling/sliding/session windows, watermarks for late data, and exactly-once processing. If you need to aggregate events over time windows, Dataflow is the right tool. See streaming-architecture for windowing patterns.

Why dual-write to Firestore AND BigQuery?

Different access patterns need different stores. Firestore serves sub-10ms point reads for dashboards and APIs. BigQuery handles full-table scans and aggregations for analytics. Writing to both from the same Dataflow pipeline ensures consistency without building a separate sync process.

Why a dead letter queue?

Messages that fail processing (malformed JSON, schema violations, transient errors after retries) should not block the pipeline. Route them to a dead letter topic for manual inspection and replay. This keeps the main pipeline flowing while preserving failed messages for debugging.

Cost Considerations

ComponentCost DriverOptimization
Pub/SubMessage volumeBatch messages where possible
DataflowWorker vCPU hoursRight-size workers, use autoscaling
FirestoreDocument reads/writesCache reads, batch writes
BigQueryStorage + query bytes scannedPartition by timestamp, cluster by key fields

streaming-architecture | pubsub-messaging | pubsub-topics-and-subscriptions | firestore-data-model-and-operations | real-time-nosql-pipelines | querying-and-cost-optimization | gcp-cloud-monitoring-deep-dive


Scenario 3: Data Warehouse for Analytics Team

Business Need

“The analytics team needs a warehouse where they can run ad-hoc SQL queries across all our data, build dashboards, and train ML models.”

The analytics team does not want to manage infrastructure. They want a SQL interface to all the organization’s data, organized in a way that makes sense for their use cases — dimensions they can filter by, facts they can aggregate, and documentation they can reference.

ComponentTechnologyWhy
WarehouseBigQueryServerless, GCP-native, integrated ML (BQML), no cluster management
Ingestion (batch)Cloud Run Jobs (Python)Containerized, scalable, cost-effective for scheduled loads
Ingestion (streaming)Dataflow or BigQuery streaming insertDepends on volume and latency requirements
Transformationdbt (staging → intermediate → mart layers)Version-controlled SQL, built-in testing, auto-generated docs
ModelingStar schema (Kimball methodology)Intuitive for analysts, fast aggregation queries
Data Qualitydbt tests + Dataplex quality scansCatch issues before they reach dashboards
Data CatalogDataplex + Data Catalog tagsSelf-service discovery for analysts
CI/CDGitHub Actions → dbt Cloud or dbt Core in Cloud RunAutomated testing and deployment of transform logic

Architecture — Data Warehouse for Analytics Team

graph TB
    subgraph Sources
        S1[SQL Server]
        S2[REST APIs]
        S3[GCS Files]
        S4[Pub/Sub Events]
    end

    subgraph Ingestion
        CR[Cloud Run Jobs]
        DF[Dataflow]
    end

    S1 --> CR
    S2 --> CR
    S3 --> CR
    S4 --> DF

    subgraph BigQuery
        STG[Staging Layer<br/>stg_*]
        INT[Intermediate Layer<br/>int_*]
        MART[Mart Layer<br/>mart_*]
    end

    CR --> STG
    DF --> STG
    STG -->|dbt| INT
    INT -->|dbt| MART

    subgraph Consumption
        BI[Looker / Tableau]
        NB[Jupyter Notebooks]
        ML[Vertex AI / BQML]
    end

    MART --> BI
    MART --> NB
    MART --> ML

    DQ[dbt Tests +<br/>Dataplex Scans] -.->|validates| INT
    DQ -.->|validates| MART
    CAT[Data Catalog] -.->|documents| MART

Key Decisions Explained

Why BigQuery over Snowflake?

Both are excellent cloud warehouses. BigQuery wins on GCP-native integration (IAM, Cloud Monitoring, Dataflow, Vertex AI), truly serverless operation (no warehouse sizing), and cost model (pay per query byte scanned, not per compute-second). Choose Snowflake if you are multi-cloud or need features like data sharing with external partners on other clouds. See data-warehouse-architecture.

Why star schema (Kimball) over normalized (Inmon)?

Analytics teams need fast, intuitive queries. Star schemas let analysts write SELECT dim.category, SUM(fact.revenue) FROM fact JOIN dim without understanding complex join chains. The denormalization trades storage efficiency for query simplicity. See dimensional-modeling for full DDL examples.

Why dbt over stored procedures or custom Python?

dbt provides version-controlled SQL transforms with built-in testing (unique, not_null, relationships, custom tests), automatic DAG generation from ref() calls, and documentation that stays in sync with code. It turns SQL into a software engineering practice. See dbt-transformation-layer.

Why three dbt layers (staging, intermediate, mart)?

  • Staging: 1:1 with source tables, minimal transformation (type casting, renaming)
  • Intermediate: Business logic, joins, deduplication — not exposed to analysts
  • Mart: Analytics-ready star schemas, one per business domain This separation keeps transforms modular, testable, and debuggable.

dbt Project Structure

dbt_project/
├── models/
│   ├── staging/
│   │   ├── stg_market_data.sql
│   │   ├── stg_corporate_actions.sql
│   │   └── _staging.yml          # source definitions + tests
│   ├── intermediate/
│   │   ├── int_adjusted_prices.sql
│   │   └── int_constituent_weights.sql
│   └── marts/
│       ├── mart_daily_index_values.sql
│       ├── mart_constituent_performance.sql
│       └── _marts.yml            # column docs + tests
├── tests/
│   └── assert_no_negative_weights.sql
└── dbt_project.yml

data-warehouse-architecture | dimensional-modeling | dbt-transformation-layer | querying-and-cost-optimization | data-loading-and-export | gcp-data-lineage-and-catalog | gcp-pipeline-health-and-sla


Scenario 4: Multi-Source Data Integration (ETL/ELT)

Business Need

“We have data in 5 different source systems (SQL Server, REST APIs, SFTP files, Pub/Sub events, GCS files). We need to bring it all together into a single, consistent data model.”

This is the integration challenge. Each source has different formats, schemas, delivery mechanisms, and SLAs. The goal is to land everything in a common store, reconcile identities across systems, handle schema drift, and produce a unified view.

ComponentTechnologyWhy
SQL Server IngestionPython + pyodbc → GCS → BigQueryIncremental extraction via watermark columns
API IngestionPython + requests → GCS → BigQueryPaginated extraction with retry logic
SFTP IngestionCloud Run + paramiko → GCS → BigQueryFile-based extraction, archive after processing
Event IngestionPub/Sub → Dataflow → BigQueryStreaming or micro-batch insert
File IngestionGCS event trigger → Cloud Run → BigQueryProcess on arrival via Eventarc
Landing ZoneGCS (one prefix per source)Raw files preserved for replay and audit
WarehouseBigQueryUnified query layer across all sources
TransformationdbtSource-specific staging, unified intermediate + mart layers
OrchestrationAirflowComplex DAG with per-source schedules and cross-source dependencies
Data Qualitydbt tests + Great ExpectationsSchema validation, freshness checks, cross-source reconciliation

Architecture — Multi-Source Data Integration

graph TB
    subgraph Sources
        SQL[SQL Server<br/>Incremental Extract]
        API[REST APIs<br/>Paginated Fetch]
        SFTP[SFTP Server<br/>File Drop]
        PS[Pub/Sub<br/>Event Stream]
        GCSsrc[GCS Bucket<br/>Partner Files]
    end

    subgraph Landing Zone
        GCS1[GCS: /sql-server/]
        GCS2[GCS: /api/]
        GCS3[GCS: /sftp/]
        GCS4[GCS: /events/]
        GCS5[GCS: /partner-files/]
    end

    SQL --> GCS1
    API --> GCS2
    SFTP --> GCS3
    PS --> GCS4
    GCSsrc --> GCS5

    subgraph BigQuery
        RAW[Raw Dataset<br/>1:1 with source]
        STG[Staging<br/>Cleaned + typed]
        INT[Intermediate<br/>Identity resolved]
        MART[Mart<br/>Unified model]
    end

    GCS1 --> RAW
    GCS2 --> RAW
    GCS3 --> RAW
    GCS4 --> RAW
    GCS5 --> RAW

    RAW -->|dbt| STG
    STG -->|dbt| INT
    INT -->|dbt| MART

    AF[Airflow] -.->|orchestrates all| SQL
    AF -.->|orchestrates all| API
    AF -.->|orchestrates all| SFTP

Key Decisions Explained

Why land everything in GCS first?

GCS acts as an immutable audit log. If a transform has a bug, you can replay from raw files without re-extracting from the source. This also decouples extraction from loading — if BigQuery is temporarily unavailable, files are safe in GCS. See gcs-buckets-and-lifecycle.

Why per-source prefixes in GCS?

Different sources have different schemas, formats, and arrival schedules. Organizing by source (/sql-server/YYYY-MM-DD/, /api/YYYY-MM-DD/) makes it easy to re-process a single source without touching others. It also simplifies IAM — you can grant a service account access to only its source prefix.

Why Airflow over simpler orchestration?

Multi-source integration requires complex dependency management: “load API data only after SQL Server extract completes, but SFTP can run in parallel.” Airflow’s DAG model makes these dependencies explicit and visual. It also handles per-source retry logic and SLA monitoring. See airflow-dag-patterns.

How do you handle schema drift?

  • Landing zone: Store raw files as-is (JSON, CSV, Parquet). Schema drift is the source’s problem at this layer.
  • Raw dataset: Use BigQuery’s schema auto-detection or explicit RECORD types for nested JSON.
  • Staging: dbt models cast to explicit types. If a new column appears, the staging model ignores it until you add it.
  • Contracts: Define expected schemas in dbt’s _sources.yml. Tests fail if columns are missing or types change. See context-and-metadata-architecture and serialization-formats.

Identity Resolution Pattern

When the same entity (e.g., a company) exists across multiple sources with different IDs:

-- intermediate/int_company_master.sql
-- Resolve company identity across SQL Server (company_id),
-- API (ticker), and SFTP (ISIN) into a single surrogate key
 
WITH sql_companies AS (
    SELECT company_id, company_name, ticker, isin
    FROM {{ ref('stg_sql_server_companies') }}
),
api_companies AS (
    SELECT ticker, api_company_name, sector
    FROM {{ ref('stg_api_companies') }}
),
sftp_companies AS (
    SELECT isin, sftp_company_name, country
    FROM {{ ref('stg_sftp_companies') }}
)
SELECT
    {{ dbt_utils.generate_surrogate_key(['COALESCE(s.isin, f.isin)']) }}
        AS company_sk,
    COALESCE(s.company_name, a.api_company_name, f.sftp_company_name)
        AS company_name,
    s.company_id   AS sql_server_id,
    a.ticker       AS api_ticker,
    f.isin         AS sftp_isin,
    a.sector,
    f.country
FROM sql_companies s
FULL OUTER JOIN api_companies a ON s.ticker = a.ticker
FULL OUTER JOIN sftp_companies f ON s.isin = f.isin

medallion-architecture | idempotent-pipeline-design | dbt-transformation-layer | airflow-dag-patterns | serialization-formats | context-and-metadata-architecture | data-modeling-patterns | gcs-buckets-and-lifecycle | database connections


Scenario 5: Data Platform for a Small Team (2-3 Engineers)

Business Need

“We’re a small team. We need something that works, is cheap, and doesn’t require a DevOps team to operate.”

The goal is maximum simplicity. Every tool you add is a tool you have to learn, operate, monitor, and debug. A small team cannot afford the operational burden of Airflow, Terraform, Datadog, and a dozen other tools. Pick the smallest stack that solves the problem and expand only when you hit a real limitation.

ComponentTechnologyWhy NOT the “enterprise” choice
SchedulingCloud SchedulerAirflow is overkill for <10 jobs
ComputeCloud Run JobsNo VMs to manage, scales to zero
WarehouseBigQueryServerless, no cluster sizing
Transformsdbt Core (run in Cloud Run)Keep transforms version-controlled
CI/CDGitHub ActionsFree tier covers small teams
MonitoringGCP Cloud MonitoringFree tier is sufficient
Infrastructuregcloud CLI scripts in a repoTerraform is overhead at this scale

What You Skip (and Why)

  • Airflow: Operational burden (database, webserver, scheduler, workers). Use Cloud Scheduler + Cloud Run until you have 10+ interdependent jobs.
  • Terraform: Learning curve and state management overhead. A well-organized gcloud script achieves the same result for <20 resources.
  • Datadog: Expensive per-host pricing. Cloud Monitoring’s free tier covers logs, metrics, and alerting.
  • Kubernetes: You do not need container orchestration for batch jobs. Cloud Run handles this.
  • Data catalog: At this scale, a well-organized dbt schema.yml with column descriptions is your catalog.

Architecture — Small Team Platform (2-3 Engineers)

graph LR
    CS[Cloud Scheduler<br/>Cron triggers] --> CR1[Cloud Run Job<br/>Ingest from API]
    CR1 --> GCS[GCS Bucket<br/>Raw files]
    GCS --> BQ[BigQuery<br/>Raw dataset]

    CS --> CR2[Cloud Run Job<br/>dbt run]
    CR2 --> BQT[BigQuery<br/>Transformed]
    BQT --> Looker[Looker Studio<br/>Free dashboards]

    GA[GitHub Actions] -.->|deploys| CR1
    GA -.->|deploys| CR2
    CM[Cloud Monitoring] -.->|alerts| CS

Cost Estimate (~$50-100/month)

ComponentEstimated Monthly Cost
Cloud Scheduler~$0 (3 free jobs)
Cloud Run~$5-20 (pay per invocation, scales to zero)
BigQuery~$10-40 (first 1TB query/month free, 10GB storage free)
GCS~$5-10 (Standard storage)
Cloud Monitoring~$0 (free tier: 150MB logs, basic metrics)
GitHub Actions~$0 (2,000 free minutes/month)
Total~$20-70/month

Key Decisions Explained

When do you graduate from this stack?

Upgrade when you experience one of these pain points:

  • >10 scheduled jobs with dependencies → Add Airflow
  • >3 engineers needing infra changes → Add Terraform
  • Need for APM traces or custom dashboards → Add Datadog or Grafana
  • Multiple environments (dev/staging/prod) → Add Terraform + proper CI/CD
  • Data quality issues slipping through → Add Dataplex or Great Expectations

How do you handle dependencies without Airflow?

Chain Cloud Scheduler triggers sequentially with offset timing (Job A at 06:00, Job B at 06:30). For hard dependencies, have Job A call Job B’s Cloud Run endpoint on success. This is fragile at scale but workable for 3-5 jobs.

cloud-run-jobs-vs-services | gcp-scheduling | querying-and-cost-optimization | dbt-transformation-layer | github-actions-ci-cd | gcp-cloud-monitoring-deep-dive | gcs-buckets-and-lifecycle


Scenario 6: Data Platform for a Large Team (10+ Engineers)

Business Need

“We have multiple teams producing and consuming data. We need governance, lineage, quality, and clear ownership.”

At this scale, the problem is not technology — it is coordination. Multiple teams writing pipelines that depend on each other’s output. Schema changes that break downstream consumers. No one knows who owns which table. Data quality issues that get discovered by the CEO looking at a dashboard. The architecture must enforce boundaries, contracts, and visibility.

ComponentTechnologyWhy
OrchestrationCloud Composer (managed Airflow)Managed service eliminates Airflow ops burden
WarehouseBigQuery (primary) + SQL Server (transactional)BigQuery for analytics, SQL Server for OLTP workloads
Transformsdbt (per-domain projects)Each team owns their dbt project and models
InfrastructureTerraform (modules per team)Reproducible, auditable, peer-reviewed via PRs
GovernanceDataplex (zones, data quality, lineage)Centralized governance with federated ownership
CatalogData Catalog + Dataplex discoverySelf-service data discovery for all teams
Inter-servicegRPC for service-to-service, REST for externalHigh-performance internal communication
CI/CDGitHub Actions (per-repo)Automated testing, linting, deployment per team
MonitoringCloud Monitoring + Datadog (APM)Cloud Monitoring for infra, Datadog for application traces
Data ContractsProtobuf schemas + dbt contractsSchema enforcement at API and warehouse boundaries

Architecture — Large Team Platform (10+ Engineers)

graph TB
    subgraph Team A: Ingestion
        A1[Cloud Run Jobs<br/>Source Extractors]
        A2[Dataflow<br/>Stream Processing]
        A3[GCS Landing Zone]
    end

    subgraph Team B: Core Transforms
        B1[Cloud Composer<br/>Airflow DAGs]
        B2[dbt Project<br/>Core Models]
        B3[BigQuery<br/>Core Dataset]
    end

    subgraph Team C: Domain Analytics
        C1[dbt Project<br/>Domain Models]
        C2[BigQuery<br/>Domain Mart]
        C3[Looker<br/>Dashboards]
    end

    subgraph Platform Team
        P1[Terraform Modules]
        P2[Dataplex<br/>Governance]
        P3[Data Catalog]
        P4[Monitoring Stack]
    end

    A1 --> A3
    A2 --> A3
    A3 --> B3
    B1 -->|orchestrates| B2
    B2 -->|transforms| B3
    B3 -->|published contract| C1
    C1 -->|transforms| C2
    C2 --> C3

    P1 -.->|provisions| A1
    P1 -.->|provisions| B1
    P2 -.->|governs| B3
    P2 -.->|governs| C2
    P3 -.->|catalogs| B3
    P3 -.->|catalogs| C2
    P4 -.->|monitors all| A1
    P4 -.->|monitors all| B1

Key Decisions Explained

Why Cloud Composer over self-hosted Airflow?

At 10+ engineers, Airflow becomes critical infrastructure. Cloud Composer handles upgrades, scaling, and high availability. The cost premium (~$300-500/month for a small environment) is cheaper than an engineer spending time on Airflow ops. See airflow-deployment.

Why data mesh principles?

Data mesh assigns ownership: Team A owns ingestion, Team B owns core models, Team C owns domain analytics. Each team publishes “data products” with defined contracts (schema, SLA, freshness). Consumers depend on contracts, not implementation details. See data-mesh-architecture.

Why Terraform modules per team?

Each team gets a Terraform module that provisions their resources (BigQuery datasets, Cloud Run services, IAM bindings). The platform team maintains shared modules (networking, monitoring). Changes are peer-reviewed via pull requests. See module-composition.

Why gRPC for inter-service communication?

gRPC provides strongly-typed contracts (Protobuf), bi-directional streaming, and 2-10x better performance than REST for internal service-to-service calls. Use REST only for external-facing APIs where browser compatibility matters. See grpc-for-data-pipelines and api-protocols-comparison.

Data Contract Example

# contracts/core_daily_prices.yml
# Published by Team B, consumed by Team C
contract:
  name: core_daily_prices
  owner: team-b-core
  description: Daily adjusted close prices for all tracked securities
  sla:
    freshness: 2 hours after market close
    availability: 99.9%
  schema:
    - name: trade_date
      type: DATE
      not_null: true
    - name: ticker
      type: STRING
      not_null: true
    - name: adjusted_close
      type: FLOAT64
      not_null: true
    - name: volume
      type: INT64
  breaking_changes:
    notification: 2 weeks advance
    channel: "#data-contracts"

data-mesh-architecture | airflow-deployment | module-composition | grpc-for-data-pipelines | api-protocols-comparison | gcp-data-lineage-and-catalog | context-and-metadata-architecture | gcp-pipeline-health-and-sla | service-accounts-and-iam


Scenario 7: Financial Index Calculation Platform

Business Need

“We calculate and publish stock market indices. We need to ingest market data, run calculations (weighting, capping, corporate actions), and publish results with SLA guarantees.”

This is the domain-specific reference architecture that brings together financial domain knowledge with data engineering patterns. Index calculation requires deterministic, auditable computation — the same inputs must always produce the same outputs. Every calculation must be traceable for regulatory compliance.

ComponentTechnologyWhy
Market Data IngestionPython + vendor APIs → GCS → SQL ServerMultiple vendors (Bloomberg, Refinitiv, exchange feeds)
Corporate ActionsSQL Server (bronze/silver/gold)Complex event processing: splits, dividends, mergers
Calculation EnginePython (NumPy/Pandas) or SQL stored proceduresDeterministic math: weighting, capping, divisor adjustments
PublicationBigQuery (analytics) + REST API (real-time)Clients consume via API or data feeds
Audit TrailSQL Server temporal tables + GCS raw archiveFull provenance: which data, which version of logic, which result
OrchestrationAirflow with strict SLA monitoringMarket-driven deadlines (publish by 18:00 UTC)
MonitoringCloud Monitoring + custom SLA dashboardsSLA breaches trigger PagerDuty escalation

Architecture — Financial Index Calculation Platform

graph TB
    subgraph Data Ingestion
        V1[Vendor A API] --> CR1[Cloud Run<br/>Ingestion Job]
        V2[Vendor B API] --> CR2[Cloud Run<br/>Ingestion Job]
        V3[Exchange Feed] --> PS[Pub/Sub]
    end

    subgraph Landing & Storage
        CR1 --> GCS[GCS Archive<br/>Immutable Raw]
        CR2 --> GCS
        PS --> GCS
        GCS --> Bronze[SQL Server<br/>bronze schema]
    end

    subgraph Processing
        Bronze --> Silver[SQL Server<br/>silver schema<br/>Cleaned + CA Applied]
        Silver --> Calc[Calculation Engine<br/>Python / SP]
        Calc --> Gold[SQL Server<br/>gold schema<br/>Index Values]
    end

    subgraph Publication
        Gold --> BQ[BigQuery<br/>Client Analytics]
        Gold --> API[REST API<br/>FastAPI + Cloud Run]
        Gold --> Feed[Data Feed<br/>SFTP / GCS]
    end

    subgraph Governance
        Audit[Audit Trail<br/>Temporal Tables]
        SLA[SLA Monitor<br/>18:00 UTC Deadline]
    end

    AF[Airflow DAG] -.->|orchestrates| CR1
    AF -.->|orchestrates| CR2
    AF -.->|orchestrates| Bronze
    AF -.->|orchestrates| Calc
    AF -.->|orchestrates| Gold

    Silver -.->|writes to| Audit
    Calc -.->|writes to| Audit
    SLA -.->|monitors| Gold

Key Decisions Explained

Why SQL Server for the calculation pipeline (not BigQuery)?

Index calculation requires ACID transactions — if a corporate action adjustment fails midway, you need to roll back the entire calculation, not end up with partially adjusted data. SQL Server’s transaction model, stored procedures, and temporal tables make this safe. BigQuery is append-optimized and lacks row-level transactions.

Why immutable GCS archive?

Regulatory requirement: you must be able to reproduce any historical index value. This requires proving which market data was used (GCS raw files), which version of the calculation logic ran (Git commit hash in audit table), and what the output was (temporal table history). See gcs-buckets-and-lifecycle.

Why temporal tables for audit?

SQL Server temporal tables automatically maintain a history of every row change with system-time versioning. You can query “what was the index value at any point in time” and “what data was used to calculate it.” This satisfies regulatory audit requirements without custom audit trigger code.

Why strict SLA monitoring?

Index values drive trading decisions. A late publication can cause trading halts or client penalties. The Airflow DAG includes SLA callbacks that trigger PagerDuty if the gold layer is not populated by the deadline. See gcp-pipeline-health-and-sla and airflow-core-concepts.

Corporate Actions Processing Pattern

-- silver schema: apply corporate actions to raw price data
-- This is the most complex transform in index calculation
 
-- Step 1: Identify pending corporate actions
SELECT ca.action_type, ca.effective_date, ca.adjustment_factor,
       ca.ticker, ca.ex_date
FROM bronze.corporate_actions ca
WHERE ca.effective_date <= @calc_date
  AND ca.processed_flag = 0;
 
-- Step 2: Apply adjustment factors to historical prices
-- (split: divide price by factor, dividend: subtract amount)
UPDATE silver.adjusted_prices
SET adjusted_close = CASE
        WHEN ca.action_type = 'SPLIT'
        THEN raw_close / ca.adjustment_factor
        WHEN ca.action_type = 'DIVIDEND'
        THEN raw_close - ca.dividend_amount
    END,
    adjustment_reason = ca.action_type,
    adjusted_date = GETUTCDATE()
FROM silver.adjusted_prices ap
JOIN bronze.corporate_actions ca
    ON ap.ticker = ca.ticker
   AND ap.trade_date < ca.ex_date;
 
-- Step 3: Recalculate index divisor to maintain continuity
-- (the index value should not jump due to a corporate action)

medallion-architecture | bronze-layer-loading | silver-transforms | gold-transforms | idempotent-pipeline-design | dimensional-modeling | airflow-core-concepts | airflow-dag-patterns | rest-api-design-and-consumption | gcp-pipeline-health-and-sla | fastapi and polars


Scenario 8: Machine Learning Feature Pipeline

Business Need

“The ML team needs features computed daily and served at low latency for real-time predictions.”

ML models need features — derived values computed from raw data. The challenge is that features must be computed consistently for both training (batch, historical) and serving (real-time, single-record). This is the “training-serving skew” problem. The solution is a feature store that decouples feature computation from feature consumption.

ComponentTechnologyWhy
Batch Feature Computedbt (BigQuery) or Python (Dataflow)Scheduled daily, outputs feature tables
Streaming Feature ComputeDataflow (Apache Beam)Real-time features from event streams
Feature StoreVertex AI Feature Store or FirestoreLow-latency serving with point lookups
Offline StoreBigQueryHistorical features for training datasets
Model TrainingVertex AI or custom PythonReads from offline store
Model ServingVertex AI Endpoints or Cloud RunReads features from online store at prediction time
OrchestrationAirflowCoordinates feature computation with model retraining

Architecture — ML Feature Pipeline

graph TB
    subgraph Feature Computation
        BQ[BigQuery<br/>Raw Data] --> DBT[dbt Models<br/>Batch Features]
        PS[Pub/Sub<br/>Event Stream] --> DF[Dataflow<br/>Streaming Features]
    end

    subgraph Feature Store
        DBT --> OFF[Offline Store<br/>BigQuery]
        DBT --> ON[Online Store<br/>Vertex AI FS / Firestore]
        DF --> ON
    end

    subgraph Training
        OFF --> TRAIN[Vertex AI Training<br/>or Custom Python]
        TRAIN --> MODEL[Trained Model<br/>Model Registry]
    end

    subgraph Serving
        REQ[Prediction Request] --> SRV[Model Endpoint<br/>Cloud Run / Vertex]
        ON -->|feature lookup| SRV
        SRV --> RESP[Prediction Response]
    end

    AF[Airflow] -.->|schedules| DBT
    AF -.->|triggers| TRAIN

Key Decisions Explained

Why a feature store instead of just querying BigQuery?

BigQuery has ~1-2 second query latency. Real-time predictions need <50ms feature lookups. The feature store (Firestore or Vertex AI Feature Store) pre-materializes features for point lookups. It also ensures training and serving use identical feature definitions, preventing training-serving skew.

Why both batch and streaming feature computation?

Some features are naturally batch (e.g., “average revenue over last 90 days”) while others must be real-time (e.g., “number of events in the last 5 minutes”). Batch features are cheaper to compute and cover most use cases. Add streaming features only when freshness matters.

Why Firestore over Redis for the online store?

Firestore is fully managed, scales automatically, and has a generous free tier. Redis (Memorystore) requires capacity planning and cluster management. Choose Redis only if you need sub-millisecond latency (Firestore is ~10-50ms). See firestore-data-model-and-operations.

Feature Definition Pattern

# features/daily_trading_features.py
# Computed in dbt or Python, materialized to both offline and online stores
 
FEATURE_DEFINITIONS = {
    "avg_volume_30d": {
        "description": "30-day rolling average trading volume",
        "sql": "AVG(volume) OVER (PARTITION BY ticker ORDER BY trade_date ROWS 29 PRECEDING)",
        "entity": "ticker",
        "freshness": "daily",
        "type": "FLOAT64",
    },
    "price_momentum_5d": {
        "description": "5-day price return (close-to-close)",
        "sql": "(close - LAG(close, 5) OVER (PARTITION BY ticker ORDER BY trade_date)) / LAG(close, 5) OVER (PARTITION BY ticker ORDER BY trade_date)",
        "entity": "ticker",
        "freshness": "daily",
        "type": "FLOAT64",
    },
    "volatility_20d": {
        "description": "20-day realized volatility (annualized)",
        "sql": "STDDEV(LN(close / LAG(close, 1) OVER (PARTITION BY ticker ORDER BY trade_date))) OVER (PARTITION BY ticker ORDER BY trade_date ROWS 19 PRECEDING) * SQRT(252)",
        "entity": "ticker",
        "freshness": "daily",
        "type": "FLOAT64",
    },
}

firestore-data-model-and-operations | real-time-nosql-pipelines | dbt-transformation-layer | streaming-architecture | querying-and-cost-optimization | ai-augmented-data-engineering


Scenario 9: Data Migration — On-Prem to Cloud

Business Need

“We’re migrating from on-prem SQL Server to GCP BigQuery. We need zero data loss, minimal downtime, and a way to validate that everything transferred correctly.”

Migration is not a one-time event — it is a multi-phase process that can take weeks or months. The key insight is that you should not do a “big bang” cutover. Instead, use the strangler fig pattern: run both systems in parallel, gradually shift workloads, validate continuously, and cut over only when you have confidence.

ComponentTechnologyWhy
Initial Bulk Loadbcp export → GCS → bq loadFastest for large tables (millions of rows)
Incremental SyncPython + pyodbc (watermark-based) → GCS → BigQueryDaily delta extraction during dual-write period
Change Data CaptureSQL Server CDC or DebeziumReal-time sync for tables that need it
ValidationCustom Python (row counts, checksums, sample comparison)Automated comparison between source and target
OrchestrationAirflowManages the multi-step migration DAG
InfrastructureTerraformProvision BigQuery datasets, IAM, networking

Architecture — On-Prem to Cloud Migration

graph TB
    subgraph Phase 1: Dual Write
        SQL[On-Prem SQL Server] -->|bulk export| GCS1[GCS: Initial Load]
        SQL -->|daily delta| GCS2[GCS: Incremental]
        GCS1 --> BQ[BigQuery<br/>Target]
        GCS2 --> BQ
        SQL -->|still serving| APP1[Application<br/>Reads from SQL]
    end

    subgraph Phase 2: Validation
        VAL[Validation Job] -->|row counts| SQL
        VAL -->|row counts| BQ
        VAL -->|checksums| SQL
        VAL -->|checksums| BQ
        VAL --> RPT[Validation Report<br/>Discrepancy Log]
    end

    subgraph Phase 3: Cutover
        BQ -->|now serving| APP2[Application<br/>Reads from BQ]
        SQL -.->|decommission| ARCHIVE[Archive]
    end

    AF[Airflow] -.->|orchestrates all phases| SQL
    AF -.->|orchestrates all phases| VAL

Migration Phases

Phase 1: Assessment (1-2 weeks)

  • Inventory all tables, views, stored procedures, and jobs
  • Classify tables: which migrate to BigQuery, which stay in SQL Server
  • Identify SQL Server features without BigQuery equivalents (temporal tables, triggers, CLR functions)
  • Estimate BigQuery storage and query costs

Phase 2: Schema Translation (1-2 weeks)

  • Convert SQL Server DDL to BigQuery DDL (data type mapping)
  • Design partitioning and clustering strategy for BigQuery
  • Rewrite stored procedures as dbt models or scheduled queries
  • Handle SQL Server-specific syntax: TOP, NOLOCK, CROSS APPLY, window function differences

Phase 3: Bulk Load + Dual Write (2-4 weeks)

  • Export large tables with bcp to CSV/Parquet → GCS → BigQuery
  • Set up daily incremental sync for active tables
  • Run both systems in parallel — SQL Server for production, BigQuery for validation

Phase 4: Validation (1-2 weeks)

  • Automated row count comparison (per table, per partition)
  • Checksum comparison (hash of key columns)
  • Sample query comparison (run the same analytical query on both, compare results)
  • Freshness validation (BigQuery is within SLA of SQL Server)

Phase 5: Cutover (1 day)

  • Final incremental sync
  • Switch application connection strings
  • Monitor for 24 hours
  • Decommission SQL Server (or archive)

Key Decisions Explained

Why strangler fig over big-bang migration?

Big-bang migrations have a single point of failure: if anything goes wrong during cutover, you roll back entirely and lose weeks of work. Strangler fig lets you migrate table by table, validate incrementally, and roll back individual tables without affecting the rest. See migration-idempotency-backfills.

Why watermark-based incremental extraction?

Each table has a column that monotonically increases (e.g., modified_date, row_version). The extraction job records the last watermark value and only extracts rows modified since then. This is idempotent — re-running with the same watermark extracts the same rows. See idempotent-pipeline-design.

What about stored procedures?

BigQuery does not support traditional stored procedures in the same way. Options:

  1. Rewrite as dbt models (preferred — version-controlled, testable)
  2. Rewrite as BigQuery scripting (procedural SQL, less testable)
  3. Keep complex logic in Python Cloud Run jobs See dbt-transformation-layer for the dbt approach.

Validation Query Example

# validation/compare_tables.py
# Run against both SQL Server and BigQuery, compare results
 
VALIDATION_QUERIES = {
    "row_count": {
        "sql_server": "SELECT COUNT(*) FROM {schema}.{table}",
        "bigquery": "SELECT COUNT(*) FROM `{project}.{dataset}.{table}`",
    },
    "checksum": {
        "sql_server": """
            SELECT CHECKSUM_AGG(CHECKSUM(
                ticker, trade_date, CAST(close_price AS VARCHAR(50))
            ))
            FROM {schema}.{table}
            WHERE trade_date >= '{start_date}'
        """,
        "bigquery": """
            SELECT FARM_FINGERPRINT(
                STRING_AGG(
                    CONCAT(ticker, CAST(trade_date AS STRING),
                           CAST(close_price AS STRING)),
                    ',' ORDER BY ticker, trade_date
                )
            )
            FROM `{project}.{dataset}.{table}`
            WHERE trade_date >= '{start_date}'
        """,
    },
    "freshness": {
        "sql_server": "SELECT MAX(modified_date) FROM {schema}.{table}",
        "bigquery": "SELECT MAX(modified_date) FROM `{project}.{dataset}.{table}`",
    },
}

migration-idempotency-backfills | idempotent-pipeline-design | data-loading-and-export | dbt-transformation-layer | database connections | querying-and-cost-optimization | plan-apply-destroy


Scenario 10: Cost-Optimized Pipeline (Maximum Savings)

Business Need

“Our cloud bill is too high. We need to cut costs without sacrificing reliability.”

Cost optimization is not about choosing the cheapest option for each component — it is about eliminating waste across the entire stack. The biggest cost savings come from three areas: compute that runs when not needed, storage that is never accessed, and queries that scan more data than necessary.

Optimization Techniques by Component

Compute (Cloud Run, VMs, Dataflow)

TechniqueSavingsEffort
Cloud Run min-instances=050-90% of idle costLow
Spot/preemptible VMs for batch jobs60-91% vs on-demandMedium
Right-size VM machine types20-50%Low
Schedule non-production VMs to stop at night60% of dev/staging costLow
Use Cloud Run Jobs instead of always-on VMs80-95% for batch workloadsMedium

Storage (GCS, BigQuery, SQL Server)

TechniqueSavingsEffort
GCS lifecycle policies (Standard → Nearline → Coldline → Archive)50-80% on old dataLow
BigQuery table expiration for temp tablesEliminates forgotten temp tablesLow
BigQuery long-term storage pricing (auto after 90 days)50% on old partitionsFree
SQL Server table compression (PAGE for cold, ROW for warm)50-80% storage reductionMedium
Delete raw GCS files after loading to warehouse100% of duplicate storageLow

Queries (BigQuery)

TechniqueSavingsEffort
Partition tables by date90%+ query cost reductionLow
Cluster tables by frequently filtered columns30-50% scan reductionLow
Use SELECT specific_columns instead of SELECT *Proportional to unused columnsLow
Materialized views for repeated queriesEliminates redundant scansMedium
BI Engine reservation for dashboards60-80% for repeated dashboard queriesMedium
Set per-user and per-project query byte limitsPrevents runaway queriesLow

Architecture: Before and After

graph LR
    subgraph Before - $2000/month
        VM1[Always-On VM<br/>$200/month]
        VM2[Dev VM<br/>Running 24/7<br/>$150/month]
        BQ1[BigQuery<br/>No partitioning<br/>Full scans<br/>$800/month]
        GCS1[GCS Standard<br/>3 years of data<br/>$300/month]
        DD[Datadog<br/>$400/month]
        AF1[Self-hosted Airflow<br/>3 VMs<br/>$150/month]
    end
graph LR
    subgraph After - $400/month
        CR[Cloud Run Jobs<br/>Scales to zero<br/>$20/month]
        VM3[Dev VM<br/>Scheduled stop<br/>$50/month]
        BQ2[BigQuery<br/>Partitioned + Clustered<br/>$150/month]
        GCS2[GCS Lifecycle<br/>Archive old data<br/>$80/month]
        CM[Cloud Monitoring<br/>Free tier<br/>$0/month]
        CS[Cloud Scheduler<br/>$0/month]
    end

Key Decisions Explained

What is the single highest-impact optimization?

BigQuery partitioning and clustering. If you have a 10TB table and every query scans all of it, you pay ~$50 per query. Partition by date and cluster by your most common filter column, and the same query might scan 10GB — $0.05. This is a 1000x cost reduction for time-range queries. See querying-and-cost-optimization.

When is Datadog worth the cost?

Datadog becomes worth it when: (1) you have 5+ services that need distributed tracing, (2) you need custom APM dashboards that Cloud Monitoring cannot provide, or (3) you need log analytics beyond simple search. For pipelines with <5 components, Cloud Monitoring’s free tier is sufficient. See gcp-cloud-monitoring-deep-dive and datadog-cost-optimization.

Should we use reserved capacity (BigQuery slots)?

Only if your monthly BigQuery on-demand spend exceeds ~$2,000/month consistently. Below that, on-demand is cheaper. BigQuery Editions flex slots let you commit for 1 hour minimum, which is useful for large batch windows. See querying-and-cost-optimization.

How do we prevent cost surprises?

  • Set BigQuery per-user byte limits (maximum_bytes_billed)
  • Set GCP budget alerts at 50%, 80%, 100% of monthly budget
  • Use Cloud Billing export to BigQuery for cost analytics
  • Review the billing dashboard weekly
  • Tag all resources with team and environment labels for cost attribution

Cost Audit Checklist

  • All BigQuery tables are partitioned (by ingestion time at minimum)
  • BigQuery tables with >1TB are clustered by common filter columns
  • No SELECT * in production queries or views
  • GCS lifecycle policies are set (Standard → Nearline at 30d → Coldline at 90d)
  • No always-on VMs that could be Cloud Run Jobs
  • Dev/staging VMs are scheduled to stop outside business hours
  • Spot/preemptible VMs are used for fault-tolerant batch jobs
  • Cloud Monitoring is used instead of Datadog where possible
  • BigQuery slot usage is reviewed monthly
  • Unused datasets and tables are identified and deleted quarterly
  • Per-user query byte limits are set in BigQuery
  • GCP budget alerts are configured

querying-and-cost-optimization | gcs-buckets-and-lifecycle | cloud-run-jobs-vs-services | vm-lifecycle | finops-cost-optimization | gcp-cloud-monitoring-deep-dive | datadog-cost-optimization | table-compression | partitioning-strategies


Quick Lookup: “I Need To… Use This”

Bookmark This Section

When you have a specific task and need to know which tool to reach for, scan this table first.

Scheduling and Orchestration

I need to…UseNotesLink
Schedule a simple daily jobCloud Scheduler + Cloud RunNo dependencies, single jobgcp-scheduling
Run a complex DAG with dependenciesAirflowMulti-step with retries and SLAairflow-core-concepts
Chain jobs with dependencies (simple)Cloud Workflows2-5 steps, no complex logicgcp-scheduling
Run a job on a Linux VMcron + systemdOn-prem or persistent VMlinux-scheduling
Run a job on a Windows serverTask SchedulerWindows-only environmentswindows-scheduling
Manage Airflow in productionCloud ComposerManaged Airflow, GCP-nativeairflow-deployment
Debug a failed Airflow DAGAirflow UI + logsCheck task logs, XComs, connectionsairflow-troubleshooting

Storage and Data

I need to…UseNotesLink
Store raw files cheaplyGCS (Nearline/Coldline)Lifecycle policies auto-tiergcs-buckets-and-lifecycle
Run ad-hoc SQL on large dataBigQueryServerless, pay per queryquerying-and-cost-optimization
Store transactional data with ACIDSQL ServerRow-level transactions, stored procsmoc-sql-server
Serve data to a real-time dashboardFirestoreSub-10ms point readsfirestore-data-model-and-operations
Store time-series at massive scaleBigtableBillions of rows, single-digit msreal-time-nosql-pipelines
Choose a file format for data exchangeParquet (analytics) or JSON (APIs)See format comparisonserialization-formats
Load data into BigQuerybq load or streaming insertBatch vs real-time trade-offdata-loading-and-export
Transfer files between systemsgsutil rsync or gcloud transferGCS-native toolsdata-transfer

Processing and Transformation

I need to…UseNotesLink
Transform data in a warehousedbtVersion-controlled SQL transformsdbt-transformation-layer
Process streaming eventsPub/Sub + DataflowWindowing, exactly-oncestreaming-architecture
Run Python data processingPandas/Polars in Cloud RunContainerized, scalablepython pipeline execution
Build a medallion pipelinebronze/silver/gold schemasSQL Server or BigQuerymedallion-architecture
Handle idempotent writesDELETE-INSERT or MERGESafe re-runs, no duplicatesidempotent-pipeline-design
Migrate data between platformsStrangler fig + dual writeIncremental, validatedmigration-idempotency-backfills
Compare ETL vs ELT approachesSee comparison tableDepends on compute locationETL vs ELT

Infrastructure and DevOps

I need to…UseNotesLink
Deploy infrastructure reproduciblyTerraformState-managed, peer-reviewedmoc-terraform
Deploy a Cloud Run serviceTerraform + DockerOr gcloud run deploy for small teamscloud-run
Manage secrets securelyGCP Secret Manager + TerraformNever commit secrets to Gitiam-and-secrets
Set up CI/CD for data pipelinesGitHub ActionsTest, lint, deploy on mergegithub-actions-ci-cd
Manage Terraform stateGCS backend with lockingRemote state for teamsstate-management
Create reusable infra modulesTerraform modulesComposition over inheritancemodule-composition

Monitoring and Governance

I need to…UseNotesLink
Monitor pipeline health for freeGCP Cloud MonitoringFree tier: logs, metrics, alertsgcp-cloud-monitoring-deep-dive
Track data lineageDataplex Lineage APIAuto-captured for BigQuerygcp-data-lineage-and-catalog
Monitor SLA complianceCustom metrics + alertingDefine freshness and completeness SLAsgcp-pipeline-health-and-sla
Deep application performance tracingDatadog APMDistributed traces across servicesdatadog-apm-traces
Monitor SQL Server performanceWait stats + execution plansIdentify bottleneckswait-stats-analysis
Audit database accessSQL Server audit loggingCompliance and securityaudit-logging

APIs and Communication

I need to…UseNotesLink
Build a REST API for data servingFastAPI + Cloud RunPython, async, auto-docsrest-api-design-and-consumption
High-performance service-to-service callsgRPCProtobuf, streaming, 2-10x faster than RESTgrpc-for-data-pipelines
Choose an API protocolSee comparisonREST vs gRPC vs GraphQLapi-protocols-comparison
Query data flexibly from frontendGraphQLClient-specified fields, nested queriesgraphql-for-data-access
Consume a third-party REST APIPython + requestsRetry logic, pagination, authrest-api-design-and-consumption

Shell and Quick Tasks

I need to…UseNotesLink
Parse a log file quicklyawk / grepPattern matching and text extractionawk-data-processing, grep-and-pattern-matching
Edit a file in-placesedStream editing, regex substitutionsed-stream-editing
Transfer files via SSHscp / rsyncSecure copy, incremental syncdata-transfer
Debug network connectivitycurl, netcat, telnetTest endpoints and portsconnectivity-testing
Manage background processesnohup, screen, tmuxLong-running jobs on VMsviewing-processes

Decision Flowchart: Choosing Your Architecture

Use this flowchart when you are starting from scratch and do not know which scenario fits.

flowchart TD
    START[What is your primary need?] --> Q1{Real-time or batch?}

    Q1 -->|Batch| Q2{How many data sources?}
    Q1 -->|Real-time| Q3{What latency do you need?}

    Q2 -->|1-2 sources| Q4{Team size?}
    Q2 -->|3+ sources| S4[Scenario 4:<br/>Multi-Source Integration]

    Q3 -->|Sub-second| S2[Scenario 2:<br/>Streaming Pipeline]
    Q3 -->|Minutes OK| Q5{Is it just a dashboard?}

    Q5 -->|Yes| S1B[Scenario 1 with<br/>frequent scheduling]
    Q5 -->|No, ML serving| S8[Scenario 8:<br/>ML Feature Pipeline]

    Q4 -->|2-3 people| S5[Scenario 5:<br/>Small Team Platform]
    Q4 -->|10+ people| S6[Scenario 6:<br/>Large Team Platform]
    Q4 -->|4-9 people| Q6{Need governance?}

    Q6 -->|Yes| S6
    Q6 -->|No| S1[Scenario 1:<br/>Daily Batch Pipeline]

    S1 --> Q7{Need analytics warehouse?}
    Q7 -->|Yes| S3[Scenario 3:<br/>Data Warehouse]
    Q7 -->|No| DONE[Done]

    S4 --> Q7
    S5 --> Q7

Anti-Patterns: What NOT to Do

Common Mistakes

These are patterns that look reasonable but cause pain at scale. Learn from others’ mistakes.

Recognize Anti-Patterns Early and Apply the Canonical Fix

Each anti-pattern below has a well-known fix. When you encounter one in a real project, apply the fix before the pattern calcifies into “how we do things here.” The cost of correction grows exponentially with time — a schema-less data lake caught at 3 months is a week of work; caught at 3 years, it is a full platform migration.

”We’ll Build a Data Lake and Figure Out the Schema Later”

The mistake: Dumping everything into GCS or S3 with no schema enforcement, no catalog, no naming conventions. Six months later, nobody knows what any file is.

The fix: Define schemas upfront (even if they are flexible). Use a catalog (Dataplex, Data Catalog). Enforce naming conventions (/source/entity/YYYY-MM-DD/). See data-lake-architecture and context-and-metadata-architecture.

”We Need Kafka for Everything”

The mistake: Deploying Kafka for a pipeline that processes 1,000 events per day. You now have ZooKeeper, brokers, schema registry, and Connect to manage — for something Cloud Scheduler + Cloud Run could handle.

The fix: Use Pub/Sub for GCP-native workloads under 100K messages/second. Use Kafka only when you need multi-day replay, strict ordering, or you are already running Kafka. See streaming-architecture.

”Let’s Use Microservices for Data Pipelines”

The mistake: Building 20 microservices for a pipeline that is fundamentally a linear DAG. Each service has its own deployment, monitoring, and failure mode. Debugging requires tracing through 20 services.

The fix: Use a monolithic pipeline (Airflow DAG with task functions) until you have a genuine reason to decompose. Microservices solve organizational scaling problems, not technical ones. See airflow-dag-patterns.

”We Don’t Need Tests for Data”

The mistake: No validation between pipeline stages. A source schema change silently produces NULL values that propagate to dashboards. The CEO discovers the issue.

The fix: dbt tests at every layer. Freshness checks. Row count assertions. Schema contracts. See dbt-transformation-layer and context-and-metadata-architecture.

”Terraform Everything from Day One”

The mistake: A 2-person team spending 40% of their time writing Terraform modules for 10 resources. The overhead exceeds the benefit.

The fix: Start with gcloud CLI scripts in a Git repo. Switch to Terraform when you have 20+ resources, multiple environments, or 3+ engineers making infra changes. See Scenario 5.

”One Database to Rule Them All”

The mistake: Using SQL Server for everything — transactional writes, analytical queries, real-time serving, and ML feature storage. Performance degrades as workloads compete for resources.

The fix: Use the right store for the right access pattern. SQL Server for transactions, BigQuery for analytics, Firestore for real-time reads. See data-modeling-patterns for when to use which model.


Technology Comparison Matrix

A quick reference for when two technologies seem interchangeable.

Compute

CriteriaCloud RunGCE VMDataflowCloud Functions
Best forStateless batch/webStateful, long-runningStream processingSimple triggers
Scales to zeroYesNoYes (batch)Yes
Max execution time60 min (jobs)UnlimitedUnlimited9 min
Docker supportNativeManualBeam containersNo
Cost modelPer-requestPer-hourPer-worker-hourPer-invocation
When to chooseDefault for most jobsNeed GPUs, large memoryWindowed stream processingSimple event response

Storage

CriteriaBigQuerySQL ServerFirestoreGCS
Best forAnalyticsTransactionsReal-time readsFile storage
Latency1-30s<10ms<10ms~100ms
ACID transactionsLimitedFullDocument-levelNo
Cost modelStorage + query bytesLicense + VMReads/writesStorage + egress
Max scalePetabytesTerabytesMillions of docsExabytes
When to chooseAd-hoc SQL, ML, BIOLTP, stored procs, ACIDMobile/web, real-timeRaw files, archive

Orchestration

CriteriaCloud SchedulerAirflowCloud Workflows
Best forSimple cron triggersComplex DAGsSimple sequences
DependenciesNoneFull DAGLinear/parallel
Retry logicBasicAdvanced (per-task)Basic
MonitoringCloud MonitoringAirflow UI + logsCloud Monitoring
CostFree (3 jobs)$300+/month (Composer)Pay per step
When to choose<10 independent jobsComplex pipelines2-5 step workflows

Version History

DateChange
2026-03-22Initial creation — 10 scenarios, quick lookup table, decision flowchart, anti-patterns, comparison matrices

This guide is a living document. As new scenarios emerge or technologies change, add new sections and update existing ones. The goal is that any data engineer can open this note and find a starting point for their next architecture decision.

See also: moc-data-architecture | five-pillars-of-data-engineering |