.NET Разработчик
6.71K subscribers
475 photos
4 videos
14 files
2.38K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2713. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

38. Blazor WebAssembly
«Опишите, как бы вы создали клиентское веб-приложение с использованием Blazor WebAssembly? Укажите, как бы вы настроили проект, управляли зависимостями и обрабатывали вызовы API».

Хороший ответ
Создание клиентского веб-приложения с использованием Blazor WebAssembly в .NET включает в себя несколько ключевых шагов для эффективного использования фреймворка Blazor для запуска кода .NET в браузере.

Начнём с создания нового проекта Blazor WebAssembly с помощью .NET CLI:
dotnet new blazorwasm -o MyBlazorApp

Эта команда настроит новый проект автономного приложения Blazor WebAssembly, который включает в себя всё необходимое для начала работы с клиентским приложением Blazor.

В проекте Blazor WebAssembly файл Program.cs используется для настройки сервисов и запуска приложения. Здесь можно настроить внедрение зависимостей, как в серверном приложении ASP.NET Core:
var builder = WebApplication.CreateBuilder(args);

// Добавим HttpClient для вызовов API
builder.Services.AddScoped(sp => new HttpClient
{ BaseAddress =
new Uri(builder.HostEnvironment.BaseAddress) });

var app = builder.Build();
app.Run();


Вызовы API из приложения Blazor WebAssembly обрабатываются с помощью HttpClient. Можно внедрить HttpClient в компоненты для выполнения вызовов REST API:
@inject HttpClient Http

@code {
private List<Product> products;

protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<Product>>("api/products");
}
}

Этот компонент получает данные из серверного API и привязывает их к UI, позволяя Blazor реактивно управлять обновлениями интерфейса.

Blazor обрабатывает маршрутизацию на стороне клиента в файле App.razor, определяя, какой компонент отображать в зависимости от URL:
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Не найдено</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Извините, страница не найдена.</p>
</LayoutView>
</NotFound>
</Router>


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

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

Часто встречающийся плохой ответ
«Blazor WebAssembly позволяет обрабатывать всю логику на стороне клиента. А всё, что нельзя сделать на C#, можно сделать через JavaScript-interop».

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

- Чрезмерное полагание на JavaScript-interop: это противоречит одной из главных целей использования Blazor, а именно — позволить разработчикам использовать C# для выполнения задач, обычно предназначенных для JavaScript. Хотя взаимодействие с JavaScript полезно для доступа к API браузера, выходящим за рамки возможностей Blazor, его следует использовать экономно.

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

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎5
День 2714. #ЗаметкиНаПолях #HTML
В URL Можно Использовать Перенос Строки

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

Например, эта ссылка работает в браузерах:
<a href="https://example.com/docs/
url-parsing">Documentation</a> <!-- табы, перенос строки, но не пробелы -->

Браузеры сообщают о внутренней ошибке валидации, когда встречают символы табуляции или перевода строки ASCII в URL, затем удаляют эти символы и продолжают анализ. Таким образом, совместимые парсеры перенаправляют на тот же адрес, что и эквивалентный URL без этих символов. Стандарт WHATWG URL описывает это в базовом алгоритме парсера URL:
«Если входные данные содержат какие-либо символы табуляции или перевода строки ASCII, возникает ошибка валидации invalid-URL-unit. Удалите все символы табуляции или перевода строки ASCII из входных данных.»

Поэтому эти ссылки обычно интерпретируются одинаково:
<a href="https://exa
mple.com/path">A</a>

<a href="https://example.com/path">B</a>


Многострочный URL данных с встроенным SVG
URL данных также могут быть написаны в несколько строк. Это полезно при встраивании читаемого документа SVG:
<img alt="Simple sunset" src='data:image/svg+xml,
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 80">
<rect width="120" height="80" fill="deepskyblue" />
<circle cx="60" cy="45" r="14" fill="gold" />
<rect y="58" width="120" height="22" fill="sienna" />
</svg>' />

Этот пример намеренно отформатирован для удобства чтения. Для производственного кода лучше по-прежнему использовать URL в одну строку или процентное кодирование.

