TG Telegram Group Link
Channel: .NET Разработчик
Back to Bottom
День 2331. #ЧтоНовенького
Вышел .NET Aspire 9.3
Команда .NET выпустила версию 9.3 .NET Aspire, в которой представлены обновления для диагностики, интеграции и рабочих процессов развёртывания. Этот выпуск направлен на улучшение опыта разработчиков путём интеграции GitHub Copilot в панель управления Aspire, расширения возможностей трассировки и упрощения развёртываний в Azure.

Интеграция GitHub Copilot в панель управления Aspire (на первой картинке выше) позволяет разработчикам анализировать журналы, исследовать ошибки в распределённых сервисах и выявлять проблемы производительности с помощью ИИ, не покидая среду панели управления разработчика. По словам команды .NET, Copilot дополняет диагностику на основе OpenTelemetry, представляя саммари журналов, интерпретируя коды ошибок и помогая выявлять основные причины в сложных сценариях трассировки.

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

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

Улучшения интеграции Aspire включают поддержку размещения самостоятельно управляемого экземпляра YARP через пакет Aspire.Hosting.Yarp. Кроме того, интеграция с MySQL теперь позволяет создавать базу данных во время настройки хоста приложения:
builder.AddMySql("mysql").AddDatabase("mydb");

Для контейнеризированных сервисов в выпуске представлены упрощенные API конфигурации для Postgres, Redis и SQL Server, позволяющие разработчикам указывать порты хоста и пароли с помощью метода RunAsContainer:
var sql = builder.AddAzureSqlServer("sql");
sql.RunAsContainer(c =>
{
c.WithHostPort(12455);
});


Интеграция с Azure также была расширена. Разработчики теперь могут создавать и добавлять контейнеры Blob Storage в AppHost. Были введены два новых API — AddAzureKeyVaultKeyClient и AddAzureKeyVaultCertificateClient — для упрощения доступа к Azure Key Vault для операций с ключами и сертификатами.

Функция Custom URLs в модели приложения была обновлена для поддержки относительных путей и большего контроля над видимостью. Новая перегрузка WithUrlForEndpoint позволяет прикреплять несколько конечных точек к одному ресурсу.

Для Azure App Service версия 9.3 представляет предварительную поддержку для развёртывания проектов .NET в ней. Поток развёртывания поддерживается через API AddAzureAppServiceEnvironment(…) и позволяет настраивать общие сценарии, такие как проекты .NET с одной конечной точкой, опубликованные в реестре контейнеров Azure:
builder.AddAzureAppServiceEnvironment("env");

builder.AddProject<Projects.Api>("api")
.WithExternalHttpEndpoints()
.PublishAsAzureAppServiceWebsite((infra, site) =>
{
site.SiteConfig.IsWebSocketsEnabled = true;
});


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

Источник: https://www.infoq.com/news/2025/06/dotnet-aspire-93-release/
День 2332. #ЗаметкиНаПолях #Git
Объединяем Репозитории Git, Сохраняя Историю
Объединение двух репозиториев Git может быть полезным в сценариях, когда вы хотите объединить связанные проекты в один репозиторий для более простого управления, совместной работы и контроля версий.

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

Используя Git, вы можете объединить два репозитория, сохранив их истории коммитов. Этот процесс гарантирует, что история обоих репозиториев останется нетронутой, что позволит вам отслеживать изменения и понимать эволюцию кодовой базы. Сохраняя историю коммитов, вы сохраняете ценный контекст о прошлых модификациях, авторстве и причинах конкретных изменений.
# Клонируем первый репозиторий
git clone https://github.com/mygit/project1.git project1
cd project1

# Добавляем второй репозиторий как удалённый (remote) и скачиваем
git remote add project2 https://github.com/mygit/project2.git
git fetch project2

# Сливаем второй репозиторий в первый
git merge project2/main --allow-unrelated-histories

# TODO: Разрешаем любые возникшие конфликты слияния и фиксируем изменения

# Делаем push объединённого репозитория в новую удалённую ветку
git push origin main


Источник: https://www.meziantou.net/merging-2-git-repositories-into-one.htm
День 2333. #Оффтоп
Проклятие Знания или Исправляем Всё. Начало

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

