Як підтримувати різні, спеціалізовані версії одного і того ж програмного забезпечення для декількох клієнтів


46

у нас є кілька клієнтів з різними потребами. Незважаючи на те, що наше програмне забезпечення є модульованим до певної міри, майже певно, що нам потрібно дещо коригувати ділову логіку кожного модуля тут і там для кожного клієнта. Зміни, ймовірно, занадто крихітні, щоб виправдати поділ модуля на окремий (фізичний) модуль для кожного клієнта, я побоююсь проблем зі збіркою, хаосу, що пов'язує. Тим не менше, ці зміни є занадто великими і занадто великими, щоб їх конфігурувати перемикачами в якомусь файлі конфігурації, оскільки це призведе до проблем під час розгортання і, ймовірно, до багатьох проблем із підтримкою, особливо з адміністраторами типу тинкер.

Я хотів би, щоб система збірки створила кілька збірок, по одному для кожного клієнта, де зміни містяться в спеціалізованій версії єдиного фізичного модуля, про який йдеться. Тож у мене є кілька питань:

Чи порадили б ви дозволити системі збирання створювати кілька збірок? Як слід зберігати різні налаштування в керуванні джерелами, зокрема svn?


4
Чи #ifdefпрацює для вас?
оххо

6
Попередження директора можуть швидко ускладнитися і зробити код важчим для читання і важче налагоджувати.
Сокіл

1
Для кращих відповідей слід включити детальну інформацію про фактичну платформу та тип програми (настільний / веб-сайт). Налаштування в настільному додатку C ++ зовсім інше, ніж веб-додаток PHP.
ГрандмайстерB

2
@Falcon: оскільки ви вибрали відповідь у 2011 році, у мене є багато сумнівів, чи можете ви сказати нам, чи маєте ви досвід використання SVN запропонованим способом між ними? Чи мої заперечення є необґрунтованими?
Док Браун

2
@Doc Brown: розгалуження та злиття було втомливим та складним. Тоді ми використовували систему плагінів із специфічними для клієнта плагінами або "патчами", які змінювали поведінку чи конфігурації. Ви матимете кілька накладних витрат, але ним можна керувати за допомогою Dependendy Injection.
Сокіл

Відповіді:


7

Що вам потрібно - це організація кодового розгалуження . У вашому конкретному сценарії це повинно називатися специфічним для клієнта розгалуженням магістралі, тому що ви, ймовірно, будете використовувати розгалуження функцій також під час розробки нових матеріалів (або усунення помилок).

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

Ідея полягає в тому, щоб стовбур коду з гілками нових функцій об'єднався в. Я маю на увазі всі функції, що не стосуються клієнта.

Тоді ви також маєте свої клієнтські гілки, де ви також з’єднуєте ті самі гілки функцій, як потрібно (якщо вибираєте вишню).

Ці відділення клієнтів виглядають подібними до функціональних гілок, хоча вони не є тимчасовими або короткочасними. Вони зберігаються протягом більш тривалого періоду часу і в основному вони лише об'єднуються. Повинно бути якомога менше розробок галузевих особливостей.

Спеціалізовані для клієнта гілки - це паралельні гілки до стовбура і активні до тих пір, як сам стовбур, і навіть не зливаються ніде як цілі.

            feature1
            ———————————.
                        \
