Spring til indhold
← Blog Blog

Vejen til stjernerne

På et tidligere job omstrukturerede jeg 250.000 linjer kode, og en opgave, der havde taget to dage, begyndte at tage tre minutter. Ingen blev hundrede gange bedre til at programmere — omstruktureringen slettede bare alt det, der ikke var arbejdet. Og så begyndte frontendudviklere at tage backendopgaver. Det her er planen om at gøre det igen her, og den del af den, der bliver værre, før den bliver bedre.

Et trofæ på en green ved solopgang, binære tal svagt i græsset

Spørg en lille virksomhed, hvor arbejdet reelt venter, og svaret er næsten aldrig kapacitet. Det er routing.

En ændring bliver beskrevet på én sætning, alle er enige om, at den bør ske, og så ligger den stille — fordi det repository, den bor i, har præcis én person, der trygt kan åbne det. Ikke fordi vedkommende vogter over det, men fordi han er den eneste, der ved, at projektet vil have en JDK tre majorversioner bag den, du sidst brugte, og at det, der ligner indgangen, holdt op med at være indgangen i 2021.

Vi har over halvfjerds repositories hos GolfNext, bygget over tretten år i en halv snes sprog, og for en pæn del af dem er det antal mennesker ét. Det er emnet for det her indlæg; alt andet ligger nedstrøms for det.

“Vejen til stjernerne” er et ambitiøst navn til en plan, hvis første tre skridt er kedelige. Titlen er ikke ironisk ment: ambitionen er, at det her skal blive den platform, golfbranchen kører på, i hele verden — og kedeligt er det, en motor til det er lavet af.

Tallet, der faktisk gør ondt

Vores tech lead har skrevet her om tilstanden i vores kodelandskab, og jeg brugte mine første to uger på at finde ud af, hvilke repositories der stadig kunne starte på en frisk maskine. Ingen af delene var en klage. Over halvfjerds repositories er, hvad tretten års levering af software, der virker, til rigtige golfklubber ser ud som, og oprydningen er kun mulig, fordi produktet virker. Men der er en fejltilstand i det, de ikke satte navn på direkte, og det er den, der afgør, hvor hurtigt den her virksomhed kan vokse.

Når en kodebase har én vedligeholder, er tre ting sande på én gang, og ingen af dem er den persons skyld.

Arbejdet står i kø bag ham. Alt, der rører det system, venter på den ene kalender, det kan vente på. Fint ved tre ting i kvartalet. Ødelæggende ved tre om ugen.

Viden forlader aldrig hovedet på ham. Ikke fordi han ikke vil dele den, men fordi intet får den ud. Dokumentation gør det ikke: folk skriver om de dele, de selv fandt interessante, og det er per definition ikke de dele, en nyankommen sætter sig fast i.

Ingen kan efterse hans arbejde. Et review foretaget af en, der ikke kan køre koden, er et review af diffens stavning.

Læg det sammen, og du har et system, der teknisk set ejes af virksomheden og i praksis ejes af en person. Vi har flere. Det har enhver virksomhed i den alder.

Der er en afledt omkostning, jeg først forstod ved at leve den: når en nyankommen støder på noget, der er i stykker, kan vedkommende ikke afgøre, om det er repositoryet, der er i stykker, eller ham selv. Jeg mistede dage på en lokal database, der viste sig at være umulig at bygge fra bunden — fire migreringer, i en kæde på 251, som ikke kunne køre mod en frisk import — og i de fleste af de dage gik jeg ud fra, at jeg var problemet. Det er den naturlige antagelse i sine første fjorten dage, og ingen kunne have rettet mig, for alle, der allerede var sat op, havde en database, der lå før bruddet. Til sidst holdt jeg op med at gå ud fra, at det var mig, og rettede det migrering for migrering.

Den told rammer kun nye mennesker, og det er præcis derfor, den er usynlig for dem, der kunne fjerne den. Det er også et af de renere argumenter for denne plan: når hvert repository opfører sig ens, har en nyankommen en kontrolgruppe. Hvis dette ene afviger fra de fem foregående, er det repositoryet, der er galt på den.

