Apa Kesamaan Lemari Pakaian Barack Obama Dengan Sejarah Git yang Baik

Dec 02 2022
Suatu hari musim panas di akhir Agustus 2014, Presiden Barack Obama saat itu membuat keputusan yang akan mengejutkan bangsa: dia mengenakan setelan yang berbeda. "Kontroversi tan suit" yang dihasilkan mendominasi siklus berita dan menyebar karena berbagai alasan, tetapi pada akhirnya dipicu oleh kebaruan dari gugatan itu sendiri.

Suatu hari musim panas di akhir Agustus 2014, Presiden Barack Obama saat itu membuat keputusan yang akan mengejutkan bangsa: dia mengenakan setelan yang berbeda. Hasil dari “ kontroversi gugatan tan ” mendominasi siklus berita dan menyebar karena berbagai alasan, tetapi pada akhirnya dipicu oleh kebaruan gugatan itu sendiri. Seperti turtleneck hitam Steve Jobs dan kemeja abu-abu Mark Zuckerberg, Obama biasanya memakai jas biru atau abu-abu yang sama setiap hari.

Penyebut umum di balik perilaku bersama ini adalah konsep psikologis dari kelelahan keputusan : bahwa bahkan keputusan terkecil yang kita buat setiap hari dapat menghabiskan jumlah terbatas kekuatan otak yang kita miliki untuk membuat keputusan dan membuatnya dengan baik. Strategi yang diambil oleh orang-orang ini untuk melestarikan sumber daya yang berharga ini adalah dengan menghilangkan sebanyak mungkin keputusan kecil: memakai barang yang sama, makan barang yang sama, mengikuti jadwal yang sama, dan seterusnya. Ini memungkinkan Anda untuk memfokuskan energi mental Anda pada keputusan yang benar-benar penting.

Jadi apa hubungannya semua ini dengan sistem kontrol versi favorit semua orang, git? Seperti banyak hal dalam pemrograman, tidak ada cara yang "benar" untuk menyusun komit git atau mengelola riwayat git suatu proyek; Anda cukup memilih prinsip panduan dan mengatur pola Anda di sekitarnya. Saya pribadi percaya dalam memilih strategi yang mengurangi kelelahan keputusan (dan "kelelahan mental" secara lebih luas) untuk semua "konsumen" dari suatu komitmen. Saya akan membahas lebih detail tentang cara melakukan ini di bawah (dan jangan ragu untuk langsung melompat ke daftar bernomor itu jika Anda mau) tetapi menurut saya sangat penting untuk membahas terlebih dahulu mengapa saya percaya ini. Dan kita harus mulai dengan siapa sebenarnya komit itu.

Siapa Sejarahnya?

Harus ditekankan bahwa selama keseluruhan siklus hidup proyek rata-rata, jumlah orang yang melihat komit yang tidak menulisnya akan jauh lebih banyak daripada yang melakukannya. Orang-orang lain ini juga akan memiliki pengetahuan yang paling tidak mendalam tentang apa sebenarnya komit itu dan bagaimana komit itu dimaksudkan untuk bekerja. Secara praktis, membangun sejarah git yang baik seharusnya benar-benar untuk mereka . Dan, dengan waktu yang cukup, bahkan kode yang Anda tulis sendiri suatu hari nanti dapat tampak asing bagi Anda. Jadi menjaga sejarah yang baik dapat membantu diri Anda di masa depan juga.

Mengingat hal ini, perlu dicatat bahwa ada dua kategori besar orang yang, pada titik tertentu, akan melihat komitmen tertentu:

  1. Peninjau Kode
    Mereka yang melihat komit sebelum digabungkan ke dalam riwayat melalui proses peninjauan kode. Orang-orang ini umumnya dikenal sebagai "peninjau kode". Di tim yang baik, setiap orang pada suatu saat akan menjadi peninjau kode dan mungkin ada beberapa untuk setiap rangkaian perubahan kode baru.
  2. Detektif Kode
    Mereka yang melihat komit setelah digabungkan. Ini biasanya individu yang menelusuri sejarah untuk mencoba dan memahami mengapa sesuatu ditambahkan atau ketika bug diperkenalkan. Karena tidak ada nama yang lebih baik, saya akan menyebut orang-orang ini "detektif kode" untuk membedakan mereka dari orang-orang di atas.

