paint-brush
Cara Menangani Kompleksitas Saat Mendesain Sistem Perangkat Lunakoleh@fairday
64,464 bacaan
64,464 bacaan

Cara Menangani Kompleksitas Saat Mendesain Sistem Perangkat Lunak

oleh Aleksei23m2024/02/05
Read on Terminal Reader
Read this story w/o Javascript

Terlalu panjang; Untuk membaca

Kompleksitas adalah musuh! Mari kita pelajari cara menghadapinya!
featured image - Cara Menangani Kompleksitas Saat Mendesain Sistem Perangkat Lunak
Aleksei HackerNoon profile picture

Tentang apa semua ini?

Setiap hari, setiap saat selama karier teknik kami, kami menghadapi banyak masalah dengan berbagai kompleksitas dan situasi yang mengharuskan kami mengambil keputusan atau menundanya karena kurangnya data. Setiap kali kami membangun layanan baru, membangun infrastruktur, atau bahkan membentuk proses pengembangan, kami menghadapi berbagai tantangan yang sangat besar.


Sulit, dan mungkin bahkan mustahil, untuk mencantumkan semua masalah. Anda akan menemui beberapa masalah ini hanya jika Anda bekerja di bidang tertentu. Di sisi lain, ada banyak masalah yang harus kita semua pahami cara penyelesaiannya, karena masalah-masalah tersebut sangat penting dalam membangun sistem TI. Dengan kemungkinan besar, Anda akan menemuinya di semua proyek.


Dalam artikel ini, saya akan berbagi pengalaman saya dengan beberapa masalah yang saya temui saat membuat program perangkat lunak.

Apa itu Kepedulian Lintas Sektor?

Jika kita melihat di Wikipedia, kita akan menemukan definisi berikut


Dalam pengembangan perangkat lunak berorientasi aspek, masalah lintas sektor adalah aspek program yang memengaruhi beberapa modul, tanpa kemungkinan dirangkum dalam salah satu modul. Masalah ini sering kali tidak dapat dipisahkan dengan jelas dari sistem lainnya baik dalam desain maupun implementasi, dan dapat mengakibatkan penyebaran (duplikasi kode), kekusutan (ketergantungan signifikan antar sistem), atau keduanya.


Ini menggambarkan dengan jelas apa itu, tetapi saya ingin memperluas dan menyederhanakannya sedikit:

Keprihatinan lintas sektor merupakan suatu konsep atau komponen sistem/organisasi yang mempengaruhi (atau 'melintasi') banyak bagian lainnya.


Contoh terbaik dari masalah tersebut adalah arsitektur sistem, pencatatan, keamanan, manajemen transaksi, telemetri, desain basis data, dan masih banyak lagi. Kami akan menguraikan banyak di antaranya nanti dalam artikel ini.


Pada level kode, masalah lintas sektor sering kali diimplementasikan menggunakan teknik seperti Aspect-Oriented Programming (AOP) , di mana masalah-masalah ini dimodularisasi menjadi komponen-komponen terpisah yang dapat diterapkan di seluruh aplikasi. Hal ini menjaga logika bisnis tetap terisolasi dari masalah-masalah ini, sehingga kode menjadi lebih mudah dibaca dan dipelihara.

Klasifikasi Aspek

Ada banyak cara yang memungkinkan untuk mengklasifikasikan aspek dengan mengelompokkannya berdasarkan properti yang berbeda seperti cakupan, ukuran, fungsionalitas, kepentingan, target, dan lain-lain, tetapi dalam artikel ini, saya akan menggunakan klasifikasi cakupan yang sederhana. Yang saya maksud dengan ini adalah ke mana aspek spesifik ini diarahkan, apakah itu keseluruhan organisasi, sistem tertentu, atau elemen tertentu dari sistem tersebut.


Jadi, saya akan membagi aspek menjadi Makro dan Mikro .


