Обеспечение совместимости библиотек

#std644

Область применения: управляемое приложение, мобильное приложение, обычное приложение.

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

Например, версии библиотеки 2.0.1, 2.0.2 и 2.0.5 должны быть совместимы. Однако допустимо, если следующая подредакция 2.1 будет содержать существенные изменения, нарушающие это правило.

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

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

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

Что изменилось в новой версии? РР .ПП .ВВ .СС
Архитектурные изменения,
нарушена совместимость
(есть инструкция по переходу)
v v x x
Новые функции v v v x
Исправлены ошибки v v v v

Здесь:

  1. РР (редакция) – существенно нарушена совместимость (серьезные архитектурные или «знаковые» изменения в библиотеке);
  2. ПП (номер подредакции) – нарушена совместимость (требуется отработать инструкцию по переходу на эту версию, иначе конфигурация будет неработоспособна), удалены устаревшие объекты метаданных;
  3. ВВ (номер версии) – доступны только новые функции для пользователей и/или разработчиков (возможна «механическая» автозаливка метаданных библиотеки в конфигурацию-потребитель; инструкция по переходу на эту версию не обязательна, без ее отработки конфигурация сохранит работоспособность);
  4. СС (номер сборки) – содержит только исправление ошибок (возможна «механическая» автозаливка метаданных библиотеки в конфигурацию-потребитель).

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

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

1.2. В библиотеках с несколькими функциональными подсистемами нарушение совместимости хотя бы в одной подсистеме означает увеличение номера подредакции библиотеки (2-я цифра).

1.3. При незапланированном нарушении совместимости в 3-й и 4-й цифре следует отозвать релиз библиотеки, устранить нарушение и выпустить новый исправленный релиз.

1.4. Аналогичные требования распространяются и на незапланированное добавление новых функций в 4-й цифре.

1.5. В целях обеспечения совместимости следует

Требования обеспечения обратной совместимости имеют приоритет над следующими стандартами разработки:

1.6. К программному интерфейсу библиотеки относятся те ее объекты метаданных, которые предназначены для использования в прикладном коде:

1.7. Совместимость не нарушает расширение программного интерфейса библиотеки, т.е. в 3-й цифре допустимо, например:

В то же время:

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

1.9. Рекомендуется размещать программный интерфейс библиотеки только в ее общих модулях, а не в модулях объектов, менеджеров, наборов записей и т.п.

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

При размещении в разных общих модулях, к модулям со служебными процедурами и функциями может быть добавлен постфикс Служебный (англ. Internal).
Например:

Такое размещение программного интерфейса в отдельных общих модулях позволяет

2.2. Раздел Программный интерфейс так же может содержать в себе процедуры и функции, предназначенные для вызова конкретными потребителями из других функциональных подсистем библиотеки или из других библиотек. Такие процедуры и функции рекомендуется выделять в отдельный подраздел Для вызова из других подсистем, оформленный в виде области ДляВызоваИзДругихПодсистем (англ. InterfaceImplementation). 

Внутри подраздела процедуры и функции должны быть разделены на группы комментариями с именем потребителя, для которого они предназначены. Подробнее см. Разработка конфигураций с повторным использованием общего кода и объектов метаданных. Например:

#Область ПрограммныйИнтерфейс
//Код процедур и функций

#Область ДляВызоваИзДругихПодсистем

// СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов
// Код процедур и функций
// Конец СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов

// ТехнологияСервиса.ВыгрузкаЗагрузкаДанных
// Код процедур и функций
// Конец ТехнологияСервиса.ВыгрузкаЗагрузкаДанных

#КонецОбласти

#КонецОбласти

Англоязычный вариант синтаксиса:

#Region Public
// Enter code here.

#Region InterfaceImplementation

// StandardSubsystems.BatchObjectModification
// Enter code here.
// End StandardSubsystems.BatchObjectModification

// SaaSTechnology.DataExportImport
// Enter code here.
// End SaaSTechnology.DataExportImport

#EndRegion

#EndRegion

