Критические изменения
При разработке библиотеки .NET очень важно сохранять правильный баланс между стабильностью для существующих пользователей и возможностью реализации новых функций. Авторы библиотек, как правило, стремятся довести код до совершенства, однако влияние на работу существующих пользователей имеет негативные последствия, особенно для библиотек низкого уровня.
Типы проектов и критические изменения
Способ использования библиотеки сообществом .NET определяет степень влияния критических изменений на работу конечных пользователей-разработчиков.
Библиотеки низкого и среднего уровня, например сериализатор, средство синтаксического анализа HTML, объектно-реляционный модуль сопоставления баз данных или веб-платформа, чаще всего затрагиваются критическими изменениями.
Пакеты стандартных блоков используются конечными пользователями-разработчиками для создания приложений и другими библиотеками как зависимости NuGet. Например, вы создаете приложение и используете клиент с открытым кодом для вызова веб-службы. При этом вы не можете исправить критическое изменение для зависимости, используемой клиентом. Это клиент с открытым кодом, который необходимо изменить, и вы не можете это контролировать. Вам придется найти совместимые версии библиотек или отправить исправление в клиентскую библиотеку и дожидаться новой версии. В худшем случае вам требуется использовать две библиотеки, которые зависят от взаимно несовместимых версий третьей библиотеки.
Высокоуровневые библиотеки, такие как набор элементов управления пользовательского интерфейса, менее чувствительны к критическим изменениям.
Ссылка на высокоуровневые библиотеки включается непосредственно в приложение конечного пользователя. При возникновении критических изменений разработчик может обновить приложение до последней версии или изменить его для работы с критическим изменением.
✔️ СЛЕДУЕТ подумать о вариантах использования вашей библиотеки. Какой эффект критические изменения окажут на приложения и библиотеки, которые его используют?
✔️ СЛЕДУЕТ свести критические изменения к минимуму при разработке низкоуровневой библиотеки .NET.
✔ РЕКОМЕНДУЕТСЯ опубликовать библиотеку как новый пакет NuGet после внесения большого числа изменений.
Типы критических изменений
Критические изменения делятся на разные категории, и их влияние неодинаково.
Критическое изменение исходного кода
Критическое изменение исходного кода не влияет на выполнение программы, но приводит к ошибкам компиляции при следующей повторной компиляции приложения. Например, новая перегрузка может создавать неоднозначность при вызове методов, которые ранее были однозначными, или переименование параметра приведет к нарушению работы вызывающих объектов, использующих именованные параметры.
public class Task
{
// Adding a type called Task could conflict with System.Threading.Tasks.Task at compilation
}
Так как критическое изменение исходного кода опасно только при перекомпиляции приложений, оно считается наименее разрушительным критическим изменением. Разработчики могут легко исправить собственный неработающий исходный код.
Критическое изменение поведения
Изменения поведения являются наиболее распространенным типом критического изменения: почти любое изменение в поведении может вызвать ошибку логики для потребителя. Изменения в библиотеке, например изменение сигнатур методов, создаваемые исключения или форматы входных или выходных данных, будут отрицательно воздействовать на потребителей библиотеки. Даже исправление ошибки можно определить как критическое изменение, если работа пользователей зависела от прежнего некорректного поведения.
Добавление функций и улучшение некорректного поведения — это хорошо и правильно, однако если вы не примете должные меры, обновление может оказаться сложной задачей для существующих пользователей. Одним из подходов, помогающих разработчикам в случае критических изменений поведения, является их скрытие за параметрами. Параметры позволяют разработчикам обновить библиотеку до последней версии и в то же время согласиться или отказаться от критических изменений. Эта стратегия позволяет разработчикам поддерживать актуальное состояние среды, при этом код, обращающийся к библиотеке, может адаптироваться со временем.
Например, MVC в ASP.NET Core использует концепцию версии совместимости, изменяющей включенные и отключенные функции в MvcOptions
.
✔ РЕКОМЕНДУЕТСЯ оставить новые функции отключенными по умолчанию, если они влияют на существующих пользователей, и дать разработчикам возможность соглашаться на включение функции с помощью параметра.
Дополнительные сведения о критических изменениях поведения в API .NET см. в статье о совместимости изменений поведения .NET.
Критическое изменение двоичного кода
Критическое изменение двоичного кода происходит при изменении открытого API-интерфейса библиотек так, что сборки, скомпилированные для более старых версий библиотеки, больше не могут вызывать API. Например, изменение сигнатуры метода путем добавления нового параметра приведет к тому, что сборки, скомпилированные для более старой версии библиотеки, будут создавать исключение MissingMethodException.
Критическое изменение двоичного кода также может нарушить работу всей сборки. Переименование сборки с помощью AssemblyName
изменит удостоверение сборки, так же как и добавление, удаление или изменение ключа строгого именования сборки. Изменение удостоверения сборки нарушит работу всего скомпилированного кода, который ее использует.
❌НЕ ️СЛЕДУЕТ изменять имя сборки.
❌ НЕ СЛЕДУЕТ добавлять, удалять или изменять ключ строгого именования.
✔ РЕКОМЕНДУЕТСЯ использовать абстрактные базовые классы вместо интерфейсов.
Добавление каких-либо объектов в интерфейс вызовет сбой существующих типов, реализующих этот интерфейс. Абстрактный базовый класс позволяет добавлять виртуальную реализацию по умолчанию.
✔ РЕКОМЕНДУЕТСЯ использовать ObsoleteAttribute для типов и членов, которые планируется удалить. Этот атрибут должен иметь инструкции по обновлению кода, запрещающие использовать устаревший API.
Код, который вызывает типы и методы с атрибутом ObsoleteAttribute, выдаст предупреждение сборки с сообщением, переданным в атрибут. Предупреждения дают пользователям, использующим устаревший API, время для миграции. Таким образом, к моменту удаления устаревшего API большинство разработчиков не будет его использовать.
public class Document
{
[Obsolete("LoadDocument(string) is obsolete. Use LoadDocument(Uri) instead.")]
public static Document LoadDocument(string uri)
{
return LoadDocument(new Uri(uri));
}
public static Document LoadDocument(Uri uri)
{
// Load the document
}
}
✔ РЕКОМЕНДУЕТСЯ хранить типы и методы с атрибутом ObsoleteAttribute в библиотеках низкого и среднего уровня бессрочно.
Удаление API — это критическое изменение двоичного кода. Рассмотрите возможность сохранения устаревших типов и методов, если оно не влечет за собой больших затрат и рост технического долга. Отказ от удаления типов и методов может помочь избежать неблагоприятных ситуаций, упомянутых выше.
Дополнительные сведения о том, что API .NET изменяет совместимость двоичных файлов, см. в статье о совместимости общедоступных контрактов .NET.