.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1905. #ЗаметкиНаПолях
8 Способов Задать URL в Приложении
ASP.NET Core. Продолжение
Начало
Способы 1-3
Способы 4-6

7. launchSettings.json
Помимо файла appsettings.json, большинство шаблонов проектов .NET также включают файл launchSettings.json в папке Properties. Этот файл не добавляет значений в конфигурацию, а содержит различные профили для запуска разрабатываемого приложения в dotnet run. Он управляет выпадающим списком отладки в Visual Studio.

Типичный файл содержит определения для запуска профиля из командной строки и из IIS Express:
{

"iisSettings": {

"iisExpress": {
// URL для профиля IIS Express
"applicationUrl": "https://localhost:49412",
"sslPort": 44381
}
},
"profiles": {
// профиль только HTTP
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5005",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
// профиль HTTP и HTTPS
"https": {
// аналогично "http"

},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Как видите, launchSettings.json также предоставляет простой способ установки дополнительных переменных окружения в environmentVariables.
Когда вы запускаете приложение из командной строки с помощью dotnet run, оно будет использовать свойства applicationUrl из команды Project: https://localhost:5005 в профиле http выше. В IISExpress - будет использовать applicationUrl из узла iisSettings.iisExpress: https://localhost:49412.

Этот файл — самый простой способ настроить среду при локальной разработке. Чтобы запустить приложение без использования launchSettings.json, нужно добавить параметр:
dotnet run --no-launch-profile

Окончание следует…

Источник:
https://andrewlock.net/8-ways-to-set-the-urls-for-an-aspnetcore-app/
👍11
День 1906. #ЗаметкиНаПолях
8 Способов Задать URL в Приложении
ASP.NET Core. Окончание
Начало
Способы 1-3
Способы 4-6
Способ 7

8. KestrelServerOptions.Listen()
Kestrel настроен по умолчанию в большинстве приложений ASP.NET Core. При желании вы можете настроить конечные точки для Kestrel, если вам требуется тонкая настройка, например, сертификатов HTTPS, протоколов SSL/TLS и комбинаций шифров, а также конфигураций SNI. Доступно множество вариантов конфигурации (см. документацию).

Например, вы можете использовать функции Listen(), предоставляемые KestrelServerOptions, следующим образом:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(opts =>
{
opts.Listen(IPAddress.Loopback, port: 5002);
opts.ListenAnyIP(5003);
opts.ListenLocalhost(5004,
listenOptions => listenOptions.UseHttps());
opts.ListenAnyIP(5005,
listenOptions =>
{
listenOptions.UseHttps("testCert.pfx", "testPassword");
});
});

var app = builder.Build();

Эта конфигурация устанавливает прослушивание по нескольким адресам. Когда вы так устанавливаете URL для Kestrel таким образом, они переопределяют значения из конфигурации и вы получите предупреждение об этом в логах.

Но и конфигурацию Kestrel можно привязать с помощью IConfiguration, аналогично urls или http_ports. Например, приведенную выше конфигурацию Kestrel можно настроить в appsettings.json:
{
"Kestrel": {
"Endpoints": {
"HttpLoopback": {
"Url": "https://localhost:5002"
},
"HttpAny": {
"Url": "https://*:5003"
},
"HttpsDefaultCert": {
"Url": "https://localhost:5004"
},
"HttpsInlineCertFile": {
"Url": "https://*:5005",
"Certificate": {
"Path": "testCert.pfx",
"Password": "testPassword"
}
}
}
}
}

Это позволяет полностью настроить привязку вашего приложения и конфигурацию сертификата HTTPS из IConfiguration. Т.е. вы можете воспользоваться безопасным секретным хранилищем, например Key Vault, для хранения сертификатов и паролей сертификатов.
Если вы используете конфигурацию appsettings.json, указанную выше, не нужно вызывать ConfigureKestrel().

Источник: https://andrewlock.net/8-ways-to-set-the-urls-for-an-aspnetcore-app/
👍7
День 1907. #Карьера
Просить о Помощи — Основной Навык Разработчика

В одном из форумов Reddit был пост, где автор рассказал, что испытывает трудности в работе. Он застрял с проблемой уже на 3 недели и беспокоился, что скажет начальник, когда узнает. Он переживал, что взялся за что-то слишком сложное и не довёл до конца.

Как мы учим детей становиться взрослыми? Мы пытаемся помочь им стать теми, кем, по нашему мнению, они могут быть. Не нужно специально подталкивать их становиться инженерами, учёными или врачами. Задача родителя - научить их быть вдумчивыми, организованными и, конечно же, уметь просить о помощи.

Индивидуалисты часто теряют понимание, что мы не можем существовать сами по себе. Мне потребовалось много времени, чтобы усвоить это. Я хотел всего добиваться сам. Мне не приходило в голову, что я добьюсь большего, если другие люди помогут мне.

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

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

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

Иногда вы получаете задачу, которая не соответствует вашим ограничениям: времени, ресурсам, часто знаниям и опыту. Когда вы берётесь за проблему, вы не знаете, каким будет решение. Вы тыкаетесь туда и сюда, находите её края и заполняете пробелы, пока не получите чёткое представление, на какую головоломку вы смотрите. Чем меньше у вас опыта в конкретной области, тем дольше это займёт. Такова природа обучения по ходу дела, и так мы развиваемся.

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

Вам придётся установить ограничения на то, сколько времени вы потратите, пытаясь добиться успеха самостоятельно, прежде чем обратиться к коллегам с просьбой о помощи. Это один из основных принципов Agile-разработки: блокирующие факторы следует выявлять как можно раньше, чтобы команда могла вместе с ними справиться. В компании мы действуем как пул знаний о бизнесе и коде. Если ответа нет в вашей голове, он, скорее всего, есть в чьей-то другой. Индивидуалист часто проводит слишком много времени в тупике, и это отрицательно влияет как на продукт, над которым он работает, так и на бизнес, который платит ему зарплату. Подходящее время для обращения за помощью – раньше, а не позже.

В жизни нас часто приучают к индивидуальному успеху. В школе оценивают не успехи класса, а ваши индивидуальные. Мы учим детей добиваться успеха как личности. Но это плохо отражает то, как на самом деле устроен мир. Я никто без поддержки жены, друзей и группы, в которой работаю. Успешное ПО — это не о вас и не о головоломках, которые вы можете решить. Просить о помощи – ключевой навык. Он может помочь гарантировать, что продукт, который вы создаёте, увидит свет, и, что вы продолжите прогрессировать как разработчик.

Источник: https://www.ramijames.com/thoughts/asking-for-help-is-a-core-skill
👍33
День 1908. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 5. Разработка требований — итеративный процесс
Тщательное изучение требований важно перед тем, как начнётся написание кода. Но в процессе разработки ПО для других людей часто могут возникать вопросы и обнаруживаться пробелы в знаниях. Приходится возвращаться к клиентам, чтобы прояснить задачи, освежить информацию в памяти и закрыть пробелы. Клиенты не всегда рады видеть вас снова, но разработка требований — итеративный и поэтапный процесс.

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

Эффективная разработка требований подразумевает постепенное уточнение требований и их деталей. Получите достаточно точную информацию о требованиях, прежде чем создавать какую-либо часть продукта, иначе придётся создавать её заново. Вот примерный процесс:
1. Разработайте предварительный список пользовательских требований. Узнайте достаточное количество подробностей о каждом из них, чтобы понять их объём и относительную важность.
2. Распределите требования по циклам разработки в зависимости от их приоритета.
3. Продолжите выявлять и уточнять детали тех требований, реализация которых запланирована в ближайшем цикле разработки.
4. Перераспределите приоритеты, добавляя любые новые требования, и двигайтесь вниз по списку приоритетов по мере разработки.
5. Вернитесь к шагу 2 и повторите.

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

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

Мы можем использовать разные методы для выявления некоторых возникающих требований, например, создать несколько представлений требований.
1) Вместо записи сценариев использования и историй нарисуйте несколько картинок. Визуальные модели описывают требования на более высоком уровне абстракции, позволяют отвлечься от деталей и увидеть более широкую картину рабочего процесса и взаимосвязей.
2) Тесты на ранней стадии помогут обнаружить неясности и ошибки в требованиях, либо отсутствующие требования, например необработанные исключения. Можно заметить, что некоторые требования не нужны, если нельзя придумать тесты, требующие их реализации. Это легло в основу разработки через тестирование.
3) Благодаря прототипам пользователи получают нечто осязаемое. Инкрементное прототипирование ускоряет обсуждение требований и помогает пользователям находить ошибки и упущения в требованиях до того, как на создание продукта будет затрачено слишком много усилий.

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