Og det skalerer den forkerte vej. Vi er i atten lande med mere end fire hundrede venues, og planen indebærer betydeligt mere af begge dele, så en bus factor på ét bliver ikke stående, når man vokser — den samme person bliver ganske enkelt flaskehals for flere ting.

Hvad “forskelligt” koster, post for post

Det er nemt at sige, at en blandet stack er dyr, og svært at sige, hvor pengene går hen. Her er, hvor de går hen, i den rækkefølge jeg faktisk har mærket det.

Skift. Bevæg dig mellem tre repositories på en formiddag, og du betaler en lille told ved hvert hop: hvilket byggeværktøj, hvilken version, hvilken kommando, hvilke idiomer. Hver for sig ubetydeligt. Det er frekvensen, der tager dig, og frekvensen er en funktion af, hvor mange forskellige faconer du ejer.

Integrationer. Det er den, der betyder mest for vores forretning og bliver diskuteret mindst. Vores position i markedet er, at en klub beholder sit eget hardware og sit eget managementsystem, og at vi arbejder sammen med det, de allerede kører. Det betyder, at hver ny partner er endnu en integration, og prisen på en integration er groft sagt selve arbejdet ganget med, hvor fremmed den kodebase, du lægger den ind i, tilfældigvis er. Skriv den femte ind i samme facon som de første fire, og den koster en brøkdel. Skriv den ind i en kodebase med sine egne konventioner, og du betaler arkæologien om igen.

Onboarding. En ny udvikler skal lære virksomheden én gang og derefter hvert system for sig. Anden del er der, hvor ugerne går. Hvis alle systemer har samme facon, er det næsten gratis at lære system nummer to, og det er forskellen på, om en ny person er brugbar efter fjorten dage eller efter et kvartal.

Review. Standardisering er det, der gør et rigtigt review muligt for mere end én person. Hvis jeg kan køre din kode, kan jeg være uenig i den.

Agenter. Vores tech lead har skrevet, at de repositories, hvor han stoler mest på en kodeagent, uden undtagelse er dem, nogen allerede har ryddet op i. Det passer præcist med min egen erfaring. En agent, der arbejder i en klar, konsistent struktur, producerer noget, du kan bruge. En agent i en ukendt struktur gætter flydende, og du bruger eftermiddagen på at finde ud af, hvilke dele der var gæt. Konsistens er ikke længere bare rart for mennesker. Det er det, der afgør, om halvdelen af vores værktøjskasse virker.

Ens facon slår ens sprog

Her er den del, jeg vil diskutere med hvem som helst, for det er den, folk vender om.

“Lean tech stack” bliver hørt som “færre sprog”, og færre sprog er kun cirka en tredjedel af gevinsten. De tre ting, der afgør, om du kan gå ind i et ukendt repository og være til nytte, er nogenlunde i denne rækkefølge:

  1. Er filstrukturen den samme? Ligger en given slags ting på en forudsigelig sti, under et forudsigeligt navn, i hvert repository?
  2. Er udsagnsordene de samme? Kan du bygge, teste og køre det med de kommandoer, du brugte i det forrige repository?
  3. Er det samme sprog og framework?

Nummer tre er den, der får overskriften, og den mindst vigtige af de tre. Jeg har bevæget mig mellem to Java-services med vidt forskellige pakkestrukturer, og det var sværere end at bevæge sig mellem en Java-service og en TypeScript-service, der var lagt op ens.

Nummer et og to er også langt billigere at rette. Udsagnsordene har vi stort set løst allerede: mise lægger et repositorys værktøjskæde og dets kommandoer i en fil i repoet, så build, test og run betyder det samme alle de steder, det er rullet ud, uanset hvad der ligger nedenunder. Det var vores tech leads arbejde, og det er det, der har givet mest afkast per indsats af alt, nogen har lavet her.

