Linearin teknologiajohtaja Tuomas Artman antoi kehittäjälleen kaksi toimeksiantoa samassa tiketissä: laske CI:n kustannukset ja tee siitä nopeampi. Tiketin otsikko oli lyhyt, mutta ongelma oli rakenteellinen. Agentit kirjoittavat koodia nopeammin kuin koskaan, mutta muutosten todentaminen ei ole pysynyt samassa tahdissa.
Jokainen pull request kulkee yhä saman jatkuvan integraation läpi. Kun kehitys kiihtyy, CI muuttuu pullonkaulaksi, joka nostaa infrastruktuurin hintaa ja pidentää palautteen odotusta sekä kehittäjille että agenteille. Linear kertoo yhtiön omassa teknologiablogissa, että testijoukot lähes nelinkertaistuivat vuoden alusta. Silti pull requestin odotusaika lyheni yli kuudesta minuutista reiluun viiteen, ja konetunnit testiä kohden putosivat noin puoleen.
Raudan ja työkalujen vaihto
Ensimmäiset hyödyt eivät vaatineet CI-putken optimointia lainkaan. Linear siirsi työkuormat GitHub Actionsista kolmannen osapuolen runnereille, joissa on nopeammat prosessorit, tehokkaampi tallennus ja parempi välimuisti-infrastruktuuri.
Vaihtoa edeltävän ja seuraavan päivän vertailussa työt ajautuivat keskimäärin 34 prosenttia nopeammin. Osa kuormista hyötyi selvästi enemmän: TypeScriptin tyyppitarkistus nopeutui 52 prosenttia pelkällä koneiden vaihdolla.
Työkaluketjun modernisointi tuotti toisen harppauksen. Siirtyminen tsgo-kääntäjään, TypeScriptin natiiviin toteutukseen, leikkasi tyyppitarkistuksen viikkomediaanin 73 prosenttia. Pudotus oli niin suuri, että pullonkaula siirtyi kokonaan pois tyyppitarkistuksesta.
Linttaus oli seuraava kohde. Osa yhtiön omista lint-säännöistä nojasi TypeScriptin tyyppitietoon, joten jokainen ajo rakensi koko tyyppigraafin ennen sääntöjen arviointia. Tiimi kirjoitti säännöt uudelleen staattiseksi analyysiksi syntaksipuun päälle, jolloin ESLint pystyi luopumaan TypeScriptistä täysin. Rajapinnan linttausaika laski 68 prosenttia ja koko repositorion linttaus 55 prosenttia. Muutos teki myös myöhemmästä siirtymästä Oxlintiin huomattavasti helpomman, koska pelkkään syntaksiin nojaavat säännöt on suoraviivaista siirtää.

Portinvartijat kriittisellä polulla
Kun yksittäiset tarkistukset oli saatu nopeammiksi, Linear katsoi CI:tä järjestelmänä. Huomio kiinnittyi pieniin töihin, jotka istuvat kaiken muun edessä. Jokainen ajo alkaa selvittämällä, mitä tiedostopolkuja pull request koskettaa ja onko samoilla syötteillä jo ajettu testit läpi.
Nämä tarkistukset on portitettu työtasolla, joten ohitettu työ ei varaa runneria. Sama ratkaisu asettaa ne kuitenkin suoraan kriittiselle polulle. Yksikään rajapinnan kahdeksasta testilohkosta ei käynnisty ennen kuin portinvartijat ovat valmiit, joten pienetkin viiveet moninkertaistuvat.
Työt hakivat koko työpuun, vaikka tarvitsivat siitä vain pienen osan. Linear rajasi haun syvyyden, mikä vei hitaimman portin 94 sekunnista 20 sekuntiin. Töistä, jotka eivät tarvitse työpuuta lainkaan, checkout poistettiin kokonaan, ja niiden kesto putosi 27 sekunnista seitsemään. Muutostunnistuksen mediaanikesto laski 26 sekunnista kahdeksaan ja hitain ajo 138 sekunnista 37 sekuntiin.
Runnereiden vaihto toi mukanaan oman ongelmansa. Koska kolmannen osapuolen koneet ovat GitHubin verkon ulkopuolella, ne käyttävät suoraa IP-yhteyttä, jossa toimittaja jäljitti ajoittaista heikentymistä. Linear korvasi actions/checkout-toiminnon omalla komposiittitoiminnolla, joka yrittää uudelleen kasvavin väliajoin ja katkaisee pysähtyneen yhteyden noin 30 sekunnissa sen sijaan, että jäisi roikkumaan. Lisäksi yhtiö siirsi välimuistimerkintöjen kirjoituksen pois yhdistämispolulta, mikä leikkasi 42 sekuntia jokaiselta rajapinnan pull requestilta.

Toistuva alustus pois
Kolmas kohde oli alustuskustannus, joka toistuu jokaisessa työssä: runnerin käynnistys, pakettien asennus ja käännösriippuvuuksien pystytys. Sekunteja hyödyllistä työtä tekevä työ voi kuluttaa kokonaisia minuutteja infrastruktuurin aikaa.
Rajapinnan testilohkot käyttivät joka ajossa seitsemän tai kahdeksan sekuntia saman Postgres-asiakkaan asentamiseen apt-paketinhallinnalla. Linear siirsi sen pieneen CI-perusimageen, joka sisältää Noden ja asiakkaan valmiina. Myöhemmin imageen lisättiin myös tarvittavat natiivit käännösotsikot, koska niiden lataus jumiutui ajoittain.
Koodikanta on pnpm-työtilana hallittu monorepo. Testityönkulku asensi koko työtilan, vaikka tarvitsi vain rajapintapaketin ja sen riippuvuudet. Asennuksen rajaaminen vei pnpm installin 44-73 sekunnista 16-18 sekuntiin, ja sama kuvio toistettiin rajapinnan lähitöissä.
Yhtiö testasi myös node_modules-hakemiston tallentamista välimuistiin ja totesi, että uudelleenrakentaminen on nopeampaa. Välimuistin avain riippui tiheästi muuttuvasta lukkotiedostosta, ja jopa osuma vei noin 28 sekuntia palautukseen, kun rajattu asennus vei noin 7,5 sekuntia. Yhdessä nämä kolme muutosta leikkasivat testilohkon alustusajan noin 44 prosenttia, 110-140 sekunnista 67-73 sekuntiin.

Yhteenveto
Linearin luvut kertovat, että agenttien tuottama koodimäärä siirtää ohjelmistokehityksen pullonkaulan kirjoittamisesta todentamiseen. Yksikään yksittäinen muutos ei ratkaissut ongelmaa, vaan tulos syntyi neljästä rinnakkaisesta suunnasta: nopeammasta raudasta, kevyemmistä tarkistuksista, lyhyemmästä kriittisestä polusta ja poistetusta toistotyöstä.
Suurin osa muutoksista ei ole sidoksissa TypeScriptiin. Haun syvyyden rajaaminen, riippuvuuksien asentaminen vain tarvittavalle paketille ja välimuistin hylkääminen silloin, kun uudelleenrakentaminen on nopeampaa, toimivat missä tahansa työkaluketjussa. Mittaaminen kannattaa aloittaa siitä, mikä istuu kaiken muun edessä.
