35 poin oleh GN⁺ 2024-12-23 | 4 komentar | Bagikan ke WhatsApp
  • Perangkat lunak modern sering diperbarui melalui continuous deployment (CD) dan pengujian otomatis (CI), tetapi "perangkat lunak yang digunakan dalam jangka panjang" memerlukan pendekatan yang berbeda
    • Contoh: pembangkit listrik tenaga nuklir, pesawat, alat pacu jantung, sistem pemilu, dll.
    • Di bidang yang mengutamakan keandalan dan stabilitas, stabilitas dan perubahan yang dapat diprediksi lebih disukai daripada perubahan terus-menerus

Prinsip inti pengembangan perangkat lunak jangka panjang

Dependensi

  • Dependensi perangkat lunak merupakan faktor penting untuk keberhasilan jangka panjang
  • Perangkat lunak harus mempertimbangkan interaksi dengan dunia luar, dan pilihan mendasar seperti bahasa pemrograman sangat penting
  • Memahami lapisan dependensi perangkat lunak
    • Dunia luar: perangkat lunak klien yang tidak dapat kita kendalikan (misalnya browser, dll.).
    • Pilihan mendasar: elemen yang hanya bisa diubah dengan menulis ulang seluruh stack, seperti bahasa pemrograman.
    • Framework: seperti Spring Framework, React, dan lain-lain, yang terikat kuat dengan codebase. Dapat diubah, tetapi biayanya sangat tinggi.
    • Database: sebagian besar dapat diganti, tetapi memerlukan penyesuaian detail dan pekerjaan tambahan.
    • Library helper: library yang dapat diganti dan menyediakan fungsi tertentu.
  • Seiring waktu, dependensi dan dunia luar berubah:
    • Perubahan pada dependensi dapat menyebabkan modifikasi kode atau perubahan perilaku.
    • Rilis major version baru dapat menimbulkan masalah kompatibilitas.
    • Ada risiko proyek dihentikan atau menghilang.
    • Risiko keamanan: dependensi dapat disusupi oleh pelaku jahat (npm, PyPI, dll.).
    • Komersialisasi: pemilik baru dari venture capital (VC) mengubahnya menjadi layanan berbayar.
    • Masalah konflik antar dependensi.
  • Hal-hal yang perlu diperiksa saat memilih dependensi untuk penggunaan jangka panjang:
    • Tingkat teknis: apakah kualitas dapat dinilai dengan melihat source code.
    • Basis pengguna: periksa siapa yang menggunakannya.
    • Tujuan pengembangan: pahami siapa pengembangnya dan apa tujuannya.
    • Dukungan pendanaan: apakah ada pendanaan dan dari mana sumbernya.
    • Pemeliharaan: periksa apakah security release dilakukan secara berkala.
      • Kemungkinan komunitas mengambil alih pemeliharaan.
      • Apakah saya bisa memeliharanya sendiri.
      • Apakah perlu memastikan keberlanjutan proyek melalui dukungan pendanaan bila diperlukan.
    • Dependensi dari dependensi:
      • Riwayat keamanan dari dependensi turunan juga perlu ditinjau.
  • Pendekatan yang realistis
    • Membatasi dependensi:
      • Proyek dengan lebih dari 1600 dependensi sangat mungkin mengalami perubahan kode yang tajam dan menjadi tidak stabil.
      • Dalam proyek dengan sangat banyak dependensi, bahkan sulit mengetahui kode apa yang sebenarnya didistribusikan.
    • Penambahan secara hati-hati:
      • Saat menambahkan dependensi, berikan tingkat kesulitan teknis agar ada waktu peninjauan yang alami.
      • Dalam proyek jangka panjang, sebaiknya hindari dependensi yang tidak benar-benar diperlukan.