Detektif kode memiliki semua tantangan yang sama di samping yang lain: mereka mungkin tidak selalu tahu apa yang mereka cari dan bahkan ketika mereka menemukannya, mereka mungkin kekurangan konteks penting untuk memahaminya. Sering kali mereka bahkan tidak mendapat manfaat untuk dapat berbicara dengan pembuat kode asli. Untuk alasan ini, sebagian besar dari apa yang dilakukan seorang detektif kode adalah mencoba dan menyimpulkan maksud dari kode yang ada tanpa benar-benar dapat menanyakannya atau menindaklanjuti kecurigaan mereka.

Pekerjaan kedua kelompok ini sedikit berbeda tetapi pada intinya keduanya melibatkan serangkaian keputusan yang harus dibuat, baris demi baris, untuk menjawab pertanyaan pamungkas: apa yang dilakukan kode ini? Bergantung pada bagaimana komit disusun oleh penulis, ini bisa relatif mudah atau kerja keras yang menyakitkan dengan penghalang jalan dan pengalih perhatian yang tidak perlu.

Keputusan, Keputusan

Sekarang mari kita pertimbangkan keputusan seperti apa yang digunakan untuk memahami sebuah komitmen. Pertama kita perlu mencatat bahwa setiap baris komit dapat dikategorikan sebagai salah satu dari dua hal: baris kode yang "ditambahkan" atau yang "dihapus".

Tanpa konteks tambahan apa pun, keputusan berikut harus dibuat saat melihat satu baris kode "tambahan":

  1. Apakah ini baris kode yang sama sekali baru?
  2. Jika ini bukan baris kode baru, apakah itu baris kode yang sudah ada yang baru saja dipindahkan dari tempat lain?
  3. Jika ini bukan baris kode baru dan belum dipindahkan , apakah ini merupakan modifikasi sepele dari baris yang sudah ada (seperti perubahan pemformatan) atau merupakan perubahan logis yang sah?
  4. Jika itu adalah baris kode yang sama sekali baru atau modifikasi yang mengarah pada perubahan logis, mengapa dilakukan? Apakah itu dilakukan dengan benar? Bisakah itu disederhanakan atau diperbaiki?

Kita dapat melihat proses serupa untuk setiap baris kode yang "dihapus":

  1. Apakah baris ini dihapus seluruhnya?
  2. Jika tidak dihapus seluruhnya, apakah dipindahkan atau diubah?
  3. Jika tidak dihapus seluruhnya dan tidak hanya dipindahkan, apakah itu hasil modifikasi sepele (mis: pemformatan) atau hasil dari perubahan logis?
  4. Jika itu sebenarnya adalah modifikasi yang logis, mengapa itu dimodifikasi? Apakah itu dilakukan dengan benar?
  5. Jika saluran dihapus seluruhnya, mengapa tidak diperlukan lagi?

Jadi ini akhirnya membawa kita kembali ke kelelahan keputusan:

Bagaimana kita mengatur komitmen sehingga kita menghilangkan beberapa pilihan sepele pertama ini dan memungkinkan pemirsa untuk fokus pada yang penting?

Anda tidak ingin tim Anda menghabiskan kekuatan otak dan waktu mereka yang terbatas untuk memutuskan, misalnya, bahwa beberapa potongan kode baru saja dipindahkan dari satu modul ke modul lainnya tanpa modifikasi dan kemudian melewatkan kesalahan pengkodean yang ada dalam kode baru yang sebenarnya. Lipat gandakan ini di seluruh tim di organisasi besar dan ini dapat menambah hilangnya produktivitas yang terukur.

