Cross-channel matching: de bug die pas opduikt bij site nummer twee
Een EAN-lookup zonder channelId werkt prima — totdat hetzelfde product op een tweede kanaal verschijnt. Hoe een audit in het MFP-platform vijf van zulke leaks vond en welke patronen ze daarna onmogelijk maakten.
Cross-channel matching: de bug die pas opduikt bij site nummer twee
Eén kanaal: alles werkt
Een typische e-commerce-codebase begint single-channel. Eén database, één productcatalogus, één winkel. Een EAN-lookup heet dan eenvoudig findByEan($ean): er is maar één kanaal, dus de query is per definitie eenduidig. ProductRepository doet zijn werk, AiBulkService doet zijn werk, exports doen hun werk. Geen ambiguïteit, geen probleem.
Twee kanalen: alles lijkt te werken
Wanneer een tweede kanaal aanlandt — bij MFP (Multi Feed Processing) waren dat eerst ditpetje.nl en dithorloge.nl, daarna ditparfum.nl — verandert de aard van het probleem stilletjes. Producten worden nu per kanaal opgeslagen, met dezelfde EAN potentieel actief op meerdere kanalen tegelijk. findByEan($ean) geeft nog steeds een product terug — alleen niet altijd het product van het kanaal dat de aanroeper bedoelde.
Het faalmodel is verraderlijk: zichtbaar geen crash, geen 500, geen logregel. Wel een productdetailpagina op ditpetje.nl die per ongeluk een horloge van dithorloge.nl toont, of een AI-enrichment die petten-tekst koppelt aan parfum-data. De bug presenteert zich als slechte content en niet als technische fout, en glipt daardoor langs de gebruikelijke alarmsignalen.
De audit en wat hij vond
Een gerichte audit op het MFP-platform traceerde alle aanroepen van findBy(['id' => $ids]) en findBy(['ean' => $ean]) en zocht naar exemplaren waarin het channelId-criterium ontbrak. Vijf cross-channel leaks kwamen aan het licht in:
- ProductService — bulk-lookups die producten over kanaalgrenzen heen samenvoegden.
- AiBulkService — AI-verrijking-batches waarin een verkeerd kanaal kon binnensluipen.
- OutputQueueService — exports die producten van meerdere kanalen samenvoegden tot één feed.
- GuideController — redactionele guides die producten ophaalden zonder kanaalfilter.
- ProductEnrichCommand — een CLI-commando dat in onbedoelde kanalen werkte.
De fix: maak het verkeerde pad onmogelijk
De directe fix is overal channelId toevoegen. De duurzame fix is anders. Bij MFP geldt nu de regel dat alle ProductRepository-queries verplicht een channelId-criterium meekrijgen. Geen findBy(['ean' => $ean]) zonder kanaal. Geen findBy(['id' => $ids]) zonder kanaal. De repository wordt zodanig vormgegeven dat de kanaal-loze aanroep een PHPStan-fout, of in striktere varianten een type-error, oplevert. Het verkeerde pad wordt onmogelijk te schrijven, niet alleen ontmoedigd.
De les voor multi-iets-systemen
Cross-channel matching is een multi-tenant-bug onder een ander uniform. Iedere codebase die op een dag van één naar twee tenants, kanalen, organisaties of klanten gaat, kent dit risico — en de meeste codebases ontdekken het pas wanneer een klant een rare productpagina screenshot. Het preventieve werk is goedkoop: een lint-regel, een PHPStan-rule, een review-checklist. Het reactieve werk is duur: data-corruption ophelderen, customer-trust herstellen, retro's draaien. Ontwerpen voor N voordat er 2 zijn, is de goedkoopste vorm van defensive engineering die een multi-channel-platform zich kan veroorloven.
Dit artikel delen
Benieuwd wat er mogelijk is?
Eén gesprek is vaak genoeg om te weten of het past. Binnen 24 uur een reactie, en zonder tussenpersonen.