Forrige uke trengte jeg A/B-testing for landing pages. Denne uken har jeg en fungerende plugin.
Det høres smidig ut når man sier det sånn. Det var det ikke. Men det var heller ikke det mange måneders prosjektet det ville vært for bare et par år siden. Og det er det som er interessant – ikke bare at pluginen finnes, men hvordan den ble til.
Dette er historien om hvorfor jeg bygde den, hvordan prosessen med Claude Code så ut, og hvilke problemer vi støtte på underveis. Spoiler: de fleste problemene var ikke de jeg forventet.
Hvorfor jeg trengte dette
Jeg bygger verktøy til WordPress-økosystemet mitt – plugins som løser spesifikke problemer for de kundene og prosjektene jeg jobber med. Cookie Consent Manager, AI Internal Linking, bloggarkiv-blocks. Alle følger samme filosofi: sofistikerte under overflaten, enkle å bruke, ingen eksterne avhengigheter.
A/B-testing var neste logiske steg. Jeg bygger landing pages. Jeg driver trafikk til dem. Jeg vil vite hvilken versjon som fungerer best. Og jeg ville ikke betale en månedlig SaaS-avgift for å få svaret.
Alternativene som fantes var enten for dyre, for komplekse, eller JavaScript-baserte – noe som betyr at besøkende ser originalsiden flimre forbi før den «riktige» versjonen lastes. Det er ikke akseptabelt for sider som skal konvertere.
Så jeg bestemte meg for å bygge det selv. Server-side traffic splitting, cookie-basert besøkssporing, statistisk konfidensberegning, dashboard med grafer. Alt innenfor WordPress, null eksterne tjenester.
Hvordan det gikk med Claude Code
Jeg brukte Claude Code med Opus 4 som par-programmeringspartner. Ikke som en kodegenerator jeg blindt kopierer fra, men som en samarbeidspartner jeg resonerer med.
Arbeidsflyten ser omtrent slik ut: jeg beskriver hva jeg vil oppnå – ikke bare teknisk, men funksjonelt. Hva skal skje når en besøkende lander på entry page? Hvordan skal trafikkfordelingen fungere? Hva teller som en konvertering? Claude foreslår en arkitektur, jeg gjennomgår den, vi itererer. Så skrives koden, jeg tester, og vi feilsøker sammen.
Det som gjør denne arbeidsflyten produktiv er ikke at AI-en skriver kode raskere enn jeg kan. Det er at jeg kan fokusere på hva som skal bygges og hvorfor, mens Claude håndterer mye av hvordan. Men – og dette er viktig – jeg må fortsatt forstå «hvordan» godt nok til å vite om løsningen er god. AI-en kan skrive en elegant funksjon som løser helt feil problem hvis jeg ikke vet hva jeg leter etter.
Toolstacken endte opp med Meta Box for admin-UI og custom fields, Chart.js for grafer på dashboardet, og ren PHP/JS for alt annet. Ingen rammeverk, inget byggsystem, ingen pakkeforvaltere. WordPress-nativt hele veien.
Det som overrasket meg mest var hvor mye av arbeidet som handlet om å beskrive hva jeg ville, ikke om å skrive kode. Jeg brukte mer tid på å forklare flyten – «når en besøkende med en test-cookie besøker en goal page skal det telles som en konvertering, men bare én gang per besøkende per test» – enn på å finjustere implementasjonen. Det var et tankeskifte. I stedet for å tenke i funksjoner og klasser tenkte jeg i atferd og regler.
Problemene vi støtte på
Det er her det blir interessant. Ikke fordi problemene var uvanlige – de fleste var ganske klassiske – men fordi de illustrerer hvordan AI-assistert utvikling fungerer i praksis. Det er ikke en rett linje fra idé til ferdig produkt. Det er en serie av «hvorfor fungerer ikke dette?» fulgt av systematisk feilsøking.
Objektene som forsvant
Det første problemet var at ingenting skjedde. Pluginen lastet, testene var konfigurert, men besøkende ble aldri redirectet. Ingen besøk ble registrert.
Årsaken viste seg å være PHPs garbage collection. RedirectHandler- og ConversionTracker-objektene ble opprettet inne i en metode, men ble aldri lagret som egenskaper på Plugin-klassen. PHPs søppelsamler ødela dem før WordPress rakk å kalle deres callbacks. Stille, uten feilmelding, uten advarsel.
Det tok en stund å finne. Koden så korrekt ut – objektene ble opprettet, callbacks ble registrert. Men når template_redirect-hooken kjørte, fantes ikke objektene lenger. Løsningen var enkel: lagre objektene som klasseegenskaper i stedet for lokale variabler. Men å finne problemet krevde at vi systematisk gikk gjennom livssyklusen for hvert objekt.
Alle besøkende fikk samme hash
Dette var den mest frustrerende buggen. Dashboardet viste nøyaktig 1 besøk per variant, uansett hvor mange ganger jeg testet i inkognitomodus.
Årsaken: hash-funksjonen som opprettet unike besøksidentifikatorer brukte cookie-verdien – altså variant-ID-en (et sidetall som «821») – som input. Hver besøkende som ble tildelt samme variant fikk identisk hash. Kombinert med databasens UNIQUE-indeks og INSERT IGNORE betydde det at hvert besøk etter det første stille ble ignorert.
Løsningen var å innføre en separat cookie med en UUID som genereres én gang per nettleser. Hashen beregnes nå fra den unike ID-en i stedet for variant-ID-en. Enkel fiks, men å identifisere årsaken tok tid – spesielt fordi alt så korrekt ut i koden. Det var logikken som var feil, ikke syntaksen.
Meta Box og multiple values
Konverteringer ble ikke sporet selv om besøkende nådde takk-siden. Grunnen: Meta Box lagrer felt med multiple => true som separate databaserader, ikke som en serialisert array. WordPress’ get_post_meta() med true som tredje parameter returnerer bare den første verdien. Hvis du hadde tre goal pages ble bare den første registrert.
Dette er et typisk eksempel på rammeverks-spesifikk kunnskap som AI-en ikke nødvendigvis har. Claude foreslo først den «vanlige» WordPress-løsningen, som fungerer med standard meta fields men ikke med Meta Box sin implementasjon. Løsningen var å bruke Meta Box sin egen funksjon rwmb_meta() i stedet.
Chart.js som ikke sluttet å vokse
Mer visuelt underholdende: stolpediagrammet på dashboardet vokste uendelig. Hver rendering-syklus ble det litt høyere, i en loop som aldri stoppet.
Årsaken var Chart.js’ responsive mode i kombinasjon med CSS Grid. Chart.js beregner canvas-størrelsen basert på sin container. Uten en definert høyde trigger hver resize en ny beregning, som endrer containerhøyden, som trigger enda en resize. Uendelig loop.
Fiks: en wrapper-div med fast høyde. 220 piksler, position: relative. Det var alt. Men det tok en stund å forstå hvorfor det skjedde, ikke bare at det skjedde.
Nonce-problemet som ikke burde vært et problem
Det siste problemet var mer av en designutfordring. Dashboardet har en «End Test»-knapp som åpner en modal der du velger vinner. Modalen trenger å opprette en URL med en gyldig WordPress nonce – en sikkerhetstoken som verifiserer at kallet er legitimt.
Problemet: test-ID-en er dynamisk. Den bestemmes av hvilken knapp brukeren klikker på. Men WordPress genererer nonces server-side, og hvis du oppretter en nonce med en plassholder-ID (som __TEST_ID__) genereres en token for den bokstavelige strengen – ikke for den faktiske test-ID-en.
Løsningen var å forgenerere nonces for alle aktive tester i PHP og sende dem som en lookup-tabell til JavaScript via wp_localize_script(). Ikke rakettvitenskap, men det krevde at vi tenkte gjennom flyten ordentlig i stedet for å bare «løse det raskt». Det var et godt eksempel på hvordan Claude foreslo en elegant løsning etter at jeg beskrev problemet tydelig – og hvordan resultatet ble bedre enn min opprinnelige idé om å håndtere det med en AJAX-request.
Hva jeg lærte om AI-assistert utvikling
Fire ting skiller seg ut etter dette prosjektet.
Domenekunnskap er ikke valgfritt. Claude kan skrive utmerket kode, men den vet ikke hva du prøver å oppnå med ditt spesifikke prosjekt. Du trenger å vite nok om problemdomenet til å formulere riktige spørsmål, vurdere foreslåtte løsninger, og vite når noe ikke stemmer – selv om koden ser korrekt ut.
Feilsøking krever fortsatt systematikk. AI gjør debugging raskere, men ikke automatisk. De fleste av buggene vi fant krevde at vi resonerte oss frem til årsaken steg for steg. AI-en kan foreslå hypoteser, men du må verifisere dem.
Rammeverkskunnskap er en blind flekk. AI-modeller har generell kunnskap om populære rammeverk, men edge cases – som hvordan Meta Box lagrer multiple values – krever spesifikk erfaring. Det er i de detaljene at menneskelig ekspertise fortsatt gjør størst forskjell.
Resultatet er bedre enn delene. Det beste med AI-assistert utvikling er ikke at det går raskere (selv om det gjør det). Det er at jeg kan bygge ting som jeg alene ikke hadde hatt tid eller ork til å bygge. Pluginen hadde tatt uker å bygge manuelt. Med Claude Code tok det én intensiv økt. Og resultatet er ikke en halvferdig prototype – det er en produksjonsklar plugin med statistikkberegninger, bot-filtrering og et dashboard med grafer.
Der jeg er nå
Pluginen fungerer. Den gjør nøyaktig det den skal – deler trafikk server-side, sporer konverteringer korrekt, viser statistisk konfidensberegning, og håndterer hele livssyklusen fra test til arkivert resultat.
Den er ikke perfekt. Det finnes funksjoner jeg vil legge til etter hvert – multisite-støtte, mer avansert segmentering, kanskje heatmap-integrasjon. Men det finnes alltid mer man kan bygge. Poenget var aldri å bygge det ultimate A/B-testverktøyet. Poenget var å bygge et som fungerer, som jeg eier, og som løser et faktisk problem.
Det er en del av hva det innebærer å bygge et personal enterprise. Noen ganger bygger du ikke bare innhold og systemer for andre – noen ganger bygger du verktøyene også. Og med AI-assistert utvikling som Claude Code er terskelen for det lavere enn den noensinne har vært.
Ikke null. Men lavere.