Jadi, setelah kita membahas mengapa saya yakin kita harus mengikuti strategi ini, mari kita bahas bagaimana saya menganjurkan untuk mempraktikkannya.

1. Tempatkan modifikasi sepele di komit mereka sendiri

Hal paling sederhana dan paling penting untuk dilakukan adalah memisahkan modifikasi sepele ke dalam komitmen mereka sendiri. Beberapa contohnya termasuk:

  • Perubahan pemformatan kode
  • Fungsi / variabel / kelas mengganti nama
  • Penataan ulang fungsi / variabel / impor dalam kelas
  • Menghapus kode yang tidak digunakan
  • Memindahkan lokasi file

Pertimbangkan komit berikut untuk mencampur perubahan sepele dengan yang tidak sepele:

Pesan komit: "Perbarui daftar buah yang valid"

Berapa lama waktu yang Anda butuhkan untuk menemukan perubahan yang tidak sepele ? Sekarang lihat apa yang terjadi jika kedua perubahan ini dibagi menjadi dua komit terpisah:

Pesan komit: "Perbarui pemformatan daftar buah yang valid"

Pesan komit: "Tambahkan tanggal ke daftar buah yang valid"

Komit "hanya pemformatan" pada dasarnya dapat diabaikan dan penambahan kode dapat segera ditemukan dalam sekejap.

2. Tempatkan refaktor kode di komit mereka sendiri

Refactor kode melibatkan perubahan pada beberapa struktur kode tetapi bukan fungsinya. Kadang-kadang ini dilakukan untuk kepentingannya sendiri tetapi seringkali dilakukan karena kebutuhan: untuk membangun kode yang ada kadang-kadang perlu untuk memfaktor ulang terlebih dahulu dan kemudian tergoda untuk melakukan kedua hal sekaligus. Kesalahan dapat dibuat selama refactor, dan perhatian khusus diperlukan saat meninjaunya. Dengan menempatkan kode ini dalam komitnya sendiri yang ditunjukkan dengan jelas sebagai refactor, peninjau tahu untuk menandai setiap penyimpangan dari perilaku logis yang ada sebagai kemungkinan kesalahan.

Misalnya, seberapa cepat Anda dapat menemukan kesalahan di sini?

Pesan komit: "Perbarui logika tip"

Bagaimana kalau sekarang dengan refactor terpisah?

Pesan komit: "Ekstrak tingkat tip default"

Pesan komitmen: “Izinkan tarif tip khusus”

3. Tempatkan perbaikan bug di komit mereka sendiri

Kadang-kadang dalam proses membuat perubahan kode Anda melihat bug dalam kode yang ada yang ingin Anda modifikasi atau kembangkan. Demi kemajuan Anda, Anda dapat memperbaiki bug itu dan memasukkannya ke dalam perubahan Anda yang tidak terkait dalam komit yang sama. Bila dicampur dengan cara ini ada beberapa komplikasi:

  • Orang lain yang melihat kode ini mungkin tidak tahu bahwa ada bug yang sedang diperbaiki.
  • Meskipun diketahui ada perbaikan bug yang disertakan, sulit untuk mengetahui kode mana yang merupakan bagian dari perbaikan bug dan mana yang merupakan bagian dari perubahan logis lainnya.

4. Tempatkan perubahan logis terpisah di komit mereka sendiri

