Enam Perintah dari Kode yang Baik: Tulis Kode yang Tahan Uji Waktu
Diterbitkan: 2022-03-11Manusia baru bergulat dengan seni dan ilmu pemrograman komputer selama kurang lebih setengah abad. Dibandingkan dengan kebanyakan seni dan ilmu pengetahuan, ilmu komputer dalam banyak hal masih balita, berjalan ke dinding, tersandung kakinya sendiri, dan kadang-kadang melemparkan makanan ke seberang meja.
Sebagai konsekuensi dari relatif muda, saya tidak percaya kita memiliki konsensus tentang apa definisi yang tepat dari "kode yang baik", karena definisi itu terus berkembang. Beberapa orang akan mengatakan "kode yang baik" adalah kode dengan cakupan pengujian 100%. Orang lain akan mengatakan itu super cepat dan memiliki kinerja yang mematikan dan akan berjalan dengan baik pada perangkat keras 10 tahun.
Meskipun ini semua adalah tujuan yang patut dipuji untuk pengembang perangkat lunak, namun saya berani memasukkan target lain ke dalam campuran: pemeliharaan. Secara khusus, "kode yang baik" adalah kode yang mudah dan siap dipelihara oleh suatu organisasi (bukan hanya oleh pembuatnya!) dan akan bertahan lebih lama dari hanya sprint yang ditulisnya. Berikut ini adalah beberapa hal yang saya temukan di karir sebagai insinyur di perusahaan besar dan kecil, di AS dan luar negeri, yang tampaknya berkorelasi dengan perangkat lunak "baik" yang dapat dipelihara.
Perintah #1: Perlakukan Kode Anda Seperti Anda Ingin Kode Orang Lain Memperlakukan Anda
Saya jauh dari orang pertama yang menulis bahwa audiens utama untuk kode Anda bukanlah kompiler/komputer, tetapi siapa pun yang selanjutnya harus membaca, memahami, memelihara, dan meningkatkan kode (yang belum tentu Anda enam bulan dari sekarang ). Setiap insinyur yang layak dibayar dapat menghasilkan kode yang "berfungsi"; apa yang membedakan seorang insinyur yang hebat adalah mereka dapat menulis kode yang dapat dipelihara secara efisien yang mendukung bisnis jangka panjang dan memiliki keterampilan untuk memecahkan masalah dengan sederhana dan dengan cara yang jelas dan dapat dipelihara.
Dalam bahasa pemrograman apa pun, dimungkinkan untuk menulis kode yang baik atau kode yang buruk. Dengan asumsi kita menilai bahasa pemrograman dengan seberapa baik memfasilitasi penulisan kode yang baik (setidaknya harus menjadi salah satu kriteria teratas, bagaimanapun), bahasa pemrograman apa pun bisa "baik" atau "buruk" tergantung pada bagaimana digunakan (atau disalahgunakan ).
Contoh bahasa yang oleh banyak orang dianggap “bersih” dan mudah dibaca adalah Python. Bahasa itu sendiri memberlakukan beberapa tingkat disiplin ruang putih dan API bawaan berlimpah dan cukup konsisten. Yang mengatakan, itu mungkin untuk membuat monster yang tak terkatakan. Misalnya, seseorang dapat mendefinisikan kelas dan mendefinisikan/mendefinisikan ulang/mendefinisikan setiap dan setiap metode pada kelas itu selama runtime (sering disebut sebagai patching monyet). Teknik ini secara alami mengarah ke API yang paling tidak konsisten dan paling buruk adalah monster yang mustahil untuk di-debug. Orang mungkin dengan naif berpikir, "Tentu saja, tetapi tidak ada yang melakukan itu!" Sayangnya, mereka melakukannya, dan tidak butuh waktu lama untuk menjelajahi pypi sebelum Anda menemukan perpustakaan besar (dan populer!) yang (ab) menggunakan patch monyet secara ekstensif sebagai inti dari API mereka. Saya baru-baru ini menggunakan perpustakaan jaringan yang seluruh API berubah tergantung pada status jaringan suatu objek. Bayangkan, misalnya, memanggil client.connect()
dan terkadang mendapatkan kesalahan MethodDoesNotExist
alih-alih HostNotFound
atau NetworkUnavailable
.
Perintah #2: Kode yang Baik Mudah Dibaca dan Dipahami, Sebagian dan Secara Keseluruhan
Kode yang baik mudah dibaca dan dipahami, sebagian dan seluruhnya, oleh orang lain (juga oleh penulis di masa mendatang, berusaha menghindari sindrom “Apakah saya benar-benar menulis itu?” ).
Dengan "sebagian" Maksud saya, jika saya membuka beberapa modul atau fungsi dalam kode, saya harus dapat memahami apa yang dilakukannya tanpa harus juga membaca seluruh basis kode lainnya. Itu harus seintuitif dan mendokumentasikan diri sendiri mungkin.
Kode yang terus-menerus merujuk detail kecil yang memengaruhi perilaku dari bagian lain (tampaknya tidak relevan) dari basis kode seperti membaca buku di mana Anda harus merujuk catatan kaki atau lampiran di akhir setiap kalimat. Anda tidak akan pernah bisa melewati halaman pertama!
Beberapa pemikiran lain tentang keterbacaan "lokal":
Kode yang dienkapsulasi dengan baik cenderung lebih mudah dibaca, memisahkan masalah di setiap level.
Nama penting. Aktifkan sistem Berpikir Cepat dan Lambat 2 cara di mana otak membentuk pikiran dan memasukkan beberapa pemikiran yang aktual dan hati-hati ke dalam variabel dan nama metode. Beberapa detik ekstra dapat membayar dividen yang signifikan. Variabel dengan nama yang baik dapat membuat kode lebih intuitif, sedangkan variabel dengan nama yang buruk dapat menyebabkan headfake dan kebingungan.
Kepintaran adalah musuh. Saat menggunakan teknik, paradigma, atau operasi mewah (seperti pemahaman daftar atau operator ternary), berhati-hatilah untuk menggunakannya dengan cara yang membuat kode Anda lebih mudah dibaca, bukan hanya lebih pendek.
Konsistensi adalah hal yang baik. Konsistensi dalam gaya, baik dalam hal bagaimana Anda menempatkan kawat gigi tetapi juga dalam hal operasi, sangat meningkatkan keterbacaan.
Pemisahan kekhawatiran. Sebuah proyek yang diberikan mengelola sejumlah asumsi penting lokal yang tak terhitung banyaknya di berbagai titik dalam basis kode. Paparkan setiap bagian dari basis kode ke sesedikit mungkin masalah tersebut. Katakanlah Anda memiliki sistem manajemen orang di mana objek seseorang terkadang memiliki nama belakang nol. Untuk seseorang yang menulis kode di halaman yang menampilkan objek orang, itu bisa sangat canggung! Dan kecuali Anda memelihara buku pegangan "Asumsi yang canggung dan tidak jelas yang dimiliki basis kode kami" (saya tahu saya tidak) programmer halaman tampilan Anda tidak akan tahu nama belakang bisa nol dan mungkin akan menulis kode dengan pointer nol pengecualian di dalamnya ketika nama belakang-menjadi kasus nol muncul. Alih-alih, tangani kasus ini dengan API dan kontrak yang dipikirkan dengan matang yang digunakan oleh bagian berbeda dari basis kode Anda untuk berinteraksi satu sama lain.
Perintah #3: Kode yang Baik Memiliki Tata Letak dan Arsitektur yang Dipikirkan dengan Baik untuk Membuat Pengelolaan Negara Menjadi Jelas
Negara adalah musuh. Mengapa? Karena ini adalah satu-satunya bagian paling kompleks dari aplikasi apa pun dan perlu ditangani dengan sangat hati-hati dan penuh pertimbangan. Masalah umum termasuk inkonsistensi basis data, pembaruan UI parsial di mana data baru tidak tercermin di mana-mana, operasi yang tidak sesuai pesanan, atau hanya memikirkan kode yang sangat rumit dengan pernyataan if dan cabang di mana-mana yang mengarah ke sulit untuk dibaca dan bahkan lebih sulit untuk mempertahankan kode. Menempatkan status pada alas untuk diperlakukan dengan sangat hati-hati, dan menjadi sangat konsisten dan disengaja sehubungan dengan bagaimana status diakses dan dimodifikasi, secara dramatis menyederhanakan basis kode Anda. Beberapa bahasa (Haskell misalnya) menerapkan ini pada tingkat terprogram dan sintaksis. Anda akan kagum betapa kejelasan basis kode Anda dapat ditingkatkan jika Anda memiliki pustaka fungsi murni yang tidak mengakses status eksternal, dan kemudian area permukaan kecil kode stateful yang merujuk fungsionalitas murni luar.