Маскируем mailto: от парсеров, использующих регулярные выражения
Такое поведение можно использовать для маскировки URL, таких как mailto:, от парсеров, использующих простые регулярные выражения:
<a href="mail
to:security@example.com">Замаскированный контакт</a>

Браузер обрабатывает это как mailto:security@example.com, но наивный шаблон, например mailto:[^\"'\s>]+, может не обнаружить его из-за добавленного символа новой строки.

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

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

В общем случае, пишите обычные однострочные URL-адреса, если у вас нет веской причины форматировать их иначе.

Источник: https://www.meziantou.net/you-can-use-newline-characters-in-urls.htm
👍1
Хайлоад: производительность и планирование мощностей


Приглашаем на практический курс для Middle/Senior-разработчиков, техлидов, архитекторов, EM и CTO, которые хотят не просто “знать про хайлоад”, а руками разобраться, как работают производительность, нагрузочное тестирование и масштабирование.

Будем выжимать 20–100K RPS из своих сервисов на своей инфраструктуре, строить latency/RPS-диаграммы, искать ограничения в стеке и использовать эти данные для capacity planning. В программе: Linux-инфраструктура, nginx, Prometheus/Grafana, нагрузочное тестирование через wrkx, тюнинг производительности, планирование мощностей.

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

📌 Старт потока 13 июля.

Кто мы: R&D-центр Devhands, основатель и автор курса Алексей Рыбак, ex-СТО Badoo и Yum! Brands, член программного комитета Highload.

Изучайте программу и записывайтесь.

Реклама. ИП Рыбак А.А. ИНН 771407709607 Erid: 2VtzquZx6p5
#реклама
👍1
День 2715. #ЧтоНовенького #NET11
Закрытые Иерархии Классов. Начало

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

Пусть у нас есть иерархия классов в одной сборке:
// Закрытый базовый класс 
public closed class Animal { }

// Все наследуют от закрытого класса
public class Dog : Animal { }
public class Cat : Animal { }
public class Horse : Animal { }

Здесь обычное наследование C#, ключевое слово closed никак не влияет.
Разница ощущается, если попробовать наследовать от Animal из другой сборки. Этот выдаст ошибку компиляции:
error CS9382: '…' cannot use a closed type 'Animal' from another assembly as a base type. (класс не может использовать закрытый тип 'Animal' из другой сборки в качестве базового типа).

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

Ведь это можно было сделать с помощью приватных конструкторов?
Имея приватный защищенный (private protected) или внутренний (internal) конструктор по умолчанию в базовом классе, практически невозможно создать производный тип. Вы получали ошибку CS0122 или CS1729 о невозможности использования конструктора.

Использование closed явно семантически чище. Но кроме того, в подходе с приватным конструктором компилятор на самом деле «не знал», что нельзя создавать производные типы, потому что теоретически это не исключено, даже если невозможно на практике.

С ключевым словом closed, когда компилятор собирает сборку, он точно знает все производные типы есть у закрытого класса. Теперь компилятор может применять проверку исчерпываемости к выражениям switch.

Допустим, у нас есть «обычная» иерархия классов, похожая на показанную выше, но на этот раз с использованием abstract вместо closed. Мы можем определить следующий метод:
static string Speak(Animal animal) => animal switch
{
Dog => "Гав",
Cat => "Мяу",
Horse => "Игого",
};

Но мы получим предупреждение CS8509, что выражение switch не охватывает все возможные значения, и вынуждены будем добавить вариант «по умолчанию» (catch-all), типа _ => …. Даже если вы обрабатываете все типы, компилятор об этом не знает. С его точки зрения, вы можете создать производный от Animal тип в другой сборке и передать его методу.

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

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

Заметьте, что это более «функциональный» подход, в котором данные (классы Animal) отделены от методов (Speak()). В ООП метод Speak, скорее всего, был бы определен непосредственно в иерархии типов Animal.

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

Источник:
https://andrewlock.net/exploring-the-dotnet-11-preview-4-closed-class-hierarchies/
👍11
День 2716. #ЧтоНовенького #NET11
Закрытые Иерархии Классов. Окончание

Начало

Закрытые классы и объединения
Одним из преимуществ поддержки объединений в .NET 11 является то, что вы получаете аналогичную проверку исчерпываемости в выражениях switch:
// 3 разных типа
public record Windows(string Version);
public record Linux(string Distro, string Version);
public record MacOS(string Name, int Version);

// Объединение представляет один из них
public union SupportedOS(Windows, Linux, MacOS);

// Объединение в switch
string GetDescription(SupportedOS os)
=> os switch
{
Windows windows => $"Windows {windows.Version}",
Linux linux => $"{linux.Distro} {linux.Version}",
MacOS macOS => $"MacOS {macOS.Name} ({macOS.Version})",
};

Как и в случае с закрытой иерархией классов, объединение поддерживает проверку исчерпываемости, поэтому вам не нужно обрабатывать варианты «catch-all». Однако в объединении нет иерархии; оно определяет конкретные возможные значения, и компилятор гарантирует обработку всех этих случаев в выражении switch.

Ограничения и особенности
Когда вы применяете ключевое слово closed к классу или записи, это неявно делает класс абстрактным, поэтому вы не можете использовать модификаторы sealed или static для этого типа. Кроме того, вы также не можете явно использовать abstract для типа, даже если он является абстрактным по логике.

При наследовании от закрытого типа производные классы не являются автоматически закрытыми, но их можно объявить закрытыми:
public closed record Animal { }
public closed record Dog : Animal{ }
public record Labrador : Dog { }


Можно использовать обобщённые закрытые типы, но если производный класс также является обобщённым, то все его параметры типа должны использоваться в спецификации базового класса:
public closed class Animal<T> { }
class Dog<U> : Animal<U> { } // ОК
class Cat<W> : Animal<int> { } // ошибка CS9383
// Тип 'W' должен быть указан в базовом типе 'Animal<int>', т.к. базовый тип закрыт.


Ещё одно тонкое ограничение возникает, если вы применяете ключевое слово sealed к производным типам. Тогда говорят, что у типа запечатанная иерархия. В этих случаях компилятор может делать ещё более строгие предположения о вашем коде и, следовательно, может добавлять ошибки для сценариев, когда в вашем коде, вероятно, что-то не так. Например, объявим производные типы как sealed и добавим интерфейс IPet:
public closed class Animal { }

public sealed class Dog : Animal { }
public sealed class Cat : Animal { }

public interface IPet { }

Теперь, если у нас есть код, который пытается явно привести тип из экземпляра Animal к типу IPet, компилятор может «видеть» все производные типы и что ни один из них не реализует IPet, и, следовательно, может на этапе компиляции предположить, что это преобразование завершится неудачей:
Animal animal = GetSomeAnimal();
var pet = (IPet)animal; // CS0030: Cannot convert type 'Animal' to 'IPet' (Нельзя привести тип 'Animal' к 'IPet')

Это превращает гарантированную ошибку времени выполнения в ошибку компиляции. Отлично!

Источник: https://andrewlock.net/exploring-the-dotnet-11-preview-4-closed-class-hierarchies/
День 2717. #Оффтоп #AI
Прекратите Промптить. Спроектируйте Цикл. Начало

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

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

Борис Черни, возглавляющий Claude Code в Anthropic больше не пишет промпты для Клода. У него работают циклы, которые запрашивают Клод и решают, что делать дальше, сканируя трекер задач, командный чат и временную шкалу в поисках того, что нужно создать.

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

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

Удивительно, что это уже почти не проблема инструментов. Год назад цикл означал кучу кода на Bash, который вы писали и поддерживали бесконечно. Теперь же компоненты поставляются внутри продуктов, и аналогичные структуры встречаются в Claude Code и Codex. Цикл работает по таймеру, запускает вспомогательные средства и питает себя сам.

Пять составляющих и шестая, которая их объединяет
И Claude Code, и Codex теперь имеют все пять. Названия немного различаются, но возможности одинаковы.

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

2. Рабочие деревья (Worktrees). Как только вы запускаете более одного агента, они начинают конфликтовать за файлы. Рабочее дерево Git — это отдельный рабочий каталог в своей собственной ветке, поэтому изменения одного агента не могут коснуться изменений другого.

3. Навыки. Агент начинает каждую сессию с нуля и заполняет любые пробелы в вашем намерении уверенным предположением. Навык — это соглашения, этапы сборки, фраза «мы так не делаем из-за одного инцидента», записанные один раз, и агент читает их при каждом запуске. Без навыков цикл каждый раз заново анализирует весь ваш проект с нуля.

4. Коннекторы. Созданные на основе MCP, они позволяют агенту читать трекер задач, запрашивать БД, обращаться к API или отправлять сообщения в чат. В этом разница между агентом, который говорит «вот исправление», и циклом, который открывает пул-реквест, связывает тикет и отправляет уведомление в канал, как только CI завершится успешно.

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

И наконец, память. Файл Markdown, файл состояния, всё, что хранит информацию о том, что сделано и что будет дальше. Модель забывает всё между запусками, поэтому память должна храниться на диске, а не в окне контекста. Агент забывает. Репозиторий — нет.

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

Источник:
https://www.pulumi.com/blog/stop-prompting-design-the-loop/
👎11👍3
День 2718. #Оффтоп #AI
Прекратите Промптить. Спроектируйте Цикл. Окончание

Начало

Что обеспечивает целостность цикла?
Цикл, работающий без присмотра, также втихаря совершает ошибки. Единственное, что обеспечивает его правильность, — это верификация, а верификации нужен «оракул», нечто вне модели, что возвращает однозначное «да» или «нет». Пройденные тесты, чистая сборка, зелёный конвейер CI, и т.п. Без «оракула» цикл уверенно накапливает ошибки быстрее, чем вы успеваете их читать.

Самая чистая версия этого уже включена в инструменты. Функция /goal в Claude Code продолжает работать на протяжении нескольких итераций, пока не выполнится условие, которое вы фактически написали, например, «все тесты в auth/ проходят и сборка не имеет предупреждений», и после каждой итерации отдельная, более быстрая модель считывает протокол и решает, достигли ли вы цели. Агент, написавший код, не тот, кто его оценивает. Это разделение «создатель и проверяющий», применяется к самому условию остановки. Функция /goal в Claude Code достигает той же финишной линии другим способом: агент проверяет свою собственную работу на соответствие доказательствам, прежде чем объявить цель выполненной.

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

Расчёты приходят в двух валютах: токенах и внимании. Один запуск без участия пользователя может израсходовать миллионы токенов, и это стоит того только тогда, когда токены «оплачивают» что-то, что стоит больше, чем стоят они. Вторая валюта: память — то, что позволяет циклу усложняться со временем, но и плохие навыки накапливаются вместе с этим. Цикл, направленный на неопределённую цель, не устанет и остановится, а будет становиться быстрее.

Ваше понимание растает, если вы это допустите. Чем быстрее цикл выдаёт код, который вы не писали, тем больше разрыв между тем, что есть в репозитории, и тем, что вы на самом деле понимаете. Этот разрыв растёт быстрее, если вы не читаете то, что создаётся. Удобная позиция, когда вы перестаёте иметь собственное мнение и принимаете того, что выдаёт цикл, — рискованная. Два инженера могут создать одинаковый цикл, но на выходе один будет быстрее продвигаться в работе, которую глубоко понимает, а другой полностью избегать этой работы. Цикл не знает, кто из них вы.

С чего начать
1. Начните с того места, где понятие «готово» однозначно. Отладка конвейера CI, проблемы с зависимостями, поиск нестабильных тестов, падающее задание, которое вы постоянно перезапускаете вручную. Циклам нужен «оракул», поэтому начните с того места, где «оракул» уже существует.

2. Напишите файл памяти перед циклом. Один файл Markdown. Что сделано, что дальше, что было опробовано и не удалось. Это основа, и всё остальное от неё зависит.

3. Отделите проверку от создания. Используйте /goal с проверяемым условием или второй агент с отдельными инструкциями. Никогда не позволяйте агенту, который выполнил работу, решать, что работа завершена.

4. Остановите агента, а затем прочтите всё. Максимальное количество итераций, бюджет токенов, шаг завершения. Запустите его один раз от начала до конца, а затем прочтите каждую строку кода. Первый запуск — это измерение, а не результат.

Затем посмотрите на то, что вы создали. Вы спроектировали это один раз, и оно работало без вашего участия на каждом шаге. Вот в чём настоящий сдвиг. Но эффект будет сохраняться только в том случае, если вы будете настраивать систему как инженер, а не как человек, ищущий разрешения перестать думать. Читайте, что получилось. Сохраняйте своё мнение. Система сама будет печатать. Думать — это ваша работа.

Источник:
https://www.pulumi.com/blog/stop-prompting-design-the-loop/
👎3👍2
День 2719. #TipsAndTricks
Паттерн «Строитель» с Неявным Оператором
Обычно паттерн «Строитель» позволяет вам выполнить сложную логику создания объекта, а потом вызвать метод Build(), который вернёт нужный вам объект. Например:
internal record PersonBuilder
{
internal PersonBuilder()
{
FirstName = "Jane";
LastName = "Doe";
Age = 42;
Height = 169;
}

internal string FirstName { get; init; }
internal string FamilyName { get; init; }
internal int Age { get; init; }
internal int Height { get; init; }

internal Person Build()
=> new Person(FirstName, LastName, Age, Height);
}

Мы можем создать объект Person так:
var person = new PersonBuilder
{
FirstName = "John"
}.Build();

Было бы неплохо, если бы можно было опустить вызов .Build() в конце. А это возможно. Можно создать неявный оператор для нашего строителя:
public static implicit operator 
Person(PersonBuilder b) => b.Build();


Теперь можно использовать строитель так:
Person person = new PersonBuilder
{
FirstName = "John"
};

Неявный оператор вызывается, т.к. мы изменили var на Person при объявлении переменной.

Источник:
https://josef.codes/bonus-builder-pattern-with-the-implicit-operator-using-c-sharp/
👎12👍2
День 2720. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

39. Преимущества микросервисов
«Расскажите о преимуществах использования микросервисной архитектуры в приложениях .NET?»

Хороший ответ
Микросервисная архитектура предлагает ряд преимуществ для разработки сложных приложений, особенно в средах, требующих гибкости, масштабируемости и высокой доступности.

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

- Гибкость: микросервисы позволяют небольшим автономным командам разрабатывать, развёртывать и масштабировать свои сервисы независимо друг от друга. Это приводит к более быстрым циклам разработки и упрощает экспериментирование и инновации.

- Устойчивость: независимость сервисов повышает общую устойчивость системы. При сбое в одном сервисе остальные могут продолжать работать.

- Гибкость развёртывания: микросервисы могут развёртываться независимо. Это особенно полезно в средах непрерывного развёртывания, где необходимо часто выпускать обновления.

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

Часто встречающийся неверный ответ
«Можно разбить приложение на более мелкие проекты для каждой сущности или группы сущностей. Это делает приложение микросервисной архитектурой.»

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

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

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

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

Источник:
https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎7👍1
День 2721. #ЗаметкиНаПолях #AI
Превращаем Claude из Автодополнителя в Коллегу. Начало
Claude не «плохо разбирается в .NET». Он слеп. Вы просите его «добавить конечную точку», а он уверенно:
- изобретает структуру папок, которая не соответствует вашему репозиторию;
- добавляет шаблоны, которые вы не используете;
- пропускает ваш рабочий процесс сборки/тестирования;
- нарушает соглашения и заставляет вас убирать за ним.
Это не проблема ИИ. Это проблема адаптации.


Решение — один файл, который становится системой оперирования вашим проектом для Claude: CLAUDE.md.

В Claude Code файлы памяти, такие как ./CLAUDE.md (и модульные правила в ./.claude/rules/*.md), автоматически загружаются в качестве контекста при запуске инструмента, поэтому Claude начинает каждую сессию с картой вашего проекта и правилами, вместо того чтобы гадать.

Что на самом деле делает CLAUDE.md
Представьте CLAUDE.md как вводные инструкции для нового старшего разработчика. Однако этот «разработчик» будет:
- последовательно следовать вашим инструкциям,
- запоминать их между сессиями,
- перестанет каждый раз заново изобретать вашу архитектуру.

В документации Claude Code файл CLAUDE.md описывается как файл конфигурации проекта, который находится в вашем репозитории и автоматически загружается в контекст.

И что важно: вам не нужно начинать с нуля. Claude Code поддерживает команду /init для генерации стартового файла CLAUDE.md путём сканирования вашего репозитория — затем вы его дорабатываете.

Правило: ваш CLAUDE.md должен предотвращать дорогостоящие ошибки
Если CLAUDE.md не предотвращает одну из следующих ошибок, он не выполняет свою работу:
- неправильные шаблоны (репозитории, AutoMapper, случайные абстракции, которые вы не используете);
- неправильный рабочий процесс (изменения без плана, отсутствие тестов, отсутствие валидации);
- неправильные значения по умолчанию (тайм-ауты, отмена, повторные попытки, фоновая работа, время жизни DI).

Поэтому не пишите его как документацию. Пишите его как: ограничители + карта + команды.

Структура памяти Claude Code
Claude Code поддерживает небольшую иерархию файлов памяти, включая:
- ./CLAUDE.md для общей памяти проекта,
- ./.claude/rules/*.md для модульных правил,
- ~/.claude/CLAUDE.md для ваших личных глобальных настроек,
- ./CLAUDE.local.md для ваших личных заметок по проекту (и он НЕ предназначен для того, чтобы его коммитили).
Это означает, что вы можете:
- держать краткий корневой файл CLAUDE.md,
- поместить подробные правила в .claude/rules/,
- хранить ваши личные данные в CLAUDE.local.md, не засоряя репозиторий.

Минимально необходимый CLAUDE.md для бэкенда на .NET 10
Создайте CLAUDE.md в корневом каталоге вашего репозитория:
## What this repo is
- .NET 10 backend API focused on scalability, p95/p99 latency, and production reliability.
- Prefer simple, copy/pasteable patterns over "clever architecture".

## Tech stack --> Зависит от ваших условий
- .NET 10 / C# (modern style)
- ASP.NET Core (Minimal APIs)
- Dapper (NOT EF Core)
- Redis (IDistributedCache / StackExchange.Redis depending on project)
- Azure hosting (App Service / Containers)
- Observability: Application Insights (logs + metrics)

## Repo map --> Измените, чтоб соответствовало вашим папкам
- src/Api/ → endpoints, DI, middleware, startup
- src/App/ → use-cases/services (business orchestration)
- src/Infra/ → DB, Redis, HTTP clients, external integrations
- tests/ → unit + integration tests

## Hard rules (do not violate)
- NEVER add new framework layers or "Clean Architecture cosplay".
- NEVER introduce patterns we don't use (AutoMapper, repositories, magic abstractions).
- Always pass CancellationToken through all async calls.
- No sync-over-async (no .Result/.Wait).
- No Task.Run inside request handlers.
- Outbound HTTP MUST have timeouts + cancellation.
- Caching MUST have: time budget, stampede protection strategy, and key versioning.

## Default workflow
1) Ask for missing requirements before changing code.
2) Propose a plan + list files to touch.
3) Implement smallest change that works.
4) Add/update tests when relevant.
5) Provide commands to verify (build/test/run).

## Commands --> Измените на нужные вам
- Build: `dotnet build`
- Test: `dotnet test`
- Run API: `dotnet run --project src/Api`
- Format: `dotnet format`

## Output format
- Prefer short sections, small code blocks, and explain trade-offs.
- When making changes: show diff-level guidance + why.


Этого достаточно, чтобы избежать 80% гаданий Claude.

Далее сделаем его жутко эффективным.

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

Источник:
https://medium.com/codetodeploy/claude-md-for-net-10-turn-claude-from-autocomplete-into-a-teammate-5ae9d5ad0b92
👎6👍1
День 2722. #ЗаметкиНаПолях #AI
Превращаем Claude из Автодополнителя в Коллегу. Окончание

Начало

Модульные правила: прекратите повторяться и остановите импровизации Claude
Claude Code поддерживает модульные правила в папке .claude/rules/*.md.

Создадим:
1. .claude/rules/api-conventions.md:
# API conventions (Minimal APIs) ---> Зависит от ваших настроек
- Prefer Minimal APIs (`MapGet/MapPost`) over controllers.
- Endpoints must accept CancellationToken.
- Validate inputs early. Return ProblemDetails for errors.
- For outbound calls: use IHttpClientFactory + timeouts.
- For "slow work": queue it (don't run it in the request).


2. .claude/rules/perf-reliability.md:
# Perf + reliability rules
- All outbound HTTP:
- Timeout per request
- CancellationToken wired through
- Retry ONLY for safe idempotent calls (and never infinite)
- Redis:
- Cache calls get a small time budget (fail fast)
- Stampede protection for hot keys (singleflight or equivalent)
- TTL jitter for large fleets
- Logging:
- Avoid high-cardinality fields
- Avoid expensive structured logs in hot paths


3. .claude/rules/security.md:
# Security rules
- No secrets in code or CLAUDE.md.
- Avoid putting sensitive IDs/tokens in query strings.
- Auth must be explicit on endpoints (no accidental public routes).
- CORS must be restrictive (no AllowAnyOrigin in prod configs).

Теперь корневой CLAUDE.md остаётся коротким и читаемым, а правила — таргетированными.

Как быстро сделать Claude полезным?
Собственные рекомендации Claude сводятся к следующему: относитесь к CLAUDE.md как к живому конфигурационному файлу, который вы совершенствуете со временем.

Пошагово:
- Запустите /init один раз, чтобы получить начальный файл.
- Удалите всё «сферическое в вакууме». Сохраняйте только специфичные для репозитория правила.
- Каждый раз, когда Claude делает что-то не то, добавляйте правило, которое это предотвращает.
- Переместите большие фрагменты правил в .claude/rules/, чтобы корневой файл оставался чистым.
- При переключении задач очищайте контекст с помощью /clear, чтобы Claude не переносил старые неактуальные детали в следующую имплементацию.
В блоге Claude также упоминается использование клавиши # для добавления инструкций, которые вы постоянно повторяете (так что они накапливаются в CLAUDE.md со временем).

Что добавлять (а что нет)
1. Добавить (полезно)
- точные версии технологий + фреймворки, которые вы действительно используете;
- карту папок + «где что находится»;
- ваши строгие правила (тайм-ауты, отмена, отсутствие Task.Run, отсутствие синхронного кода поверх асинхронного);
- команды сборки/тестирования/запуска;
- шаблоны, которые вы не хотите, чтобы предлагались.

2. Не добавлять (мало полезно или опасно)
- секреты, токены, строки подключения (никогда!);
- правила стиля, которые уже применяются вашим форматтером;
- «войну-и-мир» документации, которую Claude всё равно сможет просмотреть.

Цель не в том, чтобы научить Клода .NETу. Цель — научить Клода вашему репозиторию.

Простой «тестовый промпт» для проверки вашего CLAUDE.md:
«Добавь новую конечную точку, которая вызывает нижестоящий HTTP-сервис и безопасно кэширует ответ. Сначала покажи план, затем реализуй».


Если Claude (например):
- использует минимальный API;
- использует CancellationToken по всей цепочке;
- устанавливает тайм-аут запроса;
- избегает Task.Run;
- добавляет безопасные шаблоны кэширования
…ваш CLAUDE.md работает.

Если он импровизирует, в вашем CLAUDE.md отсутствуют правила.

Итого
Если вы серьёзно относитесь к разработке с использованием ИИ в .NET, перестаньте настойчиво промптить и начните лучше проводить адаптацию. CLAUDE.md поможет превратить Claude из «умного автозаполнения» в «надёжного члена команды».

Источник:
https://medium.com/codetodeploy/claude-md-for-net-10-turn-claude-from-autocomplete-into-a-teammate-5ae9d5ad0b92
👎5👍2