Project

AT&T Marketing Campaign Analytics & Reporting

200+ Campaigns Tracked · 150+ Self-Service Users · 60% IT Reduction · 40% Manual Effort Reduction

AT&T March 2024 – March 2025 Senior Business Intelligence Analyst

Executive Summary

Objective

Build a unified marketing analytics platform integrating Workfront, Salesforce, and ZoomInfo into Snowflake — enabling self-service analytics for 150+ marketing operations users and eliminating manual reporting across 200+ concurrent campaigns.

Solution

Consolidated Workfront, Salesforce, and ZoomInfo data via Fivetran into a Snowflake reporting layer with star schema dimensional models (SCD Type 2), Power BI semantic layer with RLS, and automated reporting pipelines. Built 25+ dashboards tracking campaign volume, resource utilization, project timelines, and budget allocation.

Impact

40% reduction in manual data retrieval across marketing operations, 150+ self-service Power BI users (up from 3 analysts), 60% reduction in IT data request tickets, and query latency improvement from 45 seconds to under 3 seconds.

Business Challenge

Analyst's Perspective

Walking into my first stakeholder meeting, I expected to hear about dashboard requests. Instead, I watched a campaign manager pull out a USB drive with 47 Excel files—her "reporting system." That moment crystallized the real problem: brilliant marketing minds trapped in data entry purgatory instead of doing actual marketing analysis.

AT&T's marketing operations team faced critical inefficiencies in their data infrastructure and reporting processes:

My Approach

I learned early that you can't just "fix the data"—you have to understand the humans behind the spreadsheets. So before writing a single line of code, I shadowed each analyst through their Monday morning data ritual. Watching someone spend 3 hours copy-pasting phone call data into a pivot table told me more about the real requirements than any formal spec ever could.

Business Impact: Inefficient manual processes cost an estimated $180K annually in analyst time, while delayed reporting resulted in suboptimal budget allocation estimated at $2-3M in missed opportunities.

Marketing Operations Dashboard

Real-time marketing performance metrics, campaign ROI tracking, asset utilization analytics, and lead attribution across all channels.

218
Active Campaigns
+12% MoM
$8.4M
Marketing Spend (Q4)
On Budget
47,382
Qualified Leads
+18% YoY
3.2x
Average Campaign ROI
+0.4x vs Target

Campaign Performance by Channel (Q4 2024)

Paid Search
4.8x ROI
Email Marketing
4.1x ROI
Social Media
3.4x ROI
Display Ads
2.9x ROI
Content Marketing
2.7x ROI
Video Campaigns
2.1x ROI

ROI calculated as (Revenue - Cost) / Cost. Paid search and email show highest returns. Video campaigns underperforming target of 2.5x.

Lead Source Distribution (Salesforce)

Paid Search: 37.5%
Email: 25%
Content / Organic: 18.5%
Other: 19%

Lead sources from Salesforce CRM enriched with ZoomInfo firmographic data. Paid search sources 37.5% of pipeline volume with highest avg deal size.

Multi-Touch Lead Attribution (Last 30 Days)

Social 8,240 Paid Search 11,450 Email 9,380 Content 7,230 Webinar 5,680 Phone 4,120 0 5K 10K 15K Awareness Consideration Decision 2.1K Conv

Customer journey funnel showing Salesforce touchpoint interactions. Average 4.3 touchpoints per conversion. Enriched with ZoomInfo intent signals to prioritize high-converting accounts.

The “Intent Score Unlock” Discovery

Everyone assumed Paid Search was the top-performing campaign type. The data confirmed that — sort of. But when I layered ZoomInfo intent scores onto the Salesforce lead records in the int_enriched_leads model, the story changed. High-intent companies (intent score >70) converted at 2.8× the rate of low-intent leads — regardless of channel. The marketing team had been spending equally across company sizes; now they could prioritize budget toward in-market accounts. That firmographic enrichment, which took about a week to build in dbt, reshuffled campaign budget priorities in a way no amount of channel-level analysis had ever revealed.

40%
Manual Work Reduction
45s→3s
Query Time
150+
Self-Service Users
60%
IT Request Reduction

Full Interactive Power BI Dashboard Available