Setelah membagi jenis perubahan di atas, Anda sekarang harus memiliki satu komit dengan perubahan logis yang sah untuk menambah/memperbarui/menghapus fungsionalitas. Untuk perubahan kecil dan singkat, ini sudah cukup. Namun, terkadang komit yang satu ini menambahkan fitur yang sama sekali baru (dengan pengujian) dan mencatat waktu 1000+ baris (atau lebih). Git tidak akan menyajikan perubahan ini dengan cara yang koheren dan berhasil memahami kode ini akan mengharuskan peninjau untuk melewati dan menyimpan sebagian besar dari baris ini dalam memori sekaligus untuk mengikutinya. Seiring dengan kelelahan keputusan yang terlibat dalam memproses setiap baris, memperluas memori kerja Anda dengan cara ini melelahkan secara mental dan pada akhirnya tidak efisien.

Jika memungkinkan, pisahkan komit berdasarkan domain sehingga setiap komit dapat dikompilasi secara independen. Artinya, kode yang paling independen dapat ditambahkan terlebih dahulu, diikuti oleh kode yang bergantung padanya, dan seterusnya. Kode yang terstruktur dengan baik harus dipecah dengan cara ini secara alami, sementara kesulitan yang ditemukan pada tahap ini mungkin mengisyaratkan masalah struktural yang lebih besar seperti ketergantungan melingkar. Latihan ini bahkan dapat mengarah pada peningkatan kode itu sendiri.

5. Gabungkan setiap perubahan ulasan ke dalam komit miliknya

Setelah membagi pekerjaan Anda menjadi beberapa komit bersih, Anda mungkin mendapatkan umpan balik ulasan yang mengharuskan Anda membuat perubahan pada kode yang muncul di satu atau beberapa komit. Beberapa pengembang akan bereaksi terhadap umpan balik ini dengan menambahkan komitmen baru yang mengatasi masalah ini. Daftar komit dalam PR tertentu mungkin mulai terlihat seperti berikut:

- <Initial commits>
- Respond to review feedback
- Work
- More work
- Addressing more review feedback

Hal yang sama berlaku ketika pull request pertama kali dibuka: setiap komit harus memiliki tujuan dan tidak boleh ditiadakan oleh perubahan pada komit selanjutnya untuk semua alasan yang sama yang disebutkan di atas.

Pertimbangkan perubahan awal ini yang diikuti oleh beberapa komitmen "pekerjaan":

Sekarang bayangkan Anda melihat perubahan ini jauh ke dalam proses (atau bahkan bertahun-tahun kemudian). Tidakkah Anda ingin melihat yang berikut ini?

6. Rebase, rebase, rebase!

Jika sebuah cabang fitur telah ada cukup lama — baik karena waktu yang diperlukan untuk menambahkan kode awal atau karena proses peninjauan kode yang panjang — itu dapat mulai bertentangan dengan perubahan yang dibuat pada cabang utama yang menjadi dasarnya. Sekarang ada dua cara untuk membuat cabang fitur terkini:

  1. Gabungkan cabang utama ke dalam cabang fitur. Ini akan menghasilkan "gabungan komit" di mana semua perubahan kode yang diperlukan untuk mengatasi konflik disertakan. Jika cabang fitur sangat tua, jenis komit ini mungkin substansial.
  2. Rebase cabang fitur terhadap cabang utama. Produk akhir di sini adalah serangkaian komit baru yang bertindak seolah-olah baru saja dibuat berdasarkan cabang utama yang diperbarui. Konflik apa pun perlu ditangani sebagai bagian dari proses rebasing tetapi semua bukti dari versi asli kode akan hilang.

Jika Anda ingin menghasilkan sejarah yang bersih (dan Anda harus melakukannya!), rebasing adalah pilihan terbaik di sini: semua perubahan saling membangun secara teratur dan linier. Anda tidak memerlukan alat penampil riwayat yang mewah untuk memahami hubungan antar cabang.

Pertimbangkan riwayat proyek berikut yang menggunakan penggabungan antar cabang:

Menggunakan strategi penggabungan, daripada strategi rebasing, dapat menghasilkan sejarah yang berbelit-belit.

Bandingkan ini dengan proyek yang mengubah semua perubahan dan melarang penggabungan bahkan ketika menggabungkan fitur ke dalam cabang utama :

