DevOps

CI Jadi Bottleneck Setelah AI Coding: Cara Linear Memangkasnya

CI Jadi Bottleneck Setelah AI Coding: Cara Linear Memangkasnya

Ada efek samping dari adopsi AI coding yang jarang dihitung di muka. Agen membuat penulisan kode jauh lebih cepat, tetapi validasinya tidak ikut mengejar dengan kecepatan yang sama. Setiap pull request tetap harus melewati CI, dan ketika laju perubahan naik, CI berubah dari alat bantu menjadi penyumbat. Biaya infrastruktur naik, dan pengembang maupun agen menunggu lebih lama untuk mendapat umpan balik.

Linear mengalami persoalan ini secara langsung. Mufeez Amjad menulis pada 21 September 2026 bahwa CTO mereka, Tuomas, membuka issue berjudul "CI costs are high" dan sekaligus meminta CI dibuat lebih cepat. Hasil akhirnya cukup kontras dengan kondisi awal: test suite mereka hampir empat kali lipat sejak awal tahun, tetapi waktu tunggu pull request turun dari lebih dari 6 menit menjadi sedikit di atas 5 menit, dan waktu runner per test kira-kira terpangkas separuh.

Yang membuat catatan ini berguna bukan angka akhirnya, tapi rincian langkahnya. Mereka membagi pekerjaan menjadi empat kelompok: memperbarui infrastruktur dan perkakas, mengoptimalkan job yang menjadi gerbang pekerjaan lain, mengurangi setup yang berulang, dan membuat eksekusi test lebih efisien. Basis kode mereka sebagian besar TypeScript, tapi banyak optimasi di bawah ini berlaku lintas bahasa.

Satu: Infrastruktur dan Perkakas

Keuntungan pertama datang tanpa menyentuh isi pipeline sama sekali. Linear memindahkan beban kerja dari GitHub Actions ke runner pihak ketiga dengan CPU lebih cepat, penyimpanan lebih baik, dan infrastruktur cache yang lebih mumpuni. Pada perbandingan apple to apple dua hari sebelum dan sesudah perpindahan, job berjalan 34 persen lebih cepat rata-rata, dan sebagian beban kerja seperti tsc turun 52 persen.

Modernisasi perkakas memberi hasil serupa. Berpindah ke tsgo, kompiler TypeScript native, memotong median mingguan pemeriksaan tsc sebesar 73 persen. Angka itu cukup besar untuk memindahkan bottleneck keluar dari typechecking sepenuhnya, yang berarti target optimasi berikutnya berubah.

Linting juga dibenahi dengan cara yang menarik. Beberapa aturan lint kustom mereka bergantung pada informasi tipe TypeScript, baik untuk menegakkan batasan maupun menerapkan autofix. Konsekuensinya, setiap lint run harus membangun graf tipe penuh sebelum aturan-aturan itu dievaluasi, sehingga linting menjadi salah satu job CI yang paling boros memori. Mereka menulis ulang aturan itu memakai analisis statis di atas abstract syntax tree, mengenali konstruksi mirip fungsi dan pola guard tanpa informasi tipe.

Hasilnya, ESLint bisa melepas TypeScript sepenuhnya. Waktu lint API turun 68 persen, dan waktu lint seluruh repositori turun 55 persen. Penggunaan memori juga turun cukup besar. Efek sampingnya, migrasi ke Oxlint menjadi jauh lebih mudah karena aturan yang murni bekerja di atas sintaks lebih gampang dipindahkan.

Dua: Job yang Menjadi Gerbang Pekerjaan Lain

Setelah infrastruktur dan pemeriksaan individual lebih cepat, Linear melihat CI sebagai sistem. Perhatian mereka tertuju pada job-job kecil yang berada di depan segalanya.

Setiap run dimulai dengan memeriksa path apa yang disentuh sebuah PR dan apakah test untuk input yang sama sudah pernah lulus. Pemeriksaan ini dijadikan gate di tingkat job supaya pekerjaan yang dilewati tidak memesan runner, tetapi konsekuensinya pemeriksaan itu berada langsung di jalur kritis. Tidak ada satu pun dari delapan shard test API yang bisa mulai sebelum pemeriksaan itu selesai, sehingga keterlambatan kecil berdampak tidak proporsional.

Perbaikan pertama adalah mengambil hanya yang dibutuhkan setiap job. Beberapa workflow dimulai dengan job deteksi perubahan yang menentukan apa yang berjalan berikutnya, misalnya memeriksa apakah diff memuat migrasi basis data. Job seperti ini melakukan checkout seluruh working tree padahal hanya butuh sebagian kecil. Mereka membatasi kedalaman fetch, dan hasilnya gate paling lambat turun dari 94 detik menjadi 20 detik. Untuk job yang sama sekali tidak butuh working tree, checkout dihapus seluruhnya dan waktunya turun dari 27 detik menjadi 7 detik. Untuk event commit push dan merge queue yang memang perlu membandingkan path, checkout sparse dan blobless dengan riwayat terbatas sudah cukup, menghemat sekitar 11 detik lagi.

