Cara mempertahankan berbagai versi yang disesuaikan dari perangkat lunak yang sama untuk banyak klien

46

kami memiliki banyak klien dengan kebutuhan berbeda. Meskipun perangkat lunak kami dimodulasi ke tingkat tertentu, hampir dapat dipastikan bahwa kami perlu menyesuaikan logika bisnis setiap modul di sana-sini sedikit untuk setiap pelanggan. Perubahannya mungkin terlalu kecil untuk membenarkan pemisahan modul menjadi modul (fisik) yang berbeda untuk setiap klien, saya khawatir ada masalah dengan build, kekacauan yang menghubungkan. Namun, perubahan itu terlalu besar dan terlalu banyak untuk mengkonfigurasinya dengan sakelar di beberapa file konfigurasi, karena ini akan menyebabkan masalah selama penyebaran dan mungkin banyak masalah dukungan, terutama dengan admin tipe tinker.

Saya ingin agar sistem build membuat banyak build, satu untuk setiap klien, di mana perubahan terkandung dalam versi khusus dari modul fisik tunggal yang dimaksud. Jadi saya punya beberapa pertanyaan:

Apakah Anda menyarankan membiarkan sistem build membuat banyak build? Bagaimana saya harus menyimpan berbagai penyesuaian dalam kontrol sumber, khususnya svn?

Elang
sumber
4
Apakah #ifdefberhasil untuk Anda?
ohho
6
Arahan preprosesor dapat dengan cepat menjadi sangat rumit dan membuat kode lebih sulit dibaca dan lebih sulit untuk debug.
Falcon
1
Anda harus memasukkan perincian tentang platform dan jenis aplikasi aktual (desktop / web) untuk jawaban yang lebih baik. Menyesuaikan dalam aplikasi C ++ desktop benar-benar berbeda dari aplikasi web PHP.
GrandmasterB
2
@ Falcon: sejak Anda memilih pada tahun 2011 jawaban saya memiliki banyak keraguan tentang, dapatkah Anda memberi tahu kami jika Anda punya pengalaman menggunakan SVN di antara yang disarankan? Apakah keberatan saya beralasan?
Doc Brown
2
@ Doc Brown: percabangan dan penggabungan itu membosankan dan rumit. Saat itu, kami menggunakan sistem plugin dengan plugin khusus klien atau "tambalan" yang mengubah perilaku atau konfigurasi. Anda akan memiliki beberapa overhead tetapi dapat dikelola dengan Injeksi Dependendy.
Falcon

Jawaban:

7

Yang Anda butuhkan adalah fitur kode-seperti organisasi kode. Dalam skenario spesifik Anda, ini harus disebut percabangan trunk khusus untuk Klien karena Anda mungkin akan menggunakan percabangan fitur saat Anda mengembangkan hal-hal baru (atau menyelesaikan bug).

http://svnbook.red-bean.com/en/1.5/svn.branchmerge.commonpatterns.html

Idenya adalah untuk memiliki kode batang dengan cabang fitur baru digabung menjadi. Maksud saya semua fitur non-spesifik klien.

Kemudian Anda juga memiliki cabang khusus klien di mana Anda juga menggabungkan cabang fitur yang sama seperti yang diperlukan (memetik ceri jika Anda mau).

Cabang klien ini memang terlihat mirip dengan cabang fitur meskipun mereka tidak sementara atau berumur pendek. Mereka dipertahankan dalam jangka waktu yang lebih lama dan sebagian besar hanya digabung. Seharusnya ada sesedikit mungkin pengembangan cabang fitur khusus klien.

Cabang khusus klien adalah cabang paralel untuk trunk dan aktif selama trunk itu sendiri dan bahkan tidak digabung secara keseluruhan di mana saja.

            feature1
            ———————————.
                        \
