Kapan perpustakaan 'inti' adalah ide yang buruk?


8

Ketika mengembangkan perangkat lunak, saya sering memiliki pustaka 'inti' terpusat yang berisi kode praktis yang dapat dibagikan dan dirujuk oleh berbagai proyek.

Contoh:

  • seperangkat fungsi untuk memanipulasi string
  • ekspresi reguler yang umum digunakan
  • kode penempatan umum

Namun beberapa rekan saya tampaknya berpaling dari pendekatan ini. Mereka memiliki masalah seperti overhead pemeliharaan kode pengujian ulang yang digunakan oleh banyak proyek setelah bug diperbaiki. Sekarang saya mempertimbangkan kembali kapan saya harus melakukan ini.

Apa masalah yang membuat penggunaan pustaka 'inti' menjadi ide yang buruk?


Memiliki perpustakaan inti adalah ide yang baik ketika kode tersebut biasanya digunakan kembali, tetapi perlu diuji secara agama, termasuk pengujian unit dan teknologi ruang angkasa lainnya.
Pekerjaan

Itu ide yang baik ketika telah stabil dan tidak berubah.
Martin York

Kekhawatiran pengujian ulang sangat valid. Apakah Anda ingin mengetahui Anda melanggar proyek pemeliharaan 6 bulan yang lalu?

Saya tidak bisa membayangkan menulis ulang semua kode utilitas saya setiap kali saya membutuhkannya.

Jawaban:


12

Pustaka inti buruk ketika mereka mulai menderita fitur creep, dan sangat buruk ketika mereka tidak terawat dengan baik.

Anda mungkin menemukan artikel ini menarik untuk sudut pandang diperpanjang (yang saya setujui dengan sepenuh hati):

http://www.yosefk.com/blog/redundancy-vs-dependencies-which-is-worse.html


Don Knuth: "Bagi saya, 'kode yang dapat diedit ulang' jauh, jauh lebih baik daripada kotak hitam atau toolkit yang tidak tersentuh ... Anda tidak akan pernah meyakinkan saya bahwa kode yang dapat digunakan kembali sebagian besar bukan ancaman."


3

Menggunakan ide perpustakaan inti menjadi buruk ketika beberapa proyek bergantung padanya, seperti mengatakan Anda tidak boleh menggunakan jQuery untuk web, libxml di aplikasi * nix Anda, atau kerangka kerja atau pustaka lainnya. Lihatlah seluruh ekosistem perkembangan modern (KERING, OOP, dll) dan setiap aplikasi dibangun dari sekumpulan perpustakaan dan kerangka kerja.

Apa yang bisa buruk adalah jika Anda tidak memiliki semua jenis unit test, Anda tidak melakukan uji regresi dan Anda tidak menggunakan semua jenis API / ABI dengan perpustakaan Anda. Jika semua aplikasi Anda memiliki tes yang tepat, perpustakaan Anda memiliki pengujian yang tepat, dan Anda memastikan jika Anda melanggar panggilan fungsi Anda memperbarui nomor versi api dengan tepat.

Untuk cakupan lengkap, yang mungkin orang inginkan adalah ketika perubahan dilakukan ke Perpustakaan, Anda dapat menjalankan serangkaian tes yang akan memverifikasi API belum rusak, dan bahwa pelaksanaan semua kode bebas bug. Kemudian Anda dapat menarik pembaruan perpustakaan terbaru ke dalam aplikasi Anda dan menjalankan serangkaian tes yang sama. Jika Anda memperbarui API, maka itu harus didokumentasikan sehingga Anda tahu apa yang perlu Anda lakukan dalam aplikasi Anda untuk memperbaruinya. Either way, ketika Anda menjalankan tes untuk aplikasi Anda, maka Anda dapat menjadi percaya diri seperti Anda dalam tes Anda bahwa tidak ada yang rusak.

Saat menggunakan jquery, mootools, pustaka atau kerangka javascript apa pun, Anda tidak bisa begitu saja menggunakan versi baru, sayangnya Anda bahkan tidak bisa dengan rilis minor 1.6.z kadang-kadang.


3

Mereka memiliki masalah seperti overhead pemeliharaan kode pengujian ulang yang digunakan oleh banyak proyek setelah bug diperbaiki.

Jika Anda memiliki serangkaian unit test komprehensif untuk perpustakaan inti; itu bukan masalah. Tidak ada kode yang akan diperiksa kecuali semua tes lulus. Jika Anda memperkenalkan cacat Anda menulis tes gagal untuk mereproduksi cacat dan memperbaikinya; maka Anda akan selalu menguji kesalahan itu juga. Selama-lamanya.