Filstrukturen er den anden, og det er den, jeg ville lægge rigtig arbejde i, for det er den, man bruger, før man har læst en eneste linje. Man finder ikke kode ved at læse den. Man finder den ved at gætte en sti. Når hvert repository er enige om, hvor handlerne bor, hvor databasekoden bor, hvor testene bor, og hvad filerne hedder, er det gæt rigtigt første gang, og en søgning, der virkede i ét repository, bliver ved med at virke i det næste. Når de er uenige, er hvert repository en andens arkivsystem, og man åbner fire forkerte filer, før man rammer den rigtige — hvilket er det meste af, hvad den første time i en ukendt kodebase faktisk består af.

Den er også den billigste af de tre at rette, for den kræver slet ingen omskrivning. Det er en aftale plus tålmodigheden til at følge den på den fil, man alligevel har åben. Vores tech lead gjorde præcis det, da han tog React-proof of concept’et ind i det rigtige repository: en omgang på, hvordan mapper og filer navngives, før der blev bygget noget oven på det. Og det er den del, en agent mærker mest, af samme grund som du gør — den finder kode ved at forudsige, hvor koden burde være.

Det er nyttigt at vide, for det betyder, at det meste af gevinsten ved en lean stack er tilgængelig, før man har konsolideret et eneste sprog. Det betyder også, at sprogkonsolidering skal retfærdiggøres på egne meritter frem for at låne æren for de to andre.

To en halv dag → tre minutter

Indtil videre er det her et argument, og argumenter er billige. Så her er, hvorfor jeg tror på det.

På et tidligere job brugte jeg en lang periode på at omstrukturere omkring 250.000 linjer kode. Ikke en rewrite. Samme sprog, stort set samme logik, omorganiseret: hvor tingene bor, hvad der må afhænge af hvad, og hvilken facon en ændring skal have for at passe ind.

Før det tog en simpel opgave — den helt almindelige, tilbagevendende slags, der udgør det meste af en uge — to til tre dage. Bagefter blev jeg nysgerrig nok til at tage tid på en af dem, og den landede på tre minutter.

Jeg skal være præcis om det tal, for det er den slags tal, der bliver gentaget, indtil det bliver til en løgn. Tre minutter var én opgave, målt én gang. Andre tog fem minutter eller ti. Det, der ændrede sig, var ikke, at hver opgave blev en tre-minutters opgave. Det var, at enheden ændrede sig: arbejde talt i dage blev nu talt i minutter, og intet af det gik tilbage.

Selv den forsigtige ende af det er et forhold, ingen bør tro om sig selv, så her er, hvad det faktisk betyder. Ingen blev hundrede gange bedre til at programmere. Selve arbejdet havde altid været lille — et par minutters tastearbejde, begravet under alt det, der ikke var arbejdet: at finde ud af hvor ændringen hørte hjemme, at læse nok af den omkringliggende kode til at være sikker, at regne ud hvad der ellers rørte ved den, og så den del, der aldrig havner på en timeseddel, nemlig at være bange for at ødelægge noget, man ikke forstår, og bevæge sig langsomt med vilje på grund af det.

Omstruktureringen gjorde ikke arbejdet hurtigere. Den slettede alt det, der ikke var arbejdet.

Det fortæller også, hvilke opgaver der falder sammen, og hvilke der ikke gør. Reelt nyt arbejde, hvor det svære er at beslutte, hvad tingen skal gøre, tog omtrent lige så lang tid bagefter som før; struktur hjælper dig ikke med at tænke. Det, der falder sammen, er den tilbagevendende facon — den femte integration, endnu et felt på en formular, det endpoint der ligner de elleve ved siden af. I et landskab som vores er det det meste af køen.

Resultatet jeg faktisk ville pege på

Og så den del, jeg ikke havde forudset. Frontendudviklere begyndte at tage backendopgaver.