Итого
Формирование полезного набора требований любого рода требует терпения для переосмысления, повторных попыток и накопления знаний, необходимых для создания правильного продукта.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍10
Паттерн Репозиторий отделяет код приложения от кода ...
#Quiz #DesignPatterns
Anonymous Quiz
93%
доступа к данным
1%
авторизации
5%
логики представления
1%
ведения жунрала
👍1
День 1909. #ЧтоНовенького
OpenTelemetry SDK от Elastic с Открытым Кодом для .NET
Elastic объявили об выпуске альфа-версии пакета OpenTelemetry SDK для .NET. Он настраивает сбор трассировки, метрик и логов, а также гарантирует, что экспортёр OTLP включен по умолчанию. Проект имеет открытый исходный код.

Чтобы начать работу с Elastic OpenTelemetry, необходимо добавить в проект ссылку на пакет NuGet Elastic OpenTelemetry:
<PackageReference Include="Elastic.OpenTelemetry" Version="1.0.0-alpha.1" />

Пакет добавляет транзитивную зависимость от OpenTelemetry SDK. Поэтому нет необходимости добавлять в проект OpenTelemetry SDK.

Чтобы воспользоваться преимуществами инструментария OpenTelemetry SDK для ASP.NET Core, разработчикам также следует добавить пакет NuGet OpenTelemetry.Instrumentation.AspNetCore. Он включает поддержку сбора инструментов (трассировок и метрик) для запросов, обрабатываемых конечными точками ASP.NET Core. Пакет SDK OpenTelemetry содержит методы расширения IServiceCollection для включения и настройки поставщиков трассировки, метрик и логов. Elastic переопределяет регистрацию SDK по умолчанию.