Juga fungsionalitas yang Anda gambarkan sangat mudah untuk menulis unit test.

Sebagai masalah tambahan, Anda mungkin ingin memiliki lebih dari satu pustaka inti sehingga Anda tidak harus memasukkan kode RegEx kecuali Anda mau.


2

Saya akan menawarkan sedikit berbeda dalam hal ini. Pustaka inti, dalam banyak kasus, adalah ide bagus!

Jika Anda memiliki dua proyek terpisah, mereka harus berada dalam dua repositori kode terpisah. Sekarang mereka bergantung pada fungsi umum. Mari kita pertimbangkan misalnya aplikasi pemrosesan paket. Fungsi umum dapat mencakup:

  • Pengalokasi memori
  • Protokol resolusi alamat
  • Pohon AVL
  • Kode serialisasi untuk protokol biner
  • Array dinamis
  • Daftar hash gaya kernel Linux dengan head yang terhubung sendiri dan node tengah yang terhubung ganda
  • Meja hash
  • Kode pemrosesan tajuk TCP / IP
  • Daftar tertaut biasa dengan kepala tertaut ganda dan simpul tengah tertaut ganda
  • Perpustakaan logging
  • Lain-lain (percayalah, Anda perlu ini untuk hal-hal kecil dan sepele atau jumlah modul Anda yang berbeda akan sama dengan 100!)
  • Pustaka paket capture
  • Paket I / O antarmuka perpustakaan
  • Struktur data paket
  • Memblokir antrian untuk komunikasi antar-thread
  • Generator angka acak
  • Pohon merah-hitam
  • Beberapa jenis implementasi timer

Sekarang, aplikasi pemrosesan paket yang berbeda mungkin memerlukan subset yang berbeda. Haruskah Anda menerapkan satu pustaka inti dengan satu repositori kode sumber, atau haruskah Anda memiliki 18 repositori berbeda untuk masing-masing modul ini? Ingat bahwa modul-modul ini mungkin memiliki inter-dependensi, jadi sebagian besar modul ini mungkin bergantung pada mis. Modul lain-lain.

Saya akan mengklaim bahwa memiliki satu perpustakaan inti adalah pendekatan terbaik. Ini mengurangi overhead dari banyak repositori kode sumber. Ini mengurangi neraka ketergantungan: versi tertentu dari pengalokasi memori mungkin memerlukan versi tertentu dari modul lain-lain. Dan bagaimana jika Anda ingin pengalokasi memori versi 1.7 tergantung pada 2.5 aneka dan AVL tree versi 1.2 tergantung pada aneka 2.6? Anda mungkin tidak dapat menautkan Miscellaneous 2.5 dan Miscellaneous 2.6 secara bersamaan ke program Anda.

Jadi, lanjutkan dan terapkan struktur berikut:

  • Repositori perpustakaan inti
  • Repositori proyek # 1
  • Repositori proyek # 2
  • ...
  • Repositori #N proyek

Saya telah melihat bahwa beralih ke struktur semacam ini dari struktur:

  • Repositori proyek # 1
  • Repositori proyek # 2
  • ...
  • Repositori #N proyek

Telah menyebabkan berkurangnya pemeliharaan dan peningkatan pembagian kode melalui mekanisme non-copypaste.

Saya juga melihat proyek menggunakan struktur berikut:

  • Repositori pengalokasi memori
  • Repositori protokol resolusi alamat
  • Gudang pohon AVL
  • Kode serialisasi untuk repositori protokol biner
  • Repositori array dinamis
  • Daftar hash gaya kernel Linux dengan kepala yang terhubung sendiri dan repositori node tengah yang terhubung ganda
  • Repositori tabel hash
  • Repositori kode pemrosesan tajuk TCP / IP
  • Daftar tertaut biasa dengan kepala tertaut ganda dan repositori simpul tengah tertaut ganda
  • Mencatat repositori perpustakaan
  • Repositori lain-lain (percayalah, Anda memerlukan ini untuk hal-hal kecil dan sepele atau jumlah modul berbeda Anda akan sebesar 100!)
  • Repositori perpustakaan capture paket
  • Paket I / O repositori pustaka antarmuka
  • Repositori struktur data paket
  • Memblokir antrian untuk repositori komunikasi antar utas
  • Repositori generator nomor acak
  • Repositori pohon merah-hitam
  • Beberapa jenis repositori implementasi pengatur waktu
  • Repositori proyek # 1
  • Repositori proyek # 2
  • ...
  • Repositori #N proyek