Ingen bad dem om det. Når hver service er lagt op på samme måde, viser barrieren sig at have været strukturel snarere end et spørgsmål om viden. Det er den mest brugbare dokumentation i dette indlæg, fordi den ikke er selvrapporteret: et produktivitetstal er noget, man siger om sig selv, og at krydse en grænse, man har undgået i årevis, er noget, man gør. Det flyttede det tal, indlægget åbnede med, uden at ansætte nogen.

Én ting mere, for denne blog skriver konstant om kodeagenter, og det ville være nemt at læse alt det som en AI-historie. Der var ingen AI i den. Slet ingen. Hvert eneste minut af gevinsten kom fra, hvor koden lå, og hvad der måtte afhænge af hvad — hvilket betyder, at gevinsten ikke er betinget af, at agenter bliver ved med at blive bedre, eller at vi bruger dem godt. Struktur betalte sig, før noget af det her fandtes. Det, agenter gør, er at gange den op, mest af alt i de repositories, der allerede har den. Hvis de gik i stå i morgen, ville argumentet i dette indlæg være uændret.

Det vil jeg gerne gentage her. Anden virksomhed, andet kodelandskab, samme mekanisme. Den omstrukturering er også dér, indlæggets titel kommer fra, og det er grunden til, at jeg kan skrive en plan så sikkert om arbejde, der endnu ikke er udført: jeg forudsiger ikke en effekt, jeg forventer en, jeg allerede har stået midt i.

Hvad motoren er til for

Et indlæg om at gøre udviklere mere produktive kan læses forkert, så lad mig lukke den læsning selv. Det her handler ikke om at få brug for færre udviklere. Ingen her gør det, for at det samme arbejde kan udføres af færre mennesker, og hvis det var planen, ville jeg ikke skrive et blogindlæg om den. Udviklerne her er dygtige. De har bygget et produkt, der virker i atten lande, under vilkår jeg aldrig selv har skullet leve med, og at miste nogen af dem ville sætte det her tilbage frem for frem.

Grunden er vækst, og jeg vil hellere sige størrelsen på den end pakke den ind. Vi prøver ikke at blive lidt større. Målet er, at det her bliver den platform, golfbranchen kører på — hver klub, hver range, hvert land der har begge dele — og grunden til, at det kan siges højt frem for at være pinligt, er, at efterspørgselssiden allerede melder sig. De klubber, der ser produktet, er efter min erfaring indtil videre oprigtigt imponerede. Den reaktion er aktivet. Intet i dette indlæg skaber den, og intet i dette indlæg kan erstatte den.

Det, vi ikke har, er motoren til at betjene den i den størrelse. Produktet er godt; maskineriet, der bygger det, integrerer det og supporterer det, er tretten års akkumulerede beslutninger, og det er den del, der knækker først, hvis efterspørgslen ankommer hurtigere, end teamet kan optage den. Vi bygger ikke produktet — det findes, og folk, der ikke har haft noget med at bygge det at gøre, er imponerede over det. Vi bygger det, der ligger under, og som afgør, hvor mange klubber, hvor mange lande og hvor mange integrationer et lille team kan bære på én gang. Imponerede kunder er det, der har bragt os til mere end fire hundrede venues i atten lande. Hvad der afgør, om tallet derefter får et nul mere, er, om den ellevte integration koster det samme som den første.

Derfor er den del af det her, jeg mest går op i at få rigtigt, heller ikke sproget. Det er, at de mennesker, der skal leve i resultatet, gerne vil.

React foran, Go bagved

Det, man leder efter, når man endelig vælger et sprog, er altså ikke det mest udtryksfulde. Det er det, der giver en de to andre gratis.

Målet er to sprog.

Alle brugerflader er React. Web er React og TypeScript, mobil er React Native, og det gælder også fladerne, der sidder på hardwaren — den kioskskærm, en spiller står foran klokken halv seks om morgenen, er en skærm som alle andre. Det var allerede halvt sandt, før nogen besluttede det, fordi vores mobilapplikationer er React Native, og det blev sandt i praksis, da en af vores frontends gik fra Vue til React og kom i produktion. Resten følger efter.