Dependensi runtime

  • Pembahasan sebelumnya terbatas pada dependensi build/kompilasi.
  • Namun, proyek modern sering kali juga mencakup dependensi runtime:
    • Contoh: Amazon S3, Google Firebase.
    • Sebagian dianggap hampir seperti standar de facto (seperti S3).
    • Tetapi sebagian besar dependensi runtime sangat cenderung menyebabkan lock-in ke layanan tertentu.
  • Bahkan 10 tahun kemudian, mencari alternatif yang bisa menggantikan layanan yang digunakan saat ini akan menimbulkan biaya yang sangat tinggi.
  • Daftar dependensi pada layanan pihak ketiga perlu diminimalkan atau dikosongkan:
    • Terutama dalam pengembangan perangkat lunak cloud native, penggunaan banyak layanan pihak ketiga tingkat lanjut adalah hal yang umum.
    • Untuk proyek jangka panjang, dependensi seperti ini membawa risiko tinggi.
  • Dependensi pada layanan build-time juga merupakan faktor penting:
    • Contoh: jika npm install tidak lagi berfungsi, maka membangun perangkat lunak itu sendiri menjadi mustahil.
    • Ini dapat sangat menurunkan kemampuan pakai ulang proyek.
  • Tinjau dependensi runtime secara menyeluruh:
    • Sadari potensi lock-in, lalu kurangi atau hilangkan dependensi tersebut.
  • Pastikan keberlanjutan jangka panjang:
    • Pertimbangkan sejak awal kemungkinan penggantian cloud atau layanan pihak ketiga.

Test, test, dan test lagi

  • Kebutuhan akan test adalah prinsip dasar yang disepakati semua orang:
    • Tulis sebanyak mungkin test.
    • Tidak semua test memiliki nilai yang sama, tetapi hampir tidak pernah ada penyesalan karena memiliki test.
  • Khususnya pada proyek dengan banyak dependensi, test itu wajib:
    • Jika dependensi berubah atau mengalami drift, test membantu mendeteksi masalah lebih dini.
  • Peran test
    • Membantu pemecahan masalah:
      • Memungkinkan penyesuaian cepat sesuai situasi perubahan.
    • Mendukung refactoring:
      • Memberikan rasa percaya diri saat menghapus atau mengubah dependensi kode.
    • Berguna untuk pemeliharaan jangka panjang:
      • Bahkan setelah pengembangan berhenti lebih dari 3 tahun, test dapat memastikan sistem masih berfungsi.
      • Memverifikasi apakah fungsi tetap terjaga pada compiler, runtime, dan sistem operasi baru.
  • Test bukan biaya, melainkan investasi
    • Tulis lebih banyak test:
      • Test adalah fondasi pemeliharaan dan stabilitas.
      • Saat memodifikasi atau memperluas kode, test menjadi penopang mental yang besar.

Kompleksitas: bos terakhir dalam pengembangan perangkat lunak

  • Kompleksitas adalah musuh utama pengembangan perangkat lunak:
    • Bahkan developer atau tim terbaik pun bisa runtuh karena kompleksitas.
    • Karena pengaruh entropi dan perilaku manusia, kompleksitas selalu meningkat.
    • Jika tidak dikelola secara sadar, proyek bisa jatuh ke kondisi tidak dapat dipelihara.
  • Korelasi antara kompleksitas dan jumlah kode
    • Jumlah kode dan kompleksitas:
      • Saat kode masih sedikit, meski agak kompleks tetap masih bisa dikelola.
      • Semakin banyak kode, semakin sederhana ia harus dijaga agar tetap terkendali.
      • Kompleksitas yang masih bisa dikelola harus berada dalam kapasitas tim dan di dalam "segitiga hijau".
    • Batas kompleksitas:
      • Menambah jumlah anggota tim atau merekrut developer yang sangat hebat pun tetap ada batas dalam menangani kompleksitas.
      • Jika melewati batas itu, proyek akan masuk ke kondisi yang tidak bisa dipelihara.
  • Alasan kode selalu bergerak ke 'kanan atas' (dalam grafik):
    • Permintaan fitur yang semakin banyak.
    • Upaya optimasi yang tidak perlu.
    • Saat memperbaiki bug, alih-alih mengurangi kompleksitas yang ada, justru menambah kode baru.
  • Biaya dari desain API yang buruk:
    • Contoh: fungsi CreateFile pada sebagian besar kasus sebenarnya tidak membuat file.
    • Kebingungan seperti ini meningkatkan beban kognitif tambahan dan kemungkinan kesalahan.
  • Strategi mengelola kompleksitas
    • Lakukan refactoring lebih awal, dan sering:
      • Hapus kode yang tidak perlu dan investasikan waktu untuk penyederhanaan.
    • Berinvestasi pada test:
      • Semakin banyak test, semakin mudah pekerjaan mengurangi kompleksitas.
    • Pentingnya manajemen kompleksitas:
      • Jika tidak berupaya lebih dulu untuk menyederhanakan, proyek jangka panjang pada akhirnya berisiko jatuh ke kondisi "tidak dapat dipelihara".