The complete Power BI dashboard suite includes:

  • Executive summary with KPI scorecards and trend analysis across all marketing channels
  • Campaign performance drill-down by channel, region, product line, and time period
  • Multi-touch attribution modeling showing customer journey touchpoints and conversion paths
  • ZoomInfo firmographic enrichment showing lead quality by company size, industry, and intent signals
  • Workfront project analytics including resource allocation, task completion rates, and budget tracking
  • Salesforce CRM pipeline tracking with lead scoring, stage velocity, and conversion funnel analytics
  • Automated anomaly detection alerts for campaign underperformance or budget overruns

Note: Power BI dashboards hosted on AT&T's internal infrastructure with enterprise security. The visualizations shown represent key metrics and analytical capabilities. Available for demonstration upon request.

Solution Architecture

System Architecture

Data Sources (Marketing Systems)

Workfront
Project Management & Tasks
ZoomInfo
Firmographic & Intent Data
Salesforce
CRM, Leads & Pipeline

Data Integration Layer

Fivetran
Automated SaaS Connectors
Fivetran ZoomInfo
Firmographic Enrichment
Snowflake
Cloud Data Warehouse

Data Warehouse

Snowflake Data Cloud
Centralized Marketing Data Platform | 2.8TB Storage

Transformation & Modeling (dbt)

Staging Models
Data Quality & Validation
Business Logic
Campaign Attribution & ROI
Data Marts
Marketing Analytics Views
Automated Testing
dbt Tests & Alerting

Analytics & Visualization

Power BI
Interactive Dashboards
Email Reports
Automated Distribution
Slack Alerts
Real-Time Notifications

Pipeline Metrics Summary

Marketing Systems 3 Sources • 200+ Campaigns Integration Fivetran • APIs Snowflake 2.8TB Storage dbt Transform 18 min/day Power BI 8 Dashboards Performance KPIs Volume: • 200+ active campaigns • 150K+ Salesforce leads Quality: • 200+ dbt tests • 45s → 3s query time Impact: • 40% manual reduction • 60% IT reduction

How It Works

This automated pipeline ingests marketing data from 3 systems (Workfront, Salesforce, ZoomInfo) using Fivetran connectors into Snowflake.

dbt transforms raw source data into 80+ governed models — staging, intermediate, and mart layers — applying SCD Type 2 logic for campaign ownership and incremental builds for 200+ active campaigns.

Power BI connects directly to Snowflake marts, delivering self-service dashboards to 150+ users and reducing query time from 45s to 3s vs. prior ad-hoc SQL.

Unexpected Win

The Fivetran-to-Snowflake pipeline ran flawlessly from Day 1, but what surprised me was how much trust that reliability built with skeptical stakeholders. After years of "the report broke again" moments with Excel, watching data flow automatically—and correctly—every single day transformed doubters into champions.

⚙️ Technical Stack

Ingestion:
Fivetran (3 connectors)
Workfront, Salesforce, ZoomInfo
Storage:
Snowflake XL Warehouse
Auto-scaling enabled
Transform:
dbt Core
80+ models, 200+ tests
Visualization:
Power BI Pro
Direct Query mode

📊 Data Flow Metrics

Daily Processing: 200+ incremental campaign builds, SCD Type 2 ownership tracking
Sync Frequency: Workfront (1hr), Salesforce (1hr), ZoomInfo (daily enrichment)
Scale: 200+ campaigns, 150K+ leads, 80+ dbt models in production
Pipeline SLA: 99.7% uptime, query time 45s → 3s (93% faster)

🎯 Key Achievements

40% manual reporting reduction through dbt automation of 200+ campaign pipelines
150+ self-service users consuming governed Power BI dashboards (was: ad-hoc IT requests)
60% reduction in IT data requests — teams answer their own questions via self-service
Query time 45s → 3s on campaign reporting via Snowflake clustering + dbt incremental models

Implementation Process

1

Discovery & Requirements Gathering (Week 1-2)

  • Conducted stakeholder interviews with 8 marketing team members (VP Marketing, Campaign Managers, Analysts)
  • Documented current manual workflows: Excel-based reporting taking 15–20 hours weekly per analyst
  • Mapped data sources: Workfront (200+ active campaigns), Salesforce (150K+ leads & pipeline), ZoomInfo (firmographic & intent enrichment)
  • Identified pain points: stale reporting lag, 40%+ manual effort, no governed definitions for “campaign” or “conversion”
  • Defined success metrics: dbt models covering all 3 sources, self-service Power BI for 150+ users, sub-5s query times
  • Created project roadmap with phased rollout: Workfront → Salesforce → ZoomInfo enrichment
