Как вы организуете высоко настраиваемое программное обеспечение?


28

Я работаю над большим программным проектом, который специально адаптирован для различных клиентов по всему миру. Это означает, что у нас может быть 80% кода, который является общим для разных клиентов, но также и много кода, который должен меняться от одного клиента к другому. В прошлом мы занимались разработкой в ​​отдельных репозиториях (SVN), и когда начинался новый проект (у нас мало, но крупных клиентов), создавался другой репозиторий, основанный на том, какой предыдущий проект имел наилучшую основу кода для наших нужд. Это работало в прошлом, но мы столкнулись с несколькими проблемами:

  • Ошибки, которые исправлены в одном репозитории, не исправляются в других репозиториях. Это может быть проблемой организации, но мне трудно исправить и исправить ошибку в 5 разных репозиториях, учитывая, что команда, обслуживающая этот репозиторий, может находиться в другой части мира, и у нас нет тестовой среды также не знаете своего графика или требований, которые у них есть («ошибка» в одной стране может быть «функцией» в другой).
  • Функции и улучшения, сделанные для одного проекта, которые также могут быть полезны для другого проекта, теряются или если они используются в другом проекте, часто вызывают большие головные боли при слиянии их из одной базы кода в другую (поскольку обе ветви могли разрабатываться независимо в течение года ).
  • Рефакторинг и улучшения кода, сделанные в одной ветви разработки, либо теряются, либо приносят больше вреда, чем пользы, если вам нужно объединить все эти изменения между ветвями.

Сейчас мы обсуждаем, как решить эти проблемы, и до сих пор придумали следующие идеи, как решить эту проблему:

  1. Сохраняйте разработку в отдельных ветках, но лучше организуйте, имея центральный репозиторий, в который объединяются общие исправления ошибок, и все проекты объединяют изменения из этого центрального репозитория в свои собственные на регулярной (например, ежедневной) основе. Это требует огромной дисциплины и больших усилий для слияния ветвей. Так что я не уверен, что это сработает, и мы сможем сохранить эту дисциплину, особенно, когда время ограничено.

  2. Отказаться от отдельных ветвей разработки и иметь центральное хранилище кода, где живет весь наш код, и выполнять нашу настройку, имея подключаемые модули и параметры конфигурации. Мы уже используем контейнеры Dependency Injection для разрешения зависимостей в нашем коде, и мы следуем шаблону MVVM в большей части нашего кода, чтобы четко отделить бизнес-логику от нашего пользовательского интерфейса.

Второй подход кажется более элегантным, но у нас есть много нерешенных проблем в этом подходе. Например: как обрабатывать изменения / дополнения в вашей модели / базе данных. Мы используем .NET с Entity Framework, чтобы иметь строго типизированные сущности. Я не понимаю, как мы можем обрабатывать свойства, которые требуются для одного клиента, но бесполезны для другого клиента, не загромождая нашу модель данных. Мы думаем о решении этого в базе данных с помощью спутниковых таблиц (имеющих отдельные таблицы, в которых наши дополнительные столбцы для конкретной сущности живут с отображением 1: 1 на исходную сущность), но это только база данных. Как вы справляетесь с этим в коде? Наша модель данных находится в центральной библиотеке, которую мы не сможем расширить для каждого клиента, использующего этот подход.

Я уверен, что мы не единственная команда, борющаяся с этой проблемой, и я шокирован, когда нашел так мало материала по этой теме.

Итак, мои вопросы следующие:

  1. Какой у вас опыт работы с высоко настраиваемым программным обеспечением, какой подход вы выбрали и как он работал для вас?
  2. Какой подход вы рекомендуете и почему? Есть ли лучший подход?
  3. Есть ли хорошие книги или статьи на эту тему, которые вы можете порекомендовать?
  4. У вас есть конкретные рекомендации для нашей технической среды (.NET, Entity Framework, WPF, DI)?

Редактировать:

Спасибо за все предложения. Большинство идей совпадают с теми, которые у нас уже были в нашей команде, но очень полезно увидеть опыт, который вы имели с ними, и советы, как лучше их реализовать.

