Program bukan kumpulan bagian yang benar-benar berdiri sendiri. Satu perubahan kecil dapat merambat melalui hubungan antarkomponen dan memunculkan masalah di tempat yang tidak kita duga.

Bayangkan sebuah toko daring hanya ingin mengubah aturan diskon. Programmer mengganti beberapa baris kode, mengujinya di halaman keranjang, dan semuanya berjalan sesuai harapan. Beberapa jam kemudian, justru halaman riwayat transaksi yang bermasalah.

Bagi orang di luar pengembangan perangkat lunak, kejadian semacam ini terasa aneh. Bagaimana perubahan pada diskon bisa mengganggu riwayat transaksi yang tampaknya tidak disentuh sama sekali? Jawabannya terletak pada sesuatu yang tidak selalu terlihat di layar: hubungan antarkomponen di balik sebuah program.

Semakin besar sebuah aplikasi, semakin banyak bagian yang saling menggunakan data, fungsi, layanan, dan aturan yang sama. Karena itu, ukuran perubahan pada kode tidak selalu menggambarkan ukuran dampaknya. Tiga baris yang diubah pada komponen penting terkadang lebih berisiko daripada puluhan baris pada bagian yang benar-benar terisolasi.

Baca Juga: Kenapa Memperbaiki Program Lama Kadang Lebih Sulit daripada Membuat yang Baru?

Yang Terlihat Terpisah di Layar Bisa Saling Bergantung di Belakangnya

Program modern biasanya dibangun dari banyak komponen. Ada bagian yang mengurus antarmuka, autentikasi, pembayaran, basis data, notifikasi, perhitungan harga, hingga komunikasi dengan layanan lain. Masing-masing terlihat mempunyai pekerjaan sendiri, tetapi pekerjaan tersebut sering bergantung pada keluaran bagian lainnya.

Misalnya, sebuah fungsi menghitung total transaksi dan menghasilkan angka yang kemudian digunakan oleh keranjang belanja, pembayaran, pembuatan faktur, pencatatan poin pelanggan, serta laporan penjualan. Programmer mungkin hanya ingin mengubah cara pembulatan harga di fungsi tersebut. Karena lima bagian lain menggunakan hasil yang sama, perubahan kecil tadi berpotensi ikut mengubah perilaku mereka.

Hubungan semacam ini disebut dependency. Bahkan ketergantungan tersebut tidak selalu langsung. Google pernah menjelaskan penggunaan dependency analysis dalam sistem pengujiannya untuk mengetahui pengujian mana yang secara transitif terdampak oleh sebuah perubahan kode, karena sebuah komponen dapat bergantung pada komponen lain melalui beberapa lapisan.

Masalah menjadi lebih sulit ketika ketergantungan tersebar dan tidak terstruktur dengan baik. Martin Fowler membahas shotgun surgery, sebuah code smell ketika satu perubahan mengharuskan programmer menyentuh banyak modul berbeda. Semakin banyak tempat yang harus diubah untuk satu kebutuhan, semakin besar pula kesempatan melakukan kesalahan, terutama ketika cakupan pengujiannya tidak memadai.

Itulah sebabnya programmer berpengalaman kadang terlihat sangat berhati-hati menghadapi permintaan yang terdengar sederhana. Kalimat seperti "cuma tambahkan satu pilihan di sini" belum mengatakan seberapa besar pekerjaannya. Mereka perlu mengetahui terlebih dahulu siapa saja yang menggunakan data atau fungsi yang akan diubah.

Baca Juga: Kenapa Aplikasi yang Dulu Cepat Lama-Lama Terasa Berat?

Bug Baru Bisa Lahir Ketika Bug Lama Diperbaiki

Ada istilah khusus untuk persoalan ketika perubahan membuat fungsi yang sebelumnya bekerja menjadi bermasalah: regression. IBM mendefinisikan regression testing sebagai pengujian untuk memastikan modifikasi kode tidak merusak fungsi yang sudah ada atau memperkenalkan bug baru. Pengujian semacam ini dilakukan karena keberhasilan fitur yang baru diubah belum membuktikan seluruh sistem masih baik-baik saja.

Bayangkan programmer memperbaiki kolom nama pelanggan agar dapat menerima lebih banyak karakter. Pengujian pada formulir pendaftaran berhasil, sehingga pekerjaan terlihat selesai. Namun perubahan struktur data tersebut ternyata membuat komponen lain yang masih mengharapkan format lama gagal membaca informasi pelanggan.

Karena itu, pengujian perangkat lunak tidak hanya memeriksa bagian yang baru dikerjakan. Ada unit test untuk menguji bagian tertentu dan integration test untuk memastikan beberapa bagian tetap dapat bekerja bersama. Google juga menekankan pentingnya pengujian integrasi ketika basis kode membesar karena unit-unit yang bekerja baik secara terpisah belum tentu bekerja benar ketika dipertemukan.

Desain program turut menentukan seberapa mudah dampak perubahan dikendalikan. Jika banyak komponen mengetahui terlalu banyak detail tentang satu sama lain, perubahan cenderung lebih mudah merambat. Sebaliknya, pemisahan tanggung jawab dan ketergantungan yang lebih terkelola membantu programmer memperkirakan batas pengaruh sebuah perubahan.

Namun, memisahkan program menjadi banyak bagian juga bukan jaminan otomatis. Ratusan modul tetap dapat membentuk jaringan ketergantungan yang rumit apabila batas dan hubungan di antara mereka tidak dirancang dengan baik. Tantangannya bukan sekadar membuat bagian-bagian kecil, melainkan membuat hubungan antarbagiannya cukup jelas untuk dipahami.

Inilah salah satu alasan pekerjaan programmer tidak selalu dapat dinilai dari jumlah baris yang diubah. Terkadang perubahan kodenya memang hanya beberapa baris, tetapi waktu lebih banyak dihabiskan untuk membaca hubungan, memeriksa dampak, menjalankan pengujian, dan memastikan bagian lama tetap berfungsi. Pekerjaan terbesar justru terjadi pada hal-hal yang tidak terlihat oleh pengguna.

Jadi, ketika sebuah tombol diperbaiki tetapi laporan mendadak bermasalah, belum tentu kedua bagian itu benar-benar tidak berhubungan. Di permukaan mereka mungkin berada di halaman berbeda, tetapi jauh di belakang layar keduanya bisa menggunakan fungsi, data, atau layanan yang sama. Dalam sebuah program, jarak di layar tidak selalu berarti jarak di dalam sistem. [][Rudi Tenggarawan/KK]

Penulisan artikel ini dibantu AI dan telah melewati proses kurasi Redaksi.