Skip to content
Notifications
Clear all

TIL: A simple UTM tagging error cost us $15k in misallocated spend last month.

1 Posts
1 Users
0 Reactions
6 Views
(@backend_latency_queen)
Reputable Member
Joined: 2 months ago
Posts: 159
Topic starter   [#2133]

A cautionary tale from the infrastructure side. Our marketing team reported wildly inconsistent CPA across channels, with "Social - Organic" suddenly accounting for 20% of conversions and a massive spend spike. The data pipeline seemed fine, but the business logic was broken.

The root cause was in the UTM parameter ingestion and validation microservice. We parse the `utm_source`, `utm_medium`, and `utm_campaign` from incoming click events. The bug was a simple string comparison that failed to canonicalize values before deduplication in our aggregation layer.

```go
// The problematic logic (simplified)
func bucketCampaign(rawCampaign string) string {
switch rawCampaign {
case "spring_sale", "Spring_Sale", "SPRING_SALE":
return "spring_sale_core"
default:
return rawCampaign // This was the issue
}
}
```

Campaigns "SpringSale" and "spring_sale" were treated as distinct, causing spend to fragment across dozens of "unique" campaign entries in our reporting dashboard. The misallocation obscured the true performance of our paid social efforts.

**The technical breakdown:**
* **Ingestion:** Golang service normalizes and validates UTM params, then publishes to Kafka.
* **Aggregation:** Flink job performs hourly rollups, grouping by normalized params.
* **Storage:** Aggregated data lands in PostgreSQL for reporting.
* **The Bug:** The normalization function had an incomplete mapping list. New campaign variants created by external teams bypassed it, leading to new buckets.

**The fix was twofold:**
1. Implemented a more aggressive normalization (lowercase, strip special chars, predefined alias mapping) in the ingestion service.
2. Added a nightly batch correction job in PostgreSQL to merge historical fragmented buckets.

```sql
-- Example of the correction logic
UPDATE campaign_spend
SET campaign_key = 'spring_sale_core'
WHERE campaign_key IN ('springsale', 'SpringSale2024');
```
Always validate and canonicalize your analytical dimensions at the point of entry, not just in the BI tool. A missing row in a lookup table cost real money.

-- latency


sub-100ms or bust


   
Quote