Secara keseluruhan, median durasi job deteksi perubahan turun dari 26 detik menjadi 8 detik, p90 dari 31 menjadi 12 detik, dan run paling lambat dari 138 menjadi 37 detik.

Perbaikan kedua adalah membuat checkout lebih tahan gangguan. Setelah berpindah ke runner pihak ketiga, waktu checkout mereka jadi lebih panjang dan kadang menggantung. Karena runner pihak ketiga berada di luar jaringan GitHub, mereka mengandalkan tautan IP langsung ke GitHub, dan penyedia runner melacak penyebabnya ke degradasi intermiten pada tautan itu. Karena banyak workflow dimulai dengan checkout, fetch yang macet bisa menunda seluruh run CI.

Solusinya adalah mengganti actions/checkout dengan composite action buatan sendiri yang mengulang dengan backoff, menyetel GIT_HTTP_LOW_SPEED_LIMIT dan GIT_HTTP_LOW_SPEED_TIME supaya koneksi yang macet dibatalkan setelah sekitar 30 detik alih-alih menggantung, dan memakai checkout cache berupa mirror git persisten di disk sticky.

Perbaikan ketiga adalah memangkas apa yang ada di jalur kritis. Penulisan cache marker dilakukan sebagai bagian dari pemeriksaan terakhir sebelum merge, sehingga pull request bisa tertahan di merge queue meski test-nya sudah lulus. Penulisan itu dipindah ke job yang berjalan setelah shard test selesai tetapi tidak menjadi gate apa pun. Hasilnya, 42 detik terpangkas dari jalur merge untuk setiap PR API dan setiap entri merge queue. Digabung, perubahan ini menghemat sekitar satu menit dari required check untuk PR API pada cache miss, sekaligus mengurangi jumlah runner start.

Tiga: Setup yang Berulang

Berikutnya adalah biaya setup yang terulang di setiap job: menyalakan runner, memasang paket, menyiapkan dependensi build. Overhead itu membuat job yang hanya bekerja beberapa detik bisa memakan waktu infrastruktur berhitung menit.

Langkah pertama, prainstal dependensi bersama di image CI. Setiap shard test API menghabiskan 7 sampai 8 detik memasang klien Postgres dengan apt di setiap run. Klien itu dipindah ke image dasar CI yang memuat Node dan klien tersebut, sehingga setiap shard mulai dari lingkungan yang siap. Header build native yang dibutuhkan juga ditambahkan ke image setelah diketahui bahwa mengunduhnya saat setup kadang menggantung.

Langkah kedua, pasang hanya dependensi yang dibutuhkan setiap job. Basis kode Linear adalah monorepo yang dikelola sebagai workspace pnpm. Workflow test API mereka memasang seluruh workspace padahal hanya butuh paket API dan dependensinya. Membatasi instalasi ke paket API memotong waktu pnpm install dari 44 sampai 73 detik menjadi 16 sampai 18 detik. Pola yang sama diterapkan ke job yang berdekatan dengan API, yang masing-masing memasang seluruh repositori dan mengunggah cache dependensi yang hampir tidak pernah terpakai di run berikutnya.

Langkah ketiga, jangan cache kalau rebuild lebih cepat. Mereka menguji caching node_modules dan menemukan bahwa membangun ulang justru lebih cepat. Kunci cache-nya bergantung pada lockfile yang sering berubah, dan bahkan cache hit memerlukan sekitar 28 detik untuk restore, dibandingkan sekitar 7,5 detik untuk instalasi terfilter. Cache itu menambah waktu simpan dan variabilitas tanpa keuntungan yang terlihat.

Ketiga langkah itu bersama-sama mengurangi waktu setup per shard sekitar 44 persen, dari 110 sampai 140 detik menjadi 67 sampai 73 detik.

Masih ada bentuk setup berulang lain yang bisa dihindari sepenuhnya. Container API mereka memutar ulang seluruh riwayat migrasi basis data di setiap run, bahkan ketika PR tidak mengubah skema. Untuk kasus itu, mereka beralih memuat snapshot skema yang dihasilkan dan file bootstrap, memotong setup basis data dari sekitar 12 detik menjadi 1 sampai 2 detik per container.

