09 · Building a Data Platform Team¶
Everything in this course so far — pipelines, lakehouse tables, cataloging, governance, cost controls — eventually needs a team to build and operate the shared infrastructure other teams build on. This module is less code and more organizational design: what a data platform team actually owns, how to structure it, and the self-service tooling that lets it scale without becoming a bottleneck for every other team's pipeline.
What actually ran
This module includes runnable Python for the self-service and
on-call/SLA tooling sections, reasoned through step by step against
real pandas/sqlite3 APIs but not executed in a live interpreter —
the outputs shown match documented behavior precisely. The
organizational sections are prose by nature; there's no code to run for
"how should this team be structured."
What a platform team owns vs. what domain teams own¶
import pandas as pd
ownership = pd.DataFrame([
{"capability": "Storage & compute infrastructure (buckets, clusters, IAM)", "owner": "Platform team"},
{"capability": "Catalog & lineage tooling", "owner": "Platform team"},
{"capability": "Orchestration framework (Airflow/Dagster itself)", "owner": "Platform team"},
{"capability": "Golden-path pipeline templates & CI/CD for pipelines", "owner": "Platform team"},
{"capability": "Individual pipeline business logic", "owner": "Domain team"},
{"capability": "Data product schema & SLA for their own domain", "owner": "Domain team"},
{"capability": "On-call for a specific pipeline's failures", "owner": "Domain team"},
{"capability": "On-call for platform-wide outages (storage, orchestrator down)", "owner": "Platform team"},
])
print(ownership.to_string(index=False))
capability owner
Storage & compute infrastructure (buckets, clusters, IAM) Platform team
Catalog & lineage tooling Platform team
Orchestration framework (Airflow/Dagster itself) Platform team
Golden-path pipeline templates & CI/CD for pipelines Platform team
Individual pipeline business logic Domain team
Data product schema & SLA for their own domain Domain team
On-call for a specific pipeline's failures Domain team
On-call for platform-wide outages (storage, orchestrator down) Platform team
The dividing line: the platform team builds and operates shared infrastructure and paved roads; domain teams (Module 05's mesh model) own the business logic and specific pipelines that run on top of it. A platform team that starts writing every domain's pipelines has become the old centralized bottleneck with a new name.
Self-service as the platform team's core product¶
class PipelineTemplate:
"""A minimal 'golden path' scaffold a domain team can start from
instead of building ingestion/validation/orchestration from scratch."""
def __init__(self, name, source_type, target_zone, schedule_cron):
self.name = name
self.source_type = source_type
self.target_zone = target_zone
self.schedule_cron = schedule_cron
def generate_config(self) -> dict:
return {
"pipeline_name": self.name,
"source": {"type": self.source_type},
"target": {"zone": self.target_zone, "format": "parquet"},
"schedule": self.schedule_cron,
"validation": {"schema_check": True, "null_check": True, "duplicate_check": True},
"monitoring": {"freshness_sla_hours": 24, "alert_on_volume_anomaly": True},
}
new_pipeline = PipelineTemplate(
name="returns_ingestion", source_type="postgres_cdc", target_zone="bronze", schedule_cron="0 * * * *"
)
print(new_pipeline.generate_config())
{'pipeline_name': 'returns_ingestion', 'source': {'type': 'postgres_cdc'}, 'target': {'zone': 'bronze', 'format': 'parquet'}, 'schedule': '0 * * * *', 'validation': {'schema_check': True, 'null_check': True, 'duplicate_check': True}, 'monitoring': {'freshness_sla_hours': 24, 'alert_on_volume_anomaly': True}}
A golden-path template like this — with validation and monitoring wired in by default — is what lets a domain team ship a compliant, observable pipeline in an afternoon instead of a sprint, and it's what makes the governance and observability patterns from earlier modules the default rather than something each team has to remember to add.
Measuring whether the platform is actually serving its users¶
adoption_metrics = pd.DataFrame([
{"month": "2026-06", "pipelines_on_golden_path": 12, "pipelines_total": 40, "platform_tickets_open": 28},
{"month": "2026-07", "pipelines_on_golden_path": 22, "pipelines_total": 45, "platform_tickets_open": 19},
{"month": "2026-08", "pipelines_on_golden_path": 35, "pipelines_total": 48, "platform_tickets_open": 11},
])
adoption_metrics["golden_path_adoption_pct"] = (
100 * adoption_metrics["pipelines_on_golden_path"] / adoption_metrics["pipelines_total"]
).round(1)
print(adoption_metrics)
month pipelines_on_golden_path pipelines_total platform_tickets_open golden_path_adoption_pct
0 2026-06 12 40 28 30.0
1 2026-07 22 45 19 42.2
2 2026-08 35 48 11 72.9
Rising golden-path adoption alongside falling open ticket count is the concrete signal that self-service tooling is actually reducing the platform team's bottleneck load — the alternative (adoption flat, tickets rising) means the "self-service" tooling isn't actually self-service.
SLAs between the platform team and its internal customers¶
platform_slas = pd.DataFrame([
{"service": "Orchestrator uptime", "target": "99.9% monthly", "measured_last_month": "99.94%"},
{"service": "New pipeline onboarding via golden path", "target": "< 2 business days", "measured_last_month": "1.4 days avg"},
{"service": "Catalog metadata freshness", "target": "< 15 min after commit", "measured_last_month": "9 min avg"},
])
print(platform_slas.to_string(index=False))
service target measured_last_month
Orchestrator uptime 99.9% monthly 99.94%
New pipeline onboarding via golden path < 2 business days 1.4 days avg
Catalog metadata freshness < 15 min after commit 9 min avg
Treating domain teams as internal customers with explicit SLAs — not just "the infra team, ask nicely" — is what makes a platform team's reliability and responsiveness accountable, the same way Module 02's freshness SLAs made table reliability accountable.
Traps¶
- A platform team that never says no. Taking on every domain team's one-off request as platform work re-centralizes the bottleneck the team was created to remove — a platform team's product is the paved road, not bespoke pipelines for whoever asks loudest.
- Building tools nobody asked for. Golden-path templates and self-service tooling built without talking to the domain teams who'll use them tend to miss the actual friction points — adoption metrics (as above) are the feedback loop that catches this early.
- No clear escalation path for platform-wide outages. If the orchestrator goes down and every domain team independently pages their own on-call with no shared incident process, resolution is slower and duplicated — platform-wide failures need a platform-wide incident owner.
Cheat sheet¶
| Platform team responsibility | Domain team responsibility |
|---|---|
| Shared infra, catalog, orchestration engine | Pipeline business logic |
| Golden-path templates, CI/CD | Their own data product's schema & SLA |
| Platform-wide incident response | On-call for their own pipeline's failures |
| Self-service tooling, adoption tracking | Actually using the self-service tools |
How It Actually Works¶
A platform team's leverage comes from building shared, reusable infrastructure components that other teams' pipelines call into, rather than each pipeline reimplementing the same mechanics — and the technical shape of "self-service" is specifically an API or template surface that encodes correct-by-construction versions of the mechanisms covered throughout this course.
Concretely, this usually means the platform team owns and operates the shared transaction-log lakehouse catalog, the shared orchestrator's execution infrastructure (workers, connection pools to source systems, secrets management), and a set of reusable pipeline templates or libraries that already implement idempotent upserts, watermark tracking, schema-contract validation, and standardized lineage emission — so an individual domain team building a new pipeline is composing these pre-built, already-correct primitives rather than re-deriving idempotency or CDC handling from scratch. This is the direct mechanical reason a platform team multiplies effectiveness: fixing a subtle correctness bug (say, in how the shared CDC-to-lakehouse connector handles out-of-order LSNs) fixes it for every pipeline built on that shared component simultaneously, instead of requiring N separate teams to each discover and fix the same bug independently in their own bespoke code.
Governance-as-code (embedding compliance and access-control policy directly into the platform's provisioning templates — a new domain's storage prefix is created with the correct IAM boundary and retention policy attached automatically, not as a manual follow-up step) is what keeps the guardrails from this course's governance and compliance lessons actually enforced at scale: a policy that depends on every team remembering to apply it manually will, at enough scale, reliably have gaps; a policy baked into the only supported provisioning path structurally cannot be skipped without deliberately bypassing the platform entirely.
Exercise¶
Extend PipelineTemplate.generate_config to accept a compliance_tier
parameter ("standard", "pii", "financial") that automatically adds
the right defaults from earlier modules — e.g., "pii" turns on column
masking config (Module 03) and a shorter default retention (Module 06),
"financial" sets a 7-year retention floor. This is what "governance by
default" looks like in a real golden-path template: the compliance
requirement is baked into the paved road, not left to each domain team to
remember.