Вы даже не пытаетесь умничать. Вы просто решаете мелкие проблемы. Заставляете машину делать то, что она должна была делать изначально. И тут что-то происходит. Вы пересекаете черту. Вы смотрите на свои инструменты, среду, ОС — даже на IDE — и внезапно всё становится объектом критики. Это можно было бы переделать, это можно бы улучшить… (если захотеть).

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

Технические возможности как моральный груз
До того, как я научился программировать, сломанное ПО раздражало, но это можно было игнорировать. Годами я просто «использовал» компьютер, как потребитель. Я был тем, кого компании пытались обманом заставить купить их продукты или подписаться на услуги. Но не техническим гиком.

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

Я замечаю недостатки, как хороший хирург замечает хромоту. Какого черта этот сайт отправляет 10 Мб JavaScript кода для статической страницы? Почему вывод CLI не парсится awk? Почему эта конфигурация жёстко закодирована, когда она могла бы быть декларативной? Это не просто вопросы, это обвинения. И, к сожалению, они не прекращаются.

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

Нужно представить себе Сизифа счастливым
Как Сизиф, мы обречены толкать камень наших систем в гору — одно исправление, один рефакторинг, один скрипт за раз. Но в отличие от истории о Сизифе, проклятие не наложено на вас каким-то богом. Мы сами создали этот камень. И мы продолжаем полировать его по пути наверх.

Я потерял счёт проектам, начатым с мысли, типа «я мог бы сделать это лучше»:
- Генератор статических веб-страниц, потому что существующие переусложнены.
- Инструмент для заметок, потому что мне не нравилось, как другие структурировали метаданные.
- Исполнитель заданий CLI, потому что Make — это что-то непонятное, а Taskfile — это ад YAML.
Список можно продолжать.

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

Кафка однажды написал, что «клетка отправилась на поиски птицы». Вот чем могут стать эти проекты. Пустые системы, которые мы продолжаем строить, ожидая цели, ясности,… спасения? Я не уверен, как ещё можно назвать это стремление создавать их.

Продолжение следует…

Источник:
https://notashelf.dev/posts/curse-of-knowing
Please open Telegram to view this post
VIEW IN TELEGRAM
День 2335. #Оффтоп
Проклятие Знания или Исправляем Всё. Окончание

Начало
Продолжение

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

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

Ницше предупреждал о том, что не стоит слишком долго смотреть в бездну. Но он не предупреждал, что произойдет, если бездна — это Makefile или проект из 30 тыс. строк кода.

Учимся отпускать
Так где же выход? Это похоже на описание ада Сартром, где ад — это другие люди и то, как они взаимодействуют с вашим ПО? Или это какой-то странный ад, где люди создают ПО, с которым вам приходится взаимодействовать?

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

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

Не апатия, нет. И не лень. Просто… некоторая сдержанность.

Новый вид навыка
А что, если настоящий навык — это не техническое мастерство? Или, ещё лучше, если это эмоциональная ясность?

- Знать, какие проблемы стоят вашей энергии.
- Знать, какие проекты стоит поддерживать.
- Знать, когда вы создаёте, чтобы помочь, а когда вы создаёте, чтобы превозмочь.
- Знать, когда остановиться.

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

Вы учитесь программировать. Вы учитесь чинить вещи. Но самое трудное, чему вы когда-либо научитесь, — это знать, когда оставить их сломанными.

И, возможно, это самый важный навык из всех.

Источник:
https://notashelf.dev/posts/curse-of-knowing
День 2336. #ЗаметкиНаПолях
Автоматически Перезапускаем Неудавшиеся Задания в GitHub Actions
GitHub Actions не предоставляет встроенного способа автоматического перезапуска. Если у вас возникают какие-то неполадки при выполнении заданий, это может быть проблемой, поскольку приходится перезапускать рабочий процесс вручную.

Однако вы можете попробовать использовать событие workflow_run для запуска нового рабочего процесса, когда предыдущий завершается неудачей. Это позволяет автоматически перезапускать неудавшийся рабочий процесс.