trunk                    \
================================================== · · ·
      \ client1            \
       `========================================== · · ·
        \ client2            \
         `======================================== · · ·
              \ client2-specific feature   /
               `——————————————————————————´
Robert Koritnik
sumber
7
+1 untuk percabangan fitur. Anda dapat menggunakan cabang untuk setiap klien juga. Saya ingin menyarankan hanya satu hal: gunakan VCS terdistribusi (hg, git, bzr) daripada SVN / CVS untuk mencapai ini;)
Herberth Amaral
43
-1. Bukan untuk apa "fitur branch"; itu bertentangan dengan definisi mereka sebagai "cabang sementara yang bergabung ketika pengembangan fitur selesai".
P Shved
14
Anda masih harus mempertahankan semua delta dalam kontrol sumber dan menjaganya agar tetap berbaris - selamanya. Jangka pendek ini mungkin berhasil. Jangka panjang itu mungkin mengerikan.
cepat
4
-1, ini bukan masalah penamaan - menggunakan cabang yang berbeda untuk klien yang berbeda hanyalah bentuk lain dari duplikasi kode. Ini adalah IMHO anti-pola, contoh bagaimana tidak melakukannya.
Doc Brown
7
Robert, saya pikir saya mengerti dengan baik apa yang Anda sarankan, bahkan sebelum diedit, tetapi saya pikir ini adalah pendekatan yang mengerikan. Asumsikan Anda memiliki N klien, setiap kali Anda menambahkan fitur inti baru ke trunk, tampaknya SCM akan memudahkan untuk menyebarkan fitur baru ke cabang N. Tetapi menggunakan cabang dengan cara ini membuatnya terlalu mudah untuk menghindari pemisahan yang jelas dari modifikasi khusus klien. Akibatnya, Anda sekarang memiliki N peluang untuk mendapatkan konflik penggabungan untuk setiap perubahan di bagasi. Selanjutnya, Anda sekarang harus menjalankan tes integrasi N bukan satu.
Doc Brown
38

Jangan lakukan ini dengan cabang SCM. Jadikan kode umum proyek terpisah yang menghasilkan perpustakaan atau artefak kerangka proyek. Setiap proyek pelanggan adalah proyek terpisah yang kemudian tergantung pada yang umum sebagai ketergantungan.

Ini paling mudah jika aplikasi basis umum Anda menggunakan kerangka kerja injeksi ketergantungan seperti Spring, sehingga Anda dapat dengan mudah menyuntikkan varian penggantian objek yang berbeda di setiap proyek pelanggan karena fitur khusus diperlukan. Bahkan jika Anda belum memiliki kerangka kerja DI, menambahkannya dan melakukannya dengan cara ini mungkin merupakan pilihan yang paling tidak menyakitkan.

Alb
sumber
Pendekatan lain untuk menjaga hal-hal sederhana adalah menggunakan pewarisan (dan satu proyek tunggal) alih-alih menggunakan injeksi ketergantungan, dengan menjaga kode-umum dan kustomisasi dalam proyek yang sama (menggunakan warisan, kelas parsial, atau peristiwa). Dengan desain itu, cabang klien akan berfungsi dengan baik (seperti yang dijelaskan dalam jawaban yang dipilih), dan orang selalu dapat memperbarui cabang klien dengan memperbarui kode umum.
drizin
Jika saya tidak melewatkan sesuatu, apa yang Anda sarankan adalah jika Anda memiliki 100 pelanggan, Anda akan membuat (100 x NumberOfChangedProjects ) dan menggunakan DI untuk mengelolanya? Jika memang begitu, saya pasti akan menjauh dari solusi semacam ini karena perawatan akan menjadi mengerikan ..
sotn
13

Simpan perangkat lunak untuk semua klien dalam satu cabang. Tidak perlu menyimpang dari perubahan yang dibuat untuk klien yang berbeda. Paling lilkely, Anda ingin perangkat lunak Anda memiliki kualitas terbaik untuk semua klien, dan perbaikan bug untuk infrastruktur inti Anda harus memengaruhi semua orang tanpa perlu menggabungkan biaya overhead, yang juga dapat memperkenalkan lebih banyak bug.

Modularisasi kode umum, dan terapkan kode yang berbeda dalam file yang berbeda atau jaga dengan definisi yang berbeda. Jadikan sistem build Anda memiliki target spesifik untuk setiap klien, setiap target menyusun versi dengan hanya kode yang terkait dengan satu klien. Jika kode Anda adalah C, misalnya, Anda mungkin ingin menjaga fitur untuk klien yang berbeda dengan " #ifdef" atau mekanisme apa pun yang dimiliki bahasa Anda untuk membangun manajemen konfigurasi, dan menyiapkan serangkaian definisi yang berkaitan dengan jumlah fitur yang telah dibayar oleh klien.

Jika Anda tidak suka ifdef, gunakan "antarmuka dan implementasi", "functors", "file objek" atau alat apa pun yang disediakan bahasa Anda untuk menyimpan berbagai hal di satu tempat.

Jika Anda mendistribusikan sumber ke klien Anda, yang terbaik adalah membuat skrip bangunan Anda memiliki "target distribusi sumber" khusus. Setelah Anda memanggil target seperti itu, itu menciptakan versi khusus sumber perangkat lunak Anda, menyalinnya ke folder terpisah sehingga Anda dapat mengirimkannya, dan tidak mengkompilasinya.

P Shved
sumber
8

Seperti yang telah dinyatakan oleh banyak orang: faktorkan kode Anda dengan benar dan sesuaikan berdasarkan kode umum — ini akan meningkatkan rawatan secara signifikan. Apakah Anda menggunakan bahasa / sistem yang berorientasi objek atau tidak, ini dimungkinkan (meskipun sedikit lebih sulit untuk dilakukan di C daripada sesuatu yang benar-benar berorientasi objek). Inilah jenis masalah yang harus diselesaikan oleh warisan dan enkapsulasi!

Michael Trausch
sumber
5

Dengan sangat hati-hati

Bercabang Fitur adalah pilihan tetapi saya merasa agak berat. Itu juga membuat modifikasi yang mendalam mudah yang dapat mengarah pada forking outking dari aplikasi Anda jika tidak tetap terkendali. Idealnya Anda ingin mendorong sebanyak mungkin kustomisasi dalam upaya untuk menjaga basis kode inti Anda sebagai umum dan generik mungkin.

Berikut adalah bagaimana saya akan melakukannya walaupun saya tidak tahu apakah itu berlaku untuk basis kode Anda tanpa modifikasi dan re-faktoring berat. Saya memiliki proyek serupa di mana fungsi dasarnya sama, tetapi setiap pelanggan memerlukan serangkaian fitur yang sangat spesifik. Saya membuat satu set modul dan wadah yang kemudian saya kumpulkan melalui konfigurasi (à la IoC).

kemudian untuk setiap pelanggan saya membuat proyek yang pada dasarnya berisi konfigurasi dan skrip build untuk membuat instalasi yang sepenuhnya dikonfigurasi untuk situs mereka. Kadang-kadang saya menempatkan di sana juga beberapa komponen yang dibuat khusus untuk klien ini. Tetapi ini jarang terjadi dan bila memungkinkan saya mencoba membuatnya dalam bentuk yang lebih umum dan menekannya sehingga proyek lain dapat menggunakannya.

Hasil akhirnya adalah saya mendapatkan tingkat penyesuaian yang saya butuhkan, saya mendapatkan skrip instalasi yang disesuaikan sehingga ketika saya sampai di situs pelanggan saya tidak terlihat seperti saya tweeking sistem sepanjang waktu dan sebagai tambahan bonus SANGAT signifikan saya dapatkan untuk dapat membuat tes regresi langsung ketagihan. Dengan cara ini, setiap saat saya mendapatkan bug yang spesifik untuk pelanggan, saya dapat menulis tes yang akan menegaskan sistem saat dikerahkan dan dengan demikian dapat melakukan TDD bahkan pada tingkat itu.

singkatnya:

  1. Sistem termodulasi berat dengan struktur proyek datar.
  2. Buat proyek untuk setiap profil konfigurasi (pelanggan, meskipun lebih dari satu dapat berbagi profil)
  3. Rakit set fungsi yang diperlukan sebagai produk yang berbeda dan perlakukan seperti itu.

Jika dilakukan dengan benar, perakitan produk Anda harus berisi semua kecuali beberapa file konfigurasi.

Setelah menggunakan ini untuk sementara waktu, saya akhirnya menciptakan paket meta yang merakit sistem yang paling banyak digunakan atau penting sebagai unit inti dan menggunakan paket meta ini untuk rakitan pelanggan. Setelah beberapa tahun saya akhirnya memiliki kotak peralatan besar yang dapat saya kumpulkan dengan sangat cepat untuk menciptakan solusi pelanggan. Saat ini saya sedang mencari di Spring Roo dan melihat apakah saya tidak bisa mendorong ide sedikit lebih jauh berharap suatu hari saya dapat membuat konsep pertama dari sistem dengan pelanggan dalam wawancara pertama kami ... Saya kira Anda bisa menyebutnya User Driven Pengembangan ;-).

Semoga ini bisa membantu

Newtopian
sumber
3

Perubahannya mungkin terlalu kecil untuk membenarkan pemisahan modul menjadi modul (fisik) yang berbeda untuk setiap klien, saya khawatir ada masalah dengan build, kekacauan yang menghubungkan.

IMO, mereka tidak boleh terlalu kecil. Jika memungkinkan, saya akan memfaktorkan kode khusus klien menggunakan pola strategi di mana-mana. Ini akan mengurangi jumlah kode yang harus bercabang, dan mengurangi penggabungan yang diperlukan untuk menjaga sinkronisasi kode umum untuk semua klien. Ini juga akan menyederhanakan pengujian ... Anda dapat menguji kode umum menggunakan strategi default, dan menguji kelas khusus klien secara terpisah.

Jika modul Anda dikodekan sehingga menggunakan kebijakan X1 di modul A mengharuskan menggunakan kebijakan X2 di modul B, pikirkan tentang refactoring sehingga X1 dan X2 dapat digabungkan ke dalam satu kelas kebijakan tunggal.

kevin cline
sumber
1

Anda bisa menggunakan SCM Anda untuk memelihara cabang. Jaga agar cabang master tetap bersih dari kode khusus klien. Lakukan pengembangan utama pada cabang ini. Untuk setiap versi aplikasi yang disesuaikan mempertahankan cabang yang terpisah. Setiap alat SCM yang baik akan sangat baik dengan menggabungkan cabang (Git datang ke pikiran). Pembaruan apa pun di cabang master harus digabungkan ke cabang yang dikustomisasi, tetapi kode khusus klien dapat tetap berada di cabang sendiri.


Padahal, jika memungkinkan, cobalah untuk mendesain sistem dengan cara yang modular dan dapat dikonfigurasi. Kelemahan dari cabang-cabang khusus ini bisa jadi itu bergerak terlalu jauh dari inti / master.

Htbaa
sumber
1

Jika Anda menulis dalam huruf C, berikut adalah cara yang agak jelek untuk melakukannya.

  • Kode umum (misalnya unit "frangulator.c")

  • kode khusus klien, potongan yang kecil dan hanya digunakan untuk setiap klien.

  • dalam kode unit utama, gunakan #ifdef dan #include untuk melakukan sesuatu seperti

#ifdef CLIENT = CLIENTA
#include "frangulator_client_a.c"
#berakhir jika

Gunakan ini sebagai pola berulang-ulang di semua unit kode yang membutuhkan kustomisasi khusus klien.

Ini SANGAT jelek, dan mengarah ke beberapa masalah lain tetapi juga sederhana, dan Anda dapat membandingkan file-file khusus klien satu dengan yang lainnya dengan sangat mudah.

Ini juga berarti bahwa semua bagian khusus klien terlihat jelas (masing-masing dalam file sendiri) setiap saat, dan ada hubungan yang jelas antara file kode utama dan bagian khusus klien dari file tersebut.

Jika Anda benar-benar pintar, Anda dapat mengatur makefile untuk membuat definisi klien yang benar jadi seperti:

membuat klien

akan membangun untuk client_a, dan "make clientb" akan membuat untuk client_b dan seterusnya.

(dan "membuat" tanpa target yang disediakan dapat mengeluarkan peringatan atau deskripsi penggunaan.)

Saya telah menggunakan ide yang sama sebelumnya, perlu beberapa saat untuk mengatur tetapi ini bisa sangat efektif. Dalam kasus saya satu pohon sumber membangun sekitar 120 produk yang berbeda.

dengan cepat_now
sumber
0

Dalam git, cara saya akan melakukannya adalah memiliki cabang master dengan semua kode umum, dan cabang untuk setiap klien. Setiap kali perubahan kode inti dilakukan, rebase semua cabang khusus klien di atas master, sehingga Anda memiliki satu set tambalan untuk klien yang diterapkan di atas garis dasar saat ini.

Setiap kali Anda membuat perubahan untuk klien, dan melihat bug yang harus dimasukkan dalam cabang lain, Anda dapat memilih itu menjadi master, atau ke cabang lain yang perlu diperbaiki (meskipun cabang klien yang berbeda berbagi kode , Anda mungkin harus memiliki keduanya bercabang dari cabang yang sama dari master).

Cercerilla
sumber