Alt bag de flader er Go. Ikke “de nye services” — alt. Webbackendene, det offentlige API, integrationerne, og desktop-kasseapplikationen, som bliver en Go-kerne med en React-flade i stedet for den JavaFX-applikation, den er i dag. Om en ting kører i et datacenter eller på en kasse i en proshop holder op med at være en oplysning om, hvilket sprog den er skrevet i.

Jeg vil ikke argumentere for sprogene her. Go over for alternativerne er et indlæg for sig, og den ærlige opsummering er, at det konkrete valg betyder betydeligt mindre end, at der er ét — hvilket er det, hele dette stykke har argumenteret for. Én ting skal dog siges rent ud inden næste afsnit: Go er ikke det, vi har mest af. Java, PHP, Node og Kotlin har alle mere kørende kode bag sig i dag, og det er en reel omkostning snarere end en detalje.

Det, beslutningen køber, er to ting, og de er værd at holde adskilt, fordi de bliver rørt sammen i ethvert indlæg som det her. Det første er udvikleroplevelse, som er det meste af, hvad dette indlæg handler om, og som koger ned til tallet i toppen: hvor mange mennesker kan åbne et givet repository og være til nytte i det.

Det andet er performance, og der vil jeg hellere være præcis end sloganagtig, for “simpel stack, altså hurtig” er ikke et argument. Hvad det derimod betyder, er to bestemte ting. Den ene er fodaftryk: hvad en proces koster at starte og at holde kørende, hvilket er et rigtigt tal på kioskhardware og en rigtig linje på en cloud-regning. Den anden er, at performancearbejde er betinget af forståelse — man kan ikke rette det langsomme sted i et system, ingen kan rumme i hovedet, og de fleste systemer er langsomme et sted, deres vedligeholdere holdt op med at kigge for år siden.

Én delbeslutning er reelt ikke afgjort. En Go-kerne med en React-flade på desktoppen har brug for en skal til at holde den flade, og den har vi ikke valgt. Faconen er besluttet; mekanismen er ikke, og det haster ikke, for desktopapplikationen er langtfra forrest i køen.

Slaget vi ikke slår

På de fleste baner er der et hul, hvor den ambitiøse linje går lige over vandet. Den store rewrite er det slag: frys det gamle, byg det nye ved siden af, skift over. Hurtigst, hvis det lykkes — og kirkegården er fuld af virksomheder, der brugte to år på ikke at levere noget, mens deres konkurrenter leverede features.

Vi lægger op. Fire regler.

Nyt arbejde bygges i målfaconen. Den eneste gratis regel, og den der udretter mest over et år: hver ny service er et repository, der aldrig skal konverteres.

Skriv kun om, hvad du alligevel skulle åbne. Konvertering er billigst, når nogen alligevel betaler for at forstå systemet. Software, der virker, og som ingen har brug for at ændre, er ikke et problem; det er et aktiv med et grimt logo.

Sømme før rewrites. To adminportaler, to backends, to logins. Ikke en omskrivning til én: en auth-bro først, så én frontend oven på begge backends, og først derefter konsolidering af backenden. Hvert skridt kan leveres for sig — og opgives for sig.

Struktur og udsagnsord først, overalt; sprog sidst, selektivt. De billige to tredjedele er tilgængelige i hvert eneste repository i dag, inklusive dem der aldrig bliver konverteret.

Og så det, jeg hellere vil skrive ned end blive fanget i ikke at have tænkt over: det bliver mere broget, før det bliver mindre. Go er ikke et sprog, vi har meget af, så et par år frem indeholder kodelandskabet Java, PHP, Node, Kotlin og Go. Konvergensen ankommer sidst. Vil man diskutere den her plan, skal man diskutere dér — det er det svageste punkt i den, og det eneste forsvar er, at de fire regler findes for at gøre det mellemrum til at holde ud.