Yang saya maksud dengan aspek Makro adalah pertimbangan-pertimbangan yang kita terapkan pada keseluruhan sistem, seperti arsitektur sistem yang dipilih dan desainnya (monolitik, layanan mikro, arsitektur berorientasi layanan), tumpukan teknologi, struktur organisasi, dsb. Aspek makro terutama terkait dengan keputusan-keputusan strategis dan tingkat tinggi.


Sementara itu, aspek Mikro lebih dekat dengan level kode dan pengembangan. Misalnya, kerangka kerja mana yang digunakan untuk berinteraksi dengan basis data, struktur folder dan kelas proyek, atau bahkan pola desain objek tertentu.


Meskipun klasifikasi ini tidak ideal, klasifikasi ini membantu menyusun pemahaman tentang kemungkinan masalah dan pentingnya serta dampak solusi yang kita terapkan pada masalah tersebut.


Dalam artikel ini, fokus utama saya akan tertuju pada aspek makro.

Aspek Makro

Struktur organisasi

Ketika saya baru mulai belajar tentang arsitektur perangkat lunak, saya membaca banyak artikel menarik tentang hukum Conway dan dampaknya terhadap struktur organisasi. Terutama yang satu ini . Jadi, hukum ini menyatakan bahwa


Setiap organisasi yang merancang suatu sistem (didefinisikan secara luas) akan menghasilkan desain yang strukturnya merupakan salinan dari struktur komunikasi organisasi.


Saya selalu percaya bahwa konsep ini memang sangat universal dan mewakili Aturan Emas.


Kemudian saya mulai mempelajari pendekatan Domain-Driven Design (DDD) milik Eric Evans untuk memodelkan sistem. Eric Evans menekankan pentingnya identifikasi Konteks Terbatas. Konsep ini melibatkan pembagian model domain yang kompleks menjadi beberapa bagian yang lebih kecil dan lebih mudah dikelola, yang masing-masing memiliki pengetahuan yang terbatas. Pendekatan ini membantu komunikasi tim yang efektif, karena mengurangi kebutuhan akan pengetahuan yang luas tentang seluruh domain dan meminimalkan peralihan konteks, sehingga membuat percakapan menjadi lebih efisien. Peralihan konteks adalah hal terburuk dan paling menghabiskan sumber daya. Bahkan komputer pun kesulitan mengatasinya. Meskipun tidak mungkin mencapai ketiadaan peralihan konteks sepenuhnya, saya rasa itulah yang harus kita perjuangkan.


Fantasy about keeping in mind a lot of bounded contexts

Kembali ke Hukum Conway, saya menemukan beberapa masalah dengannya.


Masalah pertama yang saya temui dengan Hukum Conway, yang menyatakan bahwa desain sistem mencerminkan struktur organisasi, adalah potensi untuk membentuk Konteks Terbatas yang kompleks dan komprehensif. Kompleksitas ini muncul ketika struktur organisasi tidak selaras dengan batasan domain, yang mengarah ke Konteks Terbatas yang sangat saling bergantung dan sarat dengan informasi. Hal ini menyebabkan seringnya terjadi pergantian konteks bagi tim pengembangan.


Masalah lainnya adalah bahwa terminologi organisasi bocor ke tingkat kode. Ketika struktur organisasi berubah, hal itu memerlukan modifikasi basis kode, yang menghabiskan sumber daya yang berharga.


Dengan demikian, mengikuti Inverse Conway Maneuver membantu membangun sistem dan organisasi yang mendukung arsitektur perangkat lunak yang diinginkan. Akan tetapi, perlu dicatat bahwa pendekatan ini tidak akan bekerja dengan baik dalam arsitektur dan struktur yang sudah terbentuk karena perubahan pada tahap ini bersifat jangka panjang, tetapi sangat efektif dalam perusahaan rintisan karena mereka cepat dalam memperkenalkan perubahan apa pun.

Bola Lumpur Besar