Как минимум необходимо настроить две переменные окружения: OTEL_EXPORTER_OTLP_ENDPOINT и OTEL_EXPORTER_OTLP_HEADERS. Elastic автоматически позволит экспортировать сигналы телеметрии с помощью экспортёра OTLP. Для экспортёра OTLP требуется настроить хотя бы одну конечную точку.

Текущая альфа-версия дистрибутива ограничена в возможностях. Elastic сообщает, что их цель — оценить пригодность дизайна API и простоту использования. Однако они подчёркивают, что он подходит не для всех сценариев применения, поэтому не рекомендуют его использовать в производстве.

Код полностью открыт и доступен на GitHub.

Источник: https://www.infoq.com/news/2024/04/elastics-open-telemetry-net/
👍13
День 1910. #ВопросыНаСобеседовании #ASP.NET #Architecture
Самые часто задаваемые вопросы на собеседовании по C#

31. Как бы вы подошли к обработке ошибок и отладке в распределённом приложении .NET Core с несколькими микросервисами?

Обработка ошибок и отладка в таком приложении может оказаться сложной задачей. Можно использовать несколько стратегий:

1. Централизованные логи.
Все микросервисы должны отправлять свои логи (естественно, структурированные) в централизованное место, где они сопоставляются и индексируются. Это позволит искать и визуализировать логи всех сервисов в одном месте.

2. Использование идентификаторов корреляции.
Идентификаторы корреляции (CorrelationId) — это уникальные идентификаторы, присваиваемые запросу. Затем этот идентификатор передаётся всем сервисам, участвующим в обработке этого запроса. Это позволяет отслеживать всю цепочку запросов и ответов.

3. Проверки работоспособности.
Проверки работоспособности можно реализовать для мониторинга состояния микросервисов. Они могут сообщать такие показатели, как время безотказной работы, загрузка ЦП, использование памяти и т. д.

4. Промежуточное ПО для обработки исключений.
В архитектуре микросервисов ошибки должны обрабатываться на уровне сервиса. Каждый сервис должен обрабатывать свои собственные исключения и возвращать подходящее сообщение об ошибке или код ответа.
Можно создать промежуточное ПО, которое обрабатывает каждый запрос микросервиса. Если во время выполнения запроса возникает исключение, это промежуточное ПО перехватит исключение и ответит подходящим сообщением об ошибке.
См. также «Улучшенная Обработка Исключений в ASP.NET Core 8.»

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

Источник: https://dev.to/bytehide/net-core-interview-question-answers-4bc1
👍21
День 1911. #ЗаметкиНаПолях
Реализуем Пессимистическую Блокировку в EF Core. Начало

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

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

Вот упрощённый фрагмент кода, иллюстрирующий проблему с билетами:
public async Task Checkout(ShopCart cart)
{
await using var trans = await
context.BeginTransactionAsync();

var order = new Order();
foreach (CartItem item in cart.Items)
{
// Проверяем доступность билета
// Что, если два запроса проверят его в одно время?
var ticket = await ticketRepo.GetAsync(
item.TicketId);

ticket.UpdateQuantity(item.Quantity);
order.Add(ticket, item.Quantity, item.Price);
}
orderRepo.Insert(order);

await context.SaveChangesAsync();
await trans.CommitAsync();

}

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