Sebuah proyek yang menggunakan rebasing dan sepenuhnya melarang penggabungan muncul sebagai garis waktu linier tunggal.

Yang pertama, hubungan antara perubahan harus diplot, direnungkan, dan diuraikan; yang terakhir, Anda hanya mengalir maju dan mundur dalam waktu.

Beberapa orang mungkin berpendapat bahwa sebenarnya rebasinglah yang menghancurkan sejarah; bahwa Anda kehilangan riwayat perubahan yang dilakukan untuk mendapatkan beberapa kode dalam bentuk akhirnya sebelum digabungkan. Namun riwayat semacam ini jarang berguna dan sangat bergantung pada pengembang: perjalanan satu orang mungkin berbeda dari yang berikutnya, tetapi yang penting adalah melihat serangkaian komitmen dalam riwayat yang mencerminkan perubahan akhir yang mereka wakili… proses apa pun yang diperlukan untuk mendapatkannya di sana. Ya, ada kasus khusus di sini di mana komit gabungan tidak dapat dihindari, tetapi harus menjadi pengecualian. Dan sering kali skenario yang menyebabkan hal ini (seperti cabang fitur berumur panjang yang digunakan bersama oleh beberapa anggota tim) dapat dihindari menggunakan alur kerja yang lebih baik (seperti menggunakan flag fitur alih-alih cabang fitur bersama ) .

Argumen balasan

Tentu saja ada argumen yang dapat diajukan untuk menentang pendekatan ini dan saya telah banyak berdiskusi dengan orang-orang yang tidak setuju dengannya. Poin-poin ini bukannya tanpa manfaat dan seperti yang saya sebutkan di awal artikel, tidak ada cara yang "benar" untuk menyusun komitmen. Saya ingin dengan cepat menyoroti beberapa poin yang saya dengar dan memberikan pemikiran saya pada masing-masing poin.

“Mengkhawatirkan struktur komit sangat memperlambat pengembangan.”

Ini adalah salah satu poin paling umum yang pernah saya dengar menentang pendekatan ini. Memang benar bahwa akan membutuhkan waktu ekstra bagi pengembang yang menulis kode untuk mempertimbangkan dan membagi perubahan mereka dengan hati-hati. Namun, ini juga berlaku untuk jenis proses tambahan lainnya yang dimaksudkan untuk melindungi dari kelemahan yang melekat pada kecepatan yang diprioritaskan dan, dalam jangka panjang, mungkin tidak menyelamatkan tim secara keseluruhan sepanjang waktu. Misalnya, argumen bahwa pengembangan akan diperlambat digunakan oleh tim yang tidak menulis pengujian unit, tetapi tim yang sama ini perlu menghabiskan lebih banyak waktu untuk memperbaiki kode yang rusak dan menguji refactor secara manual. Dan, begitu sebuah tim terbiasa membagi perubahan mereka dengan cara ini, waktu ekstra yang ditambahkan sangat berkurang karena itu hanya menjadi bagian dari proses pengembangan normal.

“Proyek saya menggunakan alat yang bahkan tidak memungkinkan perubahan pemformatan yang sepele.”

Saya setuju bahwa ini adalah cara yang bagus untuk meminimalkan kerugian yang disebabkan oleh churn kode terkait pemformatan. Sebagai pengembang Android, saya sangat percaya pada penggunaan pemformat otomatis di seluruh tim dan bersumpah dengan alat seperti ktlint . Namun, saya juga tahu secara langsung dari mengonfigurasi semua alat ini bahwa mereka tidak sempurna dan ada banyak kemungkinan perubahan pemformatan yang sama sekali agnostik. Dan, seperti dibahas di atas, beberapa perubahan sepele bukan sekadar perubahan pemformatan, seperti menyusun ulang kode. Akan selalu ada perubahan kode sepele yang dapat dilakukan dan oleh karena itu harus ada rencana cara terbaik untuk menghadapinya.