В следующем примере показан сценарий перезапуска неудавшегося рабочего процесса, если это первая попытка перезапуска. Если вы хотите продолжать попытки перезапуска, вы можете удалить условие github.event.workflow_run.run_attempt == 1. Кроме того, сценарий проверяет определённый код выхода (1), чтобы определить, что выполнение рабочего процесса завершилось неудачей. Вы можете настроить это условие в соответствии со своими потребностями.
# .github/workflows/retry.yaml (YAML)
name: retry
on:
workflow_run:
workflows: ["**"]
types:
- completed
branches:
- main

defaults:
run:
shell: pwsh

jobs:
retry:
runs-on: ubuntu-latest
permissions:
actions: write # Retry actions
checks: read # Get info about the run
steps:
- name: retry
if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }}
run: |
# Get info about the run
$output = gh run view --repo "${{ github.event.repository.full_name }}" "${{ github.event.workflow_run.id }}"
$output

# Rerun the failed workflow if needed
if ($output -match "Process completed with exit code 1") {
gh run rerun "${{ github.event.workflow_run.id }}" --repo "${{ github.event.repository.full_name }}"" --failed
}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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

Источник: https://www.meziantou.net/automatically-rerun-failed-github-actions-workflows.htm
День 2337. #ЗаметкиНаПолях
RabbitMQ в .NET с Нуля. Начало
RabbitMQ — это брокер сообщений. Он как надёжный почтовый сервис для вашего ПО. Вместо того, чтобы одна система напрямую вызывала другую (что создаёт тесную связь), RabbitMQ выступает в качестве посредника:
- одна часть вашего приложения отправляет сообщение,
- другая часть получает и обрабатывает его.
Это называется асинхронной связью, и отлично подходит для производительности, надёжности и масштабируемости.

Ключевые компоненты:
- Производитель (Producer) – отправляет сообщения.
- Потребитель (Consumer) – получает сообщения.
- Очередь (Queue) – место, где сообщения ждут обработки.
- Обменник (Exchange) – «диспетчер», который направляет сообщения в соответствующие очереди.
- Привязка (Binding) – правила, которые связывают обменники с очередями.

Установка RabbitMQ (локально с Docker)
1. Выполните команду:
docker run -d --hostname rabbitmq
--name rabbitmq \
-p 5672:5672 -p 15672:15672 rabbitmq:3-management

2. Зайдите в панель управления http://localhost:15672. Вход по умолчанию: guest/guest

Реализация в .NET
Создадим простой проект:
- API отправляет «e-mail» в RabbitMQ,
- фоновый сервис прослушивает и «обрабатывает» сообщение.

Добавим RabbitMQ в проект:
Install-Package RabbitMQ.Client


Определим класс сообщения:
public record Email(string To, string Subject, string Body);


Производитель
Его роль – отправлять сообщения в RabbitMQ. Он должен:
- подключиться к RabbitMQ,
- создать очередь (или проверить её существование),
- сериализовать сообщение (например, в JSON),
- отправить сообщение в очередь.

public class EmailPublisher
{
private const string QUEUE =
"email-queue";

public async Task Publish(Email email)
{
var fctry = new ConnectionFactory() {
HostName = "localhost" };
using var conn =
await fctry.CreateConnectionAsync();
using var channel =
await conn.CreateChannelAsync();

// Создаём очередь
await channel.QueueDeclareAsync(
queue: QUEUE,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);

// Создаём сообщение
var msg = JsonSerializer.Serialize(email);
var body = Encoding.UTF8.GetBytes(msg);

// Публикуем
await channel.BasicPublishAsync(
exchange: string.Empty,
routingKey: QUEUE,
mandatory: true,
basicProperties:
new BasicProperties { Persistent = true },
body: body);
}
}


Параметры очереди:
- durable: сохранять на диске, чтобы очередь не терялась при перезапуске брокера,
- exclusive: может ли использоваться другими соединениями,
- autoDelete: удалять ли сообщения при отключении последнего потребителя.

Продолжение следует…

Источник:
https://thecodeman.net/posts/rabbitmq-in-dotnet-from-scratch
День 2338. #ЗаметкиНаПолях
RabbitMQ в .NET с Нуля. Продолжение

Начало

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