Strategic Decision Point

I deliberately chose Workfront as our first integration target — not because it was easiest, but because the VP of Marketing checked project status there every morning. Getting her daily view into a Power BI dashboard first meant I had executive air cover for the more complex Salesforce and ZoomInfo deduplication work later. Always win your sponsors early.

2

Snowflake Data Warehouse Setup (Week 3-4)

  • Provisioned Snowflake account with role-based access control for marketing team
  • Designed database schema with 3-layer architecture: raw → staging → marts
  • Created dedicated warehouses: MARKETING_LOAD (XL for ETL), MARKETING_TRANSFORM (L for dbt), MARKETING_BI (M for queries)
  • Configured resource monitors to prevent cost overruns ($2K monthly budget allocation)
  • Set up Snowflake automatic clustering on date/campaign_id columns for query performance
  • Implemented data retention policy: raw data 2 years, aggregated data 5 years
3

Fivetran Integration — Workfront, Salesforce & ZoomInfo (Week 5–8)

  • Configured Fivetran connector for Workfront API — syncing projects, tasks, assignments, and campaign time entries
  • Set up Salesforce connector — extracting leads, opportunities, accounts, campaigns, and conversion events
  • Configured ZoomInfo connector for firmographic enrichment — company size, industry, intent signals, and tech stack data
  • Implemented incremental sync strategy: full historical load initially, then hourly incremental updates for Workfront and Salesforce
  • Validated data completeness: 200+ Workfront campaigns (18 months history), 150K+ Salesforce leads loaded successfully
  • Built data quality monitoring: Fivetran webhooks → Slack alerts for sync failures or row-count anomalies
  • Optimized sync schedules based on source update frequency: Workfront (1hr), Salesforce (1hr), ZoomInfo (daily)
4

dbt Staging Models & ZoomInfo Enrichment (Week 9–11)

  • Built dbt staging models for all 3 Fivetran sources: stg_workfront__campaigns, stg_salesforce__leads, stg_zoominfo__accounts
  • Implemented ZoomInfo firmographic enrichment join in int_enriched_leads: matched Salesforce accounts to ZoomInfo by domain for company size, industry, and intent score
  • Built SCD Type 2 macro for campaign ownership changes — critical for accurate attribution when AEs reassigned mid-flight
  • Added 200+ dbt tests: uniqueness, not-null, accepted values, and custom SQL assertions for referential integrity across all 3 sources
  • Deployed dbt Cloud scheduler: nightly full refresh + hourly incremental runs for Workfront and Salesforce sources
  • Published dbt documentation site with lineage DAG for all 80+ models, enabling team self-service on data definitions
The “Domain Mismatch” Problem

When I first joined the ZoomInfo enrichment match to Salesforce accounts by email domain, the match rate was only 38% — terrible. It turned out AT&T lead records stored corporate email domains but ZoomInfo used legal entity domains (e.g., att.com vs attinc.com). Building a domain normalization model with a fallback to fuzzy company name matching pushed enrichment to 91%. That single fix improved the firmographic coverage enough to make the intent-signal dashboard actually useful for the sales team.

ZoomInfo Enrichment — dbt Intermediate Model (SQL)

-- models/intermediate/marketing/int_enriched_leads.sql
-- Enriches Salesforce leads with ZoomInfo firmographic and intent data.
-- Uses domain normalization to improve match rate (38% raw -> 91% after cleanup).
{{
    config(
        materialized='incremental',
        unique_key='lead_id',
        cluster_by=['enrichment_date', 'industry'],
        tags=['marketing', 'enrichment']
    )
}}

WITH salesforce_leads AS (
    SELECT
        lead_id,
        email,
        company_name,
        -- Normalize email domain for ZoomInfo match
        LOWER(SPLIT_PART(email, '@', 2)) AS email_domain,
        campaign_id,
        lead_source,
        created_date AS enrichment_date,
        stage,
        owner_id
    FROM {{ ref('stg_salesforce__leads') }}
    {% if is_incremental() %}
        WHERE created_date > (SELECT MAX(enrichment_date) FROM {{ this }})
    {% endif %}
),