Én advarsel frem for en regel: agenter har gjort rewrites billige at producere og ikke en krone billigere at gennemgå. En rewrite, der ser færdig ud, er det mest smigrende, de laver, for dens huller er udeladelser, og en udeladelse dukker aldrig op i en demo.

Hvad det faktisk koster

Planen har en regning, og jeg vil hellere gøre den op selv end lade en anden gøre det.

Monokultur er en reel risiko, ikke en retorisk. To sprog overalt betyder to sæt blinde vinkler, to afhængighedsøkosystemer hvis dårlige uger nu er alles dårlige uger, og et team der gradvist mister refleksen til at spørge, om det sædvanlige værktøj nu også er det rigtige.

Den stærkeste udgave af den indvending er det rigtige værktøj til opgaven, og generelt er jeg enig i den. Men det er et argument om problemområder, og vores er ét problemområde. Tag etiketterne af vores systemer, og de laver alle sammen genkendeligt det samme arbejde: tag imod en transaktion, tal med en enhed eller en partners API, og læg en post et sted, man kan stole på bagefter. Der er ingen machine learning-arbejdsbyrde her, der trækker mod Python, og ingen signalbehandling, der trækker mod C. Variationen i vores kodelandskab svarer ikke til nogen som helst variation i de problemer, det løser, og det er tegnet på, at den er tilfældig snarere end tilsigtet. Ingen valgte en halv snes sprog som en afdækningsstrategi.

Så risikoen er virkelig og værd at sige højt, og forholdsreglen er, at “standard” skal betyde en stærk default med en dokumenteret måde at argumentere imod på, ikke en regel. Den dag et reelt anderledes problem dukker op — og i en virksomhed, der laver mere med AI for hvert kvartal, kan det ske — skal det argument kunne vindes, ellers er defaulten i stilhed blevet til en regel alligevel.

Konsolidering kan være en måde at undgå produktarbejde på. Strukturelt arbejde er tilfredsstillende og letlæseligt, og det skaber ingen kundeværdi den dag, det lander. Disciplinen er, at hvert skridt skal retfærdiggøres af noget, det låser op. Sammenlægningen af portalerne er retfærdiggjort, fordi klubpersonale logger ind to gange. React-standardiseringen er retfærdiggjort, fordi den er forudsætningen for alt det, der kommer efter. Hvis et skridt ikke kan sige, hvad det låser op, er det ikke klar.

Det erstatter arbejde, folk er stolte af. Det var det, ledelsen bragte op, da vi fremlagde planen, før omkostning og før kunderisiko, og de havde ret. Ingen har lyst til at høre, at det, de byggede godt under de vilkår de faktisk havde, nu er den gamle måde. Vi holdt en brainstorm med hele teamet og holdt bevidst vores eget forslag tilbage, indtil alle andre havde talt. Ingen var uenige. Delene om at slå portalerne sammen var folk oprigtigt begejstrede for; selve standardiseringen var folk okay med, hvilket er en rimelig ting at føle, når det er ens eget valg, der bliver erstattet, og det vil jeg hellere gengive korrekt end opgradere.

Og det kommer til at tage længere tid, end dette indlæg får det til at lyde. Golfklubber har travlt om sommeren, så vi har travlt om sommeren. Platformen bliver stille hen over vinteren, og det er der, det strukturelle arbejde har plads. Det her er en plan målt i sæsoner.

Hvordan vi ved, om det virkede

Ikke ud fra et diagram. Ud fra fire ting, man rent faktisk kan tjekke.

Hvor mange repositories har præcis én person, der trygt kan sende en ændring afsted til dem. I dag er det tal ubehageligt. Det er hovedmålet for hele denne plan, og det eneste jeg vil forsvare som handlende om virksomheden frem for om ingeniørsmag.

Hvor lang tid fra klon til kørende kode, på en maskine der har været igennem det én gang. Det var to til seks timer, da vores tech lead målte det i august, afhængigt af hvilket repository man valgte. I de repositories, mise og fnox har nået, er det én kommando.

