System & processer för dig som vill växla upp

Ett nyhetsbrev för företagaren som vill växa, projektledaren som vill optimera,
och visionären som vill realisera. Håll dig uppdaterad. Bli inspirerad.

Förra veckan behövde jag A/B-testning för landing pages. Den här veckan har jag en fungerande plugin.

Det låter smidigt när man säger det så. Det var det inte. Men det var heller inte det mångmånadersprojekt det hade varit för bara ett par år sedan. Och det är det som är intressant – inte bara att pluginen finns, utan hur den blev till.

Det här är historien om varför jag byggde den, hur processen med Claude Code såg ut, och vilka problem vi stötte på längs vägen. Spoiler: de flesta problemen var inte de jag förväntade mig.

Varför jag behövde det här

Jag bygger verktyg till mitt WordPress-ekosystem – pluginer som löser specifika problem för de kunder och projekt jag arbetar med. Cookie Consent Manager, AI Internal Linking, bloggarkiv-blocks. Alla följer samma filosofi: sofistikerade under ytan, enkla att använda, inga externa beroenden.

A/B-testning var nästa logiska steg. Jag bygger landing pages. Jag driver trafik till dem. Jag vill veta vilken version som fungerar bäst. Och jag ville inte betala en månatlig SaaS-avgift för att få svaret.

De alternativ som fanns var antingen för dyra, för komplexa, eller JavaScript-baserade – vilket betyder att besökaren ser originalsidan flimra förbi innan den ”rätta” versionen laddas. Det är inte acceptabelt för sidor som ska konvertera.

Så jag bestämde mig för att bygga det själv. Server-side traffic splitting, cookie-baserad besökarspårning, statistisk säkerhetsberäkning, dashboard med grafer. Allt inom WordPress, noll externa tjänster.

Hur det gick till med Claude Code

Jag använde Claude Code med Opus 4.6 som par-programmeringspartner. Inte som en kodgenerator jag blint kopierar från, utan som en samarbetspartner jag resonerar med.

Arbetsflödet ser ut ungefär så här: jag beskriver vad jag vill åstadkomma – inte bara tekniskt, utan funktionellt. Vad ska hända när en besökare landar på entry page? Hur ska trafikfördelningen fungera? Vad räknas som en konvertering? Claude föreslår en arkitektur, jag granskar den, vi itererar. Sen skrivs koden, jag testar, och vi felsöker tillsammans.

Det som gör det här arbetsflödet produktivt är inte att AI:n skriver kod snabbare än jag kan. Det är att jag kan fokusera på vad som ska byggas och varför, medan Claude hanterar mycket av hur. Men – och det här är viktigt – jag måste fortfarande förstå ”hur” tillräckligt för att veta om lösningen är bra. AI:n kan skriva en elegant funktion som löser helt fel problem om jag inte vet vad jag letar efter.

Toolstacken landade i Meta Box för admin-UI och custom fields, Chart.js för grafer på dashboarden, och ren PHP/JS för allt annat. Inget ramverk, inget byggsystem, inga pakethanterare. WordPress-nativt hela vägen.

Det som förvånade mig mest var hur mycket av arbetet som handlade om att beskriva vad jag ville, inte om att skriva kod. Jag lade mer tid på att förklara flödet – ”när en besökare med en test-cookie besöker en goal page ska det räknas som en konvertering, men bara en gång per besökare per test” – än på att finjustera implementeringen. Det var ett tankeskifte. Istället för att tänka i funktioner och klasser tänkte jag i beteenden och regler.

Problemen vi stötte på

Det är här det blir intressant. Inte för att problemen var ovanliga – de flesta var ganska klassiska – utan för att de illustrerar hur AI-assisterad utveckling fungerar i praktiken. Det är inte en rak linje från idé till färdig produkt. Det är en serie av ”varför fungerar inte det här?” följt av systematisk felsökning.

Objekten som försvann

Det första problemet var att ingenting hände. Pluginen laddade, testerna var konfigurerade, men besökare redirectades aldrig. Inga besök registrerades.

Orsaken visade sig vara PHP:s garbage collection. RedirectHandler- och ConversionTracker-objekten skapades inuti en metod men lagrades aldrig som egenskaper på Plugin-klassen. PHP:s skräpsamlare förstörde dem innan WordPress hann anropa deras callbacks. Tyst, utan felmeddelande, utan varning.

Det tog en stund att hitta. Koden såg korrekt ut – objekten skapades, callbacks registrerades. Men när template_redirect-hooken kördes fanns objekten inte längre. Lösningen var enkel: lagra objekten som klassegenskaper istället för lokala variabler. Men att hitta problemet krävde att vi systematiskt gick igenom livscykeln för varje objekt.

Alla besökare fick samma hash

Det här var den mest frustrerande buggen. Dashboarden visade exakt 1 besök per variant, oavsett hur många gånger jag testade i inkognitoläge.

Orsaken: hash-funktionen som skapade unika besökaridentifierare använde cookie-värdet – alltså variant-ID:t (ett sidnummer som ”821”) – som input. Varje besökare som tilldelades samma variant fick identisk hash. Kombinerat med databasens UNIQUE-index och INSERT IGNORE betydde det att varje besök efter det första tyst ignorerades.