Tulis kode yang membosankan dan sederhana. Lebih sederhana lagi. Dan lebih membosankan lagi.

"Debugging dua kali lebih sulit daripada menulis program. Jadi jika saat menulis kode Anda membuatnya secerdas mungkin, bagaimana caranya Anda akan men-debug-nya?" - Brian Kernighan

  • Tulis kode yang sangat membosankan dan jelas:
    • Pilih kode yang naif tetapi dapat dipahami secara intuitif.
    • "Optimasi prematur adalah akar dari segala kejahatan."
  • Optimasi hanya jika benar-benar diperlukan:
    • Jika ternyata terlalu sederhana dan menjadi masalah, menambahkan kompleksitas nanti bukanlah hal yang sulit.
    • Momen itu mungkin bahkan tidak pernah datang.
  • Hindari menulis kode kompleks:
    • Tunggu sampai saat itu benar-benar diperlukan.
    • Kemungkinan menyesal karena menulis kode yang sederhana sangat kecil.
  • Kode atau fitur berperforma tinggi bisa jadi hanya bekerja dalam lingkungan tertentu.
    • Contoh:
      • LMDB: PowerDNS mengalami banyak kesulitan sebelum dapat menggunakannya secara stabil.
      • RapidJSON: library JSON dengan akselerasi SIMD. Performanya sangat baik, tetapi syarat penggunaannya ketat.
  • Bahkan jika Anda yakin "saya bisa mengatasi batasan ini":
    • Mungkin tahun ini bisa, tetapi 5 tahun lagi Anda atau developer penerus bisa kesulitan.
    • Prinsip yang sama berlaku untuk bahasa pemrograman yang kompleks.
  • Kesimpulan:
    • Sederhanakan kode:
      • Buat benar-benar sederhana. Bahkan lebih sederhana lagi.
    • Tunda optimasi:
      • Kompleksitas bisa ditambahkan saat diperlukan, tetapi jika sejak awal dibuat kompleks, pemeliharaannya akan menjadi sulit.

Pengembangan perangkat lunak berbasis LinkedIn

  • Realitas vs. ideal
    • Pendekatan ideal: pemilihan dependensi memerlukan evaluasi dan peninjauan menyeluruh (gunakan checklist yang disajikan di atas).
    • Pendekatan realistis: kadang ada kecenderungan mencoba teknologi yang menarik, lalu jika berhasil langsung dipakai begitu saja.
  • Alasan terasa menarik
    • Teknologi yang direkomendasikan tokoh terkenal atau influencer di LinkedIn.
    • "Framework terbaru" yang dipuji habis-habisan di komunitas seperti Hacker News.
  • Teknologi yang sedang tren kurang memiliki validasi jangka panjang:
    • Mungkin tidak cocok untuk "proyek perangkat lunak yang harus dipelihara lebih dari 10 tahun".
    • Teknologi baru pada tahap awal lebih berpotensi memunculkan masalah dari sisi stabilitas dan maintainability.
  • Rekomendasi
    • Gunakan hanya di area eksperimental:
      • Uji dulu teknologi baru pada proyek kecil atau area non-kritis.
    • Pertimbangkan efek Lindy:
      • Umur suatu teknologi cenderung sebanding dengan lamanya ia telah digunakan sampai sekarang.
      • Semakin tua teknologinya, semakin besar harapan akan stabilitas jangka panjang.
  • Teknologi baru memang menarik, tetapi untuk proyek jangka panjang, teknologi yang sudah teruji dan stabil lebih cocok.