Рекомендуемый и наиболее удобный способ получения сообщений — настроить подписку с помощью интерфейса IAsyncBasicConsumer. Затем сообщения будут доставляться автоматически по мере их поступления. Один из способов реализации потребителя — использовать класс AsyncEventingBasicConsumer, в котором доставки и другие события жизненного цикла потребителя реализованы как события C#:
public class EmailConsumer : BackgroundService
{
private const string QUEUE =
"email-queue";

protected override async Task
ExecuteAsync(CancellationToken ct)
{
var fctry = new ConnectionFactory() {
HostName = "localhost" };
using var conn =
await fctry.CreateConnectionAsync(ct);
using var channel =
await conn.CreateChannelAsync(cancellationToken: ct);

await channel.QueueDeclareAsync(
queue: QUEUE,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null,
cancellationToken: ct);

var consumer =
new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync +=
async (sender, eventArgs) =>
{
var body = eventArgs.Body.ToArray();
var json = Encoding.UTF8.GetString(body);
var email =
JsonSerializer.Deserialize<Email>(json);

Console.WriteLine(
$"Email: {email?.To}, Тема: {email?.Subject}");

// Отправляем email…
Task.Delay(1000).Wait();

await ((AsyncEventingBasicConsumer)sender)
.Channel
.BasicAckAsync(
eventArgs.DeliveryTag,
multiple: false);
};

await channel.BasicConsumeAsync(
queue: QUEUE,
autoAck: true,
consumer: consumer,
cancellationToken: ct);
}
}


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

Источник:
https://thecodeman.net/posts/rabbitmq-in-dotnet-from-scratch
Возникла дискуссия в обсуждении перевода книги. Когда мы говорим про коллекции ConcurrentXXX/ImmutableXXX/FrozenXXX, какой перевод выражения "concurrent (collections)", на ваш взгляд, удачнее?
Anonymous Poll
2%
асинхронные
1%
защищённые
30%
конкурентные
6%
многопоточные
5%
неблокирующие
3%
параллельные
51%
потокобезопасные
3%
другой (напишите в комментариях)
День 2339. #ЗаметкиНаПолях
RabbitMQ в .NET с Нуля. Окончание
Начало
Продолжение

Типы обмена в RabbitMQ
Обменник (Exchange) в RabbitMQ решает, куда отправить сообщение. Каждый тип обмена имеет свою стратегию маршрутизации сообщений.

1. Direct Exchange (маршрутизация один-к-одному)
Сообщение направляется в очереди с точно таким же ключом маршрутизации. Это как отправка почты определённому получателю. Например, отправка email только в очередь, ответственную за маркетинговую рассылку:
channel.ExchangeDeclare(
"direct-exchange", ExchangeType.Direct);
channel.QueueBind(
"market-queue", "direct-exchange", "market");

Если вы отправите сообщение с routingKey: "market" (см. вызов BasicPublishAsync в примере кода производителя), оно попадёт в очередь "market-queue".

2. Fanout Exchange (отправка всем)
Сообщения отправляются во все очереди, связанные с этим обменником, игнорируя routingKey. Например, всеобщая рассылка по всем сервисам (email, СМС):
channel.ExchangeDeclare(
"fanout-exchange", ExchangeType.Fanout);
channel.QueueBind(
"email-queue", "fanout-exchange", "");
channel.QueueBind(
"sms-queue", "fanout-exchange", "");

Любое сообщение, отправленное в "fanout-exchange", попадёт в обе очереди.

3. Topic Exchange (маршрутизация по шаблону)
Использует шаблон routingKey для гибкой настройки:
- * - ровно одно слово,
- # - 0 и более слов.
Например, маршрутизация логов на основе важности и типа системы:
channel.ExchangeDeclare(
"topic-exchange", ExchangeType.Topic);
channel.QueueBind(
"error-queue", "topic-exchange", "log.error.#");
channel.QueueBind(
"auth-queue", "topic-exchange", "log.*.auth");

"log.error.auth" – попадёт в обе очереди.
"log.info.auth" – попадёт в auth-queue.
"log.error.database" – попадёт в error-queue.