Поскольку EF Core не предлагает пессимистическую блокировку напрямую, немного углубимся в старый добрый SQL. Заменим вызов GetAsync для получения билета на GetWithLockAsync:
public async Task<Ticket> GetWithLockAsync(Guid id)
{
return await context
.Tickets
.FromSql(
$@"SELECT id, event_id, price, quantity
FROM tickets WHERE id = {id}
FOR UPDATE NOWAIT")
.SingleAsync();
}

FOR UPDATE NOWAIT - суть пессимистической блокировки в PostgreSQL (и Oracle). Он сообщает базе данных: «Захвати эту строку, заблокируй её для меня, и, если она уже заблокирована, прямо сейчас выдай ошибку».

Вызов GetWithLockAsync нужно обернуть в try-catch, чтобы корректно обрабатывать сбои блокировки, либо повторяя попытку, либо уведомляя пользователя.

Поскольку в EF Core нет встроенного способа добавления подсказок к запросам, приходится писать чистый SQL-запрос. Мы можем использовать оператор SELECT FOR UPDATE, чтобы получить блокировку на уровне выбранных строк. Любые конкурирующие транзакции будут заблокированы до тех пор, пока текущая транзакция не снимет блокировку. Это очень простой способ реализовать пессимистическую блокировку.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/a-clever-way-to-implement-pessimistic-locking-in-ef-core
👍40
День 1912. #ЗаметкиНаПолях
Реализуем Пессимистическую Блокировку в EF Core. Окончание

Начало

Варианты блокировки и когда их использовать
Чтобы операция не ждала, пока другие транзакции освободят заблокированные строки, вы можете объединить FOR UPDATE с:
- NO WAIT — вместо ожидания снятия блокировки, сообщает об ошибке, если строку невозможно заблокировать.
- SKIP LOCKED — пропускает любые выбранные строки, которые нельзя заблокировать. Заметьте, что в этом случае вы будете получать противоречивые результаты из БД. Однако это может быть полезно, чтобы избежать конфликта блокировок, когда несколько потребителей обращаются к таблице, похожей на очередь. Реализация паттерна Outbox является отличным примером.

В SQL Server для аналогичного эффекта можно использовать подсказку запроса WITH (UPDLOCK, READPAST). Однако SQL Server блокирует все строки, которые ему необходимо прочитать, чтобы получить нужную. Таким образом, если не определить индекс для прямого доступа к строке, все предшествующие строки будут заблокированы. Допустим, есть таблица TBL с полем id. Вы хотите заблокировать строку с id=10. Необходимо определить индекс для id (или других полей, по которым выбираете):
CREATE INDEX TBLINDEX ON TBL ( id );

А затем запросить блокировку только тех нужных строк:
SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10;


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

Когда транзакция начинается на уровне изоляции Serializable, БД блокирует все данные, к которым может получить доступ транзакция. Эти блокировки удерживаются до тех пор, пока вся транзакция не будет зафиксирована или отменена. Любая другая транзакция, пытающаяся получить доступ к заблокированным данным, будет заблокирована до тех пор, пока первая транзакция не снимет блокировки.

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

Пессимистическая блокировка с помощью SELECT FOR UPDATE явно блокирует определённые строки, которые необходимо изменить. Другие транзакции, пытающиеся получить доступ к заблокированным строкам, блокируются до тех пор, пока блокировка не будет снята. Т.к. вы блокируете меньше ресурсов, вероятность взаимоблокировок снижается.

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

Итого
И сериализуемые транзакции, и пессимистическая блокировка с помощью SELECT FOR UPDATE — отличные варианты обеспечения согласованности данных. При выборе учитывайте требуемый уровень изоляции, потенциальное влияние на производительность и вероятность взаимоблокировок.

Источник: https://www.milanjovanovic.tech/blog/a-clever-way-to-implement-pessimistic-locking-in-ef-core
👍18
День 1913. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 6. Agile-требования не отличаются от других. Начало

Многие компании, занимающиеся разработкой ПО, используют методы Agile. Бизнес-аналитики и владельцы продуктов иногда применяют термин «Agile-требования». Но они не отличаются от требований в проектах с другим подходом.

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

1. Роли и обязанности
В состав большинства традиционных проектных групп входит один или несколько бизнес-аналитиков, которые отвечают за работу по выявлению требований к проекту, анализу, спецификации, проверке и управлению или возглавляют её. Во многих Agile-проектах отсутствует официальная должность бизнес-аналитика. Разработка требований — совместный процесс, в котором участвуют владелец продукта, представители пользователей и другие заинтересованные стороны. Но за полноту информации в историях ответственность несут разработчики, а не бизнес-аналитики.

2. Терминология
Традиционные подходы обычно основаны на вариантах использования и функциональных требованиях. В Agile используют пользовательские истории, обобщенные описания функций или задач, разбитые на более мелкие пользовательские истории, приёмочные тесты и пожелания клиентов. Но это те же самые знания о требованиях, просто они по-другому называются.

3. Детальность документации
Agile-методы полагаются на доступность документации в нужный момент. Благодаря тесному сотрудничеству клиентов с разработчиками в Agile-проектах требования могут содержать меньше деталей, чем в традиционных проектах. Заинтересованные стороны могут сообщить все необходимые детали, когда это действительно потребуется, на встречах и в соответствующей документации. Некоторые пользовательские истории могут содержать мало подробностей, а сложные или важные функции могут прорабатываться более детально. Но не стоит чрезмерно полагаться только на устное общение. Каждой проектной группе необходимо создать достаточный объём документации, позволяющей решать задачи поддержки и совершенствования системы, но при этом не тратить время на запись информации, которую никто не будет использовать.

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

Окончание следует…

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍3
Разожжём вечный спор вечером в пятницу с подачи Ника)))
Не учитывая кодстайл компании, какой вариант лично вам больше по душе (см. картинку в первом комментарии)?
Anonymous Poll
32%
1
11%
2
9%
3
49%
4
👍2
День 1914. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 6. Agile-требования не отличаются от других. Окончание