Я все еще не уверен, по какому пути мы пойдем, и я не принимаю решение (в одиночку), но я передам это в моей команде, и я уверен, что это будет полезно.

На данный момент тенор, похоже, представляет собой единый репозиторий с использованием различных пользовательских модулей. Я не уверен, что наша архитектура соответствует этому или сколько мы должны вложить, чтобы привести его в соответствие, поэтому некоторые вещи могут некоторое время жить в отдельных репозиториях, но я думаю, что это единственное долгосрочное решение, которое будет работать.

Итак, еще раз спасибо за все ответы!


Рассмотрим обработку таблиц базы данных как кода.

Мы делаем это уже в том смысле, что у нас есть сценарии базы данных в нашем хранилище subversion, но это не решает проблемы, упомянутые выше. Мы не хотим иметь таблицы стилей Key-Value в нашей модели базы данных, потому что они связаны с множеством проблем. Итак, как вы разрешаете дополнения к вашей модели для отдельных клиентов, сохраняя при этом общее хранилище кода для всех них?
aKzenT

Ответы:


10

Похоже, основная проблема заключается не только в обслуживании хранилища кода, но и в отсутствии подходящей архитектуры .

  1. Каково ядро ​​/ сущность системы, которая всегда будет использоваться всеми системами?
  2. Какие улучшения / отклонения требуются каждому клиенту?

Фреймворк или стандартная библиотека включает в себя первый, тогда как последний будет реализован как надстройки (плагины, подклассы, DI, все, что имеет смысл для структуры кода).

Система управления исходным кодом, которая управляет филиалами и распределенной разработкой, вероятно, также поможет; Я фанат Mercurial, другие предпочитают Git. Каркас будет основной ветвью, например, каждая настроенная система будет дочерней.

Конкретные технологии, используемые для реализации системы (.NET, WPF и т. Д.), В значительной степени не важны.

Получить это правильно нелегко , но это важно для долгосрочной жизнеспособности. И, конечно, чем дольше вы ждете, тем больше технической задолженности вам придется иметь дело.

Вы можете найти полезной книгу « Архитектура программного обеспечения: организационные принципы и шаблоны» .

Удачи!


3
Ага. Хотя архитектура часто является скорее психологической, чем технической. Если вы полностью сконцентрируетесь на продукте, то у вас будет разбивка, где компоненты полезны только для этого продукта. Если, с другой стороны, вы сосредоточены на создании набора библиотек, которые будут более полезными в целом, то вы создадите набор возможностей, которые могут быть развернуты в более широком диапазоне ситуаций. Ключ, конечно, заключается в том, чтобы найти правильный баланс между этими двумя крайностями для вашей конкретной ситуации.
Уильям Пейн

Я думаю, что есть большая общая часть между многими из наших филиалов (> 90%), но последние 10% всегда находятся в разных местах, поэтому есть очень мало компонентов, где я не могу представить некоторые специфические для клиента изменения, за исключением некоторых служебных библиотек, которые не содержат никакой бизнес-логики.
aKzenT

@aKzenT: хммм ... не представляй, померяйся. Изучите код и посмотрите на все места, где произошла настройка, составьте список модифицированных компонентов, отметьте, как часто каждый компонент был модифицирован, и подумайте о видах и шаблонах модификаций, которые фактически были сделаны. Они косметические или алгоритмические? Они добавляют или изменяют базовую функциональность? В чем причина каждого типа изменений? Опять же, это тяжелая работа , и вам может не понравиться последствия того, что вы обнаружите.
Стивен А. Лоу

1
Я принял это как ответ, потому что мы не можем принять это решение, прежде чем больше думать о нашей архитектуре, определить части, которые являются общими или которые ДОЛЖНЫ быть общими, а затем посмотреть, можем ли мы жить с одним репозиторием или нужно ли разветвление (для момент как минимум). Этот ответ отражает это лучшее ИМО. Спасибо за все другие сообщения, хотя!
aKzenT