Lösningen var att införa en separat cookie med ett UUID som genereras en gång per webbläsare. Hashen beräknas nu från det unika ID:t istället för variant-ID:t. Enkel fix, men att identifiera orsaken tog tid – särskilt eftersom allt såg korrekt ut i koden. Det var logiken som var fel, inte syntaxen.

Meta Box och multiple values

Konverteringar spårades inte trots att besökare nådde tack-sidan. Anledningen: Meta Box lagrar fält med multiple => truesom separata databasrader, inte som en serialiserad array. WordPress get_post_meta()med truesom tredje parameter returnerar bara det första värdet. Om du hade tre goal pages registrerades bara den första.

Det här är ett typexempel på ramverksspecifik kunskap som AI:n inte nödvändigtvis har. Claude föreslog först den ”vanliga” WordPress-lösningen, som fungerar med standard meta fields men inte med Meta Box:s implementation. Lösningen var att använda Meta Box:s egen funktionrwmb_meta()istället.

Chart.js som inte slutade växa

Mer visuellt underhållande: stapeldiagrammet på dashboarden växte oändligt. Varje rendering-cykel blev det lite högre, i en loop som aldrig stannade.

Orsaken var Chart.js responsive mode i kombination med CSS Grid. Chart.js beräknar canvas-storleken baserat på sin container. Utan en definierad höjd triggar varje resize en ny beräkning, som ändrar containerhöjden, som triggar ännu en resize. Oändlig loop.

Fix: en wrapper-div med fast höjd. 220 pixlar,position: relative. Det var allt. Men det tog ett tag att förstå varför det hände, inte bara att det hände.

Nonce-problemet som inte borde ha varit ett problem

Det sista problemet var mer av en designutmaning. Dashboard har en ”End Test”-knapp som öppnar en modal där du väljer vinnare. Modalen behöver skapa en URL med en giltig WordPress nonce – en säkerhetstoken som verifierar att anropet är legitimt.

Problemet: test-ID:t är dynamiskt. Det bestäms av vilken knapp användaren klickar på. Men WordPress genererar nonces server-side, och om du skapar en nonce med ett platshållar-ID (som __TEST_ID__) genereras en token för den bokstavliga strängen – inte för det faktiska test-ID:t.

Lösningen var att förgenerera nonces för alla aktiva tester i PHP och skicka dem som en lookup-tabell till JavaScript via wp_localize_script(). Inte raketvetenskap, men det krävde att vi tänkte igenom flödet ordentligt istället för att bara ”lösa det snabbt”. Det var ett bra exempel på hur Claude föreslog en elegant lösning efter att jag beskrivit problemet tydligt – och hur resultatet blev bättre än min ursprungliga idé om att hantera det med en AJAX-request.

Vad jag lärde mig om AI-assisterad utveckling

Fyra saker sticker ut efter det här projektet.

Domänkunskap är inte valfritt. Claude kan skriva utmärkt kod, men den vet inte vad du försöker åstadkomma med ditt specifika projekt. Du behöver veta tillräckligt om problemdomänen för att formulera rätt frågor, bedöma föreslagna lösningar, och veta när något inte stämmer – även om koden ser korrekt ut.

Felsökning kräver fortfarande systematik. AI gör debugging snabbare, men inte automatisk. De flesta av buggarna vi hittade krävde att vi resonerade oss fram till orsaken steg för steg. AI:n kan föreslå hypoteser, men du måste verifiera dem.

Ramverkskunskap är en blind fläck. AI-modeller har generell kunskap om populära ramverk, men edge cases – som hur Meta Box lagrar multiple values – kräver specifik erfarenhet. Det är i de detaljerna som mänsklig expertis fortfarande gör störst skillnad.

Resultatet är bättre än delarna. Det bästa med AI-assisterad utveckling är inte att det går snabbare (även om det gör det). Det är att jag kan bygga saker som jag ensam inte hade haft tid eller ork att bygga. Pluginen hade tagit veckor att bygga manuellt. Med Claude Code tog det en intensiv session. Och resultatet är inte en halvfärdig prototyp – det är en produktionsklar plugin med statistikberäkningar, bot-filtrering och en dashboard med grafer.

Där jag är nu

Pluginen funkar. Den gör exakt det den ska – delar trafik server-side, spårar konverteringar korrekt, visar statistisk konfidensberäkning, och hanterar hela livscykeln från test till arkiverat resultat.

Den är inte perfekt. Det finns funktioner jag vill lägga till längre fram – multisite-stöd, mer avancerad segmentering, kanske heatmap-integration. Men det finns alltid mer man kan bygga. Poängen var aldrig att bygga det ultimata A/B-testverktyget. Poängen var att bygga ett som fungerar, som jag äger, och som löser ett faktiskt problem.

Det är en del av vad det innebär att bygga ett personal enterprise. Ibland bygger du inte bara innehåll och system för andra – ibland bygger du verktygen också. Och med AI-assisterad utveckling som Claude Code är tröskeln för det lägre än den någonsin har varit.

Inte noll. Men lägre.