4. Headers Exchange (маршрутизация по метаданным)
Вместо routingKey использует заголовки сообщения. Например, маршрутизация сообщений по сложным условиям (x-type="invoice" и region="EU"):
channel.ExchangeDeclare(
"headers-exchange", ExchangeType.Headers);
var args = new Dictionary<string, object>
{
{"x-match", "all"}, // либо "any"
{"x-type", "invoice"},
{"region", "EU"}
};
channel.QueueBind(
"invoice-eu-queue",
"headers-exchange",
string.Empty,
args);

Только сообщения, которые включают и x-type="invoice", и region="EU" в своих заголовках, попадут в очередь "invoice-eu-queue".

Итого
RabbitMQ — мощный инструмент для создания масштабируемых, слабо связанных систем, а .NET делает его удивительно простым в интеграции. Независимо от того, создаете ли вы микросервисы или просто хотите разгрузить основное приложение от выполнения некоторых длительных задач, RabbitMQ вам поможет.

Источник: https://thecodeman.net/posts/rabbitmq-in-dotnet-from-scratch
День 2340. #TipsAndTricks
Широко известный в узких кругах дотнетчиков блогер Ник Чапсас помимо основных видео на своём ютуб-канале также выпускает шортсы с короткими советами по написанию более лучшего кода. Так вот, таких советов накопилось уже более сотни, поэтому он собрал первую сотню в одно почти часовое видео.

Смотрим и мотаем на ус 😉

https://youtu.be/8F-Pb-SKO5g
Реферальная программа для тех, кто не работает в Ozon:
порекомендуйте C#-разработчика уровня senior и заработайте 150 000 ₽.

Посмотреть вакансии, которые включает программа: https://s.ozon.ru/Rd201VO
День 2341. #ЗаметкиНаПолях
Исключаем Windows-приложение из Захвата Экрана
Некоторые приложения отображают конфиденциальную информацию. Recall в Windows может регулярно делать снимки экрана и таким образом сохранять конфиденциальную информацию, что крайне нежелательно.

Вы можете исключить своё приложение из этой функции и захвата экрана в целом, используя SetWindowDisplayAffinity и флаг WDA_EXCLUDEFROMCAPTURE. Это функция API Windows, позволяющая установить привязку отображения окна, которая определяет, как окно взаимодействует с программным обеспечением захвата экрана.

1. Добавьте NuGet-пакет Microsoft.Windows.CsWin32

2. Добавьте в проект файл NativeMethods.txt со следующим содержимым:
SetWindowDisplayAffinity


3. Добавьте следующий код в проект:
unsafe
{
// для WPF:
// new WindowInteropHelper(
// Application.Current.MainWindow)
// .Handle

// для WinForms:
// this.Handle

var windowHandle = (HWND)Handle;
Windows.Win32.PInvoke.SetWindowDisplayAffinity(
windowHandle,
Windows.Win32.UI
.WindowsAndMessaging
.WINDOW_DISPLAY_AFFINITY
.WDA_EXCLUDEFROMCAPTURE
);
}


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

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

Источник:
https://www.meziantou.net/how-to-exclude-your-windows-app-from-screen-capture-and-recall.htm
День 2342. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 56. Здравый смысл и опыт иногда важнее определённого процесса


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

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

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

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

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

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

Методы Agile-разработки получили широкое распространение с конца 1990-х. В «Википедии» определены не менее 14 важных систем и 21 широко используемая практика Agile-разработки ПО. Некоторые пуристы очень обеспокоены соответствием, скажем, методологии Scrum. Потому что «если использовать только отдельные компоненты Scrum, то это будет не Scrum». Так цель соответствовать Scrum или качественно и быстро выполнить работу?

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 7.
День 2343. #ЧтоНовенького
Вышел 5 Превью .NET 10. Начало
Microsoft объявили о выпуске .NET 10 превью 5, представив обновления для нескольких компонентов, включая ASP.NET Core, .NET MAUI, WPF и EF Core.

ASP.NET Core
1. Появилась возможность настраивать пользовательские дескрипторы безопасности для очередей запросов HTTP.sys через свойство RequestQueueSecurityDescriptor в HttpSysOptions. Это обеспечивает лучший контроль над доступом к очередям запросов на уровне ОС.

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

3. Генерация OpenAPI была улучшена в версии 3.1. Выпуск также расширяет извлечение метаданных из XML-документации, распознавая теги <returns> и <response> для описаний ответов.