... dan neraka ketergantungan dan proliferasi jumlah repositori telah menjadi masalah nyata.

Sekarang, haruskah Anda menggunakan pustaka sumber terbuka yang ada alih-alih menulis pustaka Anda sendiri? Anda perlu mempertimbangkan:

  • Masalah lisensi. Kadang-kadang hanya persyaratan untuk memberikan kredit kepada penulis dalam dokumentasi yang disediakan mungkin terlalu banyak, karena 20 perpustakaan biasanya akan memiliki 20 penulis yang berbeda.
  • Dukungan versi sistem operasi yang berbeda
  • Ketergantungan perpustakaan tertentu
  • Ukuran perpustakaan tertentu: apakah terlalu besar untuk fungsi yang disediakan? Apakah ini menyediakan terlalu banyak fitur?
  • Apakah tautan statis mungkin? Apakah penautan dinamis diinginkan?
  • Apakah antarmuka perpustakaan itu yang Anda inginkan? Perhatikan bahwa dalam beberapa kasus menulis pembungkus untuk menyediakan antarmuka yang diinginkan mungkin lebih mudah daripada menulis ulang seluruh komponen sendiri.
  • ... dan banyak, banyak hal lain yang belum saya sebutkan dalam daftar ini

Saya biasanya menggunakan aturan bahwa semuanya di bawah 1000 baris kode yang tidak memerlukan sesuatu di luar keahlian programmer harus diimplementasikan sendiri. Catatan: 1000 baris termasuk unit test. Jadi saya tentu tidak akan menganjurkan menulis 1000 baris kode sendiri jika diperlukan 10.000 baris tambahan untuk tes unit. Untuk program pemrosesan paket saya, ini berarti satu-satunya komponen eksternal yang saya gunakan adalah:

  • Semuanya disediakan oleh distribusi Linux standar, karena begitu banyak baris kode sehingga tidak masuk akal untuk mengimplementasikan kembali Linux. Bagian dari implementasi ulang Linux juga akan melampaui tingkat keahlian saya.
  • Bison / fleksibel karena penguraian LALR berada di luar tingkat keahlian saya dan lebih dari 1000 baris kode. Saya tentu bisa menulis parser keturunan rekursif sendiri, tetapi Bison / flex sangat berguna saya melihat mereka sebagai berguna.
  • Netmap, karena lebih dari 1000 baris dan di luar tingkat keahlian saya
  • Lewati penerapan pengatur waktu berbasis daftar dari DPDK, karena melampaui tingkat keahlian saya meskipun kurang dari 1000 baris kode (meskipun saya memiliki implementasi pengatur waktu alternatif yang tidak menggunakan daftar lompatan)

Beberapa hal yang saya implementasikan sendiri karena sederhana termasuk bahkan hal-hal seperti:

  • MurMurHash
  • SipHash
  • Mersenne Twister

... karena implementasi khusus ini dapat memungkinkan inlining berat, yang mengarah ke peningkatan kinerja.

Saya tidak melakukan kriptografi; jika saya melakukannya, saya akan menambahkan beberapa jenis perpustakaan kripto dalam daftar, karena menulis algoritma kripto Anda sendiri mungkin rentan terhadap serangan waktu cache bahkan jika Anda dapat dengan pengujian unit menyeluruh menunjukkan bahwa mereka kompatibel dengan algoritma resmi.


1

Pustaka inti dapat menjadi buruk ketika banyak proyek bergantung padanya, Anda tidak hanya harus menguji perubahan apa pun pada inti Anda, tetapi Anda juga harus menguji kemunduran setiap proyek bergantung. Kedua, API inti Anda tidak akan pernah bisa berubah karena Anda harus memperbaiki setiap proyek yang bergantung. Semakin banyak proyek yang menggunakan perpustakaan Anda, semakin dalam jebakan.

Masalah lain adalah kecenderungan untuk mulai melemparkan segala sesuatu yang "biasa" ke perpustakaan inti Anda, membengkaknya dan membuatnya lebih sulit untuk menarik bagian-bagian kecil. Saya hanya akan mengatakan bahwa pada suatu waktu saya mendengar tentang suatu tempat yang menjadi takut untuk menyentuh salah satu dari banyak perpustakaan inti mereka, overhead pengujian regresi QA begitu besar.

