FlowingDev

Diff, dijelaskan: Bumbu rahasia di balik Git dan setiap code review

Pelajari bagaimana algoritma diff menemukan penambahan, penghapusan, dan perubahan yang presisi antar teks, mesin inti di balik version control dan kolaborasi kode.

Coba tool-nya: Pemeriksa Perbedaan

Dalam satu kalimat

'Diff' adalah ringkasan hasil komputasi dari perbedaan presisi antara dua file atau blok teks, yang menunjukkan dengan tepat apa yang ditambahkan, dihapus, atau diubah untuk beralih dari kondisi "sebelum" ke "sesudah".

Masalah yang dipecahkannya

Bayangin dunia komputasi di awal tahun 1970-an. Kapasitas penyimpanan itu harganya selangit, dan koneksi ke komputer remote pakai modem yang lebih lambat dari siput ngantuk. Kamu seorang developer di Bell Labs, dan kamu perlu memperbarui file kode sumber di server seberang kampus. File itu isinya beberapa ribu baris, tapi kamu cuma mengubah tiga baris di antaranya.

Apakah kamu bakal mengirim seluruh file lagi lewat koneksi super lemot itu? Jelas enggak dong. Itu buang-buang waktu dan sumber daya. Yang sebenarnya kamu mau adalah mengirim hanya perubahannya saja.

Inilah masalah yang mendorong Douglas McIlroy untuk menciptakan perintah diff orisinal untuk sistem operasi Unix pada tahun 1974. Tujuannya adalah membuat alat yang bisa secara terprogram menemukan serangkaian perubahan baris-per-baris yang minimal untuk mengubah satu file menjadi file lain. Output dari perintah diff ini, yang disebut file "patch", ukurannya kecil dan bisa dikirim dengan cepat. Penerima kemudian bisa menggunakan program pendamping, patch, untuk menerapkan perubahan ini ke salinan file asli mereka, sehingga file tersebut menjadi versi terbaru.

Ide sederhana tapi powerful ini—mengisolasi perubahan itu sendiri sebagai sebuah data—adalah sebuah revolusi. Ini adalah fondasi dasar dari semua sistem version control modern seperti Git, Subversion, dan Mercurial. Inilah mesin di balik code review, alat kolaborasi dokumen (seperti mode "Suggesting" di Google Docs), dan sistem manajemen konfigurasi. Ini memecahkan masalah inti dalam melacak dan mengomunikasikan evolusi dalam teks digital apa pun.

Cara kerjanya di balik layar

Sekilas, proses diffing kelihatan sederhana: cukup pindai dua teks dan tandai apa yang berbeda. Tapi untuk melakukannya secara efisien dan menghasilkan serangkaian perbedaan yang paling kecil dan paling mudah dibaca adalah masalah klasik dalam ilmu komputer. Rahasianya bukanlah mencari apa yang berbeda, melainkan mencari apa yang sama.

The Longest Common Subsequence (LCS)

Sebagian besar algoritma diff, termasuk algoritma Hunt–McIlwain yang terkenal yang menjadi dasar diff orisinal, didasarkan pada penyelesaian masalah "Longest Common Subsequence" (LCS).

Subsequence adalah urutan item yang muncul dalam urutan yang sama seperti di urutan aslinya, tapi tidak harus bersebelahan. LCS adalah subsequence terpanjang yang sama-sama dimiliki oleh dua urutan.

Mari kita gunakan contoh sederhana, bukan kode.

  • Asli: The quick red fox
  • Baru: The slow red cat

Algoritma, yang bekerja baris demi baris (atau dalam kasus ini, kata demi kata), menemukan Longest Common Subsequence-nya adalah: The red.

Setelah LCS ditemukan, logikanya sederhana:

  • Item apa pun di teks Asli yang tidak ada di LCS pasti telah dihapus. (quick, fox)
  • Item apa pun di teks Baru yang tidak ada di LCS pasti telah ditambahkan. (slow, cat)

Dengan menemukan fondasi konten terpanjang yang sama, algoritma dapat dengan jelas dan ringkas mengidentifikasi "pulau-pulau" perubahan di sekitarnya. Metode ini menghasilkan set perbedaan minimal, yang kita inginkan untuk sebuah diff yang bersih dan mudah dipahami.

Dari LCS menjadi Diff yang Mudah Dibaca

Menemukan perubahan hanyalah separuh perjuangan. Separuh lainnya adalah menyajikannya dalam format standar yang mudah dibaca. Kamu mungkin pernah melihat ini jika pernah melihat pull request di GitHub. Format yang paling umum adalah "unified diff format."

Mari kita terapkan pada contoh yang sedikit berbeda:

  • File A (lama):
    An apple a day.
    Keeps the doctor away.
    Or so they say.
    
  • File B (baru):
    An apple a day,
    Keeps the doctor away.
    For what it's worth.
    

