Saya sedang mengerjakan proyek perangkat lunak besar yang sangat disesuaikan untuk berbagai pelanggan di seluruh dunia. Ini berarti bahwa kami mungkin memiliki kode 80% yang umum di antara berbagai pelanggan, tetapi juga banyak kode yang harus diubah dari satu pelanggan ke pelanggan lainnya. Di masa lalu kami melakukan pengembangan dalam repositori terpisah (SVN) dan ketika sebuah proyek baru dimulai (kami memiliki sedikit, tetapi pelanggan besar) menciptakan repositori lain berdasarkan proyek apa pun yang memiliki basis kode terbaik untuk kebutuhan kita, apa pun proyek masa lalu. Ini telah berhasil di masa lalu, tetapi kami mengalami beberapa masalah:
- Bug yang diperbaiki dalam satu repositori tidak ditambal dalam repositori lain. Ini mungkin masalah organisasi, tapi saya merasa sulit untuk memperbaiki dan menambal bug di 5 repositori yang berbeda, mengingat bahwa tim yang mengelola repositori ini mungkin ada di bagian lain dunia dan kami tidak memiliki lingkungan pengujian mereka , tidak tahu jadwal mereka atau persyaratan apa yang mereka miliki ("bug" di satu negara mungkin merupakan "fitur" di negara lain).
- Fitur dan perbaikan yang dibuat untuk satu proyek, yang mungkin juga berguna untuk proyek lain hilang atau jika mereka digunakan dalam proyek lain sering menyebabkan sakit kepala besar menggabungkan mereka dari satu basis kode ke yang lain (karena kedua cabang mungkin telah dikembangkan secara mandiri selama satu tahun ).
- Perbaikan dan peningkatan kode yang dibuat dalam satu cabang pengembangan hilang atau menyebabkan lebih banyak kerugian daripada manfaat jika Anda harus menggabungkan semua perubahan ini di antara cabang-cabang.
Kami sekarang sedang mendiskusikan bagaimana menyelesaikan masalah ini dan sejauh ini muncul dengan ide-ide berikut tentang bagaimana menyelesaikannya:
Pertahankan pengembangan di cabang-cabang terpisah tetapi kelola lebih baik dengan memiliki repositori pusat tempat perbaikan bug umum digabungkan ke dalam dan mintalah semua proyek menggabungkan perubahan dari repositori pusat ini ke milik mereka sendiri secara teratur (misalnya setiap hari). Ini membutuhkan disiplin besar dan banyak upaya untuk bergabung di antara cabang-cabang. Jadi saya tidak yakin itu akan berhasil dan kita bisa menjaga disiplin ini, terutama ketika tekanan waktu masuk.
Abaikan cabang pengembangan yang terpisah dan miliki repositori kode pusat tempat semua kode kita hidup dan lakukan kustomisasi kita dengan memiliki modul pluggable dan opsi konfigurasi. Kami sudah menggunakan wadah Dependency Injection untuk menyelesaikan dependensi dalam kode kami dan kami mengikuti pola MVVM di sebagian besar kode kami untuk memisahkan logika bisnis dari UI kami.
Pendekatan kedua tampaknya lebih elegan, tetapi kami memiliki banyak masalah yang belum terpecahkan dalam pendekatan ini. Misalnya: bagaimana menangani perubahan / penambahan dalam model / basis data Anda. Kami menggunakan .NET dengan Entity Framework untuk memiliki entitas yang sangat diketik. Saya tidak melihat bagaimana kami dapat menangani properti yang diperlukan untuk satu pelanggan tetapi tidak berguna untuk pelanggan lain tanpa mengacaukan model data kami. Kami berpikir untuk menyelesaikan ini dalam database dengan menggunakan tabel satelit (memiliki tabel terpisah di mana kolom tambahan kami untuk entitas tertentu hidup dengan pemetaan 1: 1 ke entitas asli), tetapi ini hanya database. Bagaimana Anda menangani ini dalam kode? Model data kami tinggal di perpustakaan pusat yang kami tidak akan dapat memperluas untuk setiap pelanggan menggunakan pendekatan ini.
Saya yakin bahwa kami bukan satu-satunya tim yang berjuang dengan masalah ini dan saya terkejut menemukan begitu sedikit materi tentang topik ini.
Jadi pertanyaan saya adalah sebagai berikut:
- Pengalaman apa yang Anda miliki dengan perangkat lunak yang sangat khusus, pendekatan apa yang Anda pilih dan bagaimana cara kerjanya untuk Anda?
- Pendekatan apa yang Anda rekomendasikan dan mengapa? Apakah ada pendekatan yang lebih baik?
- Apakah ada buku atau artikel bagus tentang topik yang dapat Anda rekomendasikan?
- Apakah Anda memiliki rekomendasi khusus untuk lingkungan teknis kami (.NET, Entity Framework, WPF, DI)?
Edit:
Terima kasih atas semua sarannya. Sebagian besar ide cocok dengan yang sudah kami miliki di tim kami, tetapi sangat membantu untuk melihat pengalaman yang Anda miliki dengan mereka dan tips untuk menerapkannya dengan lebih baik.
Saya masih tidak yakin ke arah mana kita akan pergi dan saya tidak membuat keputusan (sendirian), tetapi saya akan meneruskan ini dalam tim saya dan saya yakin itu akan membantu.
Saat ini tenor tampaknya menjadi repositori tunggal menggunakan berbagai modul khusus pelanggan. Saya tidak yakin arsitektur kami mencapai ini atau berapa banyak yang harus kami investasikan untuk membuatnya cocok, jadi beberapa hal mungkin tinggal di repositori terpisah untuk sementara waktu, tetapi saya pikir itu satu-satunya solusi jangka panjang yang akan bekerja.
Jadi, terima kasih sekali lagi untuk semua tanggapan!
Jawaban:
Kedengarannya seperti masalah mendasar bukan hanya pemeliharaan repositori kode, tetapi kurangnya arsitektur yang sesuai .
Kerangka kerja atau pustaka standar meliputi yang pertama, sedangkan yang kedua akan diimplementasikan sebagai add-on (plugins, subclass, DI, apa pun yang masuk akal untuk struktur kode).
Sistem kontrol sumber yang mengelola cabang dan pengembangan yang didistribusikan mungkin akan membantu juga; Saya penggemar Mercurial, yang lain lebih suka Git. Kerangka kerja akan menjadi cabang utama, setiap sistem yang disesuaikan akan menjadi cabang pembantu, misalnya.
Teknologi spesifik yang digunakan untuk mengimplementasikan sistem (.NET, WPF, apa pun) sebagian besar tidak penting.
Mendapatkan ini dengan benar tidak mudah , tetapi sangat penting untuk kelangsungan hidup jangka panjang. Dan tentu saja semakin lama Anda menunggu, semakin besar utang teknis yang harus Anda tangani.
Anda dapat menemukan buku Arsitektur Perangkat Lunak: Prinsip dan Pola Organisasi berguna.
Semoga berhasil!
sumber
Satu perusahaan tempat saya bekerja memiliki masalah yang sama, dan pendekatan untuk mengatasi masalah ini adalah ini: Kerangka kerja umum untuk semua proyek baru telah dibuat; ini termasuk semua hal yang harus sama di setiap proyek. Misalnya alat penghasil formulir, ekspor ke Excel, pencatatan. Upaya diambil untuk memastikan bahwa kerangka kerja umum ini hanya ditingkatkan (ketika proyek baru membutuhkan fitur baru), tetapi tidak pernah bercabang dua.
Berdasarkan kerangka kerja itu, kode khusus pelanggan dipertahankan dalam repositori terpisah. Bila berguna atau perlu, perbaikan bug dan peningkatan disalin-salin antara proyek (dengan semua peringatan yang dijelaskan dalam pertanyaan). Perbaikan yang bermanfaat secara global masuk ke dalam kerangka kerja umum.
Memiliki semuanya dalam basis kode yang sama untuk semua pelanggan memiliki beberapa keuntungan, tetapi di sisi lain, membaca kode menjadi sulit ketika ada banyak yang
if
membuat program berperilaku berbeda untuk setiap pelanggan.EDIT: Satu anekdot untuk menjadikan ini lebih dimengerti:
sumber
Salah satu proyek yang saya kerjakan pada banyak platform yang didukung (lebih dari 5) di sejumlah besar rilis produk. Banyak tantangan yang Anda gambarkan adalah hal-hal yang kami hadapi, meskipun dengan cara yang sedikit berbeda. Kami memiliki DB berpemilik, jadi kami tidak memiliki jenis masalah yang sama di arena itu.
Struktur kami mirip dengan milik Anda, tetapi kami memiliki satu repositori untuk kode kami. Kode spesifik platform masuk ke folder proyek mereka sendiri di dalam pohon kode. Kode umum hidup di dalam pohon berdasarkan pada lapisan apa itu.
Kami memiliki kompilasi bersyarat, berdasarkan pada platform yang sedang dibangun. Mempertahankan itu agak merepotkan, tetapi itu hanya harus dilakukan ketika modul baru ditambahkan pada lapisan platform tertentu.
Memiliki semua kode dalam satu repositori membuatnya mudah bagi kami untuk melakukan perbaikan bug di beberapa platform dan rilis pada saat yang sama. Kami memiliki lingkungan build otomatis untuk semua platform untuk dijadikan sebagai backstop jika kode baru merusak platform yang dianggap tidak terkait.
Kami mencoba untuk mencegahnya, tetapi akan ada kasus di mana platform membutuhkan perbaikan berdasarkan bug khusus platform yang ada dalam kode umum. Jika kita dapat mengganti kompilasi secara kondisional tanpa membuat modul terlihat buruk, kita akan melakukannya terlebih dahulu. Jika tidak, kami akan memindahkan modul dari wilayah umum dan mendorongnya ke platform tertentu.
Untuk database, kami memiliki beberapa tabel yang memiliki kolom / modifikasi platform khusus. Kami akan memastikan bahwa setiap versi platform dari tabel memenuhi tingkat fungsionalitas dasar sehingga kode umum dapat merujuknya tanpa khawatir tentang ketergantungan platform. Permintaan / manipulasi khusus platform didorong ke dalam lapisan proyek platform.
Jadi, untuk menjawab pertanyaan Anda:
sumber
Saya bekerja selama bertahun-tahun pada aplikasi Administrasi Pensiun yang memiliki masalah serupa. Rencana pensiun sangat berbeda antara perusahaan, dan membutuhkan pengetahuan yang sangat khusus untuk menerapkan logika perhitungan dan laporan dan juga desain data yang sangat berbeda. Saya hanya bisa memberikan deskripsi singkat tentang bagian dari arsitektur, tetapi mungkin itu akan memberikan cukup banyak ide.
Kami memiliki 2 tim terpisah: tim pengembangan inti , yang bertanggung jawab atas kode sistem inti (yang akan menjadi 80% kode bersama Anda di atas), dan tim implementasi , yang memiliki keahlian domain dalam sistem pensiun, dan bertanggung jawab untuk mempelajari klien persyaratan dan skrip skrip dan laporan untuk klien.
Kami memiliki semua tabel kami yang ditentukan oleh Xml (ini sebelum waktu ketika kerangka kerja entitas diuji-waktu dan umum). Tim implementasi akan mendesain semua tabel dalam Xml, dan aplikasi inti dapat diminta untuk menghasilkan semua tabel di Xml. Ada juga file skrip VB terkait, Crystal Reports, Word docs dll untuk setiap klien. (Ada juga model warisan yang dibangun ke dalam Xml untuk memungkinkan penggunaan kembali implementasi lain).
Aplikasi inti (satu aplikasi untuk semua klien), akan men-cache semua hal spesifik klien ketika permintaan untuk klien itu datang, dan itu menghasilkan objek data umum (seperti kumpulan catatan ADO jarak jauh), yang dapat diserialisasi dan disahkan sekitar.
Model data ini kurang apik dari objek entitas / domain, tetapi sangat fleksibel, universal, dan dapat diproses oleh satu set kode inti. Mungkin dalam kasus Anda, Anda bisa mendefinisikan objek entitas dasar Anda hanya dengan bidang umum, dan memiliki Kamus tambahan untuk bidang khusus (tambahkan beberapa jenis set deskriptor data ke objek entitas Anda sehingga memiliki meta data untuk bidang kustom. )
Kami memiliki repositori sumber terpisah untuk kode sistem inti dan untuk kode implementasi.
Sistem inti kami sebenarnya memiliki logika bisnis yang sangat sedikit, selain beberapa modul perhitungan umum yang sangat standar. Sistem inti berfungsi sebagai: generator layar, script runner, generator laporan, akses data dan lapisan transport.
Mengelompokkan logika inti dan logika khusus adalah tantangan yang sulit. Namun, kami selalu merasa lebih baik memiliki satu sistem inti yang menjalankan banyak klien, daripada beberapa salinan dari sistem yang berjalan untuk setiap klien.
sumber
Saya telah bekerja pada sistem yang lebih kecil (20 kloc), dan menemukan bahwa DI dan konfigurasi keduanya merupakan cara yang bagus untuk mengelola perbedaan antara klien, tetapi tidak cukup untuk menghindari forking sistem. Basis data dibagi antara bagian spesifik aplikasi, yang memiliki skema tetap, dan bagian bergantung klien, yang didefinisikan melalui dokumen konfigurasi XML kustom.
Kami telah menyimpan cabang tunggal dalam lincah yang dikonfigurasi seolah-olah itu dapat dikirimkan, tetapi bermerek dan dikonfigurasi untuk klien fiksi. Perbaikan bug diarahkan pada proyek itu, dan pengembangan baru fungsi inti hanya terjadi di sana. Pers untuk klien yang sebenarnya adalah cabang dari itu, disimpan dalam repositori mereka sendiri. Kami melacak perubahan besar pada kode melalui nomor versi yang ditulis secara manual dan melacak perbaikan bug menggunakan nomor komit.
sumber
Saya khawatir bahwa saya tidak memiliki pengalaman langsung tentang masalah yang Anda uraikan, tetapi saya memiliki beberapa komentar.
Opsi kedua, menyatukan kode ke dalam repositori pusat (sebanyak mungkin), dan merancang kustomisasi (sekali lagi, sebanyak mungkin) hampir pasti merupakan cara untuk pergi dalam jangka panjang.
Masalahnya adalah bagaimana Anda berencana untuk sampai di sana, dan berapa lama waktu yang dibutuhkan.
Dalam situasi ini, mungkin OK untuk (sementara) memiliki lebih dari satu salinan aplikasi dalam repositori sekaligus.
Ini akan memungkinkan Anda untuk secara bertahap pindah ke arsitektur yang secara langsung mendukung kustomisasi tanpa harus melakukannya sekaligus.
sumber
Saya yakin semua masalah itu dapat diselesaikan, satu demi satu. Jika Anda buntu, tanyakan di sini atau di SO tentang masalah spesifik.
Seperti yang telah ditunjukkan orang lain, memiliki satu basis kode pusat / satu repositori adalah opsi yang harus Anda pilih. Saya mencoba menjawab pertanyaan contoh Anda.
Ada beberapa kemungkinan, semuanya telah saya lihat di sistem dunia nyata. Yang mana yang akan dipilih tergantung pada situasi Anda:
perkenalkan tabel "CustomAttributes" (menggambarkan nama dan tipe) dan "CustomAttributeValues" (untuk nilai-nilai, misalnya disimpan sebagai representasi string, bahkan jika itu adalah angka). Itu akan memungkinkan untuk menambahkan atribut tersebut pada waktu instal atau waktu berjalan, memiliki nilai individual untuk setiap pelanggan. Jangan memaksakan setiap atribut khusus dimodelkan "terlihat" di model data Anda.
sekarang harus jelas bagaimana menggunakannya dalam kode: hanya memiliki kode umum untuk mengakses tabel-tabel itu, dan kode individual (mungkin dalam DLL plug-in yang terpisah, terserah Anda) untuk menafsirkan atribut itu dengan benar
Dan untuk kode spesifik: Anda juga dapat mencoba memperkenalkan bahasa skrip ke dalam produk Anda, terutama untuk menambahkan skrip khusus pelanggan. Dengan cara itu Anda tidak hanya membuat garis yang jelas antara kode Anda dan kode khusus pelanggan, Anda juga dapat memungkinkan pelanggan Anda untuk menyesuaikan sistem sampai tingkat tertentu sendiri.
sumber
Saya hanya membangun satu aplikasi seperti itu. Saya akan mengatakan bahwa 90% dari unit yang terjual terjual apa adanya, tanpa modifikasi. Setiap pelanggan memiliki kulit khusus mereka sendiri dan kami melayani sistem di dalam kulit itu. Ketika ada mod yang mempengaruhi bagian inti, kami mencoba menggunakan IF branching . Ketika mod # 2 masuk untuk bagian yang sama kami beralih ke logika CASE yang diizinkan untuk ekspansi di masa depan. Ini tampaknya menangani sebagian besar permintaan kecil.
Permintaan khusus minor apa pun ditangani dengan menerapkan logika Kasus.
Jika mod dua radikal, kami membangun klon (terpisah termasuk) dan melampirkan KASUS di sekitarnya untuk memasukkan modul yang berbeda.
Perbaikan bug dan modifikasi pada intinya mempengaruhi semua pengguna. Kami diuji secara menyeluruh dalam pengembangan sebelum pergi ke produksi. Kami selalu mengirimkan pemberitahuan email yang menyertai perubahan dan TIDAK PERNAH, TIDAK PERNAH, TIDAK PERNAH diposting perubahan produksi pada hari Jumat ... TIDAK PERNAH.
Lingkungan kami adalah ASP Klasik dan SQL Server. Kami BUKAN toko kode spageti ... Semuanya modular menggunakan Termasuk, Subroutine, dan Fungsi.
sumber
Ketika saya diminta untuk memulai pengembangan B yang berbagi fungsionalitas 80% dengan A, saya akan:
Anda memilih 1, dan sepertinya tidak cocok dengan situasi Anda. Misi Anda adalah untuk memprediksi mana dari 2 dan 3 yang lebih cocok.
sumber