4. В Blazor представлен метод для более простого рендеринга страниц Not Found путем указания компонента NotFoundPage в конфигурации маршрутизатора. Этот подход имеет приоритет над старым фрагментом NotFound. Теперь страница NotFound.razor включена в шаблоны проектов по умолчанию и будет отображаться всякий раз, когда в приложении вызывается метод NavigationManager.NotFound():
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>This content will be ignored because we have NotFoundPage defined.</NotFound>
</Router>


5. Введены подробные метрики и возможности трассировки для приложений Blazor. Метрики публикуются через выделенные счетчики для компонентов, событий жизненного цикла и конвейеров сервера. Трассировка использует новый источник активности Microsoft.AspNetCore.Components, включающий подробный инструментарий для навигации, обработки событий и жизненных циклов конвейера. Разработчики могут включить эту диагностику, настроив OpenTelemetry для сбора данных из соответствующих источников и счётчиков:
builder.Services
.ConfigureOpenTelemetryMeterProvider(p =>
{
p.AddMeter("Microsoft.AspNetCore.Components");
p.AddMeter("Microsoft.AspNetCore.Components.Lifecycle");
p.AddMeter("Microsoft.AspNetCore.Components.Server.Circuits");
});

builder.Services
.ConfigureOpenTelemetryTracerProvider(p =>
{
p.AddSource("Microsoft.AspNetCore.Components");
});


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

Источник:
https://www.infoq.com/news/2025/06/dotnet-10-preview-5/
День 2344. #ЧтоНовенького
Вышел 5 Превью .NET 10. Окончание

Начало

MAUI
1. Добавлена поддержка глобальных и неявных пространств имён XAML. Это упрощает разметку, позволяя разработчикам использовать элементы управления без указания нескольких объявлений xmlns. Новое глобальное пространство имён может включать пользовательские представления, конвертеры и сторонние библиотеки, что позволяет сделать XAML более чистым и удобным для обслуживания. Функцию можно активировать через свойства проекта, что снижает необходимость в подробных объявлениях. Однако Microsoft отмечает, что могут возникать ошибки инструментов, пока эта функция остаётся в предварительной версии.

2. Добавлена возможность перехвата веб-запросов в HybridWebView благодаря обработке события WebResourceRequested. Можно изменять или блокировать запросы, позволяя использовать такие сценарии, как внедрение пользовательских заголовков или обслуживание локальных ресурсов:
<HybridWebView WebResourceRequested="HybridWebView_WebResourceRequested" />


WPF
1. Представлен сокращённый синтаксис для Grid.RowDefinitions и Grid.ColumnDefinitions, делая XAML более лаконичным и улучшая поддержку Hot Reload.

2. Улучшения шрифтов и глобализации включают добавление шрифта simsun-extg для улучшения рендеринга на восточноазиатских языках. Тема Fluent была усовершенствована, устраняя сбои и улучшая стили для макетов RTL. Повышение производительности было достигнуто за счёт сокращения выделения памяти и удаления неиспользуемых путей кода.

Entity Framework
В предыдущих версиях EF Core, когда вы указывали значение по умолчанию для свойства, EF Core всегда позволял базе данных автоматически генерировать имя ограничения. Теперь вы можете явно указать имя для ограничений значения по умолчанию для SQL Server, что даёт больше контроля над схемой БД:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValue(true, "DF_Blog_IsActive");

mb.Entity<Post>()
.Property(p => b.CreatedDate)
.HasDefaultValueSql("GETDATE()", "DF_Post_CreatedDate");
}

Вы также можете вызвать UseNamedDefaultConstraints, чтобы включить автоматическое именование всех ограничений по умолчанию. Обратите внимание, что если у вас есть существующие миграции, то следующая добавленная миграция переименует каждое ограничение по умолчанию в модели:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.UseNamedDefaultConstraints();
}


Полный список новинок можно посмотреть здесь

Источник: https://www.infoq.com/news/2025/06/dotnet-10-preview-5/
День 2345. #TipsAndTricks
Добавляем Описание в Параметризованные Тесты

Часто нам требуется протестировать несколько вариантов использования метода с разными данными, и для этого подойдут параметризованные тесты, например, Theory в xUnit.