Alat diff akan menghasilkan sesuatu seperti ini:

--- a/file_a.txt
+++ b/file_b.txt
@@ -1,3 +1,3 @@
-An apple a day.
+An apple a day,
 Keeps the doctor away.
-Or so they say.
+For what it's worth.

Mari kita bedah:

  • --- a/file_a.txt: File "asal". Tanda - menunjukkan sumber penghapusan.
  • +++ b/file_b.txt: File "tujuan". Tanda + menunjukkan sumber penambahan.
  • @@ -1,3 +1,3 @@: Ini adalah "hunk header." Agak misterius, tapi ini memberimu konteks. -1,3 berarti "hunk ini dimulai dari baris 1 dan panjangnya 3 baris di file asli." +1,3 berarti "hunk ini dimulai dari baris 1 dan panjangnya 3 baris di file baru."
  • Baris yang diawali dengan spasi ( ) adalah baris konteks. Baris ini identik di kedua file dan ditampilkan untuk membantumu memahami di mana perubahan terjadi.
  • Baris yang diawali dengan - adalah penghapusan. Baris ini hanya ada di teks "sebelum".
  • Baris yang diawali dengan + adalah penambahan. Baris ini hanya ada di teks "sesudah".

Alat ini menunjukkan perubahan dari An apple a day. menjadi An apple a day, bukan sebagai modifikasi satu baris, melainkan sebagai penghapusan baris lama dan penambahan baris baru. Pendekatan berbasis baris ini adalah karakteristik inti dari sebagian besar alat diff tradisional.

Lebih dari Sekadar Teks Polos: Diff Semantik

Diff standar berbasis baris sangat bagus untuk prosa atau kode, tapi jadi berantakan jika berhadapan dengan data terstruktur seperti JSON, XML, atau YAML.

Perhatikan JSON ini:

// Original
{
  "name": "Alex",
  "role": "Developer"
}

Dan yang ini:

// New
{
  "role": "Developer",
  "name": "Alex"
}

Diff berbasis teks akan melihat ini sebagai penghapusan total dan penulisan ulang:

-  "name": "Alex",
-  "role": "Developer"
+  "role": "Developer",
+  "name": "Alex"

Secara teknis ini benar, tapi secara semantik tidak ada gunanya. Urutan key dalam objek JSON umumnya tidak penting. Alat diff semantik lebih pintar. Ia mem-parsing teks menjadi struktur data terlebih dahulu, baru kemudian membandingkan strukturnya. Alat ini akan dengan benar mengidentifikasi bahwa kedua objek JSON ini identik, sehingga tidak ada perbedaan yang dilaporkan. Ini sangat penting untuk membandingkan file konfigurasi, respons API, atau data terstruktur lainnya di mana kamu peduli pada makna, bukan hanya format teks.

Cerita dari dunia nyata

Bug Satu Karakter yang Bikin Checkout Rusak

Seorang developer junior sedang mengintegrasikan penyedia pembayaran baru. Dia menyalin contoh request API dari dokumentasi, memasukkan key miliknya, dan menjalankannya. Gagal. Dia coba lagi. Gagal. Dia menghabiskan berjam-jam menatap kodenya dan dokumentasi, yakin keduanya identik. Karena frustrasi, dia menempelkan contoh dari dokumentasi yang "berhasil" ke satu sisi alat diff checker dan kodenya sendiri di sisi lain.

Awalnya, kelihatannya identik. Tapi kemudian dia melihat sorotan samar di akhir baris API key-nya. Sebuah spasi di akhir baris yang tak terlihat. Proses copy-paste dari halaman web ternyata menyertakannya, dan kodenya dengan patuh mengirimkannya, membuat key-nya tidak valid. Alat diff, yang melihat spasi itu hanya sebagai karakter lain, adalah satu-satunya "pasang mata" yang bisa menemukannya.

Pelajaran: Diff adalah mikroskop pamungkasmu. Ia tidak punya asumsi dan akan menunjukkan persis apa yang ada di sana, termasuk karakter tak terlihat yang bisa melumpuhkan sistem.

Bencana "Configuration Drift"

Sebuah situs web dengan traffic tinggi mulai mengalami error aneh yang muncul sesekali. Engineer yang sedang bertugas, Maya, bingung. Deployment terakhir sudah seminggu yang lalu dan stabil. Tidak ada petunjuk jelas di log. Instingnya mengatakan ada sesuatu yang diubah secara manual di server.

Dia menarik file konfigurasi Nginx resmi dari repositori Git mereka, lalu melakukan SSH ke server produksi dan menyalin konfigurasi yang sebenarnya berjalan. Dia menempelkan keduanya ke alat diff. Nah, ketemu. Tiga baris berbeda. Seseorang telah menambahkan aturan redirect "sementara" langsung di server untuk memperbaiki masalah kecil minggu lalu dan benar-benar lupa tentang itu. "Perbaikan" ini sekarang bentrok dengan pola traffic baru. Maya menghapus baris-baris liar itu, dan error pun hilang. Tim segera menerapkan kebijakan untuk mengaudit konfigurasi server terhadap Git setiap hari.

