3 простые оптимизации, которые реально ускоряют код
1️⃣ Забирай данные пачкой
Меньше запросов — меньше сетевых задержек.
Вместо десятков запросов — один
2️⃣ Делай больше параллельно
Если задачи не зависят друг от друга — выполняй их одновременно.
Асинхронность часто даёт бесплатный прирост скорости.
3️⃣ Кэшируй результаты
Если данные не меняются — не пересчитывай и не запрашивай их заново.
Память дешевле времени.
Никакой магии и сложных алгоритмов — просто базовые приёмы, которые в реальных проектах дают самый заметный эффект.
1️⃣ Забирай данные пачкой
Меньше запросов — меньше сетевых задержек.
Вместо десятков запросов — один
IN (...).2️⃣ Делай больше параллельно
Если задачи не зависят друг от друга — выполняй их одновременно.
Асинхронность часто даёт бесплатный прирост скорости.
3️⃣ Кэшируй результаты
Если данные не меняются — не пересчитывай и не запрашивай их заново.
Память дешевле времени.
Никакой магии и сложных алгоритмов — просто базовые приёмы, которые в реальных проектах дают самый заметный эффект.
⚡️ В мире #dotnet есть куда больше вариантов для messaging, чем RabbitMQ и Kafka.
И для real-time систем эти инструменты часто избыточны.
Большинство брокеров сообщений заточены под надёжность, сложную маршрутизацию и enterprise-сценарии.
Это отлично, когда нужны гарантии доставки и сложные пайплайны.
Но если система живёт на частых событиях, минимальной задержке и мгновенной реакции, этот вес начинает мешать.
Здесь идеально заходит NATS.
NATS - лёгкая и сверхбыстрая система обмена сообщениями, спроектированная именно для real-time:
- минимальная латентность
- простая модель
- высокая масштабируемость
Отлично подходит для:
- телеметрии и sensor data
- live-обновлений
- edge-систем
- сценариев, где важно «сейчас», а не «через секунду»
В статье подробно разобрано:
- когда NATS имеет смысл (а когда нет)
- сравнение с RabbitMQ и Kafka
- использование NATS в .NET с понятными примерами кода
- Pub/Sub, queue groups и request–reply
https://thecodeman.net/posts/introduction-to-nats-real-time-messaging
И для real-time систем эти инструменты часто избыточны.
Большинство брокеров сообщений заточены под надёжность, сложную маршрутизацию и enterprise-сценарии.
Это отлично, когда нужны гарантии доставки и сложные пайплайны.
Но если система живёт на частых событиях, минимальной задержке и мгновенной реакции, этот вес начинает мешать.
Здесь идеально заходит NATS.
NATS - лёгкая и сверхбыстрая система обмена сообщениями, спроектированная именно для real-time:
- минимальная латентность
- простая модель
- высокая масштабируемость
Отлично подходит для:
- телеметрии и sensor data
- live-обновлений
- edge-систем
- сценариев, где важно «сейчас», а не «через секунду»
В статье подробно разобрано:
- когда NATS имеет смысл (а когда нет)
- сравнение с RabbitMQ и Kafka
- использование NATS в .NET с понятными примерами кода
- Pub/Sub, queue groups и request–reply
https://thecodeman.net/posts/introduction-to-nats-real-time-messaging
Интеграционные тесты прямо в CI/CD - максимум уверенности в коде 🚀
Я запускаю интеграционные тесты прямо внутри CI/CD пайплайна.
Так я проверяю не абстракции и моки, а реальное поведение системы.
Всё это возможно благодаря:
- Docker
- Testcontainers
Идея простая:
ты поднимаешь реальные внешние сервисы как контейнеры и используешь их прямо в тестах.
В примере поднимаются:
- PostgreSQL
- Redis
- Keycloak
Контейнеры:
- автоматически стартуют перед тестами
- доступны из кода приложения
- уничтожаются после выполнения тестов
Почему это работает лучше моков:
- тесты максимально близки к продакшену
- одинаковое поведение локально и в CI
- сразу ловятся проблемы с конфигурацией, миграциями, auth
- меньше сюрпризов после деплоя
Особенно полезно для:
- backend-сервисов
- микросервисной архитектуры
- систем с авторизацией и внешними зависимостями
- .NET-приложений с серьёзной бизнес-логикой
Если хочешь вывести интеграционное тестирование в .NET на новый уровень — Testcontainers стоит попробовать обязательно.
#dotnet, #testing
Я запускаю интеграционные тесты прямо внутри CI/CD пайплайна.
Так я проверяю не абстракции и моки, а реальное поведение системы.
Всё это возможно благодаря:
- Docker
- Testcontainers
Идея простая:
ты поднимаешь реальные внешние сервисы как контейнеры и используешь их прямо в тестах.
В примере поднимаются:
- PostgreSQL
- Redis
- Keycloak
Контейнеры:
- автоматически стартуют перед тестами
- доступны из кода приложения
- уничтожаются после выполнения тестов
Почему это работает лучше моков:
- тесты максимально близки к продакшену
- одинаковое поведение локально и в CI
- сразу ловятся проблемы с конфигурацией, миграциями, auth
- меньше сюрпризов после деплоя
Особенно полезно для:
- backend-сервисов
- микросервисной архитектуры
- систем с авторизацией и внешними зависимостями
- .NET-приложений с серьёзной бизнес-логикой
Если хочешь вывести интеграционное тестирование в .NET на новый уровень — Testcontainers стоит попробовать обязательно.
#dotnet, #testing
🔍 Как делать code review, которые находят реальные баги, а не только придираются к стилю
Большинство code review застревают на форматировании, нейминге и мелочах. В итоге реальные проблемы - логические ошибки, гонки, неправильные инварианты - проходят мимо.
Идея правильного code review:
— Проверять поведение кода, а не его внешний вид
— Искать сценарии отказа, а не соответствие линтеру
— Думать как система, а не как форматтер
Что реально стоит смотреть на ревью:
• Граничные случаи и ошибки в бизнес-логике
• Null / empty сценарии и некорректные состояния
• Побочные эффекты и порядок операций
• Работа с асинхронностью, транзакциями и ресурсами
• Соответствие инвариантам доменной модели
Инструменты с AI-ассистентами для code review помогают сместить фокус:
— меньше шума про стиль
— больше внимания к логике и потенциальным багам
— автоматические подсказки прямо в PR
Хороший code review — это не «код красивый»,
а «код не сломается в проде».
Большинство code review застревают на форматировании, нейминге и мелочах. В итоге реальные проблемы - логические ошибки, гонки, неправильные инварианты - проходят мимо.
Идея правильного code review:
— Проверять поведение кода, а не его внешний вид
— Искать сценарии отказа, а не соответствие линтеру
— Думать как система, а не как форматтер
Что реально стоит смотреть на ревью:
• Граничные случаи и ошибки в бизнес-логике
• Null / empty сценарии и некорректные состояния
• Побочные эффекты и порядок операций
• Работа с асинхронностью, транзакциями и ресурсами
• Соответствие инвариантам доменной модели
Инструменты с AI-ассистентами для code review помогают сместить фокус:
— меньше шума про стиль
— больше внимания к логике и потенциальным багам
— автоматические подсказки прямо в PR
Хороший code review — это не «код красивый»,
а «код не сломается в проде».
В какой строке возникнет первое исключение?
Anonymous Quiz
25%
1
11%
2
8%
3
7%
4
5%
5
29%
Весь код выполнится корректно
14%
🎄
💡 В EF Core есть штука, о которой многие забывают: CompileAsyncQuery.
С её помощью можно заранее скомпилировать LINQ-запрос,а потом выполнять его асинхронно: быстрее и без лишнего overhead’а.
И важный еще момент:
вам не нужно писать отдельную “async-версию” самого LINQ-запроса.
EF сам оборачивает выполнение в асинхронный пайплайн. Немного странно, но работает.
Пример:
Что получаем:
• повторные запросы выполняются быстрее
• нет лишней компиляции выражений
• async-подход сохраняется
Если в проекте есть часто вызываемые запросы - Compiled Queries могут дать хороший прирост производительности, особенно под нагрузкой.
С её помощью можно заранее скомпилировать LINQ-запрос,а потом выполнять его асинхронно: быстрее и без лишнего overhead’а.
И важный еще момент:
вам не нужно писать отдельную “async-версию” самого LINQ-запроса.
EF сам оборачивает выполнение в асинхронный пайплайн. Немного странно, но работает.
Пример:
private static Func<AppDbContext, string, Task<Newsletter?>> GetByTitle =
EF.CompileAsyncQuery(
(AppDbContext context, string noscript) =>
context.Set<Newsletter>()
.FirstOrDefault(c => c.Title == noscript)
);
public async Task<Newsletter?> GetNewsletterByTitleAsync(string noscript)
{
return await GetByTitle(this, noscript);
}
Что получаем:
• повторные запросы выполняются быстрее
• нет лишней компиляции выражений
• async-подход сохраняется
Если в проекте есть часто вызываемые запросы - Compiled Queries могут дать хороший прирост производительности, особенно под нагрузкой.
Индекс TIOBE подвел итоги года: звание «Язык 2025 года» досталось C#, который показал рекордный рост популярности (+2.94%)? однако в общем зачете он по-прежнему занимает 5-ю строчку. Абсолютным лидером остается Python с 22.61% долей рынка.
В первой пятерке произошли перестановки: язык C поднялся на 2 место, сместив C++ на 4-ю позицию; 3 место досталось Java, а R вернулся в топ-10. Провал года - Go, который неожиданно сдал позиции, опустившись сразу на 16-е место.
Индекс оценивает популярность технологий на основе поисковых запросов, активности комьюнити и количества обучающих материалов.
https://www.tiobe.com/tiobe-index/
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥 Как просто добавить multi-tenant в .NET через заголовок запроса
Иногда нужно понимать, какому клиенту (tenant) принадлежит запрос.
Самый простой вариант - передавать
Пример сервиса:
Иногда нужно понимать, какому клиенту (tenant) принадлежит запрос.
Самый простой вариант - передавать
X-TenantId в HTTP-заголовке и читать его в сервисе.Пример сервиса:
public sealed class TenantProvider
{
private const string TenantIdHeaderName = "X-TenantId";
private readonly IHttpContextAccessor _httpContextAccessor;
public TenantProvider(IHttpContextAccessor httpContextAccessor)
=> _httpContextAccessor = httpContextAccessor;
public string TenantId =>
_httpContextAccessor.HttpContext.Request.Headers[TenantIdHeaderName];
}
using System;
class Base
{
public virtual void Foo(object o) => Console.WriteLine("Base.Foo(object)");
}
class Derived : Base
{
public override void Foo(object o) => Console.WriteLine("Derived.Foo(object)");
public void Foo(string s) => Console.WriteLine("Derived.Foo(string)");
}
class Program
{
static void Main()
{
Base b = new Derived();
b.Foo("A");
((Derived)b).Foo("A");
dynamic d = b;
d.Foo("A");
object x = "A";
d.Foo(x);
d.Foo(null);
}
}
📌 Ответ:
Derived.Foo(string)
Derived.Foo(string)
Derived.Foo(string)
Derived.Foo(string)
Пояснение:
Статический тип переменной - Base.
Компилятор видит только Foo(object).
Вызов виртуальный, поэтому выполняется override:
Derived.Foo(object)
2) ((Derived)b).Foo("A")
Статический тип - Derived.
Есть перегрузка Foo(string), она точнее для string:
Derived.Foo(string)
3) dynamic d = b; d.Foo("A")
dynamic откладывает выбор метода до runtime.
Реальный тип объекта - Derived.
Реальный тип аргумента - string:
Derived.Foo(string)
4) object x = "A"; d.Foo(x)
Для dynamic учитывается реальный тип аргумента.
x содержит string:
Derived.Foo(string)
5) d.Foo(null)
null подходит и для object, и для string.
string - более специфичный тип:
Derived.Foo(string)
Please open Telegram to view this post
VIEW IN TELEGRAM
.NET 9 стал гораздо устойчивее к сбоям 💪
В .NET 9 появились официальные пакеты для отказоустойчивости:
-
-
Они построены поверх Polly, поэтому API знакомый - легко описывать политики:
- retry
- circuit-breaker
- fallback
- timeout
- hedging
- rate limiter
Подключаете и ваши HTTP-клиенты и сервисы автоматически восстанавливаются при сетевых ошибках и таймаутах.
Итог: меньше падений и больше стабильности. Отлично для продакшена 🚀
В .NET 9 появились официальные пакеты для отказоустойчивости:
-
Microsoft.Extensions.Resilience -
Microsoft.Extensions.Http.ResilienceОни построены поверх Polly, поэтому API знакомый - легко описывать политики:
- retry
- circuit-breaker
- fallback
- timeout
- hedging
- rate limiter
Подключаете и ваши HTTP-клиенты и сервисы автоматически восстанавливаются при сетевых ошибках и таймаутах.
Итог: меньше падений и больше стабильности. Отлично для продакшена 🚀
Не все стратегии балансировки нагрузки одинаково полезны.
Если ты используешь YARP (Yet Another Reverse Proxy) в .NET - у него есть 5 встроенных способов распределять трафик между серверами. Но какой выбрать?
Вот понятный разбор:
1) RoundRobin (по кругу)
Классика: запросы равномерно идут по очереди на каждый сервер.
✅ просто
❌ плохо работает, если сервера/запросы не одинаковые по скорости
2) LeastRequests (минимум активных запросов)
Самый “умный” вариант: отправляет запрос туда, где сейчас меньше всего работы.
✅ отлично при разном времени обработки запросов
✅ помогает снизить задержки на “хвосте” (tail latency)
3) Random (случайно)
Сервер выбирается случайно.
✅ неожиданно хорошо работает при большом потоке однотипных запросов
❌ может иногда “не повезти” и нагрузить один сервер сильнее
4) PowerOfTwoChoices (2 случайных - выбираем лучший)
Баланс между качеством и стоимостью: берём 2 случайных сервера и выбираем тот, где меньше активных запросов.
✅ почти как LeastRequests, но дешевле по логике
✅ не надо проверять все сервера каждый раз
5) FirstAlphabetical (первый доступный)
Всегда выбирает “первый” сервер из списка (условно самый верхний/раньше по имени).
✅ хорошо для failover (есть основной сервер, остальные - запасные)
❌ плохо распределяет нагрузку (по сути почти без балансировки)
Большинство по привычке ставят RoundRobin, но если время обработки запросов разное - переход на LeastRequests часто заметно улучшает tail latency.
Разбор с примерами, как масштабировать ASP.NET Core API через YARP
А какая стратегия у тебя по умолчанию?
Если ты используешь YARP (Yet Another Reverse Proxy) в .NET - у него есть 5 встроенных способов распределять трафик между серверами. Но какой выбрать?
Вот понятный разбор:
1) RoundRobin (по кругу)
Классика: запросы равномерно идут по очереди на каждый сервер.
✅ просто
❌ плохо работает, если сервера/запросы не одинаковые по скорости
2) LeastRequests (минимум активных запросов)
Самый “умный” вариант: отправляет запрос туда, где сейчас меньше всего работы.
✅ отлично при разном времени обработки запросов
✅ помогает снизить задержки на “хвосте” (tail latency)
3) Random (случайно)
Сервер выбирается случайно.
✅ неожиданно хорошо работает при большом потоке однотипных запросов
❌ может иногда “не повезти” и нагрузить один сервер сильнее
4) PowerOfTwoChoices (2 случайных - выбираем лучший)
Баланс между качеством и стоимостью: берём 2 случайных сервера и выбираем тот, где меньше активных запросов.
✅ почти как LeastRequests, но дешевле по логике
✅ не надо проверять все сервера каждый раз
5) FirstAlphabetical (первый доступный)
Всегда выбирает “первый” сервер из списка (условно самый верхний/раньше по имени).
✅ хорошо для failover (есть основной сервер, остальные - запасные)
❌ плохо распределяет нагрузку (по сути почти без балансировки)
Большинство по привычке ставят RoundRobin, но если время обработки запросов разное - переход на LeastRequests часто заметно улучшает tail latency.
Разбор с примерами, как масштабировать ASP.NET Core API через YARP
А какая стратегия у тебя по умолчанию?
Что выведет на экран это код?
Anonymous Quiz
24%
Base Foo, Base Foo
31%
Base Foo, Derived Foo
24%
Derived Foo, Derived Foo
10%
Derived Foo, Base Foo
10%
🎯 Открытый урок «Сетевой чат на C#».
🗓 22 января в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer».
На вебинаре:
✔️ Рассмотрим написание сетевого приложения на C#.
✔️ Мы реализуем простые клиент и сервер с помощью одного из сетевых протоколов.
✔️Также затронем темы многопточности и асинхронности
Кому будет полезно:
• Вебинар будет полезен начинающим разработчикам, желающим разобраться в сетевом и многопочном\асинхронном программировании.
Что вы получите:
• По итогам вебинара смогут проектировать сетевые приложения.
• Получат представление о работе сетевых протоколов, и многопоточности\асинхронности в приложениях.
• На практике попробуют разработать такое приложение.
🔗 Ссылка на регистрацию: https://otus.pw/zifo/?erid=2W5zFJaPXHA
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
🗓 22 января в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «C# Developer».
На вебинаре:
✔️ Рассмотрим написание сетевого приложения на C#.
✔️ Мы реализуем простые клиент и сервер с помощью одного из сетевых протоколов.
✔️Также затронем темы многопточности и асинхронности
Кому будет полезно:
• Вебинар будет полезен начинающим разработчикам, желающим разобраться в сетевом и многопочном\асинхронном программировании.
Что вы получите:
• По итогам вебинара смогут проектировать сетевые приложения.
• Получат представление о работе сетевых протоколов, и многопоточности\асинхронности в приложениях.
• На практике попробуют разработать такое приложение.
🔗 Ссылка на регистрацию: https://otus.pw/zifo/?erid=2W5zFJaPXHA
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.
IMemoryCache
✅ Очень быстрый
❌ Работает только внутри одной ноды
(в кластере из 10 серверов у вас 10 разных кэшей)
Redis / IDistributedCache
✅ Один общий кэш на все ноды - данные одинаковые
❌ Медленнее: сеть + сериализация + лишние накладные расходы
Правильный подход - Hybrid Cache.
Он не заставляет выбирать - он комбинирует оба мира:
- L1 (Local / RAM) - сначала читаем из памяти приложения (самый быстрый слой)
- L2 (Distributed / Redis) - если локально нет, идём в общий кэш
- Source (DB/API) - если нет и там, берём из источника и прогреваем оба уровня
🚀 скорость как у MemoryCache
🔒 данные согласованы между нодами как в Redis
📉 меньше нагрузки на БД и меньше «холодных» запросов
Microsoft делает нативный HybridCache, но если нужно решение уже сейчас - FusionCache остаётся самым надёжным и реально продакшн-готовым вариантом.
📌 Гайд
Please open Telegram to view this post
VIEW IN TELEGRAM
🚀 Опасен баг, который вылазит через 3 дня после релиза.
И это ровно тот риск, который прячется в стандартном Options Pattern в .NET.
Ты спокойно делаешь так:
- биндишь
- инжектишь настройки через DI
- приложение стартует без проблем ✅
А потом внезапно…
В прод падает , потому что:
❌ не задан обязательный
❌
❌ строка подключения пустая
И ты узнаёшь об этом только тогда, когда кто-то нажмёт нужную кнопку в твоем приложении.
Вот почему принцип Fail Fast критичен для конфигов:
если конфигурация невалидна - приложение не должно запускаться вообще.
Как это сделать правильно:
используй расширение Options Pattern через IValidateOptions.
Суть подхода:
1) задаём правила (например:
2) регистрируем валидатор
3) если условия нарушены - DI кидает исключение сразу при старте ✅
Можно прйти дальше и подключиит FluentValidation, чтобы условия были:
- чище
- читабельнее
- удобнее расширять
Полная реализация: https://milanjovanovic.tech/blog/options-pattern-validation-in-aspnetcore-with-fluentvalidation?utm_source=X&utm_medium=social&utm_campaign=05.01.2026
И это ровно тот риск, который прячется в стандартном Options Pattern в .NET.
Ты спокойно делаешь так:
- биндишь
appsettings.json в класс- инжектишь настройки через DI
- приложение стартует без проблем ✅
А потом внезапно…
В прод падает , потому что:
❌ не задан обязательный
ApiKey ❌
RetryCount = 0 ❌ строка подключения пустая
И ты узнаёшь об этом только тогда, когда кто-то нажмёт нужную кнопку в твоем приложении.
Вот почему принцип Fail Fast критичен для конфигов:
если конфигурация невалидна - приложение не должно запускаться вообще.
Как это сделать правильно:
используй расширение Options Pattern через IValidateOptions.
Суть подхода:
1) задаём правила (например:
ApiKey не null, `RetryCount > 0`) 2) регистрируем валидатор
3) если условия нарушены - DI кидает исключение сразу при старте ✅
Можно прйти дальше и подключиит FluentValidation, чтобы условия были:
- чище
- читабельнее
- удобнее расширять
Полная реализация: https://milanjovanovic.tech/blog/options-pattern-validation-in-aspnetcore-with-fluentvalidation?utm_source=X&utm_medium=social&utm_campaign=05.01.2026