См. подробнее про параметризованные тесты в xUnit.

При этом бывает полезно добавить не только тестовые данные, но и описание к каждому тестовому случаю. Рассмотрим, как это сделать.

Представим, что у нас есть такой тест:
[Theory]
[MemberData(nameof(InvalidFilters))]
public async Task ShouldNotAllowInvalidInvariants(
LimitFilters filters)
{

}


Тест принимает следующую запись с тестовыми данными:
public record LimitFilters(
Guid? WorkpieceNumber,
IEnumerable<int>? Ids,
IEnumerable<int>? Tools,
IEnumerable<int>? LimitIds);
}

Если мы выполним тест, мы увидим в окне выполнения теста что-то вроде следующего:
 ShouldNotAllowInvalidInvariants(filters: { WorkpieceNumber = … })
ShouldNotAllowInvalidInvariants(filters: { WorkpieceNumber = … })


Как видите, сложно понять, о чём каждый тестовый случай, особенно учитывая, что у нас есть коллекции в параметрах. Но обратите внимание, что из-за использования record, мы видим строковое представление записи, т.к. среда выполнения вызывает метод ToString() параметров. Мы можем использовать это.

Чтобы заставить среду выводить более осмысленное описание, мы можем добавить описание теста в LimitDesignerFilters и переопределить метод ToString():
public record LimitDesignerFilters(
string Description,
Guid? WorkpieceNumber,
IEnumerable<int>? Ids,
IEnumerable<int>? Tools,
IEnumerable<int>? LimitIds)
{
public override string ToString()
=> Description;
}


Теперь мы можем задать свойству Description описание каждого тестового случая:
public static TheoryData<LimitDesignerFilters> 
InvalidFilters =>
[
new("Workpiece is null", null, [1], [1], [1]),
new("Param1 is null", Guid.NewGuid(), null, [1], [1]),
];

Тогда в окне выполнения теста мы увидим следующее:
 ShouldNotAllowInvalidInvariants(filters: Param1 is null)
ShouldNotAllowInvalidInvariants(filters: Workpiece is null)

Тут всё ещё присутствует название параметра (filters), но всё же, понять, что проверяет каждый тест, уже гораздо проще.

Источник: https://steven-giesel.com/blogPost/80a53df4-a867-4202-916c-08e980f02505/adding-test-description-for-datadriven-tests-in-xunit
День 2346. #BestPractices
Пишем Хорошую Документацию. Начало

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

1. Знайте свою аудиторию (глубоко)
Прежде чем написать хоть слово, поймите, для кого вы пишете. Опытные или младшие разработчики? Внутренние команды, уже знакомые с экосистемой, или внешние пользователи, впервые сталкивающиеся с продуктом?
- Техническая компетентность: адаптируйте язык и глубину объяснения. Избегайте чрезмерно упрощённых объяснений для экспертов, но не предполагайте глубоких знаний предметной области от новичков.
- Цели: чего они пытаются достичь с помощью вашего ПО/API? Они ищут краткое руководство по началу работы, устранение конкретной ошибки, понимание расширенных концепций или интеграцию с другими системами? Структурируйте документы так, чтобы они отвечали этим конкретным потребностям.
- Контекст: где они обычно ищут информацию? Подойдёт ли интерактивный UI для API или лучше статические справочные страницы?
Поставьте себя на место разработчика. Какая информация вам нужна и как бы вы хотели, чтобы она была представлена?

2. Важна структура: создайте логическую основу
Даже самый точный контент бесполезен, если разработчики не могут его найти. Логическая структура обеспечивает ментальную карту, позволяя пользователям интуитивно ориентироваться.
- Чёткая иерархия: организуйте контент логически, начиная с вводных концепций (Начало работы, Установка) и переходя к конкретике (Справочник API, Руководства, Устранение неполадок). Используйте чёткие заголовки и подзаголовки (H1, H2, H3) последовательно.
- Оглавление: хорошо структурированное, всегда видимое (на боковой панели) оглавление позволяет пользователям видеть общую компоновку и быстро переходить к нужным разделам.
- Функциональность поиска: убедитесь, что поиск эффективно индексирует контент и выделяет ключевые слова в результатах. Разработчики часто знают, что им нужно, но не знают, где это находится.
- Перекрёстные ссылки: делайте ссылки на связанные концепции, руководства и API. Если вы упоминаете конечную точку API в руководстве, давайте прямую ссылку на неё. Это создаёт сеть знаний, а не изолированные хранилища.
- Информационная архитектура: планируйте поток. Рассмотрите возможность использования устоявшихся фреймворков, таких как Diátaxis (концепции, руководства, пояснения, справочные материалы), чтобы гарантировать, что вы систематически охватываете различные потребности в обучении.