Pola atau "anti-pola" ini mendorong pembangunan sistem tanpa arsitektur apa pun. Tidak ada aturan, tidak ada batasan, dan tidak ada strategi tentang cara mengendalikan kompleksitas yang tak terelakkan. Kompleksitas adalah musuh yang paling tangguh dalam perjalanan membangun sistem perangkat lunak.


Entertaining illustration made by ChatGPT

Untuk menghindari pembangunan sistem jenis seperti itu, kita perlu mengikuti aturan dan batasan khusus.

Arsitektur sistem

Ada banyak sekali definisi untuk Arsitektur Perangkat Lunak. Saya menyukai banyak definisi karena mencakup berbagai aspeknya. Akan tetapi, untuk dapat bernalar tentang arsitektur, kita perlu secara alami membentuk beberapa definisi di dalam pikiran kita. Dan perlu dicatat bahwa definisi ini dapat berkembang. Jadi, setidaknya untuk saat ini, saya memiliki deskripsi berikut untuk diri saya sendiri.


Arsitektur Perangkat Lunak adalah tentang keputusan dan pilihan yang Anda buat setiap hari yang memengaruhi sistem yang dibangun.


Untuk membuat keputusan, Anda perlu memiliki prinsip dan pola dalam "tas" Anda untuk memecahkan masalah yang timbul, penting juga untuk menyatakan bahwa memahami persyaratan adalah kunci untuk membangun apa yang dibutuhkan bisnis. Namun, terkadang persyaratan tidak transparan atau bahkan tidak didefinisikan, dalam hal ini, lebih baik menunggu untuk mendapatkan klarifikasi lebih lanjut atau mengandalkan pengalaman Anda dan memercayai intuisi Anda. Namun, bagaimanapun, Anda tidak dapat membuat keputusan dengan benar jika Anda tidak memiliki prinsip dan pola untuk diandalkan. Di situlah saya sampai pada definisi Gaya Arsitektur Perangkat Lunak.


Gaya Arsitektur Perangkat Lunak adalah serangkaian prinsip dan pola yang menentukan cara membangun perangkat lunak.


Ada banyak gaya arsitektur berbeda yang difokuskan pada berbagai sisi arsitektur yang direncanakan, dan menerapkan beberapa di antaranya sekaligus adalah situasi yang normal.


Misalnya saja seperti:

  1. Arsitektur monolitik

  2. Desain berbasis domain

  3. Berbasis komponen

  4. Layanan mikro

  5. Pipa dan filter

  6. Berbasis pada peristiwa

  7. Mikrokernel

  8. Berorientasi pada layanan


dan sebagainya…


Tentu saja, mereka memiliki kelebihan dan kekurangan, tetapi hal terpenting yang saya pelajari adalah bahwa arsitektur berevolusi secara bertahap sambil bergantung pada masalah aktual. Memulai dengan arsitektur monolitik adalah pilihan yang bagus untuk mengurangi kompleksitas operasional, kemungkinan besar arsitektur ini akan sesuai dengan kebutuhan Anda bahkan setelah mencapai tahap Product-market Fit (PMI) dalam membangun produk. Dalam skala besar, Anda dapat mempertimbangkan untuk beralih ke pendekatan berbasis peristiwa dan layanan mikro untuk mencapai penerapan independen, lingkungan tumpukan teknologi heterogen, dan arsitektur yang kurang terhubung (dan kurang transparan untuk sementara waktu karena sifat pendekatan berbasis peristiwa dan pub-sub jika ini diadopsi). Kesederhanaan dan efisiensi dekat dan memiliki dampak besar satu sama lain. Biasanya, arsitektur yang rumit memengaruhi kecepatan pengembangan fitur baru, mendukung dan memelihara yang sudah ada, dan menantang evolusi alami sistem.


Akan tetapi, sistem yang kompleks sering kali memerlukan arsitektur yang kompleks dan komprehensif, yang tidak dapat dihindari.