-- Domain alias mapping (legal entity vs. corporate email domain)
domain_aliases AS (
    SELECT
        canonical_domain,
        alias_domain
    FROM {{ ref('seed_domain_aliases') }}
),

zoominfo_accounts AS (
    SELECT
        zi.account_id,
        COALESCE(da.canonical_domain, zi.website_domain) AS matched_domain,
        zi.company_name        AS zi_company_name,
        zi.employee_count,
        zi.industry,
        zi.revenue_range,
        zi.intent_score,         -- buying intent from ZoomInfo Bombora
        zi.tech_stack_array
    FROM {{ ref('stg_zoominfo__accounts') }} zi
    LEFT JOIN domain_aliases da
        ON zi.website_domain = da.alias_domain
),

enriched AS (
    SELECT
        sl.lead_id,
        sl.email,
        sl.email_domain,
        sl.campaign_id,
        sl.lead_source,
        sl.enrichment_date,
        sl.stage,
        sl.owner_id,
        -- ZoomInfo firmographic enrichment
        zi.zi_company_name,
        zi.employee_count,
        zi.industry,
        zi.revenue_range,
        zi.intent_score,
        zi.tech_stack_array,
        CASE WHEN zi.account_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_enriched,
        CASE
            WHEN zi.intent_score >= 70 THEN 'High'
            WHEN zi.intent_score >= 40 THEN 'Medium'
            ELSE 'Low'
        END AS intent_tier
    FROM salesforce_leads sl
    LEFT JOIN zoominfo_accounts zi
        ON sl.email_domain = zi.matched_domain
)

SELECT
    {{ dbt_utils.generate_surrogate_key(['lead_id']) }} AS enriched_lead_sk,
    *,
    CURRENT_TIMESTAMP() AS _loaded_at
FROM enriched
5

dbt Transformation Layer (Week 11-14)

  • Built 80+ dbt models organized in staging → intermediate → marts layer structure
  • Staging layer: canonical definitions for every source (Workfront, Salesforce, ZoomInfo — no more analyst notebooks redefining “campaign”)
  • Intermediate layer: ZoomInfo enrichment join, SCD Type 2 campaign ownership macro, multi-touch attribution logic
  • Marts layer: fct_campaign_daily incremental model powering Power BI direct query
  • Implemented 200+ dbt tests: uniqueness, not_null, accepted_values, referential integrity across all source joins
  • Set up dbt Cloud scheduler: nightly full refresh + hourly incremental for Workfront and Salesforce deltas
  • Published dbt documentation site: lineage DAG and data dictionary browsable by all 150+ users
A Lesson in Humility

My first version of the attribution model had a subtle bug: I was double-counting conversions when customers called from multiple campaigns on the same day. It passed all my tests, but a sharp-eyed campaign manager caught it during UAT. That's when I learned to always include business users in testing—they know their data in ways automated tests never will. Added 12 more edge-case tests as a result.

Campaign Attribution Model (dbt SQL)

-- models/marts/marketing/fct_campaign_attribution.sql
{{
    config(
        materialized='incremental',
        unique_key='attribution_id',
        cluster_by=['attribution_date', 'campaign_id'],
        tags=['marketing', 'attribution']
    )
}}

WITH leads AS (
    SELECT
        lead_id,
        lead_created_date AS attribution_date,
        campaign_id,
        opportunity_id,
        is_qualified_lead,
        is_closed_won,
        deal_amount
    FROM {{ ref('stg_salesforce__leads') }}
    {% if is_incremental() %}
        WHERE lead_created_date > (SELECT MAX(attribution_date) FROM {{ this }})
    {% endif %}
),

campaigns AS (
    SELECT
        campaign_id,
        campaign_name,
        campaign_type,
        channel,
        start_date,
        end_date,
        budget_amount,
        campaign_owner
    FROM {{ ref('stg_workfront__campaigns') }}
),

firmographic AS (
    SELECT
        account_id,
        campaign_id,
        industry,
        employee_count,
        intent_score
    FROM {{ ref('int_zoominfo__account_enrichment') }}
),