11

Одна компания, в которой я работал, имела ту же проблему, и подход к решению этой проблемы заключался в следующем: была создана общая структура для всех новых проектов; это включает в себя все вещи, которые должны быть одинаковыми в каждом проекте. Например, инструменты для создания форм, экспорт в Excel, ведение журнала. Усилия были предприняты, чтобы убедиться, что эта общая структура только улучшена (когда новый проект нуждается в новых функциях), но никогда не раздваивались.

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

Наличие всего в единой кодовой базе для всех клиентов имеет некоторые преимущества, но, с другой стороны, чтение кода становится трудным, когда существует бесчисленное множество параметров, ifчтобы заставить программу вести себя по-разному для каждого клиента.

РЕДАКТИРОВАТЬ: Один анекдот, чтобы сделать это более понятным:

Домен этой компании - управление складом, и одной из задач системы управления складом является поиск свободного места для хранения поступающих товаров. Звучит просто, но на практике нужно соблюдать множество ограничений и стратегий.

В какой-то момент руководство попросило программиста создать гибкий, параметризуемый модуль для поиска мест хранения, в котором реализовано несколько различных стратегий, и их следовало использовать во всех последующих проектах. Благородные усилия привели к сложному модулю, который было очень трудно понять и поддерживать. В следующем проекте руководитель проекта не мог понять, как заставить его работать на этом складе, и разработчик указанного модуля ушел, поэтому он в конечном итоге проигнорировал его и написал собственный алгоритм для этой задачи.

Несколько лет спустя изменилась планировка склада, где первоначально использовался этот модуль, и модуль при всей своей гибкости не соответствовал новым требованиям; поэтому я заменил его на собственный алгоритм там тоже.

Я знаю, что LOC не очень хорошее измерение, но в любом случае: размер «гибкого» модуля составлял ~ 3000 LOC (PL / SQL), в то время как пользовательский модуль для той же задачи занимает ~ 100..250 LOC. Поэтому, попытка быть гибкой чрезвычайно увеличивала размер кодовой базы, не получая многократного использования, на которое мы надеялись.


Спасибо за ваш отзыв. Это расширение первого описанного решения, и мы тоже подумали об этом. Однако, поскольку большинство наших библиотек используют некоторые основные объекты, и эти основные объекты обычно расширяются для одного или другого клиента, я думаю, что мы могли бы поместить только очень немного библиотек в этот основной репозиторий. Например, у нас есть определенная сущность «Клиент», и ORM отображается в одной библиотеке, которая используется почти всеми другими нашими библиотеками и программами. Но у каждого клиента есть несколько дополнительных полей, которые они должны добавить к «Клиенту», поэтому нам нужно было бы форкнуть эту библиотеку и, следовательно, все библиотеки в зависимости от нее.
aKzenT

3
Наша идея избежать бесчисленных «если» - это широко использовать внедрение зависимостей и обмениваться полными модулями для разных клиентов. Не уверен, как это будет работать, хотя. Как этот подход работал для вас?
aKzenT

+1, это в основном соответствует моему опыту проектов, которые справились с этим хорошо. Если вы собираетесь использовать подход «если для разных клиентов», то 2 пункта: 1. Не делайте if (customer1) ..., вместо этого делайте if (configurationOption1) ... и используйте параметры конфигурации для каждого клиента. 2. Постарайтесь не делать этого! Может быть, 1% времени будет лучшим (более понятным / легко обслуживаемым) вариантом, чем наличие специфичных для конфигурации модулей.
vaughandroid

@Baqueta: Просто чтобы уточнить: вы рекомендуете использовать модули для каждого клиента вместо параметров конфигурации (если), верно? Мне нравится ваша идея различать функции вместо клиентов. Таким образом, комбинация обоих будет иметь различные «функциональные модули», которые управляются параметрами конфигурации. Клиент тогда представляется только как набор независимых функциональных модулей. Мне очень нравится этот подход, но я не уверен, как его спроектировать. DI решает проблему загрузки и обмена модулями, но как вы управляете различными моделями данных между клиентами?
aKzenT