Alih-alih, mungkin Anda dapat membuat sumber snipet kode untuk membiarkan tim proyek mencari dan menarik kode yang mereka butuhkan dan memisahkan diri dari masalah pemeliharaan atau regresi? Itu yang saya lakukan di rumah.


4
Jauh lebih sulit untuk memperbaiki bug dalam cuplikan kode yang telah disalin dan ditempelkan ke beberapa tempat, bukan?
Alex Angas

Kutipan dari Donald Knuth: "Saya juga harus mengakui bias kuat terhadap mode untuk kode yang dapat digunakan kembali. Bagi saya," kode yang dapat diedit kembali "jauh, jauh lebih baik daripada kotak hitam atau toolkit yang tidak tersentuh. Saya bisa terus dan terus tentang ini. Jika Anda benar-benar yakin bahwa kode yang dapat digunakan kembali itu bagus, saya mungkin tidak akan dapat mempengaruhi Anda, tetapi Anda tidak akan pernah meyakinkan saya bahwa kode yang dapat digunakan kembali sebagian besar bukan ancaman. "
Patrick Hughes

@AlexAngas: Itu benar, tetapi mungkin ada kasus di mana pustaka bermasalah, tetapi hanya berfungsi dengan benar karena pustaka lain memiliki bug halus yang mengimbangi bug pada awalnya. Sementara kedua set bug harus diperbaiki ketika praktis, memiliki salinan kode sumber perpustakaan kedua menjadi bagian dari proyek dengan yang pertama akan berarti bahwa perbaikan bug yang diterapkan untuk kode itu akan menjadi perubahan yang dapat dikenali pada proyek, yang dapat sementara dibatalkan jika rusak (sehingga memungkinkan untuk diidentifikasi sebagai penyebab kerusakan).
supercat

@AlexAngas: Tentu saja, mengidentifikasi perbaikan ke rutinitas kedua sebagai penyebab kerusakan tidak berarti perbaikannya bukan untuk memperbaiki yang kedua, melainkan menunjuk pada fakta bahwa beberapa kode secara keliru bergantung pada perilaku bandel rutin itu. ; penemuan itu akan menjadi kunci untuk menyelesaikan masalah yang sebenarnya secara efisien. Sebaliknya, jika semua orang tahu bahwa kode yang dulu bekerja secara spontan berhenti bekerja, akan sangat sulit untuk melacak apa yang harus dilakukan.
supercat

1

Satu hal yang belum disebutkan adalah bahwa setiap kode akan memiliki ketergantungan pada sesuatu , bahkan jika itu benar-benar berjalan di ROM mikrokontroler yang tertanam; jika pembuat pengontrol mengubah beberapa perilaku yang diandalkan oleh kode, kode tersebut harus dimodifikasi untuk bekerja pada chip yang diproduksi setelah perubahan, atau produsen perangkat yang menggunakan kode harus entah bagaimana memperoleh chip yang melakukan tidak memasukkan perubahan - mungkin membayar harga premium untuk mereka.

Menggunakan pustaka untuk melakukan berbagai fungsi perangkat keras dapat berarti bahwa kode sekarang tergantung pada pustaka yang sebelumnya tidak, tetapi juga dapat menghilangkan ketergantungan antara kode dan perangkat keras. Sebagai contoh, produsen chip mungkin berjanji untuk memasok perpustakaan untuk semua chip sekarang dan masa depan yang akan selalu melakukan fungsi I / O tertentu dengan cara tertentu. Kode yang menggunakan pustaka itu untuk melakukan fungsi-fungsi I / O tersebut akan menjadi bergantung pada pabrikan untuk menyediakan versi pustaka yang sesuai, tetapi tidak lagi tergantung pada pabrikan untuk menggunakan implementasi perangkat keras yang sama dari fungsi-fungsi tersebut.

Sayangnya, seringkali sulit untuk mengetahui pendekatan mana yang tepat untuk kode pemeriksaan masa depan. Saya telah melihat kasus di mana vendor chip mengubah cara perpustakaan bekerja (sehingga dapat mengakomodasi chip baru), bahkan ketika sedang digunakan untuk mengakses chip yang telah berubah. Saya juga melihat kasus di mana produsen chip mengubah cara perangkat kerasnya bekerja, tetapi perpustakaan yang disediakan disesuaikan dengan tepat, sehingga kode yang menggunakan rutinitas perpustakaan akan terus bekerja tanpa perubahan, sementara kode yang mengakses perangkat keras secara langsung harus disesuaikan.