-- Multi-touch attribution using linear model
lead_touchpoints AS (
    SELECT
        opportunity_id,
        COUNT(DISTINCT campaign_id) AS touchpoint_count,
        ARRAY_AGG(DISTINCT campaign_id) AS campaign_journey,
        MIN(lead_created_date) AS first_touch_date,
        MAX(lead_created_date) AS last_touch_date,
        DATEDIFF('day', MIN(lead_created_date), MAX(lead_created_date)) AS journey_length_days
    FROM leads
    GROUP BY opportunity_id
),

-- Revenue allocation (equal credit to all touchpoints)
attributed_revenue AS (
    SELECT
        l.opportunity_id,
        l.campaign_id,
        l.lead_created_date,
        CASE 
            WHEN l.is_closed_won THEN 
                l.deal_amount / lt.touchpoint_count
            ELSE 0
        END AS attributed_revenue,
        1.0 / lt.touchpoint_count AS attribution_weight
    FROM leads l
    INNER JOIN lead_touchpoints lt ON l.opportunity_id = lt.opportunity_id
)

SELECT
    {{ dbt_utils.generate_surrogate_key(['l.lead_id', 'l.campaign_id']) }} AS attribution_id,
    l.lead_created_date AS attribution_date,
    l.campaign_id,
    camp.campaign_name,
    camp.channel,
    l.lead_id,
    l.opportunity_id,
    l.is_qualified_lead,
    l.is_closed_won,
    lt.touchpoint_count,
    lt.journey_length_days,
    ar.attribution_weight,
    ar.attributed_revenue,
    
    -- Campaign metrics
    camp.budget_amount AS campaign_budget,
    COALESCE(f.intent_score, 0) AS zoominfo_intent_score,
    COALESCE(f.industry, 'Unknown') AS lead_industry,
    
    -- Calculated metrics
    CASE 
        WHEN camp.budget_amount > 0 THEN 
            ar.attributed_revenue / camp.budget_amount
        ELSE NULL 
    END AS campaign_roi,
    
    -- Metadata
    CURRENT_TIMESTAMP() AS _loaded_at
    
FROM leads l
INNER JOIN campaigns camp ON l.campaign_id = camp.campaign_id
LEFT JOIN lead_touchpoints lt ON l.opportunity_id = lt.opportunity_id
LEFT JOIN attributed_revenue ar ON l.lead_id = ar.lead_id AND l.campaign_id = ar.campaign_id
LEFT JOIN firmographic f ON l.campaign_id = f.campaign_id

{% if is_incremental() %}
WHERE l.lead_created_date > (SELECT MAX(attribution_date) FROM {{ this }})
{% endif %}
6

Power BI Dashboard Development (Week 15-18)

  • Built 5 Power BI reports: Executive Summary, Campaign Performance, Asset Analytics, Lead Attribution, Project Tracking
  • Configured DirectQuery connection to Snowflake for real-time data (no import/refresh needed)
  • Designed semantic model with 12 fact tables and 8 dimension tables
  • Created 47 DAX measures for KPIs: ROI, CAC, ROAS, conversion rates, asset utilization
  • Implemented row-level security: Campaign managers only see their campaigns
  • Set up automated email subscriptions: Weekly executive summary to VP Marketing, daily alerts for budget overruns
  • Published to Power BI Service with Pro licenses for 12 marketing team members
The Adoption Accelerator

I'd planned elaborate training sessions, but the real adoption breakthrough was simpler: I added a "Last Updated" timestamp to every dashboard header showing data was 12 minutes old. After years of stale weekly reports, seeing "refreshed 12 minutes ago" was magic. Three team members told me they started checking dashboards before their morning coffee—that's when I knew we'd won.

7

Testing, Training & Rollout (Week 19-22)

  • Conducted user acceptance testing with 4 campaign managers - validated metrics against manual calculations
  • Ran parallel systems for 3 weeks: New automated platform vs. legacy Excel reports
  • Achieved 98.7% metric accuracy match - discrepancies due to manual Excel errors, not platform issues
  • Created training documentation: Snowflake data dictionary, dbt model guide, Power BI user manual
  • Delivered 3 training sessions: Power BI self-service navigation, campaign setup in Workfront, and ZoomInfo intent signal interpretation
  • Established support process: Slack #marketing-analytics channel, weekly office hours
  • Decommissioned legacy Excel workflows after successful 30-day validation period
The 98.7% That Mattered