Да, модули для каждой функции, а затем для каждого клиента конфигурация / выбор функций. Я был бы идеальным. К сожалению, мне никогда не приходилось сочетать ваши требования существенной индивидуальной настройки для каждого клиента с одной библиотекой данных, поэтому я не уверен, что смогу там сильно помочь ...
vaughandroid

5

Один из проектов, над которым я работал, поддерживал несколько платформ (более 5) в большом количестве выпусков продукта. Многие проблемы, которые вы описываете, были вещами, с которыми мы столкнулись, хотя и немного по-другому. У нас была собственная БД, поэтому у нас не было проблем такого же типа на этой арене.

Наша структура была похожа на вашу, но у нас был один репозиторий для нашего кода. Специфичный для платформы код помещался в собственные папки проекта в дереве кода. Общий код жил в дереве в зависимости от того, к какому слою он принадлежал.

У нас была условная компиляция, основанная на создаваемой платформе. Поддержание этого было болезненным, но это нужно было делать только тогда, когда на уровне платформы были добавлены новые модули.

Наличие всего кода в одном репозитории позволило нам легко исправлять ошибки на разных платформах и выпусках одновременно. У нас была автоматизированная среда сборки для всех платформ, которая могла бы служить защитой на случай, если новый код сломает предполагаемую несвязанную платформу.

Мы пытались препятствовать этому, но были случаи, когда платформе требовалось исправление, основанное на ошибке, специфичной для платформы, которая была в другом общем коде. Если бы мы могли условно переопределить компиляцию, не заставляя модуль выглядеть беспорядочно, мы сделали бы это в первую очередь. Если нет, мы бы переместили модуль из общей территории и вставили его в платформу.

Для базы данных у нас было несколько таблиц, которые имели специфичные для платформы столбцы / модификации. Мы бы позаботились о том, чтобы каждая версия таблицы платформы соответствовала базовому уровню функциональности, чтобы общий код мог ссылаться на него, не беспокоясь о зависимостях платформы. Специфичные для платформы запросы / манипуляции были перенесены в уровни проекта платформы.

Итак, чтобы ответить на ваши вопросы:

  1. Много, и это была одна из лучших команд, с которыми я работал. Кодовая база в то время была около 1 млн. Лок. Я не смог выбрать подход, но он сработал чертовски хорошо. Даже в ретроспективе я не видел лучшего способа справиться с вещами.
  2. Я рекомендую второй подход, который вы предложили, с нюансами, которые я упомянул в своем ответе.
  3. Нет книг, о которых я мог бы подумать, но я бы начал изучать мультиплатформенную разработку.
  4. Институт сильного управления. Это ключ к соблюдению ваших стандартов кодирования. Следование этим стандартам - единственный способ сохранить управляемость и ремонт. У нас была страстная просьба сломать модель, которой мы следовали, но ни одно из этих обращений не поколебало всю команду старших разработчиков.

Спасибо за обмен вашего опыта. Просто чтобы лучше понять: как определяется платформа в вашем случае. Windows, Linux, X86 / x64? Или что-то более связанное с различными клиентами / средами? Вы делаете хорошую точку в 4) Я думаю, что это одна из проблем, которые у нас есть. У нас есть команда из очень умных и квалифицированных людей, но у всех есть немного разные идеи о том, как что-то делать. Без кого-то, кто явно отвечает за архитектуру, трудно договориться об общей стратегии, и вы рискуете потерять себя в дискуссиях, ничего не меняя.
aKzenT

@aKzenT - да, я имел в виду физическое оборудование и ОС как платформы. У нас был большой набор функций, некоторые из которых можно было выбрать с помощью модуля лицензирования. И мы поддерживали широкий спектр оборудования. В связи с этим у нас был каталог общего уровня, в котором было несколько устройств с собственными каталогами на этом уровне для дерева кода. Таким образом, наши обстоятельства были не так уж далеко от того места, где вы находитесь. Наши старшие разработчики могли бы вести дебаты друг с другом, но как только коллективное решение было принято, все согласились с этим.

4

Я много лет работал над заявлением в пенсионное управление, в котором были похожие проблемы. Пенсионные планы сильно различаются между компаниями и требуют узкоспециализированных знаний для реализации логики расчетов и отчетов, а также очень разного дизайна данных. Я могу только дать краткое описание части архитектуры, но, возможно, она даст достаточно идеи.

У нас было 2 отдельные команды: основная команда разработчиков , которая отвечала за код основной системы (который будет вашим кодом на 80% выше), и команда внедрения , которая обладала знаниями в области пенсионных систем и отвечала за обучение клиентов. требования и кодирование скриптов и отчетов для клиента.

Мы определили все наши таблицы в Xml (это было до того времени, когда структуры сущностей были проверены временем и стали общепринятыми). Группа разработчиков спроектирует все таблицы в Xml, а ядро ​​приложения может получить запрос на создание всех таблиц в Xml. Для каждого клиента были также связаны файлы сценариев VB, отчеты Crystal Reports, документы Word и т. Д. (В Xml также была встроена модель наследования, позволяющая повторно использовать другие реализации).

Базовое приложение (одно приложение для всех клиентов) будет кэшировать все специфичные для клиента вещи при поступлении запроса для этого клиента и генерирует общий объект данных (вроде как набор записей удаленного ADO), который можно сериализовать и передать около.

Эта модель данных менее удобна, чем объекты сущностей / доменов, но она очень гибкая, универсальная и может обрабатываться одним набором базового кода. Возможно, в вашем случае вы могли бы определить базовые объекты сущности только с общими полями и иметь дополнительный словарь для настраиваемых полей (добавить некоторый набор дескрипторов данных к вашему объекту сущности, чтобы у него были метаданные для настраиваемых полей. )

У нас были отдельные репозитории для исходного кода системы и кода реализации.

Наша базовая система на самом деле имела очень мало бизнес-логики, за исключением некоторых очень стандартных общих модулей расчета. Основная система функционировала как: экранный генератор, скрипт-бегун, генератор отчетов, доступ к данным и транспортный уровень.

Сегментация базовой и настраиваемой логики - сложная задача. Тем не менее, мы всегда чувствовали, что лучше иметь одну базовую систему с несколькими клиентами, а не несколько копий системы, запущенной для каждого клиента.


Спасибо за ответ. Мне нравится идея иметь дополнительные поля в словаре. Это позволило бы нам иметь единственное определение сущности и поместить все специфичные для клиента вещи в словарь. Однако я не уверен, есть ли хороший способ заставить его работать с нашей оболочкой ORM (Entity Framework). И я также не уверен, является ли хорошей идеей иметь модель данных с глобальным общим доступом вместо модели для каждой функции / модуля.
aKzenT

2

Я работал над небольшой системой (20 килокалорий) и обнаружил, что DI и конфигурация являются отличными способами управления различиями между клиентами, но их недостаточно, чтобы избежать разветвления системы. База данных разделена между определенной частью приложения, которая имеет фиксированную схему, и зависимой от клиента частью, которая определяется через пользовательский документ конфигурации XML.

Мы сохранили одну ветку в Mercurial, настроенную так, как если бы она была доставляемой, но фирменную и настроенную для вымышленного клиента. Исправления ошибок включены в этот проект, и новые разработки основных функциональных возможностей происходят только там. Релизы для реальных клиентов - это ветки, хранящиеся в их собственных репозиториях. Мы отслеживаем большие изменения в коде с помощью написанных вручную номеров версий и отслеживаем исправления ошибок, используя номера коммитов.


Вы делали различие между основными библиотеками, которые одинаковы для клиентов, или вы разветвляли полное дерево? Регулярно ли вы сливаетесь с магистрали на отдельные вилки? И если да, сколько времени стоило ежедневное слияние и как вы избежали того, что эта процедура разваливается, когда начинается нехватка времени (как это всегда происходит в одной точке проекта)? Извините за столько вопросов: p
aKzenT