trunk                    \
================================================== · · ·
      \ client1            \
       `========================================== · · ·
        \ client2            \
         `======================================== · · ·
              \ client2-specific feature   /
               `——————————————————————————´

7
+1 для розгалуження функцій. Ви також можете використовувати відділення для кожного клієнта. Я хотів би запропонувати лише одне: використати розподілений VCS (hg, git, bzr) замість SVN / CVS для цього;)
Герберт Амарал

43
-1. Це не те, для чого призначені "гілки функцій"; це суперечить їх визначенню як "тимчасові гілки, які зливаються, коли розвиток функції закінчується".
П Швед

14
Вам залишається підтримувати всі ці дельти в контролі джерел і тримати їх у черзі - назавжди. За короткий термін це може спрацювати. Довгостроково це може бути жахливим.
quick_now

4
-1, це не проблема іменування - використання різних гілок для різних клієнтів - це ще одна форма дублювання коду. Це IMHO антидіаграма, приклад того, як цього не робити.
Док Браун

7
Роберте, я думаю, я добре зрозумів те, що ти запропонував, ще до редагування, але я думаю, що це жахливий підхід. Припустимо, у вас є N клієнтів, щоразу, коли ви додаєте нову основну функцію до магістралі, схоже, SCM полегшить розповсюдження нової функції до N гілок. Але використання гілок таким чином дозволяє занадто легко уникнути чіткого розмежування модифікацій, характерних для клієнта. Як результат, тепер у вас є N шансів отримати конфлікт злиття для кожної зміни магістралі. Крім того, тепер вам доведеться запустити N тестів на інтеграцію замість одного.
Док Браун

38

Не робіть цього з відділеннями SCM. Зробіть загальний код окремим проектом, який створює артефакт бібліотеки чи скелета проекту. Кожен проект-замовник - це окремий проект, який потім залежить від загального як залежності.

Це найпростіше, якщо ваш звичайний базовий додаток використовує рамку для ін'єкцій залежностей, як Spring, так що ви можете легко вводити різні варіанти заміни об'єктів у кожен проект клієнта, оскільки потрібні спеціальні функції. Навіть якщо ви ще не маєте рамки DI, додавання цього та це зробити може бути вашим найменш болісним вибором.


Іншим підходом до спрощення речей є використання успадкування (і одного проекту) замість використання ін'єкції залежності, зберігаючи як загальний код, так і налаштування в одному проекті (використовуючи або спадкування, часткові класи, або події). З таким дизайном клієнтські відділення спрацювали б добре (як описано у вибраній відповіді), і завжди можна оновити клієнтські гілки, оновивши загальний код.
дризін

Якщо я чогось не пропускаю, що ви пропонуєте, якщо у вас є 100 клієнтів, ви створите (100 x NumberOfChangedProjects ) і використаєте DI для управління ними? Якщо це так, я б напевно тримався осторонь такого рішення, оскільки ремонтопридатність була б жахливою ..
sotn

13

Зберігайте програмне забезпечення для всіх клієнтів в одному відділенні. Немає необхідності розходити зміни, внесені для різних клієнтів. Найбільш лайно, ви хочете, щоб ваше програмне забезпечення було найкращої якості для всіх клієнтів, а виправлення вашої основної інфраструктури повинно впливати на всіх без зайвих об'єднань накладних витрат, що також може ввести більше помилок.

Модулюйте загальний код та реалізуйте код, який відрізняється у різних файлах, або захистіть його різними значеннями. Зробіть так, щоб у вашій системі побудови були визначені конкретні цілі для кожного клієнта, кожна з яких складала версію лише з кодом, що стосується одного клієнта. Наприклад, якщо ваш код є C, можливо, ви хочете захистити функції для різних клієнтів за допомогою " #ifdef" або будь-якого механізму, який має ваша мова для управління конфігурацією побудови, і підготувати набір визначень, що відповідає кількості функцій, які клієнт заплатив.

Якщо вам не подобаються ifdefs, використовуйте "інтерфейси та реалізації", "функтори", "об'єктивні файли" або будь-які інструменти, які надає ваша мова для зберігання різних речей в одному місці.

Якщо ви поширюєте джерела своїм клієнтам, найкраще, щоб у ваших сценаріїв побудови були спеціальні "цілі розподілу джерел". Щойно ви викликаєте таку ціль, вона створює спеціальну версію джерел вашого програмного забезпечення, копіює їх в окрему папку, щоб ви могли доставити їх, а не компілювати їх.


8

Як багато хто з них заявив: правильно розподіліть свій код і налаштуйте на основі загального коду - це значно покращить технічне обслуговування. Незалежно від того, використовуєте ви об'єктно-орієнтовану мову / систему чи ні, це можливо (хоча це зробити в C набагато складніше, ніж те, що справді орієнтоване на об'єкти). Саме такий тип проблеми допомагає успадкування та інкапсуляція!


5

Дуже обережно

Розгалуження функцій - це варіант, але я вважаю, що він дещо важкий. Крім того, це робить глибокі модифікації легкими, що може призвести до розгортання вашої програми, якщо не тримати її під контролем. В ідеалі ви хочете максимально активувати налаштування, намагаючись зберегти базу основного коду максимально спільною та загальною.

Ось як я це зробив би, хоча я не знаю, чи застосовний він до вашої кодової бази без великих модифікацій та повторних факторів. У мене був подібний проект, де основна функціональність була однаковою, але кожен клієнт вимагав дуже специфічного набору функцій. Я створив набір модулів і контейнерів, які потім збираю за допомогою конфігурації (à la IoC).

то для кожного замовника я створив проект, який в основному містить конфігурації та сценарій збірки для створення повністю налаштованої установки для їх сайту. Інколи розміщую там також деякі компоненти, виготовлені на замовлення для цього клієнта. Але це трапляється рідко, і коли це можливо, я намагаюся зробити це в більш загальній формі і відсунути його, щоб інші проекти могли їх використовувати.

Кінцевий результат - я отримав потрібний мені рівень налаштування, я отримав налаштовані сценарії встановлення, так що, потрапляючи на сайт клієнта, я не виглядаю так, як весь час налаштовую систему, і як доданий ДУЖЕ значний бонус, який я отримую мати можливість створювати тести регресії, підключені безпосередньо на збірці. Таким чином, у будь-який час я отримую помилку, що відповідає специфіці замовника, я можу написати тест, який буде стверджувати систему під час її розгортання, і таким чином можна робити TDD навіть на тому рівні.

так коротше:

  1. Сильно модульна система з плоскою структурою проекту.
  2. Створіть проект для кожного профілю конфігурації (клієнт, хоча більше одного може поділитися профілем)
  3. Зберіть необхідні набори функціональних можливостей як інший продукт і обробіть його як такий.

Якщо зробити все правильно, збірка продукту повинна містити всі файли конфігурації, окрім кількох.

Користуючись цим деякий час, я закінчила створення метапакетів, які збирають в основному використовувані або основні системи як основний блок і використовують цей метапакет для складання клієнтів. Через кілька років у мене з’явився великий набір інструментів, який я міг дуже швидко зібрати для створення клієнтських рішень. Я зараз розглядаю Spring Roo і бачу, чи не можу я просунути цю ідею трохи далі, сподіваючись, що одного разу я можу створити перший проект системи прямо із замовником у нашому першому інтерв'ю ... Я думаю, ви могли б назвати це користувачем Розвиток ;-).

Сподіваюся, що це допомогло


3

Зміни, ймовірно, занадто крихітні, щоб виправдати поділ модуля на окремий (фізичний) модуль для кожного клієнта, я побоююсь проблем зі збіркою, хаосу, що пов'язує.

ІМО, вони не можуть бути надто крихітними. Якщо можливо, я б визначив специфічний для клієнта код, використовуючи схему стратегії майже скрізь. Це зменшить кількість коду, який повинен бути розгалуженим, і зменшить об'єднання, необхідне для синхронізації загального коду для всіх клієнтів. Це також спростить тестування ... ви можете протестувати загальний код, використовуючи стратегії за замовчуванням, і випробовувати окремі для клієнта класи.

Якщо ваші модулі закодовані таким чином, що для використання політики X1 в модулі A потрібно використовувати політику X2 в модулі B, подумайте про рефакторинг, щоб X1 і X2 можна було об'єднати в єдиний клас політики.


1

Ви можете використовувати свій SCM для підтримки філій. Зберігайте основну гілку незайманою / чистою від користувацького коду клієнта. Робіть основний розвиток у цій галузі. Для кожної спеціалізованої версії програми підтримуйте окремі гілки. Будь-який хороший інструмент SCM справді добре поєднується з об'єднанням гілок (Git приходить на думку). Будь-які оновлення в головній гілці повинні бути об'єднані у спеціалізовані гілки, але специфічний для клієнта код може залишатися у власній гілці.


Хоча, якщо це можливо, спробуйте спроектувати систему модульно та налаштовано. Недоліком цих спеціалізованих гілок може бути те, що вона віддаляється занадто далеко від ядра / головного.


1

Якщо ви пишете простою мовою C, ось досить некрасивий спосіб це зробити.

  • Загальний код (наприклад, блок "frangulator.c")

  • специфічний для клієнта код, фрагменти, які невеликі та використовуються лише для кожного клієнта.

  • в основному коді блоку використовуйте #ifdef та #include, щоб зробити щось подібне

#ifdef КЛІЄНТ = КЛІЄНТА
#include "frangulator_client_a.c"
#endif

Використовуйте це як шаблон знову і знову у всіх кодових одиницях, які потребують індивідуального налаштування.

Це ДУЖЕ некрасиво і призводить до інших проблем, але це також просто, і ви можете легко порівняти конкретні клієнтські файли один проти іншого досить легко.

Це також означає, що всі фрагменти, характерні для клієнта, добре видно (кожен у власному файлі) у будь-який час, і існує чітке співвідношення між основним кодовим файлом та специфічною для клієнта частиною файлу.

Якщо ви дійсно розумні, ви можете налаштувати файли для створення правильного визначення клієнта таким чином:

зробити кліенту

побудує для client_a, а "make clientb" зробить для client_b тощо.

(і "make" без передбаченої цілі може видати попередження або опис використання.)

Раніше я використовував подібну ідею, це потребує певного часу, але це може бути дуже ефективно. У моєму випадку з одного вихідного дерева було створено близько 120 різних продуктів.


0

У git, як я це зробив би, це мати головну гілку з усім загальним кодом та гілками для кожного клієнта. Щоразу, коли відбувається зміна основного коду, просто перезавантажте всі спеціалізовані для клієнта гілки на вершині майстра, щоб у вас був набір рухомих патчів для клієнтів, які застосовуються поверх поточної базової лінії.

Щоразу, коли ви вносите зміни до клієнта, і помічаєте помилку, яку слід включити до інших гілок, ви можете вибирати це в вищому віці, або в інші гілки, які потребують виправлення (хоча різні клієнтські гілки мають код спільного використання ви, мабуть, повинні мати їх обох відгалужень від загальної гілки від головного).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.