During parallel testing, we found a 1.3% discrepancy between our platform and the legacy Excel reports. I initially panicked—until we traced every difference. Every single one was an error in the Excel files: typos, wrong formulas, duplicate entries. The "discrepancy" was actually our platform being more accurate. That validation report became my favorite deliverable of the entire project.

Results & Business Impact

40% Manual Data Reduction

Automated ingestion via Fivetran + 80+ dbt models eliminated 40% of manual data retrieval work across marketing operations, reclaiming analyst capacity for strategic analysis.

150+ Self-Service Power BI Users

Grew self-service analytics adoption from a team of 3 dedicated analysts to 150+ users accessing Power BI dashboards independently, powered by dbt mart models in Snowflake.

45s → 3s Query Performance

Optimized Snowflake clustering keys and dbt mart aggregations reduced Power BI dashboard load times from 45+ seconds to under 3 seconds, enabling real-time campaign monitoring.

60% IT Request Reduction

60% reduction in IT data request tickets as 150+ users self-served answers from conformed dbt mart models, eliminating the queue of "can you pull this for me?" requests.

3-Source Unified Platform

Single source of truth integrating Workfront, Salesforce, and ZoomInfo via Fivetran → Snowflake → dbt, replacing 47+ Excel workbooks with a governed analytics engineering layer.

BI Platform in Production – Key Outcomes

80+ dbt models (staging, intermediate, marts) with incremental patterns for 200+ campaigns and reusable macros (date spine, SCD Type 2) deployed with CI/CD via GitHub Actions.

The Discovery That Reduced IT Tickets by 60%

Before the platform, the IT data queue had 400+ monthly tickets. After auditing them, I found 62% were the same five questions — just asked by different analysts. Rather than answer them repeatedly, I built five mart models that targeted those exact questions directly and published them to Power BI as named reports with clear descriptions. Within four weeks, those ticket types had nearly disappeared. The 60% IT request reduction didn't require process change, training, or policy enforcement. It just required making the data findable and trustworthy. That's what analytics engineering does that custom scripting and ad hoc queries can't: it systematically removes the bottleneck of “I need a data person to get me this.”

Technical Challenges & Solutions

Challenge: ZoomInfo & Salesforce Contact Deduplication

Problem: ZoomInfo and Salesforce had overlapping contact records for the same accounts, with different keys (ZoomInfo uses company domain; Salesforce uses account ID). Direct joins produced fan-out, inflating contact counts and attribution metrics.

Solution: Built a dbt intermediate model int_contact_resolved implementing deterministic matching on company domain + email domain to produce a deduplicated contact spine. Added dbt tests to assert unique contacts per account and zero cross-system fan-out. Reduced contact count by 23% (inflated duplicates removed), correcting downstream attribution models.

Challenge: Campaign Attribution Complexity

Problem: Marketing stakeholders disagreed on attribution model - some wanted first-touch, others last-touch, others linear. No consensus on credit allocation for multi-touch customer journeys (average 4.3 touchpoints).

Solution: Built flexible attribution framework in dbt supporting 5 models: first-touch, last-touch, linear, time-decay, position-based. Created Power BI parameter allowing users to switch models dynamically. Default to linear model based on marketing literature best practices. Documented methodology in data dictionary.

Challenge: Campaign Ownership Changes via SCD Type 2

Problem: AT&T's 2024 reorganization moved 80+ campaigns between departments mid-flight. Historical Power BI reporting attributed campaigns to new owners retroactively, making before/after comparisons meaningless.

Solution: Implemented SCD Type 2 in a reusable dbt macro tracking campaign ownership with valid_from / valid_to effective dates. dim_campaign dimension now stores full ownership history. Power BI reports use point-in-time joins to the dimension, producing historically accurate attribution regardless of reorgs. Used the same macro pattern across employee, department, and territory dimensions.

Challenge: Snowflake Cost Management

Problem: Initial month Snowflake costs reached $3,200 (60% over $2K budget) due to inefficient queries from Power BI DirectQuery and lack of clustering.

Solution: Implemented cost optimization: (1) Added automatic clustering on high-cardinality columns (campaign_id, date), (2) Created aggregated summary tables for common dashboard queries, (3) Optimized Power BI queries using query folding, (4) Set up resource monitors with automatic suspend, (5) Rightsized warehouses (M for BI instead of L). Reduced costs to $1,600/month (20% under budget).