Pelajaran: Sistem version control-mu adalah sumber kebenaran. Membandingkan kenyataan dengan sumber kebenaran itu adalah cara terbaik untuk mendeteksi "configuration drift" dan menemukan perubahan yang tidak sah atau terlupakan.

Code Review "Kelihatannya Oke, Sih"

Seorang developer senior, Ben, menerima pull request dari karyawan baru. Judulnya "Updates". Diff-nya adalah lautan merah dan hijau di 20 file dan lebih dari 3.000 baris. Isinya ada fitur baru, perbaikan untuk bug yang tidak terkait, pemformatan ulang kode besar-besaran untuk beralih dari tab ke spasi, dan pembaruan library. Mustahil untuk di-review. Apakah perbaikan bug-nya benar? Apakah fitur baru itu menimbulkan celah keamanan? Semuanya tersembunyi di tengah badai perubahan spasi.

Ben menolak PR itu dengan catatan ramah: "Selamat datang! Diff dari sebuah PR itu bercerita. Yang ini mencoba menceritakan empat kisah sekaligus. Bisa tolong dipecah jadi empat PR terpisah?" Karyawan baru itu melakukannya. PR pemformatan ulang langsung disetujui. Perbaikan bug mudah diverifikasi. Pembaruan library jelas. Dan fitur baru itu akhirnya bisa di-review dengan semestinya.

Pelajaran: Nilai sebuah diff berbanding terbalik dengan ukuran dan kerumitannya. Diff yang kecil dan terfokus yang mewakili satu perubahan logis tunggal mudah di-review, dipahami, dan di-debug nanti.

Kesalahan dan jebakan umum

  • Mengabaikan whitespace. Perubahan dari tab ke spasi atau penambahan baris baru di akhir file bisa terlihat seperti perubahan besar-besaran di seluruh file bagi alat diff. Meskipun terkadang ini disengaja, sering kali ini hanya menciptakan 'noise' yang menyembunyikan perubahan yang sebenarnya penting. Konfigurasikan alatmu untuk mengabaikan atau menyorot perubahan whitespace dengan tepat.
  • Diff yang "buta semantik". Seperti yang disebutkan sebelumnya, menggunakan diff teks biasa pada data terstruktur seperti JSON atau XML bisa sangat menyesatkan. Mengubah urutan atribut atau key bisa terlihat seperti perubahan besar padahal, secara fungsional, tidak ada yang berbeda. Selalu gunakan alat diff yang sadar semantik untuk format-format ini.
  • Lupa konteks. Diff menunjukkan apa yang berubah, tapi tidak pernah memberitahumu mengapa. Itulah tugas dari pesan commit atau deskripsi pull request. Diff tanpa konteks itu seperti jawaban tanpa pertanyaan; sulit untuk menilai apakah itu benar atau salah.
  • Membuat diff "Frankenstein". Menggabungkan perubahan yang tidak terkait ke dalam satu commit (perbaikan bug, fitur, dan koreksi typo) membuat diff menjadi mimpi buruk untuk dibaca. Ini membuat mustahil untuk mengembalikan salah satu perubahan itu nanti tanpa memengaruhi yang lain. Setiap commit harus menjadi satu perubahan logis yang atomik.

Kenapa ini penting buat kamu

Memahami diffing bukan lagi pilihan bagi developer modern; ini sama fundamentalnya dengan tahu cara pakai keyboard. Kamu akan bertemu dengan diff berkali-kali setiap hari:

  • Saat kamu menjalankan git status atau git diff untuk melihat pekerjaanmu yang belum di-commit.
  • Saat kamu membuat pull request untuk di-review oleh rekan kerjamu.
  • Saat kamu me-review pull request orang lain.
  • Saat kamu menggunakan git blame untuk mencari tahu siapa yang menulis baris kode tertentu dan mengapa.
  • Saat kamu sedang men-debug masalah dengan membandingkan konfigurasi yang berfungsi dengan yang rusak.

Bahkan bagi non-developer, konsep ini sangat powerful. Ini adalah "Track Changes" di dokumen Word-mu. Ini adalah riwayat versi artikel Wikipedia-mu. Ini adalah kemampuan untuk melihat bagaimana sebuah kontrak telah berevolusi antar draf. Memahami diffing berarti memahami bagaimana kita mengelola dan mengomunikasikan perubahan di dunia digital. Ini adalah catatan kemajuan yang dapat diaudit dan diverifikasi.

Pelajari lebih dalam

Teori beres. Saatnya praktik — 100% di browser kamu.

Coba tool-nya: Pemeriksa Perbedaan