“Tidak semua situs hosting git mengizinkan Permintaan Tarik dengan banyak komitmen.”

Ini sangat benar! Rekomendasi saya terutama didasarkan pada penggunaan alat seperti GitHub dan GitLab yang memungkinkan PR memiliki komitmen sebanyak yang Anda inginkan, tetapi ada alat seperti Gerrit yang tidak. Dalam hal ini, anggap saja setiap komit sebagai PR-nya sendiri. Ini memang memperkenalkan lebih banyak overhead untuk penulis (dan kadang-kadang untuk pengulas) tetapi saya percaya bahwa dalam jangka panjang itu sepadan dengan usaha. Bahkan mungkin ada cara untuk merampingkan proses ini dan menghubungkan PR terpisah ini satu sama lain, seperti menggunakan "perubahan yang bergantung" di Gerrit.

“Satu komit memastikan bahwa semua perubahan dikompilasi dan lulus tes.”

Ini juga poin yang sangat bagus. Pemeriksaan otomatis yang berjalan di situs hosting git biasanya hanya berjalan terhadap seluruh rangkaian perubahan, bukan untuk setiap komit individu. Jika ada komit yang rusak di sepanjang jalan yang diperbaiki oleh perubahan selanjutnya, tidak ada cara untuk mendeteksi ini secara otomatis. Anda ingin setiap komit dapat berdiri sendiri jika suatu hari nanti Anda perlu kembali dan menguji status kode pada saat itu untuk melacak bug, dll. Sebagai aturan yang dipahami, setiap komit harus diwajibkan dalam a multi-commit PR untuk mengkompilasi dan lulus tes yang relevan, tetapi tidak ada cara untuk menegakkan ini secara ketat (selain membuat masing-masing melakukan PR-nya sendiri). Ini membutuhkan kewaspadaan tetapi hanya sesuatu yang perlu ditimbang terhadap manfaat yang datang dengan memisahkan kode.

“Satu komit memberikan konteks paling banyak untuk semua perubahan.”

Yang ini adalah poin yang menarik. Sementara situs hosting git seperti GitHub memungkinkan komentar ditambahkan secara massal ke sekelompok komitmen sebagai bagian dari deskripsi PR, tidak ada hal seperti itu di git itu sendiri. Ini berarti bahwa tautan antara komit yang ditambahkan dalam PR yang sama tidak sepenuhnya merupakan bagian dari sejarah. Untungnya, situs seperti GitHub memiliki fitur yang menambahkan tautan ke PR yang menghasilkan komit saat melihatnya secara terpisah:

#74 di sini adalah tautan kembali ke Permintaan Tarik yang menghasilkan komit ini, memungkinkan komit terkait dari PR yang sama untuk dilihat.

Meskipun ini tidak membantu seperti memiliki tautan ini dalam riwayat git itu sendiri, untuk banyak proyek, ini adalah cara yang memadai untuk melacak hubungan antara komit.

Pikiran Akhir

Saya berharap dapat meyakinkan Anda bahwa membagi perubahan kode menjadi komit berbeda dari beberapa jenis memiliki manfaat bagi semua orang dalam proses pengembangan:

  • Ini dapat membantu penulis memperbaiki struktur kode dan menyampaikan konten perubahan dengan lebih baik.
  • Ini dapat membantu peninjau kode lebih cepat meninjau kode dan mengurangi kelelahan mental dengan memungkinkan mereka memusatkan perhatian pada perubahan yang terpisah dan bermakna.
  • Ini dapat membantu siapa pun yang melihat kembali riwayat kode untuk menemukan perubahan logis dan bug lebih cepat dan juga mengurangi beban mental yang timbul karena membaca riwayat dalam jumlah besar.

Brian bekerja di Livefront , di mana dia selalu berusaha membuat sedikit lebih banyak sejarah (git).