Ambitionen bag det tal er mere kontant end tallet selv, så lad mig sige det, som vi faktisk siger det: at få en kodebase til at køre lokalt skal være så tæt på idiotsikkert, som vi kan gøre det. Ikke fordi nogen her er en idiot — tværtimod. Hvis det kræver kløgt at starte et repository, bliver den kløgt brugt på det forkerte, og den bliver brugt igen af hver eneste person, der kommer efter dig. Klon det, kør én kommando, få et fungerende system med en database, testdata og en konto at logge ind med. Alt, der står mellem de to punkter, er en defekt, uanset om det står i et tracker-system eller ej.

Hvad den anden integration koster i forhold til den første. Hvis vi laver det strukturelle arbejde, og den femte partnerintegration stadig koster det samme som den første, gjorde arbejdet ikke det, vi sagde, det ville.

Og hvad en ny udvikler kan nå ved udgangen af sin anden uge. Ikke “har læst kodebasen”. Har sendt noget i produktion hos en klub.

Den sidste er hele planen komprimeret til én sætning, og den er grunden til det hele. Alt i dette indlæg — React på skærmene, Go bagved dem, mappestrukturerne, auth-broen, den ene kommando der betyder det samme i hvert repository — findes, for at svaret på “kan du tage den her?” holder op med at afhænge af, hvilket repository vi taler om.

Og grunden til at gå op i et så lille spørgsmål er, at svaret på det sætter loftet for alt ovenover. En klub et sted beslutter, at vi er det bedste, de har set i år, og fra det øjeblik er det eneste spørgsmål, der betyder noget, om vi kan bygge det, de har brug for, mens de stadig er begejstrede for det. At gøre det fire hundrede gange er et produkt. At gøre det fire tusind gange er en motor.

Så her er den version, jeg faktisk tror på, skrevet ned et sted, hvor I kan holde mig fast på den.

Om to år tager en udvikler, der startede i sidste måned, en integration med et managementsystem i et land, vi endnu ikke sælger i. Han har aldrig åbnet det repository. Han kloner det, mise run serve henter værktøjskæden og starter tingen op, Go-handlerne ligger, hvor Go-handlere altid ligger, React-skærmene er lagt op som hver eneste anden React-skærm vi ejer, og testene kører igennem i første forsøg. Om torsdagen er det live hos en klub, der aldrig havde hørt om os i foråret. Ingen i huset synes, det er bemærkelsesværdigt.

Det er hele ambitionen. Ikke et heroisk kvartal — en torsdag.

Gang den torsdag med hver udvikler vi har og hvert land, der har golf i sig, og du får en virksomhed, der leverer lige så hurtigt, som den kan tænke, i en branche der aldrig har haft software, som bevægede sig i den fart. Produktet, folk bliver imponerede over, findes allerede; vi skulle ikke opfinde det, og jeg kan ikke tage æren for det. Det, vi bygger nu, er det eneste, der står mellem det produkt og hver eneste klub på kloden, som ville elske at have det — og vi vil hellere bygge det, før nogen andre regner ud, at det er dét, løbet går på.

Så: stjernerne er et stort ord, og det er det rigtige. En banerekord er ikke ét mirakuløst slag — det er atten helt almindelige huller spillet af en, der er holdt op med at tabe slag i roughen. Vi lægger ikke op til helteslaget over vandet. Vi fjerner hvert eneste sted, bolden kan blive væk, og så spiller vi vores livs runde.

Hvilket i praksis er lige så uglamourøst, som det lyder, og dér vil jeg hellere slutte end på metaforen. React på hver skærm og Go bag hver eneste af dem. Den samme mappestruktur i hvert repository, så man kan gætte en sti, før man åbner træet. Én kommando, der bygger hvad som helst af det. Hemmeligheder i et store i stedet for i en chatbesked. Over halvfjerds repositories, der konvergerer mod én facon — og en virksomhed, der derefter kan tage det hundrede ind, uden at nogen skal være modig.