Logging, telemetri, dan performa

  • Jika perangkat lunak tidak terus-menerus diperbarui atau di-deploy:
    • Kemungkinan besar Anda tidak akan menerima feedback langsung saat website rusak.
    • Setelah deployment, bisa butuh waktu lama sampai masalah nyata benar-benar diselesaikan.
  • Terapkan logging dan telemetri yang menyeluruh sejak rilis awal:
    • Catat performa, kegagalan, dan riwayat aktivitas perangkat lunak.
    • Data yang terakumulasi seiring waktu sangat berguna untuk menyelesaikan bug yang jarang terjadi.
  • Masalah akibat logging yang kurang:
    • UI pengguna telah di-deploy, lalu ada pengguna yang melaporkan masalah setelah membuat 3000 folder.
    • Pengguna hanya berkata "tidak berfungsi", sehingga butuh berbulan-bulan untuk menemukan akar masalah.
    • Jika ada logging performa dan telemetri, masalah itu bisa diselesaikan jauh lebih cepat.
  • Logging dan telemetri itu wajib:
    • Rancang agar aktivitas perangkat lunak dapat dimonitor secara menyeluruh.
    • Sangat membantu dalam menyelesaikan masalah tak terduga selama deployment dan pemeliharaan jangka panjang.

Dokumentasi

  • Pentingnya dokumentasi:
    • Bukan sekadar menulis dokumentasi API dengan baik, tetapi juga menjelaskan "mengapa dirancang seperti ini".
    • Catat ide dan filosofi tentang bagaimana sistem bekerja.
    • Alasan pemisahan solusi dan dasar dari keputusan desain yang tidak intuitif harus ditinggalkan dalam catatan.
  • Materi yang berguna selain dokumen arsitektur:
    • Postingan blog internal: tempat developer berbagi diskusi bebas tentang desain sistem.
    • Wawancara tim: rekaman percakapan yang memuat latar belakang keputusan desain.
    • Dokumen seperti ini memungkinkan transfer pengetahuan dalam tim meski waktu telah berlalu.
  • Tinggalkan komentar dalam kode:
    • Terlepas dari tren yang mengatakan "kode yang baik tidak membutuhkan komentar", komentar yang menjelaskan 'mengapa' kode itu ada tetap penting.
    • Yang penting adalah menjelaskan alasan keberadaan fungsi tertentu.
  • Menulis commit message:
    • Commit message adalah inti dari catatan kerja. Melalui itu, alasan perubahan kode dapat ditelusuri.
    • Sediakan lingkungan agar pengguna dapat dengan mudah melihat commit message.
  • Sisihkan waktu untuk dokumentasi:
    • Pada hari ketika pengembangan tidak berjalan lancar, luangkan waktu untuk meninggalkan komentar dan catatan yang berguna.
    • Secara tim, alokasikan waktu secara berkala untuk dokumentasi.
  • Catat mengapa desainnya seperti itu:
    • Tujuh tahun kemudian, materi yang bisa menyampaikan filosofi dan latar belakang kepada tim baru akan sangat berharga.
  • Tinggalkan sejarah melalui komentar dan commit message:
    • Ini adalah elemen penting bukan hanya selama pengembangan, tetapi juga untuk pemeliharaan jangka panjang.

Komposisi tim

  • Keberlanjutan tim dan keberhasilan jangka panjang perangkat lunak:
    • Beberapa perangkat lunak dirancang untuk didukung selama 80 tahun. Dalam proyek jangka panjang seperti ini, menjaga keberlangsungan tim adalah kunci.
    • Dalam lingkungan pengembangan modern, rata-rata masa kerja sekitar 3 tahun sudah dianggap lama.
    • Dokumentasi dan test yang baik dapat sedikit mengimbangi pergantian tim, tetapi ada batasnya.
  • Keuntungan masa kerja panjang:
    • Mempertahankan anggota tim lebih dari 10 tahun:
      • Penting untuk mempekerjakan mereka sebagai karyawan tetap dan mengelola developer dengan baik.
      • Ini dianggap sebagai "hack" penting untuk keberhasilan proyek jangka panjang.
  • Masalah ketergantungan pada outsourcing:
    • Developer outsourcing sering menyerahkan kode ke sistem lalu pergi.
    • Jika tujuannya adalah kualitas perangkat lunak yang bisa bertahan lebih dari 10 tahun, ini adalah cara yang sangat tidak efisien.
  • Ciptakan lingkungan agar anggota tim dapat bertahan bersama dalam jangka panjang.
  • Diperlukan strategi untuk meminimalkan ketergantungan pada konsultan eksternal dan meningkatkan keberlanjutan tim internal.

