День 2094. #ЧтоНовенького
Улучшаем Отладку Коллекций с Помощью Редактируемых Выражений
Мне частенько не хватало в отладчике Visual Studio возможности отладки LINQ-выражений или возможности использовать LINQ, чтобы отфильтровать элементы коллекции. Новая функция редактируемых выражений в отладчике Visual Studio позволяет это делать.
Эта функция позволяет использовать текстовое поле выражений в верхней части диалогового окна визуализатора IEnumerable, добавляя в него желаемые LINQ-выражения. Визуализатор обновляется в режиме реального времени, отражая результаты запроса. Можно легко применять различные фильтры или порядки сортировки к коллекциям в зависимости от ваших потребностей.
В сеансе отладки запустите визуализатор IEnumerable, наведя указатель мыши на переменную коллекции или набора данных в отладчике и щёлкнув на значок увеличительного стекла (см. картинку). Кроме того, вы можете щёлкнуть правой кнопкой мыши по переменной и выбрать View Visualizer (Просмотреть визуализатор) в контекстном меню.
Это откроет диалоговое окно визуализатора IEnumerable, в котором вы увидите текстовое поле выражений вверху. Вы можете ввести любое допустимое выражение LINQ в это текстовое поле и нажать <ENTER>, чтобы применить его к коллекции. Визуализатор обновит сетку данных ниже результатами запроса.
Функция редактируемого выражения может быть очень полезна для отладки наборов данных и сложных манипуляций с коллекциями. Вы можете экспериментировать с различными преобразованиями данных и фильтрами непосредственно в отладчике Visual Studio, без необходимости писать какой-либо код или переключаться на другой инструмент. Вы также можете скопировать выражение из текстового поля и вставить его в код, если хотите использовать его в логике приложения.
Источник: https://devblogs.microsoft.com/visualstudio/improve-your-debugger-game-with-editable-expressions/
Улучшаем Отладку Коллекций с Помощью Редактируемых Выражений
Мне частенько не хватало в отладчике Visual Studio возможности отладки LINQ-выражений или возможности использовать LINQ, чтобы отфильтровать элементы коллекции. Новая функция редактируемых выражений в отладчике Visual Studio позволяет это делать.
Эта функция позволяет использовать текстовое поле выражений в верхней части диалогового окна визуализатора IEnumerable, добавляя в него желаемые LINQ-выражения. Визуализатор обновляется в режиме реального времени, отражая результаты запроса. Можно легко применять различные фильтры или порядки сортировки к коллекциям в зависимости от ваших потребностей.
В сеансе отладки запустите визуализатор IEnumerable, наведя указатель мыши на переменную коллекции или набора данных в отладчике и щёлкнув на значок увеличительного стекла (см. картинку). Кроме того, вы можете щёлкнуть правой кнопкой мыши по переменной и выбрать View Visualizer (Просмотреть визуализатор) в контекстном меню.
Это откроет диалоговое окно визуализатора IEnumerable, в котором вы увидите текстовое поле выражений вверху. Вы можете ввести любое допустимое выражение LINQ в это текстовое поле и нажать <ENTER>, чтобы применить его к коллекции. Визуализатор обновит сетку данных ниже результатами запроса.
Функция редактируемого выражения может быть очень полезна для отладки наборов данных и сложных манипуляций с коллекциями. Вы можете экспериментировать с различными преобразованиями данных и фильтрами непосредственно в отладчике Visual Studio, без необходимости писать какой-либо код или переключаться на другой инструмент. Вы также можете скопировать выражение из текстового поля и вставить его в код, если хотите использовать его в логике приложения.
Источник: https://devblogs.microsoft.com/visualstudio/improve-your-debugger-game-with-editable-expressions/
👍20
Какая разница между составным (composite) и комбинированным (compound) ключами?
#Quiz #БазыДанных #DataModeling
#Quiz #БазыДанных #DataModeling
Anonymous Quiz
53%
Комбинированный ключ состоит из нескольких простых ключей, а составной - из нескольких любых полей
3%
Комбинированный ключ состоит из 3 или более полей, а составной из 2 булевых полей
4%
Комбинированный ключ состоит из 2 полей, а составной из 3 или более полей
41%
Комбинированный ключ состоит из нескольких любых полей, а составной - из нескольких простых ключей
👎1
День 2095. #ЗаметкиНаПолях #БазыДанных
Типы Ключей
В реляционной БД ключи гарантируют, что любая запись в таблице может быть однозначно идентифицирована одним полем или комбинацией полей в таблице. Ключи также связывают таблицы БД и определяют отношения между ними.
Первичные ключи
Первичный ключ — это поле, которое однозначно идентифицирует каждую запись в таблице в реляционной БД. Они делятся на 2 группы:
- Естественные - существующие данные, например, серия и номер паспорта человека, VIN-номер машины, ISBN книги и т.п.
- Суррогатные (синтетические) - сгенерированы специально для использования в БД и не имеют представления среди реальных атрибутов сущности. Нередко используется автоинкрементное поле для автоматического назначения уникального номера каждой записи в таблице.
См. также «Вы Пожалеете, что Использовали Естественные Ключи»
Внешние ключи
Внешний ключ позволяет связать две таблицы, устанавливая связь между полем одной таблицы и полем первичного ключа другой таблицы.
Например, БД библиотеки может состоять из 3 таблиц — книги, клиенты и выданные книги. Внешние ключи могут быть введены для связи таблицы выданных книг с таблицами книг и клиентов.
Столбец идентификатора клиента может существовать:
- в таблице клиентов как первичный ключ
- в таблице выданных книг как внешний ключ
Столбец идентификатора книги может существовать:
- в таблице книг как первичный ключ
- в таблице выданных книг как внешний ключ
Необходимо соблюдать особую осторожность при вставке и удалении данных из столбца внешнего ключа. Неосторожное удаление или вставка может разрушить связь между двумя таблицами. Чтобы избежать этого, в большинстве СУБД есть правила ограничений:
- вставка - запрет вставки записи со значением внешнего ключа, которое отсутствует как значение первичного ключа в связанной таблице;
- обновление – если существуют связанные записи с таким значением внешнего ключа: запрет обновления, каскадное обновление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL);
- удаление - если существуют связанные записи с таким значением внешнего ключа: запрет удаления, каскадное удаление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL).
Составной (composite) ключ
Это особый тип первичного ключа, который использует содержимое двух или более полей из таблицы для создания уникального значения. Например, номер паспорта не является уникальным, уникальной является комбинация двух полей: серия паспорта и номер паспорта.
Комбинированный (compound) ключ
Комбинированный ключ похож на составной ключ в том, что для создания уникального значения требуются два или более полей. Однако комбинированный ключ создаётся, когда два или более первичных ключа из разных таблиц присутствуют как внешние ключи внутри сущности. Внешние ключи используются вместе для уникальной идентификации каждой записи.
Комбинированные ключи всегда состоят из двух или более первичных ключей из других таблиц. В своих таблицах оба этих ключа уникально идентифицируют данные, но в таблице, использующей комбинированный ключ, они оба необходимы для уникальной идентификации данных.
Например, первичным ключом в таблице «товары в заказе» может быть комбинированный ключ, состоящий из идентификатора заказа и идентификатора товара.
Источник: https://www.bbc.co.uk/bitesize/guides/z4wf8xs/revision/4
Типы Ключей
В реляционной БД ключи гарантируют, что любая запись в таблице может быть однозначно идентифицирована одним полем или комбинацией полей в таблице. Ключи также связывают таблицы БД и определяют отношения между ними.
Первичные ключи
Первичный ключ — это поле, которое однозначно идентифицирует каждую запись в таблице в реляционной БД. Они делятся на 2 группы:
- Естественные - существующие данные, например, серия и номер паспорта человека, VIN-номер машины, ISBN книги и т.п.
- Суррогатные (синтетические) - сгенерированы специально для использования в БД и не имеют представления среди реальных атрибутов сущности. Нередко используется автоинкрементное поле для автоматического назначения уникального номера каждой записи в таблице.
См. также «Вы Пожалеете, что Использовали Естественные Ключи»
Внешние ключи
Внешний ключ позволяет связать две таблицы, устанавливая связь между полем одной таблицы и полем первичного ключа другой таблицы.
Например, БД библиотеки может состоять из 3 таблиц — книги, клиенты и выданные книги. Внешние ключи могут быть введены для связи таблицы выданных книг с таблицами книг и клиентов.
Столбец идентификатора клиента может существовать:
- в таблице клиентов как первичный ключ
- в таблице выданных книг как внешний ключ
Столбец идентификатора книги может существовать:
- в таблице книг как первичный ключ
- в таблице выданных книг как внешний ключ
Необходимо соблюдать особую осторожность при вставке и удалении данных из столбца внешнего ключа. Неосторожное удаление или вставка может разрушить связь между двумя таблицами. Чтобы избежать этого, в большинстве СУБД есть правила ограничений:
- вставка - запрет вставки записи со значением внешнего ключа, которое отсутствует как значение первичного ключа в связанной таблице;
- обновление – если существуют связанные записи с таким значением внешнего ключа: запрет обновления, каскадное обновление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL);
- удаление - если существуют связанные записи с таким значением внешнего ключа: запрет удаления, каскадное удаление связанных записей или установка значений ключа в NULL у связанных записей (поле должно допускать NULL).
Составной (composite) ключ
Это особый тип первичного ключа, который использует содержимое двух или более полей из таблицы для создания уникального значения. Например, номер паспорта не является уникальным, уникальной является комбинация двух полей: серия паспорта и номер паспорта.
Комбинированный (compound) ключ
Комбинированный ключ похож на составной ключ в том, что для создания уникального значения требуются два или более полей. Однако комбинированный ключ создаётся, когда два или более первичных ключа из разных таблиц присутствуют как внешние ключи внутри сущности. Внешние ключи используются вместе для уникальной идентификации каждой записи.
Комбинированные ключи всегда состоят из двух или более первичных ключей из других таблиц. В своих таблицах оба этих ключа уникально идентифицируют данные, но в таблице, использующей комбинированный ключ, они оба необходимы для уникальной идентификации данных.
Например, первичным ключом в таблице «товары в заказе» может быть комбинированный ключ, состоящий из идентификатора заказа и идентификатора товара.
Источник: https://www.bbc.co.uk/bitesize/guides/z4wf8xs/revision/4
👍16
День 2096. #ЧтоНовенького
WebStorm и Rider Теперь Бесплатны для Некоммерческого Использования
Думаю, все уже знают об этой новости, но, всё-таки, не могу не упомянуть.
JetBrains объявили об изменении модели лицензирования — WebStorm и Rider теперь бесплатны для некоммерческого использования!
Ранее в этом году бесплатными для некоммерческого использования сделали IDE RustRover и Aqua, теперь распространили эту модель на WebStorm и Rider. Если вы используете эти IDE в некоммерческих целях, таких как обучение, разработка проектов с открытым кодом, создание контента или хобби, теперь вы можете делать это бесплатно.
Для коммерческих проектов ничего не изменится — существующее лицензирование остаётся в силе. Другие IDE JetBrains также не затронуты этим обновлением.
Стоит отметить, что тот же Rider и раньше предоставлялся бесплатно для некоммерческого использования и проектов с открытым кодом, просто нужно было написать в JetBrains и попросить ключ, описав проект и дав ссылку на репозиторий проекта. Теперь этого делать не надо.
Коммерческое и некоммерческое использование
Как определено в Соглашении о подписке на Toolbox для некоммерческого использования, коммерческие продукты — это продукты, распространяемые или предоставляемые за плату или используемые в рамках вашей деловой активности.
Важно отметить, что, если вы используете некоммерческую лицензию, вы не можете отказаться от сбора анонимной статистики использования. Они используется для улучшения продуктов JetBrains. Собираемые данные — это исключительно данные об анонимном использовании функций IDE. Они сосредоточены на том, какие действия выполняются и какие типы функциональных возможностей IDE используются.
Источник: https://blog.jetbrains.com/blog/2024/10/24/webstorm-and-rider-are-now-free-for-non-commercial-use/
WebStorm и Rider Теперь Бесплатны для Некоммерческого Использования
Думаю, все уже знают об этой новости, но, всё-таки, не могу не упомянуть.
JetBrains объявили об изменении модели лицензирования — WebStorm и Rider теперь бесплатны для некоммерческого использования!
Ранее в этом году бесплатными для некоммерческого использования сделали IDE RustRover и Aqua, теперь распространили эту модель на WebStorm и Rider. Если вы используете эти IDE в некоммерческих целях, таких как обучение, разработка проектов с открытым кодом, создание контента или хобби, теперь вы можете делать это бесплатно.
Для коммерческих проектов ничего не изменится — существующее лицензирование остаётся в силе. Другие IDE JetBrains также не затронуты этим обновлением.
Стоит отметить, что тот же Rider и раньше предоставлялся бесплатно для некоммерческого использования и проектов с открытым кодом, просто нужно было написать в JetBrains и попросить ключ, описав проект и дав ссылку на репозиторий проекта. Теперь этого делать не надо.
Коммерческое и некоммерческое использование
Как определено в Соглашении о подписке на Toolbox для некоммерческого использования, коммерческие продукты — это продукты, распространяемые или предоставляемые за плату или используемые в рамках вашей деловой активности.
Важно отметить, что, если вы используете некоммерческую лицензию, вы не можете отказаться от сбора анонимной статистики использования. Они используется для улучшения продуктов JetBrains. Собираемые данные — это исключительно данные об анонимном использовании функций IDE. Они сосредоточены на том, какие действия выполняются и какие типы функциональных возможностей IDE используются.
Источник: https://blog.jetbrains.com/blog/2024/10/24/webstorm-and-rider-are-now-free-for-non-commercial-use/
5👍12
Какой(-ими) IDE вы пользуетесь для .NET разработки?
(возможно несколько ответов, если одна рабочая, другая для пет-проектов)
(возможно несколько ответов, если одна рабочая, другая для пет-проектов)
Anonymous Poll
47%
Rider
52%
Visual Studio
13%
Visual Studio + Resharper
27%
VS Code
1%
Другая (напишу в комментариях)
1%
Только консоль, только хардкор!
👍1
День 2097. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 29. Держитесь подальше от критического пути
Основу планирования проекта составляет определение задач, которые необходимо решить. Многие из этих задач должны выполняться в конкретной последовательности, а часть из них связана друг с другом, поэтому планировщик должен также определить временные зависимости между задачами. Это всё усложняет достижение цели.
Определение критического пути
Планировщики проектов часто рисуют сетевой график (также называемый диаграммой PERT), чтобы показать временные отношения между задачами. На рисунке выше показан такой сетевой график для проекта с шестью задачами, от A до Е. Показана расчётная продолжительность каждой задачи. Сетевые графики содержат гораздо больше информации, например самые ранние и самые поздние даты начала и окончания каждой задачи. Для простоты предположим, что нельзя приступить ни к одной задаче, пока не будут выполнены все предшествующие ей. Т.е. задача Е не может начаться, пока не будут выполнены задачи A, Г и Д.
Если суммировать приблизительную продолжительность задач в различных путях, то можно заметить, что путь ДЕ самый длинный: 5 + 4 = 9 дней (жирные стрелки). Эта последовательность задач является критическим путём и определяет кратчайшую расчётную продолжительность реализации проекта. Если на реализацию какой-либо задачи в критическом пути потребуется больше времени, то дата завершения всего проекта сдвинется на время этой задержки. Новые задачи, добавляемые в критический путь, тоже задерживают завершение проекта.
Задачи, не лежащие на критическом пути, имеют резервное время. Например, задачи A и Г вместе имеют один резервный день: 2 + 2 = 4 дня, что на 1 день меньше, чем продолжительность решения задачи Д, лежащей на критическом пути.
Однако критический путь может измениться. Если на решение задачи А потребовалось 4 дня, что вдвое превышает первоначальную оценку, критическим станет путь АГЕ протяженностью 10 дней (4 + 2 + 4), в результате чего срок окончания проекта на 1 день превысит путь ДЕ.
Решение задач, добавляемых не в критический путь, требует дополнительных усилий, но такие задачи не задержат завершение проекта, если имеется достаточный резерв. Однако если для их решения нужно отвлекать людей, которые в противном случае работали бы над задачами критического пути, то эти дополнительные задачи могут увеличить его продолжительность.
Опытные руководители проектов внимательно следят за критическим путём и стараются сделать так, чтобы задачи на этом пути были решены вовремя или даже раньше.
Не тормозите критический путь
Держитесь подальше от критического пути, чтобы не оказаться слабым звеном, тормозящим чье-либо продвижение. Расставляйте приоритеты в своей работе, учитывая их важность и срочность. Задача, лежащая на критическом пути, более важна. Если ваша задача может заставить других людей ждать, она должна быть приоритетной для вас. Старайтесь не быть причиной простоев и ожиданий, которые могут помешать всему проекту.
То же касается, например, код-ревью. Автор кода ждёт окончания проверки и не может завершить свою задачу, пока вы не проверите его код. Он может заняться чем-то другим, но тогда будет потрачено время на переключение контекста, когда он должен будет погрузиться в новую задачу, а затем вернуться от новой задачи к готовому ревью старой. Поэтому старайтесь давать приоритет код-ревью.
Старайтесь быстро завершать действие, которое, насколько вам известно, может замедлить кого-то ещё и, возможно, весь проект. Старайтесь максимально быстро сойти с критического пути.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
Уроки 50 Лет Разработки ПО
Урок 29. Держитесь подальше от критического пути
Основу планирования проекта составляет определение задач, которые необходимо решить. Многие из этих задач должны выполняться в конкретной последовательности, а часть из них связана друг с другом, поэтому планировщик должен также определить временные зависимости между задачами. Это всё усложняет достижение цели.
Определение критического пути
Планировщики проектов часто рисуют сетевой график (также называемый диаграммой PERT), чтобы показать временные отношения между задачами. На рисунке выше показан такой сетевой график для проекта с шестью задачами, от A до Е. Показана расчётная продолжительность каждой задачи. Сетевые графики содержат гораздо больше информации, например самые ранние и самые поздние даты начала и окончания каждой задачи. Для простоты предположим, что нельзя приступить ни к одной задаче, пока не будут выполнены все предшествующие ей. Т.е. задача Е не может начаться, пока не будут выполнены задачи A, Г и Д.
Если суммировать приблизительную продолжительность задач в различных путях, то можно заметить, что путь ДЕ самый длинный: 5 + 4 = 9 дней (жирные стрелки). Эта последовательность задач является критическим путём и определяет кратчайшую расчётную продолжительность реализации проекта. Если на реализацию какой-либо задачи в критическом пути потребуется больше времени, то дата завершения всего проекта сдвинется на время этой задержки. Новые задачи, добавляемые в критический путь, тоже задерживают завершение проекта.
Задачи, не лежащие на критическом пути, имеют резервное время. Например, задачи A и Г вместе имеют один резервный день: 2 + 2 = 4 дня, что на 1 день меньше, чем продолжительность решения задачи Д, лежащей на критическом пути.
Однако критический путь может измениться. Если на решение задачи А потребовалось 4 дня, что вдвое превышает первоначальную оценку, критическим станет путь АГЕ протяженностью 10 дней (4 + 2 + 4), в результате чего срок окончания проекта на 1 день превысит путь ДЕ.
Решение задач, добавляемых не в критический путь, требует дополнительных усилий, но такие задачи не задержат завершение проекта, если имеется достаточный резерв. Однако если для их решения нужно отвлекать людей, которые в противном случае работали бы над задачами критического пути, то эти дополнительные задачи могут увеличить его продолжительность.
Опытные руководители проектов внимательно следят за критическим путём и стараются сделать так, чтобы задачи на этом пути были решены вовремя или даже раньше.
Не тормозите критический путь
Держитесь подальше от критического пути, чтобы не оказаться слабым звеном, тормозящим чье-либо продвижение. Расставляйте приоритеты в своей работе, учитывая их важность и срочность. Задача, лежащая на критическом пути, более важна. Если ваша задача может заставить других людей ждать, она должна быть приоритетной для вас. Старайтесь не быть причиной простоев и ожиданий, которые могут помешать всему проекту.
То же касается, например, код-ревью. Автор кода ждёт окончания проверки и не может завершить свою задачу, пока вы не проверите его код. Он может заняться чем-то другим, но тогда будет потрачено время на переключение контекста, когда он должен будет погрузиться в новую задачу, а затем вернуться от новой задачи к готовому ревью старой. Поэтому старайтесь давать приоритет код-ревью.
Старайтесь быстро завершать действие, которое, насколько вам известно, может замедлить кого-то ещё и, возможно, весь проект. Старайтесь максимально быстро сойти с критического пути.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍12
День 2098. #Git #TipsAndTricks
Советы по Git: Предыдущие Ветки
Сегодня рассмотрим два совета, как легко переключаться между ветками вперед-назад и как получить последние использованные ветки.
Переключение между ветками
Представьте, что вы находитесь на ветке feature-1 и хотите переключиться на ветку main, чтобы выполнить там какую-то работу. После того, как вы закончите работу на main, вы хотите переключиться обратно на feature-1.
Не зная точного имени ветки, вы можете использовать:
или (начиная с версии Git 2.23):
На самом деле, это сокращённая версия следующей команды:
Эта команда позволяет вернуться к любой предыдущей ветке.
Получение последних 5 использованных веток
Если вы хотите увидеть последние 5 веток, которые вы использовали, вы можете выполнить следующую команду:
Это работает с git bash и такими ОС, как Linux и MacOS. Идея заключается в использовании reflog для получения истории веток, которые вы извлекали. Команда grep фильтрует вывод, чтобы показывать только ветки, которые вы извлекли. Первая команда awk извлекает имена веток из вывода. Вторая команда awk удаляет дубликаты. Команда head ограничивает вывод последними 5 ветками. Поэтому, если вам нужны только последние 3 ветки, которые вы извлекли, вы можете изменить
Источник: https://steven-giesel.com/blogPost/bbfb8333-e05a-4de7-88b9-17ac2248d77f/git-tricks-get-the-last-checked-out-branch
Советы по Git: Предыдущие Ветки
Сегодня рассмотрим два совета, как легко переключаться между ветками вперед-назад и как получить последние использованные ветки.
Переключение между ветками
Представьте, что вы находитесь на ветке feature-1 и хотите переключиться на ветку main, чтобы выполнить там какую-то работу. После того, как вы закончите работу на main, вы хотите переключиться обратно на feature-1.
Не зная точного имени ветки, вы можете использовать:
git checkout -
или (начиная с версии Git 2.23):
git switch -
На самом деле, это сокращённая версия следующей команды:
git checkout @{-1}Эта команда позволяет вернуться к любой предыдущей ветке.
Получение последних 5 использованных веток
Если вы хотите увидеть последние 5 веток, которые вы использовали, вы можете выполнить следующую команду:
git reflog | grep -o 'checkout: moving from [^ ]* to [^ ]*' | awk '{print $NF}' | awk '!seen[$0]++' | head -n 5 Это работает с git bash и такими ОС, как Linux и MacOS. Идея заключается в использовании reflog для получения истории веток, которые вы извлекали. Команда grep фильтрует вывод, чтобы показывать только ветки, которые вы извлекли. Первая команда awk извлекает имена веток из вывода. Вторая команда awk удаляет дубликаты. Команда head ограничивает вывод последними 5 ветками. Поэтому, если вам нужны только последние 3 ветки, которые вы извлекли, вы можете изменить
head -n 5 на head -n 3.Источник: https://steven-giesel.com/blogPost/bbfb8333-e05a-4de7-88b9-17ac2248d77f/git-tricks-get-the-last-checked-out-branch
👍19
День 2099. #ЗаметкиНаПолях
Реализация Идемпотентных POST-запросов в ASP.NET Core API
Идемпотентность — важнейшая концепция REST API, которая обеспечивает надёжность и согласованность системы, особенно в распределённых системах, где сетевые проблемы могут привести к повторным запросам. Реализуя идемпотентность, вы предотвращаете дублирование операций, которое может возникнуть из-за повторных попыток клиента.
POST-запросы изначально не являются идемпотентными, но мы можем реализовать идемпотентность для них. Например, проверка существования ресурсов перед созданием гарантирует, что повторные запросы не приведут к дублированию действий или ресурсов.
Мы будем использовать стратегию с использованием ключей идемпотентности:
1. Клиент генерирует уникальный ключ для каждой операции и отправляет его в пользовательском заголовке.
2. Сервер проверяет, видел ли он этот ключ раньше:
- ключ новый - обработать запрос и сохранить результат,
- ключ не новый - вернуть сохраненный результат без повторной обработки.
Это гарантирует, что повторные запросы (например, из-за проблем с сетью) будут обработаны на сервере только один раз.
Примечание: если запрос не удается (возвращает 4xx/5xx), мы не кэшируем ответ. Это позволяет клиентам повторять попытки с тем же ключом идемпотентности. Однако это означает, что неудавшийся запрос, за которым следует успешный запрос с тем же ключом, будет успешным — убедитесь, что это соответствует вашим бизнес-требованиям.
Мы можем реализовать идемпотентность для контроллеров с помощью атрибута фильтра действия, реализовав IAsyncActionFilter:
Теперь можно применить атрибут к методу контроллера:
Окончание следует…
Источник
Реализация Идемпотентных POST-запросов в ASP.NET Core API
Идемпотентность — важнейшая концепция REST API, которая обеспечивает надёжность и согласованность системы, особенно в распределённых системах, где сетевые проблемы могут привести к повторным запросам. Реализуя идемпотентность, вы предотвращаете дублирование операций, которое может возникнуть из-за повторных попыток клиента.
POST-запросы изначально не являются идемпотентными, но мы можем реализовать идемпотентность для них. Например, проверка существования ресурсов перед созданием гарантирует, что повторные запросы не приведут к дублированию действий или ресурсов.
Мы будем использовать стратегию с использованием ключей идемпотентности:
1. Клиент генерирует уникальный ключ для каждой операции и отправляет его в пользовательском заголовке.
2. Сервер проверяет, видел ли он этот ключ раньше:
- ключ новый - обработать запрос и сохранить результат,
- ключ не новый - вернуть сохраненный результат без повторной обработки.
Это гарантирует, что повторные запросы (например, из-за проблем с сетью) будут обработаны на сервере только один раз.
Примечание: если запрос не удается (возвращает 4xx/5xx), мы не кэшируем ответ. Это позволяет клиентам повторять попытки с тем же ключом идемпотентности. Однако это означает, что неудавшийся запрос, за которым следует успешный запрос с тем же ключом, будет успешным — убедитесь, что это соответствует вашим бизнес-требованиям.
Мы можем реализовать идемпотентность для контроллеров с помощью атрибута фильтра действия, реализовав IAsyncActionFilter:
[AttributeUsage(AttributeTargets.Method)]
internal sealed class IdempotentAttribute :
Attribute, IAsyncActionFilter
{
private readonly TimeSpan _cacheMins;
public IdempotentAttribute(int cacheMins = 60)
{
_cacheMins = TimeSpan.FromMinutes(cacheMins);
}
public async Task OnActionExecutionAsync(
ActionExecutingContext ctx,
ActionExecutionDelegate next)
{
// проверяем, передал ли клиент ключ
if (!ctx.HttpContext.Request
.Headers.TryGetValue(
"Idempotence-Key",
out var keyValue) ||
!Guid.TryParse(keyValue, out Guid key))
{
ctx.Result = new BadRequestObjectResult(
"Отсутствует заголовок Idempotence-Key");
return;
}
var cache = ctx.HttpContext
.RequestServices
.GetRequiredService<IDistributedCache>();
// проверяем, обработан ли ключ
var cacheKey = $"Idempotent_{key}";
var cachedResult = await
cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
var response = JsonSerializer
.Deserialize<IdempotentResponse>(cachedResult);
var result = new ObjectResult(response.Value) {
StatusCode = response.StatusCode };
ctx.Result = result;
return;
}
// выполняем действие и кэшируем результат
var executedContext = await next();
if (executedContext.Result is
ObjectResult { StatusCode: >= 200 and < 300 } objRes)
{
int status = objRes.StatusCode
?? StatusCodes.Status200OK;
IdempotentResponse response =
new(status, objRes.Value);
await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(response),
new DistributedCacheEntryOptions {
AbsoluteExpirationRelativeToNow = _cacheMins }
);
}
}
}
internal sealed class IdempotentResponse
{
[JsonConstructor]
public IdempotentResponse(
int status, object? value)
{
StatusCode = status;
Value = value;
}
public int StatusCode { get; }
public object? Value { get; }
}
Теперь можно применить атрибут к методу контроллера:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
[HttpPost]
[Idempotent(cacheMins: 60)]
public IActionResult Create(
[FromBody] CreateOrderRequest request)
{
// обработка заказа…
return CreatedAtAction(nameof(GetOrder),
new { id = orderDto.Id }, orderDto);
}
}
Окончание следует…
Источник
👍28
День 2100. #ЗаметкиНаПолях
Реализация Идемпотентных POST-запросов в ASP.NET Core API. Окончание
Начало
Идемпотентность в минимальных API
В минимальных API аналогично реализуем IEndpointFilter:
Создаём свой тип результата:
Теперь применим фильтр к конечной точке минимальных API:
Ещё одним вариантом является реализация логики идемпотентности в промежуточном ПО.
Замечания
1. Длительность кэширования — сложная задача. Разумное время - от нескольких минут до 24–48 часов в зависимости от вашего конкретного варианта использования.
2. Между проверкой и установкой значения кэша существует небольшое окно состояния гонки. Это может быть проблемой, особенно в API с высоким трафиком. Потокобезопасная реализация с использованием распределённой блокировки отлично работает. Она контролирует ситуацию, когда одновременно поступает несколько запросов. Но это должно быть редким явлением.
3. Для распределённого кэша идеально подходит Redis. Кроме того, он обрабатывает распределенную блокировку.
4. Что делать, если клиент повторно использует ключ идемпотентности с другим телом запроса? Вернуть ошибку. Можно хэшировать тело запроса и сохранять его с ключом идемпотентности. Если хеш нового запроса с тем же ключом отличается от сохранённого, возвращается ошибка. Это предотвращает неправильное использование ключей идемпотентности и поддерживает целостность вашего API.
Источник: https://www.milanjovanovic.tech/blog/implementing-idempotent-rest-apis-in-aspnetcore
Реализация Идемпотентных POST-запросов в ASP.NET Core API. Окончание
Начало
Идемпотентность в минимальных API
В минимальных API аналогично реализуем IEndpointFilter:
internal sealed class IdempotencyFilter(
int cacheMins = 60) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext ctx,
EndpointFilterDelegate next)
{
// получаем ключ из запроса
if (!ctx.HttpContext.Request
.Headers.TryGetValue(
"Idempotence-Key",
out var keyValue) ||
!Guid.TryParse(keyValue, out Guid key))
{
return Results.BadRequest(
"Отсутствует заголовок Idempotence-Key");
}
var cache = ctx.HttpContext.RequestServices
.GetRequiredService<IDistributedCache>();
// проверяем, обработан ли ключ
var cacheKey = $"Idempotent_{key}";
var cachedResult = await
cache.GetStringAsync(cacheKey);
if (cachedResult is not null)
{
var resp = JsonSerializer
.Deserialize<IdempotentResult>(cachedResult)!;
return new IdempotentResult(
resp.StatusCode, resp.Value);
}
object? result = await next(ctx);
// выполняем действие и кэшируем результат
if (result is IStatusCodeHttpResult
{
StatusCode: >= 200 and < 300
} statusResult
and IValueHttpResult valueResult)
{
var status = statusResult.StatusCode
?? StatusCodes.Status200OK;
IdempotentResult resp =
new(status, valueResult.Value);
await cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(resp),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(cacheMins)
});
}
return result;
}
}
Создаём свой тип результата:
[method: JsonConstructor]
internal sealed class IdempotentResult(
int status, object? value) : IResult
{
public int StatusCode { get; } = status;
public object? Value { get; } = value;
public Task ExecuteAsync(HttpContext ctx)
{
ctx.Response.StatusCode = StatusCode;
return ctx.Response
.WriteAsJsonAsync(Value);
}
}
Теперь применим фильтр к конечной точке минимальных API:
app.MapPost("/api/orders", CreateOrder)
.RequireAuthorization()
.WithOpenApi()
.AddEndpointFilter<IdempotencyFilter>();Ещё одним вариантом является реализация логики идемпотентности в промежуточном ПО.
Замечания
1. Длительность кэширования — сложная задача. Разумное время - от нескольких минут до 24–48 часов в зависимости от вашего конкретного варианта использования.
2. Между проверкой и установкой значения кэша существует небольшое окно состояния гонки. Это может быть проблемой, особенно в API с высоким трафиком. Потокобезопасная реализация с использованием распределённой блокировки отлично работает. Она контролирует ситуацию, когда одновременно поступает несколько запросов. Но это должно быть редким явлением.
3. Для распределённого кэша идеально подходит Redis. Кроме того, он обрабатывает распределенную блокировку.
4. Что делать, если клиент повторно использует ключ идемпотентности с другим телом запроса? Вернуть ошибку. Можно хэшировать тело запроса и сохранять его с ключом идемпотентности. Если хеш нового запроса с тем же ключом отличается от сохранённого, возвращается ошибка. Это предотвращает неправильное использование ключей идемпотентности и поддерживает целостность вашего API.
Источник: https://www.milanjovanovic.tech/blog/implementing-idempotent-rest-apis-in-aspnetcore
👍10👎1
День 2101. #ЗаметкиНаПолях
Использование Singleton в Многопоточных Сценариях. Начало
Основная идея паттерна Одиночка (Singleton) в том, что это класс, предназначенный для создания только одного экземпляра, и он обычно предоставляет простой способ доступа к этому экземпляру.
Как правило, Singletonы создаются без использования каких-либо параметров для создания экземпляра. Это необходимо для предотвращения осложнений, которые могут возникнуть при попытке создания второго экземпляра с другими параметрами. Общей характеристикой Singleton является их ленивое создание, то есть экземпляр не генерируется, пока он не потребуется впервые.
Рассмотрим варианты его реализации.
1. Базовый — не потокобезопасный
Это канонический пример реализации паттерна Singleton.
Это не потокобезопасно. Может случиться так, что два потока одновременно проверят экземпляр на null и получат true. В результате каждый создаст экземпляр, что противоречит правилу наличия только одного экземпляра Singleton. Также возможно, что экземпляр будет создан до того, как произойдёт эта проверка, но другие потоки могут не увидеть этот новый экземпляр сразу, если не предпринять определённые шаги, чтобы убедиться, что они это сделают.
2. Используем lock
Сначала поток блокирует общий объект, а затем проверяет, был ли уже создан экземпляр. Если нет, он продолжает и создаёт экземпляр. Этот процесс гарантирует, что все чтения будут происходить после того, как будет получена блокировка, а все записи будут происходить до того, как она будет освобождена. Так только один поток сможет создать экземпляр, потому что пока один поток находится в процессе его создания, никакой другой поток не может войти в эту часть кода. К тому времени, как второй поток доберётся туда, первый уже создаст экземпляр, поэтому проверка на null вернёт false.
Недостатком этого метода является то, что он может замедлить работу, поскольку требует блокировки каждый раз, когда требуется экземпляр.
А нужно ли блокировать каждый раз? Нет.
Окончание следует…
Источник: https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
Использование Singleton в Многопоточных Сценариях. Начало
Основная идея паттерна Одиночка (Singleton) в том, что это класс, предназначенный для создания только одного экземпляра, и он обычно предоставляет простой способ доступа к этому экземпляру.
Как правило, Singletonы создаются без использования каких-либо параметров для создания экземпляра. Это необходимо для предотвращения осложнений, которые могут возникнуть при попытке создания второго экземпляра с другими параметрами. Общей характеристикой Singleton является их ленивое создание, то есть экземпляр не генерируется, пока он не потребуется впервые.
Рассмотрим варианты его реализации.
1. Базовый — не потокобезопасный
Это канонический пример реализации паттерна Singleton.
public sealed class Singleton
{
private static Singleton instance = null;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
Это не потокобезопасно. Может случиться так, что два потока одновременно проверят экземпляр на null и получат true. В результате каждый создаст экземпляр, что противоречит правилу наличия только одного экземпляра Singleton. Также возможно, что экземпляр будет создан до того, как произойдёт эта проверка, но другие потоки могут не увидеть этот новый экземпляр сразу, если не предпринять определённые шаги, чтобы убедиться, что они это сделают.
2. Используем lock
Сначала поток блокирует общий объект, а затем проверяет, был ли уже создан экземпляр. Если нет, он продолжает и создаёт экземпляр. Этот процесс гарантирует, что все чтения будут происходить после того, как будет получена блокировка, а все записи будут происходить до того, как она будет освобождена. Так только один поток сможет создать экземпляр, потому что пока один поток находится в процессе его создания, никакой другой поток не может войти в эту часть кода. К тому времени, как второй поток доберётся туда, первый уже создаст экземпляр, поэтому проверка на null вернёт false.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object lck = new object();
Singleton(){}
public static Singleton Instance
{
get
{
lock (lck)
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
}
Недостатком этого метода является то, что он может замедлить работу, поскольку требует блокировки каждый раз, когда требуется экземпляр.
А нужно ли блокировать каждый раз? Нет.
Окончание следует…
Источник: https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
👍5
День 2102. #ЗаметкиНаПолях
Использование Singleton в Многопоточных Сценариях. Окончание
Начало
3. Блокировка с двойной проверкой
Первая проверка: первая проверка на null. Если экземпляр не пуст, он возвращается. Эта проверка позволяет избежать накладных расходов на блокировку в большинстве вызовов после инициализации экземпляра.
Блокировка: если экземпляр null, получается блокировка.
Вторая проверка: внутри блокировки выполняется вторая проверка, т.к. другой поток мог инициализировать экземпляр между первой проверкой и получением блокировки. Если экземпляр всё ещё null после второй проверки, он создаётся.
Это лучшее решение? Не совсем. Решение сложное и может вызвать проблемы, если его неправильно использовать.
4. Singleton с Lazy
Ленивая инициализация: класс Lazy автоматически обрабатывает ленивую инициализацию экземпляра Singleton. Экземпляр не создаётся, пока он действительно не понадобится. Lazy гарантирует, что экземпляр создаётся потокобезопасным способом, поэтому не нужно использовать блокировки или дважды проверять блокировку. Кроме того, код проще и удобнее для обслуживания, поскольку сложность синхронизации потоков инкапсулирована в классе Lazy.
Ключевые преимущества использования Lazy для реализации Singleton:
• Потокобезопасность: Lazy обрабатывает все сложности потокобезопасности, снижая риск ошибок.
• Производительность: как правило, более эффективно, так как не требует явной блокировки (которая может быть затратной).
• Простота: код намного чище и понятнее, что повышает удобство обслуживания.
Итого
Существует несколько других способов реализации этого шаблона. Lazy хорош с точки зрения производительности и ленивой работы, а также легко читаем. Это может быть хорошим стартом для начинающих изучать шаблон Singleton. Определённо стоит избегать первого способа, потому что он не потокобезопасен и может вызвать серьёзные проблемы в коде.
Источник: https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
Использование Singleton в Многопоточных Сценариях. Окончание
Начало
3. Блокировка с двойной проверкой
Первая проверка: первая проверка на null. Если экземпляр не пуст, он возвращается. Эта проверка позволяет избежать накладных расходов на блокировку в большинстве вызовов после инициализации экземпляра.
Блокировка: если экземпляр null, получается блокировка.
Вторая проверка: внутри блокировки выполняется вторая проверка, т.к. другой поток мог инициализировать экземпляр между первой проверкой и получением блокировки. Если экземпляр всё ещё null после второй проверки, он создаётся.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object lck = new object();
Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (lck)
{
if (instance == null)
instance = new Singleton();
}
}
}
return instance;
}
}
Это лучшее решение? Не совсем. Решение сложное и может вызвать проблемы, если его неправильно использовать.
4. Singleton с Lazy
Ленивая инициализация: класс Lazy автоматически обрабатывает ленивую инициализацию экземпляра Singleton. Экземпляр не создаётся, пока он действительно не понадобится. Lazy гарантирует, что экземпляр создаётся потокобезопасным способом, поэтому не нужно использовать блокировки или дважды проверять блокировку. Кроме того, код проще и удобнее для обслуживания, поскольку сложность синхронизации потоков инкапсулирована в классе Lazy.
public class Singleton
{
private static readonly Lazy<Singleton>
instance = new Lazy<Singleton>(
() => new Singleton());
private Singleton() {}
public static Singleton Instance
=> instance.Value;
}
Ключевые преимущества использования Lazy для реализации Singleton:
• Потокобезопасность: Lazy обрабатывает все сложности потокобезопасности, снижая риск ошибок.
• Производительность: как правило, более эффективно, так как не требует явной блокировки (которая может быть затратной).
• Простота: код намного чище и понятнее, что повышает удобство обслуживания.
Итого
Существует несколько других способов реализации этого шаблона. Lazy хорош с точки зрения производительности и ленивой работы, а также легко читаем. Это может быть хорошим стартом для начинающих изучать шаблон Singleton. Определённо стоит избегать первого способа, потому что он не потокобезопасен и может вызвать серьёзные проблемы в коде.
Источник: https://thecodeman.net/posts/how-to-use-singleton-in-multithreading
👍28
День 2103. #ЧтоНовенького
Улучшения Problem Details в ASP.NET 9
В ASP.NET 8 улучшили обработку исключений, представив IExceptionHandler. В ASP.NET 9 промежуточное ПО обработчика исключений было улучшено для обеспечения большей гибкости и контроля над кодом статуса ответа при использовании Problem Details.
Problem Details становится стандартизированным способом представления информации об ошибках в ответе HTTP API. ASP.NET уже поддерживает Problem Details, но вам нужно включить его. Это можно сделать, зарегистрировав промежуточное ПО Problem Details:
AddProblemDetails в сочетании с UseStatusCodePages достаточно для возврата ответов Problem Details для конечных точек, которые возвращают пустой неуспешный ответ (например, BadRequest). Если конечная точка возвращает неуспешный ответ с телом, это тело будет возвращено. Метод UseExceptionHandler преобразует исключения времени выполнения, возникшие при выполнении конечной точки, в ответы соответствующие формату Problem Details:
Без UseExceptionHandler API просто вернёт статус 500 без тела.
Ответы Problem Details можно настраивать, реализовав IExceptionHandler, чтобы предоставить больше информации пользователю. В ASP.NET 9 этот процесс упрощён.
Новое свойство конфигурации StatusCodeSelector делает пользовательскую реализацию обработчика исключений практически ненужной (в большинстве случаев). Вы можете использовать её для более сложных сценариев или для регистрации исключения. StatusCodeSelector позволяет изменять код состояния по умолчанию (500) на основе исключения:
Так, если приложение выбросит ошибку UserNotFoundException, клиенту вернётся состояние 403 (Запрещено).
Если вы решите реализовать пользовательский обработчик исключений, вы по-прежнему можете использовать свойство StatusCodeSelector для установки кода состояния. Но, если StatusCode устанавливается обработчиком исключений, StatusCodeSelector игнорируется.
Итого
StatusCodeSelector позволяет легко менять код состояния по умолчанию (500) на основе типа исключения. Поскольку StatusCodeSelector принимает исключение, другие свойства исключения также могут использоваться для определения кода состояния.
Источник: https://timdeschryver.dev/blog/taking-a-look-at-the-problem-details-enhancements-in-aspnet-9
Улучшения Problem Details в ASP.NET 9
В ASP.NET 8 улучшили обработку исключений, представив IExceptionHandler. В ASP.NET 9 промежуточное ПО обработчика исключений было улучшено для обеспечения большей гибкости и контроля над кодом статуса ответа при использовании Problem Details.
Problem Details становится стандартизированным способом представления информации об ошибках в ответе HTTP API. ASP.NET уже поддерживает Problem Details, но вам нужно включить его. Это можно сделать, зарегистрировав промежуточное ПО Problem Details:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
// Возвращаем страницы с ответом при неудаче
// По умолчанию возвращается пустое тело со статусом
app.UseStatusCodePages();
// Переводим ошибки в ответы Problem Details
app.UseExceptionHandler();
app.Run();
AddProblemDetails в сочетании с UseStatusCodePages достаточно для возврата ответов Problem Details для конечных точек, которые возвращают пустой неуспешный ответ (например, BadRequest). Если конечная точка возвращает неуспешный ответ с телом, это тело будет возвращено. Метод UseExceptionHandler преобразует исключения времени выполнения, возникшие при выполнении конечной точки, в ответы соответствующие формату Problem Details:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "An error occurred while processing your request.",
"status": 500,
"traceId": "00-f942c075462cb925f9f1820ce659036a-9a6b9ad75bfcbf22-00"
}Без UseExceptionHandler API просто вернёт статус 500 без тела.
Ответы Problem Details можно настраивать, реализовав IExceptionHandler, чтобы предоставить больше информации пользователю. В ASP.NET 9 этот процесс упрощён.
Новое свойство конфигурации StatusCodeSelector делает пользовательскую реализацию обработчика исключений практически ненужной (в большинстве случаев). Вы можете использовать её для более сложных сценариев или для регистрации исключения. StatusCodeSelector позволяет изменять код состояния по умолчанию (500) на основе исключения:
// …
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex switch {
UserNotAllowedException =>
StatusCodes.Status401Unauthorized,
UserNotFoundException =>
StatusCodes.Status403Forbidden,
NotImplementedException =>
StatusCodes.Status501NotImplemented,
_ => StatusCodes.Status500InternalServerError
}
});
// …
Так, если приложение выбросит ошибку UserNotFoundException, клиенту вернётся состояние 403 (Запрещено).
Если вы решите реализовать пользовательский обработчик исключений, вы по-прежнему можете использовать свойство StatusCodeSelector для установки кода состояния. Но, если StatusCode устанавливается обработчиком исключений, StatusCodeSelector игнорируется.
Итого
StatusCodeSelector позволяет легко менять код состояния по умолчанию (500) на основе типа исключения. Поскольку StatusCodeSelector принимает исключение, другие свойства исключения также могут использоваться для определения кода состояния.
Источник: https://timdeschryver.dev/blog/taking-a-look-at-the-problem-details-enhancements-in-aspnet-9
👍28
День 2104. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 30. Задание либо полностью выполнено, либо не выполнено
Заявление, что программа или задача готова на 90%, является чем-то вроде корпоративной шутки. Также говорят, что первая половина проекта потребляет 90% ресурсов, а вторая — оставшиеся 90%. Такое оптимистичное, но вводящее в заблуждение заявление не позволяет оценить, когда данная часть работы завершится на самом деле. Если вы говорите: «Я закончил всё, кроме...» — значит вы ещё не закончили.
Что означает «готово»?
Что мы имеем в виду, когда говорим, что какая-то часть проекта готова? Приступая к проекту, команда должна перечислить критерии, с помощью которых будут определяться завершённость конкретного элемента работы, задачи или итерации. Контрольные списки помогают установить минимальный объём работы, которую необходимо выполнить, чтобы считать конкретную задачу завершённой. Например, программный компонент полностью реализован, протестирован, интегрирован в продукт и задокументирован, то есть потенциально может быть выпущен для использования клиентами. Отдельные единицы работы, такие как реализация пользовательских историй, либо выполнены на 100%, либо не выполнены; промежуточного состояния не существует.
Не бывает частичной готовности
Большая задача - это некая единица работы, которая приносит пользу клиенту. Одна из проблем оценки степени готовности — мы слишком часто считаем готовыми задачи, которые начали, но не завершили. Однажды утром, обдумав алгоритм решения сложной задачи, вы можете решить, что выполнили около 30%, поскольку алгоритм был сложной частью работы. Возможно, это так, но написание кода, ревью, тестирование и интеграция с приложением тоже могут потребовать много времени. Трудно точно оценить процент выполнения большой задачи. Может оказаться, что она намного больше, чем предполагалось, или появится необходимость выполнить дополнительные действия, да и нет уверенности в том, как пойдёт оставшаяся работа.
Первый шаг к решению проблемы определения готовности — разбить большие задачи на несколько маленьких подзадач по 4-6 рабочих часов. Эта величина позволяет понять всё, что нужно сделать, чтобы выполнить задачу. Полезно определять их как действия, которые нельзя логически разделить на более мелкие части. Если у вас есть предыдущий опыт выполнения какого-либо действия, вы наверняка сможете точно оценить, сколько времени понадобится для его выполнения. Для менее знакомых, более неопределённых или более сложных действий лучше подойдёт разделение на более мелкие подзадачи.
Следите за своим прогрессом по этим мелким задачам в бинарной форме: «готово/не готово». Не бывает частичной готовности. Готовность незавершённой задачи — 0. Прогресс в большой задаче определяется количеством составляющих её и полностью готовых подзадач. Такое отслеживание прогресса более информативно, чем попытки угадать выполненный процент большого и, возможно, неверно определённого объёма работы.
Отслеживание по статусу требований
Другой вариант мониторинга хода выполнения проекта — отслеживание статуса требований. Каждое требование, добавленное в объём работ, имеет статус в данный момент времени, например: «предложено», «одобрено», «реализовано», «проверено», «отложено» и «удалено». При таком подходе запланированный объём работ считается завершённым, когда каждое добавленное в него требование имеет один из трёх статусов:
- «проверено» (полностью реализовано и протестировано);
- «отложено» (отложено для реализации позднее);
- «удалено» (больше не планируется к реализации).
Готовность ведёт к ценности
Следить за продвижением проекта необходимо не только для того, чтобы убедиться в том, что сделана вся запланированная работа. Такой мониторинг также позволяет гарантировать выполнение отдельных этапов, которые принесут пользу клиентам, когда они получат готовую реализацию. Лучший способ получить представление о том, насколько вы близки к достижению этой пользы, — изменять статус задачи на «готово», только когда она действительно завершена.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
Уроки 50 Лет Разработки ПО
Урок 30. Задание либо полностью выполнено, либо не выполнено
Заявление, что программа или задача готова на 90%, является чем-то вроде корпоративной шутки. Также говорят, что первая половина проекта потребляет 90% ресурсов, а вторая — оставшиеся 90%. Такое оптимистичное, но вводящее в заблуждение заявление не позволяет оценить, когда данная часть работы завершится на самом деле. Если вы говорите: «Я закончил всё, кроме...» — значит вы ещё не закончили.
Что означает «готово»?
Что мы имеем в виду, когда говорим, что какая-то часть проекта готова? Приступая к проекту, команда должна перечислить критерии, с помощью которых будут определяться завершённость конкретного элемента работы, задачи или итерации. Контрольные списки помогают установить минимальный объём работы, которую необходимо выполнить, чтобы считать конкретную задачу завершённой. Например, программный компонент полностью реализован, протестирован, интегрирован в продукт и задокументирован, то есть потенциально может быть выпущен для использования клиентами. Отдельные единицы работы, такие как реализация пользовательских историй, либо выполнены на 100%, либо не выполнены; промежуточного состояния не существует.
Не бывает частичной готовности
Большая задача - это некая единица работы, которая приносит пользу клиенту. Одна из проблем оценки степени готовности — мы слишком часто считаем готовыми задачи, которые начали, но не завершили. Однажды утром, обдумав алгоритм решения сложной задачи, вы можете решить, что выполнили около 30%, поскольку алгоритм был сложной частью работы. Возможно, это так, но написание кода, ревью, тестирование и интеграция с приложением тоже могут потребовать много времени. Трудно точно оценить процент выполнения большой задачи. Может оказаться, что она намного больше, чем предполагалось, или появится необходимость выполнить дополнительные действия, да и нет уверенности в том, как пойдёт оставшаяся работа.
Первый шаг к решению проблемы определения готовности — разбить большие задачи на несколько маленьких подзадач по 4-6 рабочих часов. Эта величина позволяет понять всё, что нужно сделать, чтобы выполнить задачу. Полезно определять их как действия, которые нельзя логически разделить на более мелкие части. Если у вас есть предыдущий опыт выполнения какого-либо действия, вы наверняка сможете точно оценить, сколько времени понадобится для его выполнения. Для менее знакомых, более неопределённых или более сложных действий лучше подойдёт разделение на более мелкие подзадачи.
Следите за своим прогрессом по этим мелким задачам в бинарной форме: «готово/не готово». Не бывает частичной готовности. Готовность незавершённой задачи — 0. Прогресс в большой задаче определяется количеством составляющих её и полностью готовых подзадач. Такое отслеживание прогресса более информативно, чем попытки угадать выполненный процент большого и, возможно, неверно определённого объёма работы.
Отслеживание по статусу требований
Другой вариант мониторинга хода выполнения проекта — отслеживание статуса требований. Каждое требование, добавленное в объём работ, имеет статус в данный момент времени, например: «предложено», «одобрено», «реализовано», «проверено», «отложено» и «удалено». При таком подходе запланированный объём работ считается завершённым, когда каждое добавленное в него требование имеет один из трёх статусов:
- «проверено» (полностью реализовано и протестировано);
- «отложено» (отложено для реализации позднее);
- «удалено» (больше не планируется к реализации).
Готовность ведёт к ценности
Следить за продвижением проекта необходимо не только для того, чтобы убедиться в том, что сделана вся запланированная работа. Такой мониторинг также позволяет гарантировать выполнение отдельных этапов, которые принесут пользу клиентам, когда они получат готовую реализацию. Лучший способ получить представление о том, насколько вы близки к достижению этой пользы, — изменять статус задачи на «готово», только когда она действительно завершена.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍8
День 2105. #ЧтоНовенького
Upgrade Assistant Теперь Поддерживает Обновление до CPM
.NET Upgrade Assistant помогает обновлять ваши решения до новых версий .NET. Про его возможности я уже писал тут и тут. Недавно в него добавили новый тип обновления до Централизованного Управления Пакетами (Central Package Management).
Независимо от того, обновляетесь ли вы с .NET Framework до .NET 8 или просто между версиями .NET, .NET Upgrade Assistant поможет понять, какие изменения необходимы, и автоматизирует многие из изменений. Он доступен как расширение Visual Studio или как инструмент командной строки.
Новейшая версия Upgrade Assistant представляет новый тип обновления, позволяющий вам преобразовать всё решение или выбранный набор проектов для использования Nuget Central Package Management (CPM).
Обновление в Visual Studio
Если Upgrade Assistant установлен как расширение в Visual Studio, щёлкните правой кнопкой мыши на узел проекта в обозревателе решений и выберите Upgrade (Обновить). Вы заметите новую функцию обновления проекта — Nuget Central Package Management (CPM).
На следующем экране вам будет предоставлена возможность также выбрать любое количество дополнительных проектов в вашем решении для обновления до CPM, но в большинстве случаев рекомендуется обновить все проекты в решении.
Далее будут предложены некоторые параметры настройки. Если вы также хотите преобразовать выбранные проекты в стиль SDK (используемый, начиная с .NET Core), вы можете установить флажок для этого параметра. По умолчанию веб-проекты не будут преобразованы в стиль SDK. Рекомендуется обновлять эти веб-проекты отдельно, но, если вы хотите включить их сюда, есть возможность сделать это, установив флажок «include web projects to SDK conversion» (включить преобразование веб-проектов в SDK).
Также будут предложены параметры управления пакетами в CPM. Рекомендуется (и по умолчанию включено) транзитивное закрепление (transitive pinning) и указан предлагаемый путь к папке, где будут храниться все централизованные версии пакетов. Кроме того, вы можете добавить JSON-файл конфигурации для определения этих параметров. Вот пример такого файла:
Обновление в CLI
Вызовите Upgrade Assistant в командной строке, используя следующую команду в папке решения:
Сначала будет предложено выбрать проект, который вы хотите обновить, но позже у вас будет возможность выбрать другие проекты и задать прочие параметры, аналогично обновлению в VS.
Источник: https://devblogs.microsoft.com/dotnet/dotnet-upgrade-assistant-cpm-upgrade/
Upgrade Assistant Теперь Поддерживает Обновление до CPM
.NET Upgrade Assistant помогает обновлять ваши решения до новых версий .NET. Про его возможности я уже писал тут и тут. Недавно в него добавили новый тип обновления до Централизованного Управления Пакетами (Central Package Management).
Независимо от того, обновляетесь ли вы с .NET Framework до .NET 8 или просто между версиями .NET, .NET Upgrade Assistant поможет понять, какие изменения необходимы, и автоматизирует многие из изменений. Он доступен как расширение Visual Studio или как инструмент командной строки.
Новейшая версия Upgrade Assistant представляет новый тип обновления, позволяющий вам преобразовать всё решение или выбранный набор проектов для использования Nuget Central Package Management (CPM).
Обновление в Visual Studio
Если Upgrade Assistant установлен как расширение в Visual Studio, щёлкните правой кнопкой мыши на узел проекта в обозревателе решений и выберите Upgrade (Обновить). Вы заметите новую функцию обновления проекта — Nuget Central Package Management (CPM).
На следующем экране вам будет предоставлена возможность также выбрать любое количество дополнительных проектов в вашем решении для обновления до CPM, но в большинстве случаев рекомендуется обновить все проекты в решении.
Далее будут предложены некоторые параметры настройки. Если вы также хотите преобразовать выбранные проекты в стиль SDK (используемый, начиная с .NET Core), вы можете установить флажок для этого параметра. По умолчанию веб-проекты не будут преобразованы в стиль SDK. Рекомендуется обновлять эти веб-проекты отдельно, но, если вы хотите включить их сюда, есть возможность сделать это, установив флажок «include web projects to SDK conversion» (включить преобразование веб-проектов в SDK).
Также будут предложены параметры управления пакетами в CPM. Рекомендуется (и по умолчанию включено) транзитивное закрепление (transitive pinning) и указан предлагаемый путь к папке, где будут храниться все централизованные версии пакетов. Кроме того, вы можете добавить JSON-файл конфигурации для определения этих параметров. Вот пример такого файла:
{
"upgrade": {
"settings": {
"cpm": {
"convertProjectsToSdkStyle": false,
"includeWebProjects": false,
"useTransitivePinning": true,
"rootPath": "somePath"
}
}
}
}Обновление в CLI
Вызовите Upgrade Assistant в командной строке, используя следующую команду в папке решения:
upgrade-assistant upgrade
Сначала будет предложено выбрать проект, который вы хотите обновить, но позже у вас будет возможность выбрать другие проекты и задать прочие параметры, аналогично обновлению в VS.
Источник: https://devblogs.microsoft.com/dotnet/dotnet-upgrade-assistant-cpm-upgrade/
👍11
День 2106. #ЗаметкиНаПолях
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Начало
Когда дело касается обычной сквозной функциональности вроде ведения журнала, отказоустойчивости или кэширования, лучшим решением обычно является применение паттерна Декоратор.
Часто можно видеть, как внедрение зависимости (DI) используется для внедрения, скажем, интерфейса журнала или классов пайплайнов Polly для отказоустойчивости в код приложения. Однако, лучшим решением было бы использование Декораторов.
Вот упрощённый пример:
Как протестировать такой класс? Да, в методе GetSomething всего пара строк, но это упрощённый код, можно предположить, что метод гораздо длиннее.
Класс MyApi связан с Polly, т.к. ResiliencePipeline определён в этой библиотеке. Связанность — главная причина спагетти-кода. Чтобы писать устойчивый код, вы должны осознавать степень связанности. Самый несвязанный код — это код, который вы можете легко удалить. Polly — прекрасная библиотека, и всё сказанное далее скорее относится ко всем сторонним зависимостям.
Также это не означает, что нельзя использовать высококачественные сторонние библиотеки, такие как Polly. Не стоит становиться жертвой синдрома «not invented here».
Когда дело доходит до классических сквозных проблем, паттерн Декоратор обычно предпочтительнее с точки зрения дизайна, чем внедрение проблемы в код приложения. Приведённый выше пример выглядит безобидным, но представьте, что вы внедряете ResiliencePipeline, логгер, возможно, сервис кэширования… И реальный код приложения в итоге тонет в «коде инфраструктуры».
Дело не в том, что мы не хотим иметь эти сторонние зависимости, а в том, что мы хотим переместить их в другое место.
Продолжение следует…
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Начало
Когда дело касается обычной сквозной функциональности вроде ведения журнала, отказоустойчивости или кэширования, лучшим решением обычно является применение паттерна Декоратор.
Часто можно видеть, как внедрение зависимости (DI) используется для внедрения, скажем, интерфейса журнала или классов пайплайнов Polly для отказоустойчивости в код приложения. Однако, лучшим решением было бы использование Декораторов.
Вот упрощённый пример:
public class MyApi
{
private ResiliencePipeline _pipeline;
private IService _svc;
public MyApi(
ResiliencePipelineProvider<string> prv,
IService service)
{
_pipeline = prv.GetPipeline("retry-pipeline");
_svc = service;
}
public List<string> GetSomething(
QueryByAttribute query)
{
var result =
_pipeline.Execute(() =>
svc.RetrieveMultiple(query));
return result.Entities
.Cast<string>().ToList();
}
}
Как протестировать такой класс? Да, в методе GetSomething всего пара строк, но это упрощённый код, можно предположить, что метод гораздо длиннее.
Класс MyApi связан с Polly, т.к. ResiliencePipeline определён в этой библиотеке. Связанность — главная причина спагетти-кода. Чтобы писать устойчивый код, вы должны осознавать степень связанности. Самый несвязанный код — это код, который вы можете легко удалить. Polly — прекрасная библиотека, и всё сказанное далее скорее относится ко всем сторонним зависимостям.
Также это не означает, что нельзя использовать высококачественные сторонние библиотеки, такие как Polly. Не стоит становиться жертвой синдрома «not invented here».
Когда дело доходит до классических сквозных проблем, паттерн Декоратор обычно предпочтительнее с точки зрения дизайна, чем внедрение проблемы в код приложения. Приведённый выше пример выглядит безобидным, но представьте, что вы внедряете ResiliencePipeline, логгер, возможно, сервис кэширования… И реальный код приложения в итоге тонет в «коде инфраструктуры».
Дело не в том, что мы не хотим иметь эти сторонние зависимости, а в том, что мы хотим переместить их в другое место.
Продолжение следует…
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
👍19👎1
Вот и настал день, когда больше нельзя оставаться в стороне. Надо определиться с выбором. Будущее зависит от вас.
Anonymous Poll
68%
Табы
32%
Пробелы
День 2107. #ЗаметкиНаПолях
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Продолжение
Начало
Устойчивый декоратор
Задача в этом примере - сделать зависимость IService более устойчивой. Класс MyApi станет более устойчивым транзитивно, получив более устойчивую зависимость. Поэтому первым шагом рефакторинга является введение устойчивого декоратора:
Как и все декораторы, этот класс содержит экземпляр IService, а также сам реализует этот интерфейс. Вместо внедрения ResiliencePipelineProvider<string> только для вызова GetPipeline для него, он просто получает ResiliencePipeline и сохраняет объект для использования в методе RetrieveMultiple. Обратите внимание, что тут используется первичный конструктор.
Упрощение MyApi
Теперь, нам не нужен код Polly в MyApi:
Да, в MyApi почти ничего не осталось, но напомню, что у нас упрощённый пример, и GetSomething может быть гораздо длиннее и требовать тестирования. А в нынешнем виде тестирование тривиально:
Однако важнее то, что теперь нам не только удалось исключить сторонние зависимости из кода приложения, но и упростить его. Создать устойчивый объект MyApi можно в корне композиции:
Так мы отделяем код приложения от сторонних зависимостей. Мы определяем ResilientService в корне композиции и оставляем зависимость от Polly там.
Окончание следует…
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Продолжение
Начало
Устойчивый декоратор
Задача в этом примере - сделать зависимость IService более устойчивой. Класс MyApi станет более устойчивым транзитивно, получив более устойчивую зависимость. Поэтому первым шагом рефакторинга является введение устойчивого декоратора:
public class ResilientService(
ResiliencePipeline pipeline,
IService inner) : IService
{
public QueryResult RetrieveMultiple(
QueryByAttribute query)
{
return pipeline.Execute(()
=> inner.RetrieveMultiple(query));
}
}
Как и все декораторы, этот класс содержит экземпляр IService, а также сам реализует этот интерфейс. Вместо внедрения ResiliencePipelineProvider<string> только для вызова GetPipeline для него, он просто получает ResiliencePipeline и сохраняет объект для использования в методе RetrieveMultiple. Обратите внимание, что тут используется первичный конструктор.
Упрощение MyApi
Теперь, нам не нужен код Polly в MyApi:
public class MyApi
{
private IService _svc;
public MyApi(IService svc)
{
_svc = svc;
}
public List<string> GetSomething(
QueryByAttribute query)
{
var result = svc.RetrieveMultiple(query);
return result.Entities
.Cast<string>().ToList();
}
}
Да, в MyApi почти ничего не осталось, но напомню, что у нас упрощённый пример, и GetSomething может быть гораздо длиннее и требовать тестирования. А в нынешнем виде тестирование тривиально:
[Theory]
[InlineData("foo", "bar", "baz")]
public void GetSomething(params string[] expected)
{
var svc = new Mock<IService>();
svc
.Setup(s => s.RetrieveMultiple(
new QueryByAttribute()))
.Returns(new QueryResult(expected));
var sut = new MyApi(svc.Object);
var actual = sut.GetSomething(
new QueryByAttribute());
Assert.Equal(expected, actual);
}
Однако важнее то, что теперь нам не только удалось исключить сторонние зависимости из кода приложения, но и упростить его. Создать устойчивый объект MyApi можно в корне композиции:
var svc = new ResilientService(pipeline, inner);
var myApi = new MyApi(svc);
Так мы отделяем код приложения от сторонних зависимостей. Мы определяем ResilientService в корне композиции и оставляем зависимость от Polly там.
Окончание следует…
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
👍12
День 2108. #ЗаметкиНаПолях
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Окончание
Начало
Продолжение
Причины для снижения связанности
Почему важно отделить код приложения от Polly?
1. Во-первых, Polly — это просто пример любой сторонней зависимости. Сторонний компонент меняется со временем и часто независимо от вашей базовой платформы. Возможно, вам придется иметь дело с критическими изменениями или исправлениями безопасности в неподходящее время. Организация, которая поддерживает компонент, может прекратить работу. Это происходит как с коммерческими организациями, так и с разработчиками открытого кода, хотя и по разным причинам.
2. Если ваш временной горизонт составляет от пяти до десяти лет, вы будете удивлены, насколько всё изменится. Вы можете возразить, что никто не проектирует программные системы с такой долгосрочной перспективой, но я думаю, что, если вы спросите бизнес, он наверняка ожидает, что система прослужит долго.
3. Предположим, что мы не хотим зависеть от какого-то случайного компонента с открытым кодом, но ведь зависимость от Polly безопасна, правда? В долгосрочной перспективе - нет. Пять лет назад была такая же ситуация с Newtonsoft.Json, но затем Microsoft сделали свою System.Text.Json. Теперь есть две конкурирующие библиотеки JSON, и Microsoft использует свою во фреймворках и библиотеках, которые они выпускают.
Решение отделить код приложения от стороннего компонента в итоге является вопросом управления рисками. Вам решать, делать ли ставку. Вы платите авансом в виде расходов ресурсов на рефакторинг или откладываете его, надеясь, что он никогда не понадобится. В случае с отделением стоимость изменений довольно низкая, зато есть и другие преимущества. Как уже упоминалось, модульное тестирование становится проще.
Конфигурация
Поскольку Polly живёт только в корне композиции, вам также нужно будет определить ResiliencePipeline там. Вы можете написать код, который создает пайплайн, где угодно, но было бы естественно сделать его функцией создания в классе ResilientService:
Это всего лишь пример, и, возможно, это не то, что вы хотели бы сделать. Возможно, вы предпочитаете, чтобы некоторые из этих значений были определены в файле конфигурации. Однако, если вы используете этот вариант, вы можете взять возвращаемое значение этого метода и внедрить его в конструктор ResilientService.
Итого
Сквозные проблемы, такие как кэширование, ведение журнала, безопасность или, в данном случае, отказоустойчивость, обычно лучше всего решаются с помощью паттерна Декоратор. Мы рассмотрели пример его использования для отделения проблемы отказоустойчивости от потребителя сервиса, который должен быть отказоустойчивым.
Polly является одним из лучших пакетов .NET с открытым кодом, поэтому здесь не наброс на Polly. Это просто пример того, как размещать полезные зависимости в вашей кодовой базе, чтобы убедиться, что они как можно меньше влияют на код приложения.
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
Не Внедряйте Сторонние Зависимости. Используйте Декораторы. Окончание
Начало
Продолжение
Причины для снижения связанности
Почему важно отделить код приложения от Polly?
1. Во-первых, Polly — это просто пример любой сторонней зависимости. Сторонний компонент меняется со временем и часто независимо от вашей базовой платформы. Возможно, вам придется иметь дело с критическими изменениями или исправлениями безопасности в неподходящее время. Организация, которая поддерживает компонент, может прекратить работу. Это происходит как с коммерческими организациями, так и с разработчиками открытого кода, хотя и по разным причинам.
2. Если ваш временной горизонт составляет от пяти до десяти лет, вы будете удивлены, насколько всё изменится. Вы можете возразить, что никто не проектирует программные системы с такой долгосрочной перспективой, но я думаю, что, если вы спросите бизнес, он наверняка ожидает, что система прослужит долго.
3. Предположим, что мы не хотим зависеть от какого-то случайного компонента с открытым кодом, но ведь зависимость от Polly безопасна, правда? В долгосрочной перспективе - нет. Пять лет назад была такая же ситуация с Newtonsoft.Json, но затем Microsoft сделали свою System.Text.Json. Теперь есть две конкурирующие библиотеки JSON, и Microsoft использует свою во фреймворках и библиотеках, которые они выпускают.
Решение отделить код приложения от стороннего компонента в итоге является вопросом управления рисками. Вам решать, делать ли ставку. Вы платите авансом в виде расходов ресурсов на рефакторинг или откладываете его, надеясь, что он никогда не понадобится. В случае с отделением стоимость изменений довольно низкая, зато есть и другие преимущества. Как уже упоминалось, модульное тестирование становится проще.
Конфигурация
Поскольку Polly живёт только в корне композиции, вам также нужно будет определить ResiliencePipeline там. Вы можете написать код, который создает пайплайн, где угодно, но было бы естественно сделать его функцией создания в классе ResilientService:
public static ResiliencePipeline CreatePipeline()
{
return new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 4
})
.AddTimeout(TimeSpan.FromSeconds(1))
.Build();
}
Это всего лишь пример, и, возможно, это не то, что вы хотели бы сделать. Возможно, вы предпочитаете, чтобы некоторые из этих значений были определены в файле конфигурации. Однако, если вы используете этот вариант, вы можете взять возвращаемое значение этого метода и внедрить его в конструктор ResilientService.
Итого
Сквозные проблемы, такие как кэширование, ведение журнала, безопасность или, в данном случае, отказоустойчивость, обычно лучше всего решаются с помощью паттерна Декоратор. Мы рассмотрели пример его использования для отделения проблемы отказоустойчивости от потребителя сервиса, который должен быть отказоустойчивым.
Polly является одним из лучших пакетов .NET с открытым кодом, поэтому здесь не наброс на Polly. Это просто пример того, как размещать полезные зависимости в вашей кодовой базе, чтобы убедиться, что они как можно меньше влияют на код приложения.
Источник: https://blog.ploeh.dk/2024/09/02/keeping-cross-cutting-concerns-out-of-application-code/
👍8
День 2109. #Оффтоп #RegEx
Доброй пятницы, дорогие подписчики.
Сегодня порекомендую вам замечательное видео с канала Мэта Паркера Stand-up Maths.
Сначала, не подглядывая, как вы думаете, что проверяет следующий код:
Этот метод проверяет, является ли число простым! Да. Но как вот это ^.?$|^(..+?)\1+$ может выдавать простые числа?
Об этом подробно, а также «Regex 101» - объяснение того, как работают регулярные выражения в принципе, и это выражение в частности - от Мэта Паркера смотрите в его видео.
Для тех, кто предпочитает текст, Илья Герасимчук подробно разобрал это регулярное выражение в своей статье.
А вообще, подписывайтесь на канал Мэта, если так же, как я, любите всякие математические приколы (не переживайте, там всё объясняется на уровне средней школы).
Доброй пятницы, дорогие подписчики.
Сегодня порекомендую вам замечательное видео с канала Мэта Паркера Stand-up Maths.
Сначала, не подглядывая, как вы думаете, что проверяет следующий код:
static bool Check(int n)
{
return !Regex.IsMatch(
new string('1', n),
@"^.?$|^(..+?)\1+$"
);
}
Об этом подробно, а также «Regex 101» - объяснение того, как работают регулярные выражения в принципе, и это выражение в частности - от Мэта Паркера смотрите в его видео.
Для тех, кто предпочитает текст, Илья Герасимчук подробно разобрал это регулярное выражение в своей статье.
А вообще, подписывайтесь на канал Мэта, если так же, как я, любите всякие математические приколы (не переживайте, там всё объясняется на уровне средней школы).
👍20👎2
День 2110. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 31. Команде нужна гибкость в отношении хотя бы одного из измерений: масштаба, плана, бюджета, персонала или качества. Начало
«Вы хотите хорошо, быстро или дёшево? Выберите два пункта».
Это фольклорная форма описания треугольника ограничений, или железного треугольника, встречающегося во многих книгах, посвященных управлению проектами.
Пять измерений проекта
На самом деле, измерений скорее пять. Во-первых, масштаб или объём, описывающий функциональные возможности продукта. Важно отделять качество от масштаба. Можно написать программу очень быстро, если она не должна работать правильно. Другие три — это время, необходимое для реализации (план), бюджет (стоимость) и люди, реализующие проект. Некоторые добавляют шестое измерение — риск. Однако он не поддается регулированию, как другие пять.
Многие часто объединяют персонал и бюджет в одно измерение — «ресурсы». Но лучше их разделять. Большую часть стоимости проекта действительно составляет зарплата персонала. Но иногда команда имеет достаточное финансирование, но ограничена численностью. Тогда руководитель проекта может использовать бюджет, чтобы купить комплексное решение или передать часть работ на аутсорсинг.
Для каждого проекта нужно решить, какие измерения являются наиболее важными, и сбалансировать другие так, чтобы основные цели были достигнуты. Поиск компромиссов между ними — непростая задача. Например, по мере увеличения численности персонала стоимость может возрасти, а плановый срок — не обязательно сократится, как описывает Брукс в «Мифическом человеко-месяце». Распространённым компромиссом является сокращение графика или добавление функций за счёт качества. Любой, кто стал жертвой ошибок в ПО, ставит под сомнение подобные компромиссы, но организации-разработчики порой делают такой выбор — иногда преднамеренно, иногда по умолчанию.
Каждое измерение может выступать для проекта в одной из трёх ролей:
1. Ограничение - определяет рамки, в которые должен уложиться проект. Проект негибок в отношении измерения-ограничения. Если численность команды проекта не может изменяться, то это ограничение по персоналу. Стоимость — ограничение для проектов, выполняемых по контракту с фиксированной ценой. Качество — ограничение для проектов, касающихся критически важных продуктов. Проекты, привязанные к событиям с фиксированной датой, ограничены по сроку.
2. Драйвер — ключевая цель или критерий успеха проекта. Для продукта с желаемым маркетинговым окном возможностей плановый срок выпуска является драйвером. Драйвер – до некоторой степени гибкое измерение. Определённый набор функций может быть основным драйвером проекта, но если он не подлежит обсуждению, то масштаб (объём) становится ограничением.
3. Степень свободы – остальные измерения проекта, которые не являются ни драйвером, ни ограничением. Можно управлять степенями свободы в определённых пределах. Главная задача — так скорректировать степени свободы, чтобы достичь успеха проекта в пределах, устанавливаемых ограничениями. Например, в Agile-разработке масштаб рассматривается как степень свободы, путём регулировки объёма, реализуемого в каждой итерации, так, чтобы уложиться во временные ограничения графика.
Проект с нулевой степенью свободы, скорее всего, потерпит неудачу. Все пять измерений не могут быть ограничениями и драйверами. Добавление новой возможности, уход члена команды, риск, который становится проблемой, слишком низкая оценка — всё это рушит плановый график, поскольку из-за чрезмерных ограничений руководитель проекта не может своевременно реагировать на эти события.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
Уроки 50 Лет Разработки ПО
Урок 31. Команде нужна гибкость в отношении хотя бы одного из измерений: масштаба, плана, бюджета, персонала или качества. Начало
«Вы хотите хорошо, быстро или дёшево? Выберите два пункта».
Это фольклорная форма описания треугольника ограничений, или железного треугольника, встречающегося во многих книгах, посвященных управлению проектами.
Пять измерений проекта
На самом деле, измерений скорее пять. Во-первых, масштаб или объём, описывающий функциональные возможности продукта. Важно отделять качество от масштаба. Можно написать программу очень быстро, если она не должна работать правильно. Другие три — это время, необходимое для реализации (план), бюджет (стоимость) и люди, реализующие проект. Некоторые добавляют шестое измерение — риск. Однако он не поддается регулированию, как другие пять.
Многие часто объединяют персонал и бюджет в одно измерение — «ресурсы». Но лучше их разделять. Большую часть стоимости проекта действительно составляет зарплата персонала. Но иногда команда имеет достаточное финансирование, но ограничена численностью. Тогда руководитель проекта может использовать бюджет, чтобы купить комплексное решение или передать часть работ на аутсорсинг.
Для каждого проекта нужно решить, какие измерения являются наиболее важными, и сбалансировать другие так, чтобы основные цели были достигнуты. Поиск компромиссов между ними — непростая задача. Например, по мере увеличения численности персонала стоимость может возрасти, а плановый срок — не обязательно сократится, как описывает Брукс в «Мифическом человеко-месяце». Распространённым компромиссом является сокращение графика или добавление функций за счёт качества. Любой, кто стал жертвой ошибок в ПО, ставит под сомнение подобные компромиссы, но организации-разработчики порой делают такой выбор — иногда преднамеренно, иногда по умолчанию.
Каждое измерение может выступать для проекта в одной из трёх ролей:
1. Ограничение - определяет рамки, в которые должен уложиться проект. Проект негибок в отношении измерения-ограничения. Если численность команды проекта не может изменяться, то это ограничение по персоналу. Стоимость — ограничение для проектов, выполняемых по контракту с фиксированной ценой. Качество — ограничение для проектов, касающихся критически важных продуктов. Проекты, привязанные к событиям с фиксированной датой, ограничены по сроку.
2. Драйвер — ключевая цель или критерий успеха проекта. Для продукта с желаемым маркетинговым окном возможностей плановый срок выпуска является драйвером. Драйвер – до некоторой степени гибкое измерение. Определённый набор функций может быть основным драйвером проекта, но если он не подлежит обсуждению, то масштаб (объём) становится ограничением.
3. Степень свободы – остальные измерения проекта, которые не являются ни драйвером, ни ограничением. Можно управлять степенями свободы в определённых пределах. Главная задача — так скорректировать степени свободы, чтобы достичь успеха проекта в пределах, устанавливаемых ограничениями. Например, в Agile-разработке масштаб рассматривается как степень свободы, путём регулировки объёма, реализуемого в каждой итерации, так, чтобы уложиться во временные ограничения графика.
Проект с нулевой степенью свободы, скорее всего, потерпит неудачу. Все пять измерений не могут быть ограничениями и драйверами. Добавление новой возможности, уход члена команды, риск, который становится проблемой, слишком низкая оценка — всё это рушит плановый график, поскольку из-за чрезмерных ограничений руководитель проекта не может своевременно реагировать на эти события.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍5
День 2111. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 31. Команде нужна гибкость в отношении хотя бы одного из измерений: масштаба, плана, бюджета, персонала или качества. Окончание
Начало
Соглашения о приоритетах
Важным аспектом является необходимость согласования относительных приоритетов измерений между командой, клиентами и руководством в начале проекта. Например, график часто представляется как ограничение, хотя на самом деле это драйвер. Чтобы понять разницу, задайте вопрос: «Я понимаю, что вы хотите, чтобы решение было готово к 30 июня. Но что случится, если оно будет готово только к концу июля?» Если в ответ вы услышите, что тогда решение утратит свою ценность или последуют штрафные санкции, то график действительно является ограничением. Но если вам ответят: «Мы бы хотели получить решение к 30 июня, но можем потерпеть и до 31 июля, если придётся», — график является драйвером.
Диаграмма гибкости
Диаграмма Кивиата (на картинке), также называемая лепестковой или радиально-круговой, помогает визуально представить степень гибкости по всем пяти измерениям. Она имеет несколько осей, исходящих из общей точки. Все оси имеют одинаковую длину и приведены к единому масштабу. В данном случае каждая ось представляет степень гибкости руководителя проекта в соответствующем измерении.
Для измерения гибкости используется относительная шкала от 0 до 10. Нулевая точка — начало координат — соответствует отсутствию гибкости, то есть данное измерение является ограничением. Точки в диапазоне от нуля до примерно 4 соответствуют драйверу, то есть в данном измерении проект имеет небольшую гибкость. Любая другая точка с более высоким значением соответствует степени свободы, когда данное измерение предлагает больше гибкости. Соединение точек, нанесенных на оси пяти измерений, дает пятиугольник неправильной формы. Диаграммы гибкости для разных проектов будут иметь разные формы.
В примере диаграммы на картинке время на разработку было ограничено, т.к. компонент должен был быть готов до того, как несколько других приложений, разрабатываемых параллельно, смогли бы его использовать. Поэтому измерение «план» получило нулевую гибкость. Надежность и правильность компонента были очень важны; соответственно, качество было важным фактором успеха. Поэтому «качество» оценено в 2 единицы гибкости. К контрольному сроку должен был быть реализован базовый набор возможностей, затем функциональность постепенно могла расширяться. Поэтому «масштаб» получил оценку гибкости — 4. Была значительная свобода в отношении бюджета и персонала, главное, чтобы все было сделано вовремя. Поэтому эти измерения получили высокие значения гибкости и были оценены как степени свободы.
Диаграмма гибкости не является ни точным, ни количественным инструментом. Форма пятиугольника визуально выделяет важные аспекты проекта, но мы не пытаемся вычислить площадь пятиугольника или что-то в этом роде. Однако размер пятиугольника дает приблизительное представление о том, с какой гибкостью имеет дело руководитель проекта. Маленький пятиугольник означает, что проект имеет несколько ограничений и драйверов, что усложняет путь к успеху.
Практическое применение анализа пяти измерений
Этот пятимерный анализ может помочь руководителю проекта решить, как лучше реагировать на меняющиеся условия или реалии проекта, чтобы достичь основных целей. Предположим, что персонал является ограничением. Если понадобится включить новые требования, то единственные параметры, которые потенциально могут измениться, — это масштаб, качество, бюджет и план. В нашем мире за все приходится платить. Диаграмма гибкости может облегчить обсуждение и поиск решения, как действовать дальше. Можно ли сократить или отложить реализацию каких-нибудь других возможностей? Можно ли добавить в процесс разработки ещё одну итерацию и отодвинуть сроки? Должен ли продукт работать идеально в первый же день? Рассмотрение этих пяти аспектов помогает понять приоритеты проекта, а не предполагать, что каждый из них жизненно важен и не подлежит обсуждению.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
Уроки 50 Лет Разработки ПО
Урок 31. Команде нужна гибкость в отношении хотя бы одного из измерений: масштаба, плана, бюджета, персонала или качества. Окончание
Начало
Соглашения о приоритетах
Важным аспектом является необходимость согласования относительных приоритетов измерений между командой, клиентами и руководством в начале проекта. Например, график часто представляется как ограничение, хотя на самом деле это драйвер. Чтобы понять разницу, задайте вопрос: «Я понимаю, что вы хотите, чтобы решение было готово к 30 июня. Но что случится, если оно будет готово только к концу июля?» Если в ответ вы услышите, что тогда решение утратит свою ценность или последуют штрафные санкции, то график действительно является ограничением. Но если вам ответят: «Мы бы хотели получить решение к 30 июня, но можем потерпеть и до 31 июля, если придётся», — график является драйвером.
Диаграмма гибкости
Диаграмма Кивиата (на картинке), также называемая лепестковой или радиально-круговой, помогает визуально представить степень гибкости по всем пяти измерениям. Она имеет несколько осей, исходящих из общей точки. Все оси имеют одинаковую длину и приведены к единому масштабу. В данном случае каждая ось представляет степень гибкости руководителя проекта в соответствующем измерении.
Для измерения гибкости используется относительная шкала от 0 до 10. Нулевая точка — начало координат — соответствует отсутствию гибкости, то есть данное измерение является ограничением. Точки в диапазоне от нуля до примерно 4 соответствуют драйверу, то есть в данном измерении проект имеет небольшую гибкость. Любая другая точка с более высоким значением соответствует степени свободы, когда данное измерение предлагает больше гибкости. Соединение точек, нанесенных на оси пяти измерений, дает пятиугольник неправильной формы. Диаграммы гибкости для разных проектов будут иметь разные формы.
В примере диаграммы на картинке время на разработку было ограничено, т.к. компонент должен был быть готов до того, как несколько других приложений, разрабатываемых параллельно, смогли бы его использовать. Поэтому измерение «план» получило нулевую гибкость. Надежность и правильность компонента были очень важны; соответственно, качество было важным фактором успеха. Поэтому «качество» оценено в 2 единицы гибкости. К контрольному сроку должен был быть реализован базовый набор возможностей, затем функциональность постепенно могла расширяться. Поэтому «масштаб» получил оценку гибкости — 4. Была значительная свобода в отношении бюджета и персонала, главное, чтобы все было сделано вовремя. Поэтому эти измерения получили высокие значения гибкости и были оценены как степени свободы.
Диаграмма гибкости не является ни точным, ни количественным инструментом. Форма пятиугольника визуально выделяет важные аспекты проекта, но мы не пытаемся вычислить площадь пятиугольника или что-то в этом роде. Однако размер пятиугольника дает приблизительное представление о том, с какой гибкостью имеет дело руководитель проекта. Маленький пятиугольник означает, что проект имеет несколько ограничений и драйверов, что усложняет путь к успеху.
Практическое применение анализа пяти измерений
Этот пятимерный анализ может помочь руководителю проекта решить, как лучше реагировать на меняющиеся условия или реалии проекта, чтобы достичь основных целей. Предположим, что персонал является ограничением. Если понадобится включить новые требования, то единственные параметры, которые потенциально могут измениться, — это масштаб, качество, бюджет и план. В нашем мире за все приходится платить. Диаграмма гибкости может облегчить обсуждение и поиск решения, как действовать дальше. Можно ли сократить или отложить реализацию каких-нибудь других возможностей? Можно ли добавить в процесс разработки ещё одну итерацию и отодвинуть сроки? Должен ли продукт работать идеально в первый же день? Рассмотрение этих пяти аспектов помогает понять приоритеты проекта, а не предполагать, что каждый из них жизненно важен и не подлежит обсуждению.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍7