Вас заставили поставить новый код за флагами функций? Они легко кажутся правильными: код заключен в IF (feature) THEN { do_this }; блоков. Многие из них могут быть выпущены сразу, все отключены, поэтому вы можете делать крупные выпуски с меньшим риском остановки производства, по крайней мере, в теории.

Можем ли мы сделать лучше, чем это?

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

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

if(FEATURE_FLAG_1) {
  do_feature1()
  // ...
  if(FEATURE_FLAG_2) {
    do_feature2()
  }
}

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

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

  • Обе функции выключены
  • Функция 1 включена
  • Функция 2 включена
  • Обе функции на

Это, очевидно, растет с количеством функций, а также с риском что-то сломать.

Использование шины сообщений и системы плагинов

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

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

Давайте посмотрим, как это работает. В этой статье мы сосредоточимся на iPlug, минималистичной системе плагинов со встроенной шиной сообщений для JavaScript.

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

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

Оптимизация? А/Б тестирование? Довольно просто: вы можете провести A/B-тестирование двух вариантов плагина, создав копию A, внеся изменения, сохранив как B, а затем запустив тесты. Я подробно расскажу об этом в другой статье.

Как работает шина сообщений?

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

file: plugin-1.js
const handler1 = data => doSomething()
const handler2 = data => doSomethingElse()
export default {
  'message1:dosomething': config => handler1,
  'message2:dosomething': config => handler2,
}

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

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

Шина сообщений позаботится о сопоставлении точек подключения с обработчиками плагинов и осуществит слабую связь.

import { plugins } from '/plugins.js'
function main() {
  const data = doSomething1()
  const result = plugins('message1:dosomething', data)
  return result
}

Как мы видим, контроль прочно удерживается ядром системы. Вы можете добавить столько точек крюка, сколько вам нужно. Если плагин не зарегистрирован или не включен, основная система возьмет result из исходного data, в противном случае каждый плагин возьмет копию data, выполнит над ней некоторые преобразования и вернет значение.

Существует несколько способов вызова плагинов, особенно последовательно или параллельно. Подробнее об этом в документации iPlug. Для этой статьи это не имеет большого значения.

Тестирование и тестируемость

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

import plugin from './plugin-1.js'
describe('plugin 1', () => {
  describe('message1', () => {
    it('works', () => {
    // initialize the plugin first
    const handler = plugin['message1']()
    // then call it
    const result = handler()
    const expected = { ... }
    return expect(result)
      .toEqual(
        expect.objectContaining(expected)
      )
  })
})

Внедрение плагинов в существующие проекты

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

Заключение

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