Pertimbangkan open source

  • Keuntungan open source:
    • Menjaga kualitas kode melalui peninjauan eksternal:
      • Sorotan dari luar menuntut standar yang lebih tinggi dari para developer.
    • Mekanisme yang kuat untuk mempertahankan standar kode yang lebih baik.
  • Realitas dalam proses menyiapkan open source:
    • Perusahaan atau pemerintah sering mengklaim bahwa menyiapkan open source membutuhkan waktu berbulan-bulan hingga bertahun-tahun.
    • Alasannya:
      • Secara internal, menulis kode yang memalukan untuk dipublikasikan ke luar adalah hal yang umum.
      • Karena kode perlu dirapikan sebelum dibuka sebagai open source.
  • Evaluasi kelayakan penerapan:
    • Open source tidak selalu menjadi pilihan yang memungkinkan.
    • Jika memungkinkan, ini adalah cara yang baik untuk meningkatkan kualitas kode dan transparansi.
  • Open source adalah strategi penting yang sebaiknya dimanfaatkan bila memungkinkan.
  • Sorotan dari luar dan standar yang tinggi membantu menjaga proyek tetap berada pada arah yang benar.

Pemeriksaan kesehatan dependensi

  • Masalah perubahan dependensi:
    • Dependensi dapat berubah atau menyimpang dari ekspektasi seiring waktu.
    • Jika dibiarkan:
      • muncul bug
      • build gagal
      • dan hasil mengecewakan lainnya.
  • Disarankan melakukan pemeriksaan kesehatan berkala:
    • Pemeriksaan dependensi berkala:
      • Memberi peluang untuk menemukan masalah lebih awal.
      • Juga memungkinkan menemukan fitur baru pada dependensi yang dapat menyederhanakan kode atau membuka peluang menghapus dependensi lain.
    • Pentingnya pemeliharaan preventif:
      • Jika Anda tidak menjadwalkan waktu pemeriksaan sendiri, pada akhirnya Anda akan dipaksa meluangkan waktu saat masalah benar-benar terjadi.
  • Analogi pemeliharaan:
    • Pepatah para mekanik:
      • "Rencanakan sendiri waktu untuk pemeliharaan. Jika tidak, peralatan yang akan merencanakannya untuk Anda."
  • Pemeriksaan dependensi secara berkala adalah aktivitas penting untuk stabilitas dan efisiensi perangkat lunak jangka panjang.
  • Gunakan ini sebagai kesempatan untuk menyelesaikan masalah lebih awal dan menemukan perubahan positif.

Referensi buku utama

Terakhir

Rekomendasi utama untuk pengembangan perangkat lunak jangka panjang:

  • Pertahankan kesederhanaan:
    • Sederhana, dan lebih sederhana lagi! Karena kompleksitas bisa ditambahkan saat diperlukan, jangan membuatnya terlalu rumit sejak awal.
    • Untuk menjaga kesederhanaan, diperlukan refactoring berkala dan penghapusan kode.
  • Pikirkan dependensi dengan hati-hati:
    • Semakin sedikit dependensi, semakin baik. Tinjau dan audit dengan cermat.
    • Jika Anda tidak bisa mengaudit 1600 dependensi, rencananya perlu dipikirkan ulang.
    • Hindari pilihan yang mengikuti tren atau hype (misalnya pengembangan berbasis LinkedIn).
    • Pemeriksaan dependensi berkala: terus monitor kondisi dependensi.
  • Test, test, dan test lagi:
    • Menangkap perubahan dependensi tepat waktu.
    • Memberi rasa percaya diri saat refactoring dan membantu menjaga kesederhanaan.
  • Dokumentasi:
    • Dokumentasikan bukan hanya kode, tetapi juga filosofi, ide, dan latar belakang "mengapa dilakukan seperti ini".
    • Ini menjadi aset berharga bagi anggota tim di masa depan.
  • Pertahankan tim yang stabil:
    • Untuk investasi proyek jangka panjang, pertimbangkan pekerjaan jangka panjang.
    • Dukung agar anggota tim dapat berkomitmen pada proyek selama bertahun-tahun.
  • Pertimbangkan open source:
    • Jika memungkinkan, gunakan open source untuk menjaga standar kode yang lebih tinggi.
  • Log dan telemetri performa:
    • Berperan penting dalam mendeteksi dan menyelesaikan masalah lebih awal.
  • Rekomendasi ini mungkin tidak baru, tetapi karena ditekankan oleh developer berpengalaman, semuanya layak dipikirkan secara mendalam.