3. Для обеспечения совместимости программного интерфейса библиотеки в условиях активного развития ее функциональности ниже приведен ряд практических рекомендаций.

3.1. При необходимости переименовать (или удалить) экспортную функцию (процедуру) следует оставить прежнюю реализацию функции, пометив ее как устаревшую с помощью комментария вида:

// Устарела: Следует использовать функцию ПересчитатьПоКурсу
// …
Функция ПересчитатьИзВалютыВВалюту(Сумма, ВалютаНач, ВалютаКон, ПоКурсуНач, ПоКурсуКон, ПоКратностьНач = 1, ПоКратностьКон = 1) Экспорт

(англоязычный аналог "Устарела" в начале комментария - "Deprecated")

и разместить новую версию функции с новым именем (в данном примере - ПересчитатьПоКурсу).

При этом устаревшую функцию следует перенести в область общего модуля УстаревшиеПроцедурыИФункции (англ. Deprecated), которая размещена внутри области ПрограммныйИнтерфейс. В процедурах и функциях, размещенных в области УстаревшиеПроцедурыИФункции, допустимы отклонения от других стандартов разработки согласно п.1.1.

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

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

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

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

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

3.3. При необходимости пересмотреть состав параметров экспортных функций (процедур) следует использовать опциональные параметры, которые добавляются в конец списка формальных параметров.
Например:

Функция ПересчитатьИзВалютыВВалюту(Сумма, ВалютаНач, ВалютаКон, ПоКурсуНач, ПоКурсуКон, ПоКратностьНач = 1, ПоКратностьКон = 1) Экспорт

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

Функция ПересчитатьИзВалютыВВалюту(Сумма, ВалютаНач, ВалютаКон, ПоКурсуНач, ПоКурсуКон, ПараметрыПересчета = Неопределено) Экспорт

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

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

Процедура ПриОпределенииНастроек(Настройки) Экспорт
 Настройки.ВыводитьОписания = Истина;
 Настройки.События.ПриСозданииНаСервере = Истина;
 Настройки.... = ...;
КонецПроцедуры

Кроме того, этот подход позволяет также сохранять совместимость программного интерфейса, через который библиотека обращается к объектам конфигурации-потребителя. В примере выше строка «Настройки.События.ПриСозданииНаСервере = Истина;» означает, что в конфигурации-потребителя определен одноименный обработчик события библиотеки, который следует вызывать из библиотеки. При этом появление нового события в следующей версии библиотеки не потребует обязательного добавления его пустых обработчика во всех конфигурациях-потребителях. Аналогичным образом, платформа 1С:Предприятие не требует вставлять пустые «заглушки» стандартных обработчиков событий в модули и менеджеры объектов.

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

Например, вместо «прямого» запроса к библиотечному регистру из прикладного кода:

 Запрос = Новый Запрос;
 Запрос.Текст = "ВЫБРАТЬ
                | ОбластиДанных.Представление
                |ИЗ
                | РегистрСведений.ОбластиДанных КАК ОбластиДанных
                |ГДЕ
                | ОбластиДанных.ОбластьДанных = &ОбластьДанных";
 Запрос.УстановитьПараметр("ОбластьДанных", ПараметрыСеанса.ОбластьДанныхЗначение);
 ТаблицаОбластейДанных = Запрос.Выполнить().Выгрузить();
 ИмяПриложения = ?(ТаблицаОбластейДанных.Количество() = 0, "", ТаблицаОбластейДанных.Получить(0).Получить(0));

следует предусмотреть в библиотеке экспортную функцию, которая специально предназначена для использования в прикладном коде:

ИмяПриложения = РаботаВМоделиСервиса.ИмяПриложения();

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

3.6. Для скрытия деталей реализации библиотеки от потребителя рекомендуется размещать программный интерфейс в обычном общем модуле вместо специализированного (ПовтИсп, ВызовСервера). Аналогично с переопределяемыми модулями. Допустим:

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

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

4. Для упрощения контроля изменений программного интерфейса в новых версиях библиотек рекомендуется воспользоваться приложенной обработкой.