← Tilbake til notatene

Et redaksjonssystem som skalerer bedre med agentic coding

Agentic coding

Et stabilt tårn av brune treklosser med en olivengrønn bærende kloss og en løs kloss ved siden av.
KI-generierte Illustration mit OpenAI Imagegen

Da jeg bygde undoom.news helt på nytt, visste jeg hvilken retning jeg ville gå i, men hadde ennå ikke et ferdig bilde av redaksjonssystemet. Jeg ville bruke mellomversjonene, se hvordan de fungerte og gjennom det forstå hva neste steg burde være. Agentic coding passet til denne arbeidsmåten: Endringer kunne gjennomføres med en gang og prøves ut i sammenheng. Grensesnittet, artikkelgjennomgangen og sammensetningen av dagsutgaver tok gradvis form på denne måten. Deretter begynte jeg å konsolidere koden som hadde vokst frem.

Et grunnlag for videre utvikling

To forutsetninger hadde jeg fastlagt tidlig: et teknisk grunnlag med PHP og Python og en modulær struktur der de enkelte oppgavene senere kunne endres og optimaliseres. PHP håndterer i dag blant annet webtilgangen, mens Python står for den redaksjonelle behandlingen; research, kontroll og publisering har egne ansvarsområder. Denne oppdelingen skulle gi rom for videre utvikling. Detaljene i den redaksjonelle arbeidsflyten kunne jeg så arbeide frem i den kjørende applikasjonen.

Samtidig laget jeg et eget lite nettsted for utviklingen under «Devnotes». For første gang samlet jeg en slik plan i sammenhengende HTML-dokumentasjon, i stedet for å bruke Markdown-filer slik jeg vanligvis gjør. Arkitektur, ideer, åpne beslutninger og utviklingstrinn var knyttet sammen på en oversiktlig måte. Også denne dokumentasjonen utviklet seg iterativt. Den hjalp meg å holde oversikt over hvor arbeidet sto og gå frem steg for steg mens produktet endret seg.

Bruken avdekker flaskehalsene

Da jeg tok systemet i bruk, viste det seg etter hvert et problem: Artiklene åpnet seg tregt, det tok tid å legge dem inn i kalenderen, og selv godkjenning av en artikkel fikk grensesnittet til å vente. Allerede med 229 artikler var forsinkelsen plagsom. Jeg beskrev observasjonene for kodeagenten og ba om en undersøkelse av årsakene. Da det ble tydelig hvor mye arbeid som lå bak en enkel handling, stilte jeg spørsmål ved hvordan datatilgangen var implementert og ba om en grunnleggende forbedring.

Én henting av den redaksjonelle tilstanden utløste opprinnelig 2 931 databasespørringer. I tillegg lastet grensesnittet store datamengder fra tidligere utviklingsverktøy. Etter enkelthandlinger ble store samlinger overført på nytt, selv om bare ett kort var endret. Også bildetellingen gikk gjennom samtlige bilder på nytt for hver artikkel. Den modulære grunnstrukturen hadde ikke forhindret denne ineffektiviteten, men den ga oss adskilte områder der vi nå kunne forbedre datatilgangen målrettet.

Vi delte opp leseoperasjonene etter formålet. Planleggingen får opplysningene som trengs til kort og kalender, mens en åpnet artikkel får detaljene sine. Tidligere versjoner lastes først når de foldes ut. Felles data hentes samlet, og bildeforslag grupperes én gang. En kontinuerlig kjørende backendtjeneste sparer oss for å starte en prosess ved hver forespørsel. Utkastlisten laster 48 kort om gangen, og påfølgende svar overfører bare endringene. Antallet spørringer for oversikten sank underveis til 15. Den optimaliserte backendforespørselen tok rundt 0,2 sekunder i målingene; et påfølgende svar uten endringer var på bare 237 byte.

Vurdere indekser ut fra spørringene

Deretter ba jeg om en egen gjennomgang av databaseindeksene. Agenten undersøkte faktiske spørringsplaner og fant blant annet manglende statistikk for uttrykksindekser som allerede var opprettet. Vi oppdaterte statistikken og la til en målrettet indeks for søk i bildemetadata. Én detaljspørring var fortsatt ineffektiv: Flere søkeveier koblet sammen med OR ble erstattet av separate, indeksstøttede spørringer med sammenslåtte resultater. I en stikkprøve sank kjøretiden fra rundt fem til ett millisekund. Vi utsatte flere indekser for kalender og publiseringer fordi disse spørringene allerede tok kort tid med den eksisterende datamengden. Gjennomgangen ga oss dermed også grunner til å la enkelte endringer vente.

Caching med begrenset driftsarbeid

Først etter dette så vi på en minnecache som bevares mellom forespørsler, for den offentlige artikkelfeeden. Redis var en mulig løsning, men ville ha innført en ekstra tjeneste med egen konfigurasjon og mer driftsarbeid. For den eksisterende lesingen i PHP valgte vi APCu på hver server. OPcache lagrer den kompilerte PHP-koden; APCu lagrer her de ferdig klargjorte svarene for hvert språk. Redaksjonelle skriveoperasjoner kontrollerer fortsatt den gjeldende tilstanden i databasen.

For denne cachen måtte vi bestemme når et lagret resultat ikke lenger er gyldig. Ved hver forespørsel kontrolleres publiseringer, ferdige oversettelser, bildedata og bildesperrer for å se om det lagrede svaret fortsatt stemmer med dataene. En tilbaketrekking eller bildesperre blir dermed tatt hensyn til ved neste forespørsel. Vi beholder bevisst små databasespørringer; det vi sparer, er gjentatt lasting av omfattende innhold og klargjøring av svaret. I 30 lokale målerunder sank denne rene klargjøringen av feeden i gjennomsnitt fra 5,95 til 0,45 millisekunder. Det beskriver ett behandlingstrinn, ikke hele lastetiden i nettleseren.

Kontrollere endringene

Konsolideringen førte også med seg en regresjon: Etter en ombygging meldte kalenderplanleggingen en feil selv om endringen allerede var lagret. Svaret feilet ved konvertering av en dato. Feilrapporten min førte til en retting og en test av hele svarforløpet. Da detaljspørringen ble optimalisert, sammenlignet agenten dessuten resultatene for alle 229 artikler, både med og uten versjonshistorikk. Den nye spørringen måtte levere de samme dataene i samme rekkefølge. Etter indeksarbeidet besto 163 backendtester; for cachen kontrollerte vi i tillegg at blant annet tilbaketrekkinger, endringer i oversettelser og bildesperrer pålitelig gjør lagrede svar ugyldige.

Min oppgave var å bruke produktet, peke på friksjon og følge opp tekniske beslutninger. Agenten undersøkte koden, målte på serveren, implementerte endringer og kontrollerte virkningen. Arbeidet gikk fra en konkret observasjon via en etterprøvbar årsak til en avgrenset endring. Nettopp rekkefølgen var nyttig for meg: De tidlige iterasjonene gjorde det klart hvilket system jeg faktisk trengte; den påfølgende konsolideringen tilpasset datatilgangen til denne bruken. Målingene med våre data dokumenterer ennå ikke at systemet tåler svært store datamengder. De viser hvilke unødvendige gjentakelser vi har fjernet, og hvor ekstra infrastruktur hittil ikke har lovet tilstrekkelig nytte.