4 komentar

 
kandk 2024-12-30

Kekuatan rekayasa yang paling penting adalah memisahkan layer yang mengutamakan stabilitas dan layer yang mengutamakan kecepatan, lalu menentukan bagaimana menangani hubungan di antara keduanya.
Jika Toss hanya mengejar stabilitas, tentu tidak akan berbeda dengan bank-bank lain.

 
kandk 2024-12-30

Yang berisiko juga SpaceX. Tesla juga begitu..

 
aer0700 2024-12-25

Apakah masalahnya adalah pengembangan yang didorong oleh resume?

 
GN⁺ 2024-12-23
Opini Hacker News
  • Secara aktif memperbarui toolchain merupakan bagian penting dari proses pengembangan. Banyak perusahaan menyingkirkan upgrade toolchain dari prioritas, yang akhirnya menimbulkan masalah seperti kerentanan keamanan. Buat branch untuk setiap rilis compiler atau build system terbaru guna memeriksa status build, dan jika ada error, anggap itu sebagai bug lalu tangani segera. Ini membantu memodernisasi dan merapikan codebase secara bertahap dengan fitur bahasa terbaru.

  • Dependensi pihak ketiga sering kali mengecewakan dalam jangka panjang. Pada proyek baru, dependensi pihak ketiga bisa dipakai untuk menyelesaikan masalah dalam jangka pendek, tetapi dalam jangka panjang sebaiknya digantikan dengan kode buatan sendiri.

  • Perlu melakukan vendoring dependensi dan mengelolanya lewat code review. Sering kali kualitas kode pihak ketiga rendah sehingga menulisnya sendiri justru lebih baik.

  • Sedang mengerjakan proyek yang menargetkan skalabilitas jangka panjang dengan menggunakan Qt, CMake, dan C++ modern. Stack teknologi seperti ini terus menyediakan fitur dan peningkatan.

  • Bekerja dengan Emacs Lisp terasa sebagai pengalaman yang menyegarkan. Keunggulannya adalah tetap berjalan stabil meski library tidak diperbarui. Pengalaman menggunakan Gatsby dan Node cukup sulit karena masalah update.

  • Penting untuk menulis kode yang sederhana. Kode kompleks hanya ditulis saat memang diperlukan, dan kode sederhana tidak akan disesali.

  • Dokumentasi sistem dan kode itu penting. Semakin banyak pengalaman dalam pengembangan perangkat lunak, semakin terasa pentingnya dokumentasi.

  • Testing memainkan peran penting dalam perencanaan. Perlu mencontoh cara NASA mengembangkan perangkat lunak dengan fokus besar pada menemukan kesalahan pemrograman. Dalam pengembangan software medis, interpretasi dihindari dan alokasi memori dinamis tidak digunakan.

  • Cara terbaik untuk menulis software yang tahan lama adalah menulis kode yang "membosankan". Hindari dependensi dan pegang teguh hal-hal dasar.

  • Ada pengalaman kesulitan dengan masalah dependensi di Python. Ini disebut "DLL Hell", dan COM pernah mencoba menyelesaikannya, tetapi tidak berhasil.

  • Praktik yang diterapkan pada software industri tidak cukup kokoh untuk diterapkan begitu saja pada software umum. Para engineer berusaha memitigasi risiko, dan kami berfokus pada mitigasi risiko.