Ini adalah topik yang sangat luas, dan ada banyak ide hebat tentang cara menyusun dan membangun sistem untuk evolusi alami. Berdasarkan pengalaman saya, saya telah menyusun pendekatan berikut:

  1. Hampir selalu dimulai dengan gaya arsitektur monolitik karena menghilangkan sebagian besar masalah yang timbul karena sifat sistem terdistribusi. Mengikuti monolit modular juga masuk akal untuk fokus membangun komponen dengan batasan yang jelas. Menerapkan pendekatan berbasis komponen dapat membantu mereka berkomunikasi satu sama lain dengan menggunakan peristiwa, tetapi memiliki panggilan langsung (alias RPC) menyederhanakan banyak hal di awal. Namun, penting untuk melacak ketergantungan antar komponen karena jika komponen A mengetahui banyak tentang komponen B, mungkin masuk akal untuk menggabungkannya menjadi satu.
  2. Saat Anda mendekati situasi saat Anda perlu meningkatkan skala pengembangan dan sistem Anda, Anda dapat mempertimbangkan untuk mengikuti pola Stangler untuk secara bertahap mengekstrak komponen yang perlu diterapkan secara independen atau bahkan ditingkatkan dengan persyaratan tertentu.
  3. Sekarang, jika Anda memiliki visi yang jelas tentang masa depan, yang merupakan sedikit keberuntungan yang luar biasa, Anda dapat memutuskan arsitektur yang diinginkan. Saat ini, Anda dapat memutuskan untuk beralih ke arsitektur layanan mikro dengan juga menerapkan pendekatan Orkestrasi dan Koreografi, menggabungkan pola CQRS untuk operasi penulisan dan pembacaan skala independen, atau bahkan memutuskan untuk tetap menggunakan arsitektur monolitik jika sesuai dengan kebutuhan Anda.


Penting juga untuk memahami angka dan metrik seperti DAU (Pengguna Aktif Harian), MAU (Pengguna Aktif Bulanan), RPC (Permintaan Per Detik), dan TPC (Transaksi Per Detik) karena ini dapat membantu Anda membuat pilihan karena arsitektur untuk 100 pengguna aktif dan 100 juta pengguna aktif berbeda.


Sebagai catatan akhir, saya akan mengatakan bahwa arsitektur memiliki dampak yang signifikan terhadap keberhasilan produk. Arsitektur yang dirancang dengan buruk untuk produk diperlukan dalam penskalaan, yang kemungkinan besar mengarah pada kegagalan karena pelanggan tidak akan menunggu saat Anda menskalakan sistem, mereka akan memilih pesaing, jadi kita harus berada di depan potensi penskalaan. Meskipun saya akui bahwa terkadang itu bukan pendekatan yang ramping, idenya adalah memiliki sistem yang dapat diskalakan tetapi belum diskalakan. Di sisi lain, memiliki sistem yang sangat rumit dan sudah diskalakan tanpa pelanggan atau rencana untuk mendapatkan banyak pelanggan akan menghabiskan uang Anda untuk bisnis Anda tanpa hasil.

Pemilihan tumpukan teknologi

Memilih tumpukan teknologi juga merupakan keputusan tingkat makro karena memengaruhi perekrutan, perspektif evolusi alami sistem, skalabilitas, dan kinerja sistem.