Perintah #4: Kode Baik Tidak Menemukan Kembali Roda, Itu Berdiri di Bahu Raksasa
Sebelum berpotensi menemukan kembali roda, pikirkan tentang seberapa umum masalah yang Anda coba selesaikan atau fungsi yang coba Anda lakukan. Seseorang mungkin telah menerapkan solusi yang dapat Anda manfaatkan. Luangkan waktu untuk memikirkan dan meneliti opsi semacam itu, jika sesuai dan tersedia.
Yang mengatakan, argumen tandingan yang sepenuhnya masuk akal adalah bahwa dependensi tidak datang secara "gratis" tanpa kerugian apa pun. Dengan menggunakan perpustakaan pihak ketiga atau sumber terbuka yang menambahkan beberapa fungsi menarik, Anda membuat komitmen untuk, dan menjadi tergantung pada, perpustakaan itu. Itu komitmen besar; jika itu adalah perpustakaan raksasa dan Anda hanya membutuhkan sedikit fungsionalitas, apakah Anda benar-benar menginginkan beban memperbarui seluruh perpustakaan jika Anda memutakhirkan, misalnya, ke Python 3.x? Dan terlebih lagi, jika Anda menemukan bug atau ingin meningkatkan fungsionalitas, Anda bergantung pada pembuat (atau vendor) untuk menyediakan perbaikan atau peningkatan, atau, jika itu open source, temukan diri Anda dalam posisi menjelajahi ( berpotensi substansial) basis kode yang sama sekali tidak Anda kenal dengan mencoba memperbaiki atau memodifikasi sedikit fungsionalitas yang tidak jelas.
Tentu saja, semakin baik Anda menggunakan kode yang Anda andalkan, semakin kecil kemungkinan Anda harus menginvestasikan waktu untuk pemeliharaan. Intinya adalah bermanfaat bagi Anda untuk melakukan riset sendiri dan membuat evaluasi sendiri apakah akan menyertakan teknologi luar atau tidak dan berapa banyak pemeliharaan yang akan ditambahkan oleh teknologi tertentu ke tumpukan Anda.
Di bawah ini adalah beberapa contoh yang lebih umum dari hal-hal yang mungkin tidak Anda temukan kembali di zaman modern dalam proyek Anda (kecuali jika ini ADALAH proyek Anda).
Database
Cari tahu CAP mana yang Anda butuhkan untuk proyek Anda, lalu pilih database dengan properti yang tepat. Database tidak hanya berarti MySQL lagi, Anda dapat memilih dari:
- Skema SQL “Tradisional”: Postgres / MySQL / MariaDB / MemSQL / Amazon RDS, dll.
- Penyimpanan Nilai Utama: Redis / Memcache / Riak
- NoSQL: MongoDB/Cassandra
- DB yang Dihosting : AWS RDS / DynamoDB / AppEngine Datastore
- Angkat berat: Amazon MR / Hadoop (Sarang/Babi) / Cloudera / Google Big Query
- Hal- hal gila: Erlang's Mnesia, Data Inti iOS
Lapisan Abstraksi Data
Anda seharusnya, dalam kebanyakan situasi, tidak menulis kueri mentah ke database apa pun yang kebetulan Anda pilih untuk digunakan. Kemungkinan besar, ada perpustakaan untuk duduk di antara DB dan kode aplikasi Anda, memisahkan kekhawatiran mengelola sesi database bersamaan dan detail skema dari kode utama Anda. Paling tidak, Anda tidak boleh memiliki kueri mentah atau SQL sebaris di tengah kode aplikasi Anda. Sebaliknya, bungkus dalam sebuah fungsi dan pusatkan semua fungsi dalam file yang disebut sesuatu yang sangat jelas (misalnya, "queries.py"). Baris seperti users = load_users()
, misalnya, jauh lebih mudah dibaca daripada users = db.query(“SELECT username, foo, bar from users LIMIT 10 ORDER BY ID”)
. Jenis sentralisasi ini juga mempermudah untuk memiliki gaya yang konsisten dalam kueri Anda, dan membatasi jumlah tempat yang harus dituju untuk mengubah kueri jika skema berubah.
Pustaka dan Alat Umum Lainnya yang Perlu Dipertimbangkan untuk Memanfaatkan
- Layanan Antrian atau Pub/Sub. Pilih penyedia AMQP, ZeroMQ, RabbitMQ, Amazon SQS
- Penyimpanan. Amazon S3, Penyimpanan Google Cloud
- Pemantauan: Grafit/Grafit yang Dihosting, AWS Cloud Watch, Relik Baru
- Koleksi / Agregasi Log. Loggly, Splunk
Penskalaan Otomatis
- Penskalaan Otomatis. Heroku, AWS Beanstalk, AppEngine, AWS Opsworks, Digital Ocean
Perintah #5: Jangan Menyeberangi Sungai!
Ada banyak model bagus untuk desain pemrograman, pub/sub, aktor, MVC, dll. Pilih mana yang paling Anda sukai, dan patuhi itu. Berbagai jenis logika yang berurusan dengan berbagai jenis data harus diisolasi secara fisik dalam basis kode (sekali lagi, pemisahan ini menyangkut konsep dan mengurangi beban kognitif pada pembaca masa depan). Kode yang memperbarui UI Anda harus berbeda secara fisik dari kode yang menghitung apa yang masuk ke UI, misalnya.
Perintah #6: Jika Memungkinkan, Biarkan Komputer Yang Bekerja
Jika kompilator dapat menangkap kesalahan logis dalam kode Anda dan mencegah perilaku buruk, bug, atau crash, kami benar-benar harus memanfaatkannya. Tentu saja, beberapa bahasa memiliki kompiler yang membuatnya lebih mudah daripada yang lain. Haskell, misalnya, memiliki kompiler ketat yang terkenal yang mengakibatkan pemrogram menghabiskan sebagian besar upaya mereka hanya untuk mendapatkan kode untuk dikompilasi. Setelah dikompilasi, "itu hanya berfungsi". Bagi Anda yang belum pernah menulis dalam bahasa fungsional yang diketik dengan kuat, ini mungkin tampak konyol atau tidak mungkin, tetapi jangan percaya kata-kata saya. Serius, klik beberapa tautan ini, sangat mungkin untuk hidup di dunia tanpa kesalahan runtime. Dan itu benar-benar ajaib.
Memang, tidak setiap bahasa memiliki kompiler atau sintaks yang cocok untuk banyak (atau dalam beberapa kasus!) pemeriksaan waktu kompilasi. Bagi mereka yang tidak, luangkan beberapa menit untuk meneliti pemeriksaan keketatan opsional apa yang dapat Anda aktifkan di proyek Anda dan evaluasi apakah itu masuk akal bagi Anda. Daftar singkat yang tidak komprehensif dari beberapa yang umum yang saya gunakan akhir-akhir ini untuk bahasa dengan runtime yang lunak meliputi:
- Python: pylint, pyflakes, peringatan, peringatan di emacs
- Ruby: peringatan
- JavaScript: jslint
Kesimpulan
Ini sama sekali bukan daftar perintah yang lengkap atau sempurna untuk menghasilkan kode "baik" (yaitu, mudah dipelihara). Yang mengatakan, jika setiap basis kode yang harus saya ambil di masa depan mengikuti bahkan setengah dari konsep dalam daftar ini, saya akan memiliki lebih sedikit uban dan bahkan mungkin dapat menambahkan lima tahun tambahan di akhir hidup saya. Dan saya pasti akan menemukan pekerjaan lebih menyenangkan dan tidak terlalu membuat stres.