Full-Stack-Entwicklung, Data Engineering
B2B-Marktplatz
2024
Vollständige Kampagnen-Attribution, null Drittanbieter-SDKs.
Ein B2B-Marktplatz investierte stark in bezahlte Akquise über Google, LinkedIn und Branchenportale - hatte aber keine zuverlässige Möglichkeit, eine Anmeldung auf die Anzeige zurückzuführen, die sie ausgelöst hatte. Drittanbieter-Analytics-Tools waren entweder durch Ad-Blocker blockiert, nicht DSGVO-kompatibel oder zu grob, um zwischen erstem und letztem Touchpoint zu unterscheiden. Sie brauchten eine Attribution, der sie vertrauen konnten - direkt in die Plattform integriert.
Analytics, denen man nicht vertrauen konnte
Der Kunde verließ sich auf Google Analytics für das Kampagnen-Tracking. Die Probleme waren sofort sichtbar: Ad-Blocker löschten 30-40% der Tracking-Daten, DSGVO-Consent-Banner reduzierten die Abdeckung weiter, und die ankommenden Daten ließen sich nicht mit tatsächlichen Anmeldungen im CRM verknüpfen. Das Marketing-Team traf Budgetentscheidungen auf Basis unvollständiger Daten. Anonyme Besucher, die tagelang stöberten, bevor sie sich anmeldeten, verloren jeglichen Attributionskontext - das System hatte kein Gedächtnis dafür, woher sie ursprünglich kamen.
30-40% aller Tracking-Daten wurden still von Ad-Blockern gelöscht. Das Marketing-Team traf sechsstellige Budget-Entscheidungen auf Basis unvollständiger Zahlen.
Zweistufige Attribution
Ich habe ein zweistufiges System entworfen, das vollständig innerhalb der Anwendungsgrenzen arbeitet. Im Frontend fängt eine schlanke JavaScript-Klasse namens CTracker UTM-artige Parameter (mit Prefix c_source, c_medium, c_campaign) aus der URL beim ersten Besuch ab und speichert sie in localStorage mit einem Dual-Slot-Modell: ein Slot für den initialen Touchpoint, einer für den aktuellsten. Keine Cookies, keine externen Anfragen. Im Backend liest eine Kette von Django-Middleware - CTrackMiddleware gefolgt von LeadSourceMiddleware - diese Parameter aus Query-Strings und Session-Daten, validiert sie und schreibt sie als LeadSource-Einträge in die Datenbank. Wenn sich ein anonymer Besucher schließlich anmeldet oder einloggt, bindet ein Django-Signal rückwirkend alle gespeicherten Attributionsdaten an das Benutzerkonto, sodass nichts verloren geht.
Die Frontend-Klasse CTracker erfasst und speichert UTM-Parameter in localStorage:
class CTracker {
constructor () {
this.storageAvailable = this._testStorage();
if (!this.storageAvailable) return;
const params = new URLSearchParams(window.location.search);
const keys = ['c_source', 'c_medium', 'c_campaign', 'c_term'];
keys.forEach(key => {
const val = params.get(key);
if (!val) return;
// Always update the "current" touchpoint
localStorage.setItem(key, val);
// Only set "initial" once — never overwrite
if (!localStorage.getItem(key + '_initial')) {
localStorage.setItem(key + '_initial', val);
}
});
}
_testStorage () {
try {
localStorage.setItem('__ct', '1');
localStorage.removeItem('__ct');
return true;
} catch { return false; }
}
}Die Attributions-Pipeline
Bewege den Mauszeiger über die einzelnen Knotenpunkte, um den Datenfluss durch das System nachzuverfolgen, vom ersten Anzeigenklick bis zum fertigen CRM-Eintrag.
Nutzer klickt eine Anzeige mit c_source-, c_medium- und c_campaign-URL-Parametern.
Frontend-JavaScript fängt UTM-Parameter ab und speichert sie in localStorage im Initial-/Current-Dual-Slot-Modell.
Django CTrackMiddleware liest URL-Parameter und Session-Daten, validiert gegen bekannte Quellen.
LeadSourceMiddleware schreibt validierte Attributionsdaten als LeadSource-Einträge in PostgreSQL.
Bei Login/Registrierung bindet ein Django-Signal rückwirkend alle anonymen Attributionsdaten an den authentifizierten Nutzer.
- 01AnzeigenklickNutzer klickt eine Anzeige mit c_source-, c_medium- und c_campaign-URL-Parametern.
- 02CTrackerFrontend-JavaScript fängt UTM-Parameter ab und speichert sie in localStorage im Initial-/Current-Dual-Slot-Modell.
- 03MiddlewareDjango CTrackMiddleware liest URL-Parameter und Session-Daten, validiert gegen bekannte Quellen.
- 04LeadSourceLeadSourceMiddleware schreibt validierte Attributionsdaten als LeadSource-Einträge in PostgreSQL.
- 05RegistrierungBei Login/Registrierung bindet ein Django-Signal rückwirkend alle anonymen Attributionsdaten an den authentifizierten Nutzer.
Die Django-Middleware-Kette extrahiert, validiert und speichert Attributionsdaten bei jedem Request:
class LeadSourceMiddleware:
"""
Reads c_* attribution params from query string + session,
validates them, and persists a LeadSource record.
"""
def __call__(self, request):
params = self._extract(request)
if not params:
return self.get_response(request)
source, created = LeadSource.objects.get_or_create(
session_key=request.session.session_key,
defaults=params,
)
# Bind to user when they authenticate
if request.user.is_authenticated and not source.user:
source.user = request.user
source.save(update_fields=['user'])
return self.get_response(request)Volle Attribution, null Abhängigkeiten
Nach dem Deployment hatte das Marketing-Team erstmals vollständige End-to-End-Sichtbarkeit. Jeder CRM-Eintrag trug seine komplette Akquisitionshistorie: initiale Quelle, neueste Quelle und die gesamte Touchpoint-Kette. Störungen durch Ad-Blocker fielen auf null, da nichts Externes geladen wurde. DSGVO-Konformität war unkompliziert: keine Drittanbieter-Cookies, kein Cross-Site-Tracking, kein Consent-Banner für First-Party-Analytics erforderlich. Das System verarbeitete die Attribution für Tausende täglicher Besucher mit weniger als 250 ms zusätzlicher Latenz pro Request.
WICHTIGE KENNZAHLEN
"Wir wissen endlich genau, woher jede Anmeldung kommt. Kein Raten mehr, keine Lücken durch Ad-Blocker. Unsere Marketing-Budget-Entscheidungen basieren zum ersten Mal auf echten Daten."
Head of Marketing
B2B-Marktplatz · Enterprise SaaS