Challenge: Data Freshness SLA Requirements

Problem: Marketing leadership wanted near-real-time campaign performance visibility, but Fivetran syncs hourly and full dbt runs are resource-intensive — creating potential lag for time-sensitive budget decisions.

Solution: Implemented incremental dbt models for high-churn tables (campaign spend, lead events), reducing full-refresh runtimes by 70%. Configured Snowflake clustering on campaign_date for Power BI direct-query performance. Achieved 45s → 3s average query time — the key metric that drove 150+ users to adopt self-service vs. opening IT tickets.

Challenge: Historical Data Quality Issues

Problem: Historical Workfront data (pre-2023) had incomplete campaign budget fields (41% null), making historical ROI analysis impossible and reducing confidence in platform.

Solution: Collaborated with finance team to backfill budget data from financial system records. For remaining gaps, implemented statistical imputation using similar campaigns (same channel, similar scale). Added data quality flags in dbt models indicating confidence level (High/Medium/Low). Created dashboard footnotes explaining data limitations. Stakeholders accepted approach after transparency demonstration.

Key Learnings & Best Practices

Stakeholder Buy-In Through Quick Wins

Delivered Workfront integration first (weeks 5–7) providing immediate value with campaign tracking dashboards. Early success built trust and executive support for the more complex Salesforce deduplication and ZoomInfo enrichment work. Phased rollout reduced risk and maintained momentum vs. a “big bang” approach that could fail spectacularly.

Data Quality Trumps Data Volume

Spent the first 30% of the project building the dbt test suite and validating staging models against source systems. Initially felt like “slowing down.” But those 200+ tests caught schema drift, null FK violations, and duplicate campaign records before they ever reached Power BI. Quality investment up front has a way of multiplying your credibility downstream — especially when stakeholders ask “how do we know this number is right?”

Document Everything for Self-Service Analytics

Created comprehensive documentation: dbt data dictionary (auto-generated), Power BI field descriptions, Snowflake data lineage. Reduced analyst support requests by 70% as users could self-serve answers. Documentation investment (40 hours) paid off in reduced ongoing support burden and faster onboarding.

Flexible Attribution Models Critical for Adoption

Initial launch with single attribution model (linear) caused stakeholder pushback - different teams wanted different approaches. Adding flexibility through Power BI parameters (5 models) satisfied everyone and demonstrated platform sophistication. No "one size fits all" for marketing attribution.

Cost Optimization Requires Ongoing Monitoring

Snowflake costs ballooned 60% over budget in month 1 before optimization. Set up daily cost alerts, weekly cost reviews, and monthly optimization sprints. Cloud data warehouse costs are variable and can spiral without active management. Budget 20% buffer and monitor religiously.

Change Management as Important as Technical Implementation

Technical platform worked perfectly but adoption lagged initially due to change resistance. Invested in training (3 sessions), office hours (weekly), and Slack support channel. Identified "champion users" in each campaign team to drive adoption. Technology alone doesn't drive transformation - people and process matter equally.

Technologies & Tools

BI & Data Modeling (Core)

  • dbt Core
  • dbt Incremental Models
  • dbt Macros (date spine, SCD Type 2)
  • dbt Tests & Documentation
  • dbt-utils / dbt-expectations
  • SCD Type 2 (conformed dimensions)

Data Warehouse & Ingestion

  • Snowflake Data Cloud
  • Fivetran (Workfront, Salesforce, ZoomInfo)
  • GitHub Actions (CI/CD)

Source Systems

  • Workfront (Project Management)
  • Salesforce (CRM)
  • ZoomInfo (Contact Enrichment)

BI & Visualization

  • Power BI (Service + Desktop)
  • DAX (Measures & Calculated Columns)
  • Power Query M
  • Row-Level Security (RLS)

Programming & Query

  • SQL (Advanced / Snowflake Dialect)
  • Python
  • Jinja Templating

DevOps & Collaboration

  • Git / GitHub
  • GitHub Actions
  • YAML (dbt schema files)
  • Jira / Agile

Need a Marketing Analytics Platform?

This project demonstrates end-to-end implementation of modern marketing data infrastructure, from multi-source integration to executive dashboards. I specialize in building scalable data platforms that eliminate manual reporting and enable data-driven decision making.