3. Содержание: ясность, краткость и примеры
Основа документации — это содержание.
- Ясность: используйте понятный, недвусмысленный язык. Избегайте жаргона, где возможно, или чётко определите понятия при их первом использовании. Предпочитайте активный залог пассивному («Функция возвращает X» вместо «X возвращается функцией»).
- Краткость: разработчики заняты. Переходите к сути. Устраняйте бессмыслицу и ненужные слова. Используйте короткие предложения и абзацы. Маркеры и нумерованные списки отлично подходят для разбиения текста и выделения ключевой информации.
- Точность: неточная документация хуже, чем её отсутствие. Установите процессы проверки и обеспечьте обновление документов вместе с изменениями кода.
- Примеры кода: многочисленные, практичные, копируемые и вставляемые примеры, которые работают из коробки (включая необходимые импорты или настройки). Объясняйте, что делает код и почему он структурирован именно так. Покажите общие случаи использования, пограничные случаи и обработку ошибок. Если ваш API/SDK поддерживает несколько языков, предоставьте примеры на каждом из них.

Продолжение следует…

Источник:
https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
День 2347. #BestPractices
Пишем Хорошую Документацию. Продолжение

Начало

4. Сосредоточьтесь на вариантах использования и используйте интеллектуальные инструменты для API
Разработчики не просто хотят знать, что делает конечная точка API; они хотят знать, как использовать её для решения своей проблемы. Создавайте документацию вокруг общих задач и рабочих процессов.
Специализированные фреймворки для API значительно улучшают процесс создания документации. Инструменты, разработанные специально для жизненного цикла API, могут оптимизировать как разработку, так и документирование, обеспечивая согласованность и интерактивность. Например, Apidog интегрирует проектирование API, отладку, тестирование непосредственно с созданием документации.

5. Поддерживайте точность и актуальность
Устаревшая документация подрывает доверие быстрее, чем что-либо ещё. Разработчики полагаются на правильность документов. Если они сталкиваются с несоответствиями, они полностью перестают доверять документации.
- Версионирование: чётко версионируйте документацию вместе с выпусками ПО/API. Позвольте пользователям легко переключаться между версиями.
- Документация-как-Код: относитесь к документации как к коду. Храните её в системе контроля версий (например, Git) вместе с исходным кодом, который она описывает. Это упрощает отслеживание изменений, просмотр обновлений и синхронизацию документов с выпусками кода. Интегрируйте обновления документации в свой конвейер CI/CD.
- Циклы обратной связи: упростите разработчикам возможность сообщать об ошибках или предлагать улучшения (например, кнопка «Предложить изменения», ссылающаяся на GitHub Issues или специальную форму обратной связи). Незамедлительно реагируйте на эти отзывы.
- Регулярные обзоры: запланируйте периодические обзоры документации для проверки точности, ясности и полноты.

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

7. Улучшите интерактивность и поисковую доступность
Выйдите за рамки статического текста:
- Интерактивные элементы: помимо пользовательского интерфейса API (например, Swagger), рассмотрите использование встроенных редакторов кода (например, CodeSandbox) или интерактивных руководств (например, Jupiter Notebooks).
- Расширенный поиск: для больших наборов документации разрешите пользователям фильтровать результаты поиска по категории, версии или разделу API.
- Виджеты "Была ли эта страница полезна?": собирайте быстрые отзывы об эффективности страницы.

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

Источник:
https://dev.to/therealmrmumba/beyond-code-how-to-create-beautiful-documentation-that-developers-actually-love-best-practices-hc4
HTML Embed Code:
2025/07/05 02:23:00
Back to Top