Berikut adalah daftar pertimbangan dasar untuk memilih tumpukan teknologi:

  • Persyaratan dan kompleksitas proyek. Misalnya, aplikasi web sederhana dapat dibangun dengan kerangka kerja Blazor jika pengembang Anda memiliki pengalaman dengannya, tetapi karena kurangnya kematangan WebAssembly, memilih React dan TypeScript untuk kesuksesan jangka panjang bisa menjadi keputusan yang lebih baik.
  • Skalabilitas dan Kebutuhan Performa. Jika Anda mengantisipasi menerima sejumlah besar lalu lintas, memilih ASP.NET Core daripada Django bisa menjadi pilihan yang bijak karena kinerjanya yang unggul dalam menangani permintaan bersamaan. Namun, keputusan ini bergantung pada skala lalu lintas yang Anda harapkan. Jika Anda perlu mengelola miliaran permintaan dengan latensi rendah, kehadiran Garbage Collection bisa menjadi tantangan.
  • Perekrutan, Waktu Pengembangan, dan Biaya. Dalam kebanyakan kasus, faktor-faktor inilah yang perlu kita perhatikan. Waktu untuk memasarkan, Biaya pemeliharaan, dan Stabilitas perekrutan mendorong kebutuhan bisnis Anda tanpa hambatan.
  • Keahlian dan Sumber Daya Tim. Keahlian tim pengembangan Anda merupakan faktor yang krusial. Umumnya lebih efektif untuk menggunakan teknologi yang sudah familier bagi tim Anda kecuali ada alasan kuat untuk berinvestasi dalam mempelajari tumpukan baru.
  • Kematangan. Komunitas yang kuat dan ekosistem pustaka serta perangkat yang lengkap dapat sangat memudahkan proses pengembangan. Teknologi populer sering kali memiliki dukungan komunitas yang lebih baik, yang dapat sangat berharga untuk memecahkan masalah dan menemukan sumber daya. Dengan demikian, Anda dapat menghemat sumber daya dan berfokus terutama pada produk.
  • Pemeliharaan dan Dukungan Jangka Panjang. Pertimbangkan keberlangsungan teknologi dalam jangka panjang. Teknologi yang diadopsi dan didukung secara luas cenderung tidak akan usang dan umumnya menerima pembaruan dan perbaikan secara berkala.


Bagaimana memiliki berbagai tumpukan teknologi dapat memengaruhi pertumbuhan bisnis?

Dari satu perspektif, memperkenalkan satu tumpukan lagi dapat meningkatkan skala perekrutan Anda, tetapi di sisi lain, hal itu akan menimbulkan biaya pemeliharaan tambahan karena Anda perlu mendukung kedua tumpukan tersebut. Jadi, seperti yang saya katakan sebelumnya, menurut sudut pandang saya, hanya kebutuhan tambahan yang seharusnya menjadi argumen untuk menggabungkan lebih banyak tumpukan teknologi.


Tetapi bagaimana dengan prinsip pemilihan alat terbaik untuk masalah tertentu?

Terkadang Anda tidak punya pilihan lain selain menghadirkan alat baru untuk memecahkan masalah tertentu berdasarkan pertimbangan yang sama yang disebutkan di atas, dalam kasus seperti itu, masuk akal untuk memilih solusi terbaik.


Penciptaan sistem tanpa keterkaitan yang tinggi dengan teknologi tertentu bisa menjadi tantangan. Namun, akan sangat membantu jika kita mengupayakan kondisi di mana sistem tidak terlalu erat kaitannya dengan teknologi, dan sistem tidak akan mati jika besok, kerangka kerja atau alat tertentu menjadi rentan atau bahkan tidak digunakan lagi.


Pertimbangan penting lainnya terkait dengan ketergantungan perangkat lunak sumber terbuka dan perangkat lunak berpemilik. Perangkat lunak berpemilik memberi Anda lebih sedikit fleksibilitas dan kemungkinan untuk disesuaikan. Namun, faktor yang paling berbahaya adalah ketergantungan pada vendor, di mana Anda menjadi tergantung pada produk, harga, ketentuan, dan peta jalan vendor. Ini bisa berisiko jika vendor mengubah arah, menaikkan harga, atau menghentikan produk. Perangkat lunak sumber terbuka mengurangi risiko ini, karena satu entitas tidak mengendalikannya. Menghilangkan satu titik kegagalan di semua level adalah kunci untuk membangun sistem yang andal untuk pertumbuhan.

Titik Kegagalan Tunggal (SPOF)