Начало

5. Итоговое оформление требований
В общем случае пользовательские истории похожи на сценарии использования. Разница лишь в том, насколько тщательно вы их детализируете и записываете ли информацию. В традиционном проекте бизнес-аналитик может разработать набор функциональных требований на основе сценариев использования. Agile-команды конкретизируют каждую пользовательскую историю, определяя критерии приёмки и тесты, которые покажут, правильно ли разработчики реализовали её.
Функциональные требования и соответствующие им тесты — альтернативные варианты представления одной и той же информации. Требования определяют, что создавать; тесты описывают, как выяснить, демонстрирует ли система ожидаемое поведение. Наилучшие результаты получаются, когда разные люди пишут требования и тесты на основе одного и того же источника информации, например сценария использования.
Можно прибегнуть к альтернативной стратегии — записывать детали пользовательской истории в форме приёмочных тестов, которые затем проверяет тестировщик. Когда создаётся несколько представлений требований, нестыковки между ними помогают выявить проблемы. Если вы создаёте только одно представление, то независимо от выбранного метода будете вынуждены доверять его точности.

6. Расстановка приоритетов
Расстановка приоритетов учитывает относительную ценность каждого требования для клиента по сравнению с усилиями, риском и стоимостью его реализации. Традиционные проекты могут определить приоритеты требований на раннем этапе и в дальнейшем почти не пересматривать их. В Agile-проекте задачи приоритизируются непрерывно. Вы должны постоянно выбирать, что добавить в ближайшие итерации, а что может быть вообще исключено. На самом деле все команды, а не только те, кто практикует Agile, должны управлять оставшимися задачами, чтобы как можно быстрее передать клиенту максимально ценный для него продукт.

Есть ли разница?
Большинству клиентов неинтересно, как создаются приложения. Они хотят, чтобы продукты отвечали их потребностям, были эффективными, удобными и легко расширяемыми, а также удовлетворяли другие ожидания в отношении качества. Большинство методов разработки требований и управления ими в традиционных проектах в равной степени применимы и к Agile-проектам. Любая команда должна адаптировать методы разработки, чтобы они соответствовали их целям, культуре, среде и ограничениям.

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

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

Успешный переход требует развития нового мышления. Роль бизнес-аналитика в Agile-проекте заключается не столько в том, чтобы заранее определить, что будет сделано, сколько в постоянном пересмотре условий в процессе разработки. Происходит постоянная оценка того, что должна или не должна делать команда, — компромиссов, необходимых для максимизации доставляемой ценности.

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
День 1915. #Оффтоп
Сегодня порекомендую очередное видео с ютуба, чтобы расслабиться после 6-дневной рабочей недели.
Мне всё больше и больше нравится этот чувак, ThePrimeTime)))

На этот раз он читает статью из 2016 года, где автор рассказывает о своей маме, программистке на COBOL для мейнфреймов. Рассказ об эпохе раннего программирования, где разработчики сталкивались с такими задачами, о которых нам не приходится и думать сейчас. Проблема в том, что до сих пор (!!!) есть крупные компании (например, банки), которые в своё время создали себе огромные системы на COBOL, и до сих пор поддерживают их за огромные деньги, потому что переписать это с нуля либо в принципе невозможно, либо стоит нереально дорого.

Мудрость дня: «То, что вы сейчас пишете либо будет выкинуто в течение полугода, либо останется жить на десятилетия.»

