# CI

## Hva det er

CI er kort for _continuous integration_, på norsk kontinuerlig integrasjon: en arbeidsmåte der
alle på et team lander endringene sine på én felles kodelinje mange ganger i uka, og en maskin
bygger og tester resultatet hver gang, så en feil kommer fram mens den som gjorde den, ennå husker
det. Det fulle navnet, kontinuerlig integrasjon, brukes like ofte som forkortelsen.

Ordet brukes om to ulike ting, og det sparer mye forvirring å holde dem fra hverandre. Den første
betydningen er praksisen, og den handler om mennesker. Martin Fowlers artikkel om emnet, kilden de
fleste team ender med å sitere, beskriver den som et sett vaner: hold én hovedlinje (_mainline_),
automatiser byggingen, la byggingen teste seg selv, send endringer til hovedlinjen minst daglig,
bygg hver push, reparer en ødelagt bygging med en gang, hold byggingen rask, skjul uferdig arbeid
i stedet for å holde det på en gren, og la alle se hvordan byggingen står. Artikkelen fører navnet
tilbake til Extreme Programming og nevner at Grady Booch brukte uttrykket før det.

Den andre betydningen er maskinen. Når noen sier «CI-en er rød», mener de tjenesten som kjørte
byggingen og meldte en feil. GitHub Actions, GitLab CI/CD og lignende tjenester har alle samme
form. En fil i repoet beskriver arbeidet: `.github/workflows/*.yml` på GitHub, `.gitlab-ci.yml` i
roten på GitLab. En hendelse starter det, f.eks. en push, en pull request eller merge request, en
tidsplan eller en person som trykker på en knapp. En runner, altså en maskin satt av til formålet,
sjekker ut commiten, kjører kommandoene i filen og melder bestått eller feilet tilbake på commiten.

En server alene er ikke kontinuerlig integrasjon. Et team som holder arbeid på grener i en måned
og lar en server bygge hver gren, har automatisert byggingen, og det er nyttig, men integreringen
skjer fortsatt på én gang til slutt. Praksisen er den hyppige sammenslåingen. Maskinen er det som
gjør hyppig sammenslåing trygg nok til at teamet fortsetter med den.

Kontinuerlig levering (_continuous delivery_) og kontinuerlig produksjonssetting
(_continuous deployment_) er trinnene som kommer etter. Levering holder hver grønn bygging klar til
utgivelse, og produksjonssetting gir ut hver av dem automatisk. Begge forutsetter at CI er på plass
først, og derfor skrives de tre så ofte sammen som CI/CD.

## Hvorfor det er viktig

Kostnaden ved å slå sammen to arbeidslinjer vokser raskere enn tiden de er adskilt. To personer som
begge endret den samme modulen i går, kan løse konflikten på noen minutter, fordi begge husker hva
de mente. To personer som gikk hver sin vei for seks uker siden, må rekonstruere hensikten ut fra
diffene. Å slå sammen små biter ofte holder hver konflikt liten, og det er det meste av gevinsten
før noen maskin er involvert.

Byggingen ved hver push legger til en ting til: en ren maskin som er enig. «Det virker på min
laptop» er en påstand om én laptop, med sine egne installerte biblioteker og sine etterlatte filer.
En runner som starter fra en fersk utsjekk hver gang, gjenskaper enten resultatet eller viser hvor
laptopen var spesiell.

Farten er der verdien ligger. Fowlers artikkel setter et mål på omtrent ti minutter for
hovedbyggingen, og begrunnelsen holder i praksis. En feil som meldes ti minutter etter en push,
går tilbake til personen som forårsaket den, mens endringen ennå står på skjermen. En feil som
meldes neste morgen, går til den som tilfeldigvis ser, og koster først en time med arkeologi.

Synlighet teller også. Når tilstanden til hovedlinjen vises for alle, starter ingen nytt arbeid
oppå en ødelagt bygging uten å vite det, og å reparere den byggingen blir teamets første jobb og
ikke noen andres problem.

CI trenger heller ikke å være en tjeneste noen andre driver. pre-commit kjører et prosjekts sjekker
som hooks i Git før hver commit eller push, og dokumentasjonen viser det samme settet kjørt på en
server med `pre-commit run --all-files`. Task gir hver sjekk et navn i én `Taskfile.yml`, så en
person og en runner kaller den samme kommandoen. Dette nettstedets egne sjekker kjører slik: på
maskinen nettstedet skrives på, med en full sjekk før hver push og uten noen driftet byggetjeneste
imellom.

## Slik ser det ut i praksis

Å få programvare ut døren hvilte i ett prosjekt på manuelle steg som hver person gjorde litt
annerledes.
[Automatiserte leveransepipeliner for bygging, testing og produksjonssetting erstattet dem](/nb/portfolio/streamlined-ci-cd-processes-saving-4-000-hours-42/),
så veien fra en endring til produksjon ble standardisert i stedet for improvisert. Siden anslår
tiden som ble gitt tilbake, til omtrent 4 000 timer, og utgivelsene ble både raskere og mer
pålitelige.

I et prosjekt med en database som har levd lenge, er skjemaet beskrevet to ganger: én gang av det
første oppsettet og én gang av migreringsrekken fra nummer 036 til 1102, 1 022 migreringer i alt.
[En CI-jobb bygger databasen begge veier ved hver endring](/nb/portfolio/kept-the-schema-honest-across-1022-migrations-99/),
én gang fra det ferske skjemaet og én gang med alle migreringene spilt av i rekkefølge, og
sammenligner strukturen og startdataene. Ethvert avvik får byggingen til å feile med forskjellen
skrevet ut, så et nytt miljø og et som har levd lenge, er den samme databasen fordi det er sjekket,
og ikke fordi noen antar det.

I et tredje prosjekt genereres API-beskrivelsen og klienten i frontenden i en kjede som starter i
databasen og ender i en typet TypeScript-klient på 44 076 linjer.
[En kontraktskontroll i CI sammenligner det frontenden sender, med det API-et forventer](/nb/portfolio/built-a-request-schema-validation-contract-with-automated-61/),
og får byggingen til å feile når de spriker. Resultatet siden rapporterer, er det CI finnes for: et
felt som endres på bare én side, feiler i en bygging minutter etter endringen, i stedet for å nå en
bruker som en feil i nettleseren.

## Hvor det går galt

De fleste CI-oppsett forfaller i stedet for å bryte sammen. Hvert av disse problemene starter
smått og ender med en bygging teamet har lært seg å gå rundt.

**Byggingen tar lengre tid enn arbeidet.** Når en push bruker førti minutter på å komme tilbake,
slutter folk å vente på den, samler flere endringer i én push og sender sjeldnere, som er det
motsatte av praksisen. Mål hvor tiden går, før du kjøper mer maskinvare. Cache avhengigheter, kjør
de raske sjekkene først, og flytt de trege testsuitene til et andre trinn eller en tidsplan, så
hovedbyggingen holder seg nær timinuttersmerket.

**Hovedlinjen står rød i et døgn.** En ødelagt bygging som ingen reparerer, blir normaltilstanden,
og en ny feil kommer ubemerket bak den første. Regelen som hindrer dette, er enkel: den som ødela
byggingen, reparerer den nå, eller tilbakestiller endringen og reparerer den på egen tid. En grønn
hovedlinje er det alle andre bygger på.

**Grener lever i ukevis mens serveren bygger hver av dem.** Hver gren viser grønt, og problemene
kommer den dagen de slås sammen, alle på en gang. Dette er automatisert bygging uten integrasjonen.
Slå sammen til hovedlinjen minst daglig, og hold uferdige funksjoner slått av i stedet for adskilt.

**Serveren sjekker én liste og laptopen en annen.** To lister med kommandoer som holdes ved like
for hånd, glir fra hverandre, og så består en endring lokalt og feiler på serveren, eller omvendt,
og til slutt stoler ingen på noen av dem. Gi sjekkene ett inngangspunkt og kall det fra begge
steder. Med pre-commit kan serveren kjøre nøyaktig de hookene hver utvikler kjører:

```yaml
# .gitlab-ci.yml
check:
    image: python:3.13
    script:
        - pip install pre-commit
        - pre-commit run --all-files --show-diff-on-failure
```

**En ustabil test lærer alle å trykke på «prøv igjen».** En test som feiler én kjøring av tjue,
av grunner som ikke har noe med endringen å gjøre, lærer teamet å kjøre på nytt i stedet for å
lese. Etter det får en ekte feil samme behandling. Sett testen i karantene, reparer den eller slett
den, og regn en prøv igjen-knapp som trykkes ofte, som et funn i seg selv.

## Les mer

- [Continuous Integration](https://martinfowler.com/articles/continuousIntegration.html)
  — Martin Fowlers lange artikkel: hver praksis, bygging på ti minutter og hvor navnet kom fra.
- [Understanding GitHub Actions](https://docs.github.com/en/actions/get-started/understand-github-actions)
  — arbeidsflyter, hendelser, jobber og runnere slik GitHub definerer dem.
- [GitLab CI/CD](https://docs.gitlab.com/ci/) — filen `.gitlab-ci.yml`, pipeliner, trinn, jobber og
  runnere med GitLabs egne ord.
- [pre-commit](https://pre-commit.com/) — sjekker som erklæres én gang i en konfigurasjonsfil og
  kjøres som hooks i Git og igjen som et steg på en CI-server.
- [Task](https://taskfile.dev/) — en `Taskfile.yml` som gir hver sjekk ett navn, så en person og
  en runner skriver den samme kommandoen.

<https://engineer.company/nb/glossary/ci/>