Titik kegagalan tunggal (SPOF) merujuk pada setiap bagian dari sistem yang, jika gagal, akan menyebabkan seluruh sistem berhenti berfungsi. Menghilangkan SPOF di semua tingkatan sangat penting untuk sistem apa pun yang membutuhkan ketersediaan tinggi. Segala hal, termasuk pengetahuan, personel, komponen sistem, penyedia cloud, dan kabel internet, dapat gagal.


Ada beberapa teknik dasar yang bisa kita terapkan untuk menghilangkan titik kegagalan tunggal:

  1. Redundansi. Terapkan redundansi untuk komponen-komponen penting. Ini berarti memiliki komponen cadangan yang dapat mengambil alih jika komponen utama gagal. Redundansi dapat diterapkan di berbagai lapisan sistem, termasuk perangkat keras (server, disk), jaringan (tautan, sakelar), dan perangkat lunak (basis data, server aplikasi). Jika Anda menghosting semuanya di satu Penyedia Cloud dan bahkan memiliki cadangan di sana, pertimbangkan untuk membuat cadangan tambahan secara berkala di penyedia lain untuk mengurangi biaya yang hilang jika terjadi bencana.
  2. Pusat Data. Distribusikan sistem Anda ke beberapa lokasi fisik, seperti pusat data atau wilayah cloud. Pendekatan ini melindungi sistem Anda dari kegagalan di lokasi tertentu seperti pemadaman listrik atau bencana alam.
  3. Failover. Terapkan pendekatan failover untuk semua komponen Anda (DNS, CDN, Load balancer, Kubernetes, API Gateway, dan Database). Karena masalah dapat muncul secara tiba-tiba, penting untuk memiliki rencana cadangan guna mengganti komponen apa pun dengan klonnya sesuai kebutuhan dengan cepat.
  4. Layanan dengan ketersediaan tinggi. Pastikan layanan Anda dibangun agar dapat diskalakan secara horizontal dan memiliki ketersediaan tinggi sejak awal dengan mematuhi prinsip-prinsip berikut:
    • Terapkan statelessness layanan dan hindari menyimpan sesi pengguna dalam cache dalam memori. Sebaliknya, gunakan sistem cache terdistribusi, seperti Redis.
    • Hindari ketergantungan pada urutan kronologis konsumsi pesan saat mengembangkan logika.
    • Minimalkan perubahan yang merusak untuk mencegah gangguan pada pengguna API. Jika memungkinkan, pilih perubahan yang kompatibel dengan versi lama. Pertimbangkan juga biaya karena terkadang, penerapan perubahan yang merusak mungkin lebih hemat biaya.
    • Gabungkan eksekusi migrasi ke dalam alur penyebaran.
    • Tetapkan strategi untuk menangani permintaan bersamaan.
    • Terapkan penemuan, pemantauan, dan pencatatan layanan untuk meningkatkan keandalan dan pengamatan.
    • Mengembangkan logika bisnis agar idempoten, dengan mengakui bahwa kegagalan jaringan tidak dapat dihindari.
  5. Tinjauan ketergantungan. Tinjau dan minimalkan ketergantungan eksternal secara berkala. Setiap ketergantungan eksternal dapat menimbulkan potensi SPOF, jadi penting untuk memahami dan mengurangi risiko ini.
  6. Berbagi pengetahuan secara berkala. Jangan pernah lupakan pentingnya menyebarkan pengetahuan di dalam organisasi Anda. Orang-orang tidak dapat diprediksi, dan mengandalkan satu orang saja sangatlah berisiko. Dorong anggota tim untuk mendigitalkan pengetahuan mereka melalui dokumentasi. Namun, berhati-hatilah agar tidak mendokumentasikan secara berlebihan. Manfaatkan berbagai alat AI untuk menyederhanakan proses ini.

Kesimpulan

Dalam artikel ini, kami membahas beberapa aspek Makro utama dan bagaimana kita dapat menangani kompleksitasnya.


Terima kasih telah membaca! Sampai jumpa di lain waktu!