Selain itu, tujuh pemeriksaan independen masing-masing menyalakan runner, melakukan checkout, dan memasang dependensi sebelum melakukan pekerjaan yang hanya beberapa detik. Ketujuhnya digabung menjadi dua job, dengan tujuh tugas dijalankan bersamaan di dalamnya. Overhead setup yang tadinya dibayar tujuh kali menjadi dua kali. Berdasarkan pemakaian Juni, perubahan itu menghemat sekitar 87.000 runner-minute per bulan, setara 11,8 persen dari total pemakaian CI mereka.

Empat: Eksekusi Test

Dengan biaya tetap per shard turun, parallelisasi yang lebih agresif jadi masuk akal. Ada dua perubahan besar di sini.

Pertama, menyeimbangkan pekerjaan sesuai cara test runner melihatnya. Vitest, test runner yang mereka pakai untuk suite TypeScript, mendistribusikan pekerjaan per file, bukan per durasi test individual. Akibatnya, beberapa file test yang luar biasa besar bisa mendominasi satu shard dan menahan penyelesaian seluruh suite, meski shard lain sudah selesai jauh lebih awal. File-file besar itu dipecah menjadi file yang lebih kecil dan fokus tanpa mengubah struktur test, lalu konfigurasi shard dan runner dievaluasi ulang. Sebelumnya mereka sudah naik dari tiga ke empat shard; berpindah ke delapan membuat job kritis sekitar 19 persen lebih cepat dan 19 persen lebih murah pada benchmark awal. Sepekan setelah perubahan, shard paling lambat turun dari 5,25 menit menjadi 4,33 menit.

Kedua, berbagi state modul dengan aturan isolasi yang ketat. Vitest normalnya mengisolasi setiap file test, dan bagi Linear itu berarti membangun ulang graf entity, GraphQL, dan decorator di setiap shard test. Mereka memperkenalkan proyek vitest opt-in dengan isolate: false, memungkinkan file yang aman berbagi registry modul di dalam setiap worker.

Ini adalah perbaikan performa tunggal terbesar mereka, bernilai sekitar 17 persen penghematan bulanan pada volume mereka. Shard paling lambat turun dari sekitar 300 sampai 379 detik menjadi sekitar 195 detik, sementara total waktu runner shard API turun dari sekitar 32,8 menjadi 22 menit per run. Namun ini juga optimasi dengan risiko kebenaran paling tinggi. Kelayakan dibuat eksplisit lewat komentar opt-in di setiap file, dan teardown yang diperlukan untuk state bersama ditambahkan. Sejumlah kecil file memakai fake timer atau state bersama dengan cara yang tidak bisa dipisahkan dengan aman, jadi file-file itu dibiarkan di proyek terisolasi. Karena agen sekarang menulis mayoritas test mereka, skill agen juga diperbarui supaya test yang dihasilkan mengikuti batasan yang sama secara default.

Ada catatan penting soal batas sharding. Menambah shard hanya menguntungkan kalau biaya tetap per shard rendah, karena menggandakan jumlah shard juga menggandakan waktu workflow untuk setup. Pada 110 sampai 140 detik per shard, delapan shard akan menghabiskan 15 sampai 19 menit waktu runner hanya untuk setup, lebih besar daripada waktu test-nya sendiri. Setelah optimasi, setup sekitar 40 detik, sehingga delapan shard menghabiskan total waktu setup lebih sedikit daripada empat shard sebelumnya, sambil memparalelkan test dua kali lebih jauh. Sebelum optimasi setup, job test memakai empat shard dan menghabiskan 8,3 menit untuk setup; sesudahnya, delapan shard dengan 7,5 menit setup.

Pelajaran yang Bisa Dipindahkan

Amjad menutup dengan satu angka yang menjelaskan seluruh urusan ini. Kalau mereka tidak melakukan perbaikan CI secara sengaja sepanjang tahun, test suite hari ini akan memakan sekitar 11 menit, hampir dua kali lipat dari yang ditunggu pengembang sekarang.

Urutan kerjanya sendiri bisa ditiru. Ukur dulu bagian mana yang menjadi bottleneck sebelum memasang optimasi, karena menambah shard di atas setup yang mahal justru memperburuk keadaan. Periksa job kecil yang menjadi gerbang pekerjaan besar, karena keterlambatan beberapa detik di sana berdampak berkali-kali lipat. Lalu kurangi biaya tetap per unit kerja, karena parallelisasi baru menguntungkan setelah biaya itu turun. Untuk konteks lain soal pengecekan dependensi dan masa pakai runtime di pipeline, ada juga catatan memakai API endoflife.date: Cek tanggal mati Node, Python, dan Postgres.

Sumber

💬 Komentar (0)

Belum ada komentar. Jadilah yang pertama! 💬

Komentar akan muncul setelah moderasi.