Situasi serupa ada dengan aplikasi Windows. Microsoft terkadang suka mengubah cara aplikasi diperlukan untuk melakukan sesuatu; kode yang menggunakan perpustakaan tertentu untuk hal-hal seperti itu dapat ditingkatkan hanya dengan memperbarui perpustakaan, sedangkan kode yang tidak menggunakan perpustakaan yang diperbarui untuk mereka harus diperbarui secara manual.


1

Saya ingin ikut serta dengan pandangan yang sedikit berbeda, meskipun saya suka Denis de Bernardyjawaban dan artikel terkait tentang meminimalkan dependensi vs meminimalkan redudansi (mereka sangat mencerminkan pemikiran saya sendiri tentang masalah ini di mana saya percaya penggunaan kembali kode adalah tindakan penyeimbang).

Masalah terbesar yang saya miliki dengan coreperpustakaan adalah ini:

Kapan itu selesai? Kapan itu akan mencapai titik stabilitas di mana ia akan melakukan semua yang perlu dilakukan dan secara efektif "dilakukan"?

Dan saya pikir sangat mungkin jawabannya " tidak pernah ". Orang-orang mungkin selalu tergoda untuk menambahkannya karena memodelkan ide yang samar-samar, terutama jika perpustakaan ini baru saja berkembang selama pengembangan perangkat lunak alih-alih memiliki tujuan yang telah diantisipasi dengan baik di muka. Dan mungkin menambahkan ke perpustakaan bukan hal terburuk di dunia karena tidak akan memutus ketergantungan yang ada ke perpustakaan, tetapi mengingat tujuan samar-samar seperti itu, perpustakaan bisa tumbuh semakin eklektik dan jelek, menyediakan fungsi berbeda yang diminati seseorang yang tertarik pada perpustakaan. menggunakan perpustakaan mungkin hanya menemukan sebagian kecil dari itu berlaku untuk kebutuhan mereka.

Ketergantungan pada basis kode Anda idealnya mengalir ke paket yang sangat stabil. Suatu corepaket dapat dengan mudah menemukan dirinya sangat tidak stabil sementara sebagian besar basis kode Anda memiliki ketergantungan mengalir ke sana.

Jadi saya pikir ada baiknya membagi perpustakaan menjadi perpustakaan yang lebih seragam yang ditujukan untuk melakukan sesuatu yang lebih spesifik dari sekadar, "perpustakaan inti dari hal apa pun yang sering dibutuhkan orang" sehingga perpustakaan dapat tumbuh ke arah yang lebih seragam dengan koordinasi yang lebih baik di antara rekan tim Anda tentang apa yang seharusnya dan, yang lebih penting, tidak boleh dilakukan, dan berpotensi mencapai titik stabilitas di mana ia telah teruji dengan baik dan Anda tidak merasa ada hal lain yang perlu ditambahkan padanya agar menjadi relatif " lengkap "dan stabil (seperti dalam, tidak berubah).


0

Menulis perpustakaan untuk hal-hal dasar seperti string dan daftar tertaut cukup konyol di milenium ini. Gunakan bahasa pemrograman termasuk baterai yang memiliki fungsi inti sudah ada di dalamnya.

Jika Anda suka menulis perpustakaan dukungan run-time inti hanya untuk bersenang-senang, maka rancang bahasa pemrograman baru. Jika Anda melakukannya dalam suatu aplikasi, maka pada dasarnya Anda mengembangkan bahasa dari sisinya.

Selain itu, bukankah seseorang sudah menulis N perpustakaan inti yang berbeda dalam bahasa yang Anda gunakan? Meneliti kerangka kerja yang ada dan memilih yang paling cocok mungkin lebih baik menggunakan waktu daripada melakukannya dari awal.


Di bidang saya, pemrosesan paket berkinerja tinggi, tentu saja menggunakan bahasa pemrograman termasuk baterai bukan pilihan. C adalah pilihan yang jelas. Dan tidak, N perpustakaan inti yang berbeda tersedia untuk misalnya tabel hash lebih buruk daripada implementasi kernel Linux. Implementasi kernel Linux, menjadi GPL, mengharuskan Anda untuk mengimplementasikan implementasi yang sama secara manual sendiri tanpa melihat kode sumber kernel Linux, tetapi dengan mengetahui fitur tabel hash canggih yang digunakan implementasi kernel Linux. Ini mungkin berbeda di lapangan, namun.
juhist
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.