В общем, приятного просмотра https://youtu.be/qNytWcW38us.
👍6
День 1916. #ЧтоНовенького
Новый Формат Файлов Решений в Visual Studio
В превью версии Visual Studio 17.10 добавлен новый формат файла решения с расширением файла .slnx.

На протяжении десятилетий файл решения .sln основывался на собственном формате файла от Microsoft, многословном, трудночитаемом с кучей GUID. Вот пример:
# Visual Studio Version 17
VisualStudioVersion = 17.10.34814.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{F95781B3-A973-4D19-9585-974DA143E6A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{6EC5A7A1-CA0C-42AC-BB97-19EF01B3C68C}"
EndProject

Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {85AD0F43-BB53-4378-94F7-8A7DF5A990D5}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{06F979AA-0ABF-4B86-91D5-7E32B7F2A081} = {6C6714E6-D117-405B-9FFD-C4AF0643EA29}
{F95781B3-A973-4D19-9585-974DA143E6A1} = {6EC5A7A1-CA0C-42AC-BB97-19EF01B3C68C}
EndGlobalSection

EndGlobal

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

Оптимизированный формат файлов .slnx – отличное изменение:
<Solution>
<Folder Name="/Test/">
<Project Path="UnitTests\UnitTests.csproj" Type="Classic C#" />
</Folder>
<Folder Name="/Application/">
<Project Path="App\App.csproj" Type="Classic C#" />
</Folder>
</Solution>

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

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

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

Чтобы попробовать его в Visual Studio 2022 17.10 превью 3 или более поздней версии, в меню Tools > Options > Preview Features (Инструменты > Параметры > Предварительные функции) найдите Use Solution File Persistence Model. Затем в меню File > Save YourSolution.sln As… (Файл > Сохранить YourSolution.sln как…) выберите тип файла .slnx. Заметьте, что этот тип файлов пока не связан с Visual Studio в Windows, поэтому привязку придётся настроить вручную.

Источник: https://blog.ndepend.com/slnx-the-new-net-solution-xml-file-format/
👍24👎1
День 1917. #ЗаметкиНаПолях
Обмен Сообщениями с Помощью MassTransit

Создание распределённых приложений – дело непростое. Есть ряд потенциальных проблем, которые необходимо учитывать. Что делать, если в сети произошел сбой или сервис неожиданно прекратил работу? Способ взаимодействия частей распределённой системы становится критически важным.

Синхронная связь, когда сервисы напрямую обращаются друг к другу, хрупка. Она создаёт тесную связь, делая всё приложение уязвимым для отдельных точек отказа. Мы можем использовать распределённый обмен сообщениями. Одна из известных утилит для этого в мире .NET – MassTransit.

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

Достоинства

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

Недостатки
1. Задержка: накладные расходы на обмен сообщениями добавляют дополнительную задержку.
2. Сложность: внедрение системы обмена сообщениями и управление дополнительной инфраструктурой могут повысить сложность проекта.

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

Например, у нас система обработки заказов и необходимо получить статус заказа из сервиса управления заказами. С помощью MassTransit создадим клиента, реализующего IRequestClient<T>, для инициирования запроса:
public class RequestController :
Controller
{
IRequestClient<StatusRequest> _client;


public async Task<IActionResult>
Get(string id, CancellationToken ct)
{
var response = await
_client.GetResponse<StatusResponse>(
new { id }, ct);

return Ok(response.Message);
}
}

Он отправит на шину сообщение GetOrderStatusRequest:
public record StatusRequest(string OrderId);

И будет ожидать ответа:
public record StatusResponse(
string OrderId,
short StatusCode,
string StatusText
}

В сервисе управления заказами ответчик (IConsumer<T>) будет прослушивать сообщения StatusRequest. Он получает запрос, возможно запрашивает БД и отправляет сообщение StatusResponse обратно на шину. Клиент будет ждать этого ответа и затем сможет обработать его соответствующим образом:
public class StatusRequestConsumer :
IConsumer<StatusRequest>
{
public async Task Consume(
ConsumeContext<StatusRequest> ctx)
{

await ctx
.RespondAsync<StatusResponse>(new
{
// задаём свойства ответа
});
}
}

Полный код примера есть в документации MassTransit.

Итого
Шаблон запрос-ответ — мощный инструмент при обмене сообщениями между сервисами. MassTransit значительно упрощает реализацию, гарантируя надежную доставку запросов и ответов.
Мы можем использовать запрос-ответ для реализации связи между модулями в модульном монолите. Однако не стоит доводить это до крайности, иначе система может страдать от увеличения задержки.

Источник: https://www.milanjovanovic.tech/blog/request-response-messaging-pattern-with-masstransit
👍10
День 1918. #Шпаргалка
Шпаргалка по Форматированию Дат

Структура System.DateTime, доступная только для чтения, определённая в библиотеке базовых классов .NET (BCL), является ключом к обработке дат и времени в C#. Отформатировать дату и время для вывода можно, указав формат в методе ToString("…"). Например:
csharp 
Console.WriteLine(
DateTime.Now.ToString("ddd, dd MMM yyyy h:mm"));

Выведет:
Wed 01 May 2024 8:01

Ниже приведены краткие пояснения некоторых часто используемых спецификаторов формата:
d – день месяца от 1 до 31.
dd – день месяца от 01 до 31.
ddd - аббревиатура дня недели (Mon, Tue, …).
dddd - полный день недели (Monday, Tuesday, …).
h - 12-часовой формат часов.
hh - 12-часовой формат часов с ведущим 0.
H - 24-часовой формат часов.
HH - 24-часовой формат часов с ведущим 0.
m - минуты.
mm - минуты с ведущим 0.
M - номер месяца.
MM - номер месяца с ведущим 0.
MMM - краткое имя месяца (Dec).
MMMM - полное имя месяца (December).
s - секунды.
ss - секунды с ведущим 0.
f - десятые доли секунды.
ff - сотые доли секунды.
t - краткое AM/PM (A или P).
tt - AM/PM (AM или PM).
yyyy - год.
z - часов от UTC (+3).
zz - часов от UTC с ведущим 0 (+03).
zzz - часов и минут от UTC (+03:00).
K - аналогично zzz, но адаптирует вывод в зависимости от типа времени (местное, UTC или не указано), поэтому может не вывести ничего.

Символы FHKMdfghmstyz%:/\"' зарезервированы в строке формата даты и времени. Они интерпретируются как символы форматирования, за исключением случаев, когда используются внутри одинарных или двойных кавычек, либо экранированы \. Все остальные символы последовательно интерпретируются как символьные литералы и остаются неизменными при выводе во время операции форматирования.

Также есть несколько стандартных спецификаторов формата, каждый из которых представлен одной буквой
* - зависит от культуры, ниже примеры для культуры "en-US":
"d" - короткая дата* (05/01/2024)
"D" - полная дата* (Wednesday, May 01, 2024)
"f" - полная дата, короткое время* (Wednesday, May 01, 2024 8:01 AM)
"F" - полная дата, полное время* (Wednesday, May 01, 2024 8:01:03 AM)
"g" - короткая дата, короткое время* (05/01/2024 8:01 AM)
"G" - короткая дата, полное время* (05/01/2024 8:01:03 AM)
"m" или "M" - день/месяц* (May 1)
"o" или "O" - полные дата/время по ISO 8601 (2024-05-01T08:01:03.7300000+04:00)
"s" - сортируемые дата/время по ISO 8601 (2024-05-01T08:03:01)
"r" или "R" - дата/время по RFC1123 (Wed, 01 May 2024 08:01:03 GMT)
"t" - короткое время* (8:01 AM)
"T" - длинное время* (8:01:03 AM)
"u" - универсальный сортируемый формат даты/времени (2024-05-01 05:01:03Z)
"U" - универсальный полный формат даты/времени (Wednesday, May 01, 2024 5:01:03 AM)
"y" или "Y" - месяц/год* (May 2024)
Любой другой символ приведёт к исключению FormatException во время выполнения.

Источник: https://blog.ndepend.com/csharp-datetime-format/
👍34
День 1919. #ЧтоНовенького
Вышла Бета-Версия pl/dotnet

Пакет pl/dotnet добавляет в PostgreSQL полную поддержку C# и F#.

В первой открытой для публики бета-версии (версия 0.99) поддерживаются все операции PL: функции, процедуры, DO, SPI, триггеры, записи, SRF, OUT/INOUT, табличные функции и т. д. Также поддерживаются 40 из 46 стандартных пользовательских типов. Пакет полностью совместим с NPGSQL, а SPI предоставляется через API NPGSQL для максимальной совместимости. Это 100% бесплатное ПО под лицензией PostgreSQL.

pl/dotnet предоставляет вам всю мощь C# и F# в процедурах, функциях и триггерах PostgreSQL. Например:
C#:
CREATE OR REPLACE FUNCTION dynamic_record_generator_srf(lim INT8)  
RETURNS SETOF record
AS $$
upperLimit = lim.HasValue ? lim : System.Int32.MaxValue;
for(long i = 0; i < upperLimit; i++)
{
yield return new object?[] {
i, $"Number is {i}" };
}
$$ LANGUAGE plcsharp;

F#
CREATE OR REPLACE FUNCTION dynamic_record_generator_srf_fsharp(lim INT8)  
RETURNS SETOF record
AS $$
let upperLimit = Option.defaultValue (int64 System.Int32.MaxValue) lim
seq { for i in 0L .. upperLimit - 1L do yield [| box i; $"Number is {i}" |] }
$$ LANGUAGE plfsharp;


Поддержка функций
Поддерживаются все типы функций SQL:
- обычные процедуры и функции;
- полная поддержка функций триггера: аргументы триггера, старая/новая строка, перезапись строки (где это разрешено) и вся стандартная информация о триггере;
- функции, возвращающие множество, сопоставлены с итераторами в C# и последовательностями в F#;
- табличные функции, а также функции, возвращающие записи или наборы записей;
- полная поддержка функций IN/OUT/INOUT.

Поддержка типов данных
Поддерживаются 40 типов PostgreSQL, все из которых сопоставлены со стандартными типами dotnet NPGSQL. Единственными исключениями являются типы multirange, enum и struct, которые планируют добавить в будущем. Все типы данных допускают значение NULL, имеют полную поддержку массивов и полностью протестированы на C# и F#.

SPI (Server Programming Interface)
SPI pl/dotnet использует клиентскую библиотеку NPGSQL для обеспечения собственной реализации dotnet, максимально совместимой с существующим клиентским кодом. Вызовы NPGSQL перехватываются на очень низком уровне, чтобы заменить обработку клиентского протокола вызовами SPI; в остальном NPGSQL не был изменен. Авторы использовали набор тестов NPGSQL для тестирования пакета, что дало им хорошее представление об уровне совместимости.

Инструкции по установке находятся здесь.
Проект на GitHub.
Авторы приветствуют участие и отзывы сообщества в дискуссиях на GitHub.

Источник: https://www.postgresql.org/about/news/announcing-pldotnet-version-099-beta-2838/
👍31👎1
День 1920. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 7. Запись знаний дешевле, чем повторное их обретение
Приходилось ли вам исследовать существующие системы, чтобы выяснить, как их изменить? А кто из вас записывал всё, что узнал, для дальнейшего использования?

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

Боязнь рутины
Некоторые отказываются тратить время на документирование требований. Но самое сложное не в их записи, а в том, чтобы понять, что они собой представляют. То же с планом проекта. Самое сложное — продумать все действия, необходимые для завершения: определить результаты, задачи, необходимые ресурсы и т. д. Это ручная работа, требующая времени. Но на это уйдёт меньше времени, чем на передачу той же информации нескольким людям на протяжении всего времени существования проекта.

Преимущества письменного общения
Команды, практикующие методику гибкой разработки Scrum, проводят короткие ежедневные митинги, чтобы уточнить информацию о состоянии дел, выявить препятствия и согласовать действия на следующие 24 часа. Но после таких встреч не остаётся документов, что не способствует хоть сколько-нибудь долгосрочному планированию. Если вы уверены, что никому и никогда не понадобится пересматривать решения или сведения, которыми участники обменивались на собрании, то нет причин записывать их. В противном случае время на документирование этой информации не будет потрачено впустую.

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

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

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍16
День 1921. #ЗаметкиНаПолях
Не Изменяйте Строки Подключения Вручную
Вам когда-нибудь приходилось изменять строку подключения в коде? Вы использовали регулярное выражение или другие манипуляции со строкой? Есть способ лучше.

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

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

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

Лучше использовать построитель строки подключения. Это классы, которые наследуют от базового System.Data.Common.DbConnectionStringBuilder. Например, System.Data.SqlClient.SqlConnectionStringBuilder используется для подключения к Microsoft SQL Server, но есть версии и для других СУБД.

Сначала нам нужен доступ к значению строки подключения (она должна поступать из системы конфигурации приложения):
var connStr = "Data Source=myServerAddress;Initial Catalog=db_1;User Id=my_user;Password=Pa55w0rd!;";

Это значение может быть использовано как аргумент конструктора построителя строк:
var builder = 
new SqlConnectionStringBuilder(connStr);

Мы можем изменять любые значения в строке подключения. Здесь обновим имя БД или исходный каталог:
builder.InitialCatalog = "new_db";

Теперь можно получить доступ к обновлённой строке подключения и использовать её:
var newConnStr = builder.ConnectionString;

Обновлённое значение будет:
Data Source=myServerAddress;Initial Catalog=new_db_name;User ID=my_user;Password=Pa55w0rd!

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

Источник: https://adamstorr.co.uk/blog/stop-using-regex-for-updating-connection-strings/
👍20