Мы разветвляем все дерево, главным образом потому, что запросы клиентов приходят раньше, чем целостность системы сборки. Слияние с основной системой происходит вручную с помощью mercurial и других инструментов и обычно ограничивается критическими ошибками или большими обновлениями функций. Мы стараемся сохранять обновления для больших нечастых блоков, как из-за стоимости слияния, так и из-за того, что многие клиенты размещают свои собственные системы, и мы не хотим ставить на них затраты на установку без предоставления стоимости.
Дэн Монего

Мне любопытно: IIRC Mercurial - это DVCS, похожая на git. Заметили ли вы какие-либо преимущества этих слияний между ветвями по сравнению с Subversion или другими традиционными VCS? Я только что закончил очень болезненный процесс слияния между двумя полностью разрабатываемыми ветвями, используя subversion, и думал, было бы проще, если бы мы использовали git.
aKzenT

Слияние с Mercurial было намного, намного проще, чем с нашим предыдущим инструментом, которым был Vault. Одним из главных преимуществ является то, что Mercurial действительно хорош в размещении истории изменений в центре приложения, что позволяет легко отслеживать, что и где было сделано. Самым сложным для нас было перенести существующие ветви: если вы объединяете две ветви, Mercurial требует, чтобы у них обоих был один и тот же корень, поэтому для их настройки потребовалось одно последнее полностью ручное слияние.
Дэн Монего

2

Боюсь, что у меня нет прямого опыта проблемы, которую вы описываете, но у меня есть некоторые комментарии.

Второй вариант - собрать код в центральном хранилище (насколько это практически возможно) и создать архитектуру для настройки (опять же, насколько это практически возможно) - почти наверняка путь в долгосрочной перспективе.

Проблема в том, как вы планируете туда добраться и сколько времени это займет.

В этой ситуации, вероятно, нормально (временно) иметь более одной копии приложения в хранилище одновременно.

Это позволит вам постепенно перейти к архитектуре, которая напрямую поддерживает настройку, не делая это одним махом.


2

Второй подход кажется более элегантным, но у нас есть много нерешенных проблем в этом подходе.

Я уверен, что любая из этих проблем может быть решена одна за другой. Если вы застряли, спросите здесь или на SO о конкретной проблеме.

Как уже отмечали другие, вам следует выбрать одну центральную кодовую базу / один репозиторий. Я пытаюсь ответить на ваш пример вопроса.

Например: как обрабатывать изменения / дополнения в вашей модели / базе данных. Мы используем .NET с Entity Framework, чтобы иметь строго типизированные сущности. Я не понимаю, как мы можем обрабатывать свойства, которые требуются для одного клиента, но бесполезны для другого клиента, не загромождая нашу модель данных.

Есть некоторые возможности, все они я видел в реальных системах. Какой из них выбрать, зависит от вашей ситуации:

  • жить с беспорядком в определенной степени
  • ввести таблицы «CustomAttributes» (описывающие имена и типы) и «CustomAttributeValues» (для значений, например, хранящихся в виде строкового представления, даже если они являются числами). Это позволит добавлять такие атрибуты во время установки или выполнения, имея индивидуальные значения для каждого клиента. Не настаивайте на том, чтобы каждый пользовательский атрибут моделировался «визуально» в вашей модели данных.

  • теперь должно быть понятно, как использовать это в коде: иметь только общий код для доступа к этим таблицам и отдельный код (возможно, в отдельной подключаемой библиотеке DLL, которая зависит от вас) для правильной интерпретации этих атрибутов

  • Другой вариант - дать каждой таблице сущностей большое строковое поле, в которое можно добавить отдельную XML-строку.
  • Попытайтесь обобщить некоторые концепции, чтобы их можно было легко использовать для разных клиентов. Я рекомендую книгу Мартина Фаулера « Анализ моделей ». Хотя эта книга не посвящена предварительной настройке программного обеспечения, она также может быть вам полезна.

И для конкретного кода: вы также можете попробовать ввести язык сценариев в свой продукт, особенно для добавления пользовательских сценариев. Таким образом, вы не только создаете четкую грань между вашим кодом и кодом, специфичным для клиента, вы также можете позволить своим клиентам настраивать систему в некоторой степени самостоятельно.


Проблемы с добавлением пользовательских атрибутов или столбцов XML для хранения пользовательских свойств заключаются в том, что они очень ограничены в своих возможностях. Например, выполнение сортировки, группировки или фильтрации на основе этих атрибутов является очень сложной задачей. Однажды я работал над системой, в которой использовалась дополнительная таблица атрибутов, и все сложнее поддерживать и обрабатывать эти пользовательские атрибуты. По этой причине я думал вместо того, чтобы помещать эти атрибуты в виде столбцов в дополнительной таблице, которая сопоставлена ​​1: 1 с оригиналом. Проблема определения, запроса и управления ими остается той же.
aKzenT

@aKzenT: Настраиваемость не предоставляется бесплатно, вам придется поменять простоту использования и настраиваемость. Мое общее предложение заключается в том, что вы не вводите зависимости, в которых основная часть системы каким-либо образом зависит от какой-либо пользовательской части, а только наоборот. Например, когда вы вводите эти «дополнительные таблицы» для клиента 1, можете ли вы избежать развертывания этих таблиц и связанного кода для клиента 2? Если ответ да, то решение в порядке.
Док Браун

0

Я только построил одно такое приложение. Я бы сказал, что 90% проданных единиц были проданы без изменений. У каждого клиента был свой собственный скин, и мы обслуживали систему в этом скине. Когда появился мод, который затронул основные разделы, мы попытались использовать IF ветвление . Когда в том же разделе появился мод № 2, мы переключились на логику CASE, которая позволяла расширяться в будущем. Это, казалось, обрабатывало большинство второстепенных запросов.

Любые дальнейшие незначительные пользовательские запросы обрабатывались с помощью логики Case.

Если моды были двухрадикальными, мы создали клон (отдельно включенный) и обернули вокруг него CASE, чтобы включить другой модуль.

Исправления ошибок и модификации ядра затрагивали всех пользователей. Мы тщательно протестировали в разработке, прежде чем идти в производство. Мы всегда отправляли уведомления по электронной почте, которые сопровождали любые изменения, и НИКОГДА, НИКОГДА, НИКОГДА не публиковали производственные изменения по пятницам ... НИКОГДА.

Нашей средой был классический ASP и SQL Server. Мы НЕ были магазином спагетти-кода ... Все было модульно с использованием Включений, Подпрограмм и Функций.


-1

Когда меня попросят начать разработку B, который разделяет функциональность на 80% с A, я либо:

  1. Клон А и измените его.
  2. Извлеките функциональные возможности, которые разделяют и A, и B, в C, который они будут использовать.
  3. Сделайте A достаточно настраиваемым, чтобы удовлетворить потребности как B, так и самого себя (поэтому B встроен в A).

Вы выбрали 1, и это, кажется, не соответствует вашей ситуации. Ваша миссия состоит в том, чтобы предсказать, какие из 2 и 3 лучше подходят.


1
Звучит просто, но как это сделать на практике? Как сделать ваше программное обеспечение настолько настраиваемым, не загромождая его словом «if (customer1)», которое через некоторое время становится не поддерживаемым.
aKzenT

@aKzenT Вот почему я оставил 2 и 3 на ваше усмотрение. Если изменения, необходимые для поддержки проекта customer1, поддерживают потребности customer2 с помощью конфигурации, то это сделает код не поддерживаемым, тогда не делайте 3.
Моше Рева

Я предпочитаю делать «if (option1)» вместо «if (customer1)». Таким образом, с помощью N опций я могу управлять множеством возможных клиентов. Например, с N булевыми опциями вы можете управлять 2 ^ N клиентами, имея только N 'if' ... невозможно управлять стратегией "if (customer1)", которая потребует 2 ^ N 'if'.
Fil
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.