⚡️ Миграция хеширования паролей без поломки логина
Ты сделал маленький и вроде бы аккуратный рефакторинг - и сломал весь логин.
Логику хеширования паролей ты аккуратно вынес в отдельный слой. Отлично.
А потом решил обновиться — заменить PBKDF2 на bcrypt.
Просто поменял реализацию. Казалось бы, безопасно.
Но внезапно:
- старые пользователи больше не могут войти
- пароли перестали совпадать
- аутентификация массово падает
Ты только что внёс breaking change, даже не заметив этого.
Это классическая ловушка в аутентификации — менять алгоритм хеширования без стратегии миграции.
Важно понимать:
- пароли в базе уже захешированы старым алгоритмом
- новый алгоритм не умеет их проверять
- простой swap реализации = мгновенный outage
-
⚡️Правильный подход:
- поддерживать несколько алгоритмов одновременно
- определять алгоритм по формату хеша
- при успешном логине пересохранять пароль новым алгоритмом
- без сброса паролей
- без даунтайма
Маленькие изменения в use case-ах, особенно в аутентификации, могут иметь огромные последствия.
Ты сделал маленький и вроде бы аккуратный рефакторинг - и сломал весь логин.
Логику хеширования паролей ты аккуратно вынес в отдельный слой. Отлично.
А потом решил обновиться — заменить PBKDF2 на bcrypt.
Просто поменял реализацию. Казалось бы, безопасно.
Но внезапно:
- старые пользователи больше не могут войти
- пароли перестали совпадать
- аутентификация массово падает
Ты только что внёс breaking change, даже не заметив этого.
Это классическая ловушка в аутентификации — менять алгоритм хеширования без стратегии миграции.
Важно понимать:
- пароли в базе уже захешированы старым алгоритмом
- новый алгоритм не умеет их проверять
- простой swap реализации = мгновенный outage
-
⚡️Правильный подход:
- поддерживать несколько алгоритмов одновременно
- определять алгоритм по формату хеша
- при успешном логине пересохранять пароль новым алгоритмом
- без сброса паролей
- без даунтайма
Маленькие изменения в use case-ах, особенно в аутентификации, могут иметь огромные последствия.
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 LOAD BALANCER ЗА 1 МИНУТУ
Load Balancer - это «диспетчер трафика» между пользователями и серверами.
Когда пользователей становится много, один сервер перестаёт справляться:
- 500 пользователей — работает нормально
- 1 000 — начинает тормозить
- 10 000 — может упасть из-за перегрузки
Load Balancer распределяет входящие запросы между несколькими серверами, чтобы ни один из них не перегружался. Это повышает производительность и позволяет системе обслуживать больше пользователей.
Проблемы без Load Balancer:
- Один сервер = одна точка отказа
- Любой сбой или проблема с сетью — приложение полностью недоступно
- Ограниченная мощность
- При росте нагрузки — медленные ответы и падения
Как работает Load Balancer:
1. Все запросы сначала попадают в Load Balancer
2. Он проверяет, какие серверы работают и доступны
3. Распределяет трафик по серверам на основе:
- текущей нагрузки
- времени ответа
- доступности
4. Если сервер перестаёт отвечать — трафик автоматически перенаправляется на рабочие
В результате:
- нагрузка распределяется равномерно
- используются только «здоровые» серверы
- уменьшаются задержки
- система остаётся стабильной
Зачем нужен Load Balancer:
- Scalability — можно добавлять новые серверы без изменений на стороне клиента
- High Availability — если один сервер падает, система продолжает работать
- Better Performance — запросы обрабатываются быстрее
- Efficient resource usage — равномерное использование ресурсов и отсутствие узких мест
Главная идея:
Load Balancer — основа масштабируемых и отказоустойчивых систем. Без него любое приложение рано или поздно упрётся в предел одного сервера.
Подписывайся, больше фишек каждый день !
Load Balancer - это «диспетчер трафика» между пользователями и серверами.
Когда пользователей становится много, один сервер перестаёт справляться:
- 500 пользователей — работает нормально
- 1 000 — начинает тормозить
- 10 000 — может упасть из-за перегрузки
Load Balancer распределяет входящие запросы между несколькими серверами, чтобы ни один из них не перегружался. Это повышает производительность и позволяет системе обслуживать больше пользователей.
Проблемы без Load Balancer:
- Один сервер = одна точка отказа
- Любой сбой или проблема с сетью — приложение полностью недоступно
- Ограниченная мощность
- При росте нагрузки — медленные ответы и падения
Как работает Load Balancer:
1. Все запросы сначала попадают в Load Balancer
2. Он проверяет, какие серверы работают и доступны
3. Распределяет трафик по серверам на основе:
- текущей нагрузки
- времени ответа
- доступности
4. Если сервер перестаёт отвечать — трафик автоматически перенаправляется на рабочие
В результате:
- нагрузка распределяется равномерно
- используются только «здоровые» серверы
- уменьшаются задержки
- система остаётся стабильной
Зачем нужен Load Balancer:
- Scalability — можно добавлять новые серверы без изменений на стороне клиента
- High Availability — если один сервер падает, система продолжает работать
- Better Performance — запросы обрабатываются быстрее
- Efficient resource usage — равномерное использование ресурсов и отсутствие узких мест
Главная идея:
Load Balancer — основа масштабируемых и отказоустойчивых систем. Без него любое приложение рано или поздно упрётся в предел одного сервера.
Подписывайся, больше фишек каждый день !
// Пример конфигурации Nginx как Load Balancer
http {
upstream backend {
server 192.168.1.10;
server 192.168.1.11;
server 192.168.1.12;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
}
⚡️ URL shortener за <100 строк на .NET - реально
Идея простая: у тебя есть входной URL -> генеришь короткий код -> сохраняешь в БД -> по коду делаешь редирект.
Что нужно собрать
- Генератор уникального кода
Делай base62 (0-9, a-z, A-Z) длиной 6-8 символов.
Главное - гарантировать уникальность:
- либо проверка в БД и повтор генерации при коллизии
- либо уникальный индекс по Code и ретрай при ошибке сохранения
- База данных
Таблица
- Id (Guid)
- LongUrl (string)
- Code (string, unique)
- CreatedOnUtc (DateTime)
Опционально:
- ExpiresOnUtc
- Clicks
- CreatedByIp
- 2 эндпоинта (Minimal API)
- POST
- валидируешь URL (Uri.TryCreate)
- генеришь
- формируешь
- сохраняешь в БД
- возвращаешь shortUrl
- GET
- ищешь code в БД
- если нет - 404
- если есть - Results.Redirect(LongUrl)
Почему чаще всего "падают" такие сервисы
- Коллизии кода -> решается unique index + retry
- Открытый редирект на мусор -> валидируй UriKind.Absolute и при желании режь опасные схемы (только http/https)
- Производительность поиска -> индекс по Code обязателен
- Правильный shortUrl за прокси -> если сервис за nginx/cloudflare, учитывай Forwarded Headers, иначе host/scheme будут неправильными
Если делать максимально чисто - генератор кода отдельным сервисом, модель + DbContext, и два эндпоинта. Это и укладывается в <100 строк.
Идея простая: у тебя есть входной URL -> генеришь короткий код -> сохраняешь в БД -> по коду делаешь редирект.
Что нужно собрать
- Генератор уникального кода
Делай base62 (0-9, a-z, A-Z) длиной 6-8 символов.
Главное - гарантировать уникальность:
- либо проверка в БД и повтор генерации при коллизии
- либо уникальный индекс по Code и ретрай при ошибке сохранения
- База данных
Таблица
ShortenedUrl:- Id (Guid)
- LongUrl (string)
- Code (string, unique)
- CreatedOnUtc (DateTime)
Опционально:
- ExpiresOnUtc
- Clicks
- CreatedByIp
- 2 эндпоинта (Minimal API)
- POST
/shorten- валидируешь URL (Uri.TryCreate)
- генеришь
code- формируешь
shortUrl из scheme + host + code- сохраняешь в БД
- возвращаешь shortUrl
- GET
/{code}- ищешь code в БД
- если нет - 404
- если есть - Results.Redirect(LongUrl)
Почему чаще всего "падают" такие сервисы
- Коллизии кода -> решается unique index + retry
- Открытый редирект на мусор -> валидируй UriKind.Absolute и при желании режь опасные схемы (только http/https)
- Производительность поиска -> индекс по Code обязателен
- Правильный shortUrl за прокси -> если сервис за nginx/cloudflare, учитывай Forwarded Headers, иначе host/scheme будут неправильными
Если делать максимально чисто - генератор кода отдельным сервисом, модель + DbContext, и два эндпоинта. Это и укладывается в <100 строк.
В .NET 8 появился простой способ сделать HttpClient устойчивым к сбоям — буквально одной строкой.
Microsoft добавила библиотеку Microsoft.Extensions.Http.Resilience, в которой уже есть готовые pipeline’ы для обработки ошибок при HTTP-запросах.
Что это даёт из коробки:
- Retry при временных сбоях
- Timeout
- Circuit Breaker
- Rate limiting
- Защиту от перегрузки
Подключается максимально просто:
Microsoft добавила библиотеку Microsoft.Extensions.Http.Resilience, в которой уже есть готовые pipeline’ы для обработки ошибок при HTTP-запросах.
Что это даёт из коробки:
- Retry при временных сбоях
- Timeout
- Circuit Breaker
- Rate limiting
- Защиту от перегрузки
Подключается максимально просто:
services.AddHttpClient<GitHubService>(static httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
})
.AddStandardResilienceHandler();
This media is not supported in your browser
VIEW IN TELEGRAM
⚡️ Языки программирования и их самые любимые фичи
• 🐍 Python - чистый и читаемый синтаксис
• 🖥️ BASIC - очень дружелюбен для новичков
• 📊 Visual Basic - простое создание GUI
• 🟨 JavaScript - запускается везде и сразу
• 🐘 PHP - очень простой деплой веб-приложений
• 💎 Ruby - красивый и элегантный синтаксис
• 🎵 Groovy - бесшовная интеграция с Java
• ☕ Java - огромная экосистема и стабильность
• 🟣 C# - отличные инструменты и IDE
• 🐹 Go - простая и быстрая конкурентность
• 🐦 Swift - современный и безопасный дизайн
• 🅺 Kotlin - встроенная защита от null
• 🎯 Dart - отлично работает с Flutter
• 🧮 Fortran - сверхбыстрые научные вычисления
• 🔧 C - полный контроль над железом
• 🍎 Objective-C - мощная динамическая runtime-система
• 🔺 Scala - сочетание функционального и ООП
• ⚡ Zig - простой и предсказуемый системный код
• 🐪 Perl - невероятно мощная обработка текста
• 🚀 C++ - высокая производительность и контроль
• 🦀 Rust - безопасность памяти без garbage collector
• ⚙️ Assembly - максимальный контроль и производительность
• 🐍 Python - чистый и читаемый синтаксис
• 🖥️ BASIC - очень дружелюбен для новичков
• 📊 Visual Basic - простое создание GUI
• 🟨 JavaScript - запускается везде и сразу
• 🐘 PHP - очень простой деплой веб-приложений
• 💎 Ruby - красивый и элегантный синтаксис
• 🎵 Groovy - бесшовная интеграция с Java
• ☕ Java - огромная экосистема и стабильность
• 🟣 C# - отличные инструменты и IDE
• 🐹 Go - простая и быстрая конкурентность
• 🐦 Swift - современный и безопасный дизайн
• 🅺 Kotlin - встроенная защита от null
• 🎯 Dart - отлично работает с Flutter
• 🧮 Fortran - сверхбыстрые научные вычисления
• 🔧 C - полный контроль над железом
• 🍎 Objective-C - мощная динамическая runtime-система
• 🔺 Scala - сочетание функционального и ООП
• ⚡ Zig - простой и предсказуемый системный код
• 🐪 Perl - невероятно мощная обработка текста
• 🚀 C++ - высокая производительность и контроль
• 🦀 Rust - безопасность памяти без garbage collector
• ⚙️ Assembly - максимальный контроль и производительность
Forwarded from C# (C Sharp) programming
Vector Search - как это работает (и почему это важно для .NET разработчиков)
Vector search ищет смысловую близость, а не просто точные совпадения.
Он сравнивает данные - текст, изображения или аудио - используя векторные эмбеддинги в многомерном пространстве.
То есть система ищет не одинаковые слова, а похожие по смыслу объекты.
Почему это важно?
Vector search лежит в основе многих AI-функций:
- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений
Добавив векторный поиск в приложение, разработчик может создавать намного более умные продукты, которые понимают смысл запросов пользователя.
Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.
📍 Полный пример реализации
Vector search ищет смысловую близость, а не просто точные совпадения.
Он сравнивает данные - текст, изображения или аудио - используя векторные эмбеддинги в многомерном пространстве.
То есть система ищет не одинаковые слова, а похожие по смыслу объекты.
Почему это важно?
Vector search лежит в основе многих AI-функций:
- семантический поиск
- рекомендательные системы
- интеграции с LLM
- умные ассистенты внутри приложений
Добавив векторный поиск в приложение, разработчик может создавать намного более умные продукты, которые понимают смысл запросов пользователя.
Это дает реальную бизнес-ценность - от поиска по документам до персонализированных рекомендаций.
📍 Полный пример реализации
Полезный паттерн для Minimal APIs в .NET, если вы хотите организовать проект по принципу Vertical Slice Architecture.
Идея очень простая.
Вместо того чтобы держать все роуты в
Создаётся небольшой интерфейс:
Дальше каждый endpoint просто реализует этот интерфейс:
Что это даёт:
• endpoints изолированы по фичам
• код становится намного чище
• проще масштабировать API
• удобно использовать вместе с CQRS / MediatR
Регистрация таких endpoints занимает буквально пару миллисекунд при старте приложения.
Отличный способ держать Minimal API структурированным даже в больших проектах.
Идея очень простая.
Вместо того чтобы держать все роуты в
Program.cs, каждый endpoint выносится в отдельный класс.Создаётся небольшой интерфейс:
public interface IEndpoint
{
void MapEndpoint(IEndpointRouteBuilder app);
}
Дальше каждый endpoint просто реализует этот интерфейс:
public class GetFollowerStats : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder app)
{
app.MapGet("users/{userId}/followers/stats", async (
Guid userId,
ISender sender) =>
{
var query = new GetFollowerStatsQuery(userId);
Result<FollowerStatsResponse> result = await sender.Send(query);
return result.Match(Results.Ok, CustomResults.Problem);
})
.WithTags(Tags.Users);
}
}
Что это даёт:
• endpoints изолированы по фичам
• код становится намного чище
• проще масштабировать API
• удобно использовать вместе с CQRS / MediatR
Регистрация таких endpoints занимает буквально пару миллисекунд при старте приложения.
Отличный способ держать Minimal API структурированным даже в больших проектах.
Forwarded from C# (C Sharp) programming
🚀 Почему этот EF Core код тормозит?
Технически - всё ок.
По производительности не очень.
Вот типичная ошибка:
❌ Загружаешь всю сущность (все колонки)
❌ Потом фильтруешь и мапишь уже в памяти
Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение
✅ Как правильно:
Используй проекцию через `.Select()` прямо в запросе:
- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему
📌 Правило простое:
Не тащи всё - бери только то, что используешь
Именно такие мелочи чаще всего дают x2–x10 к скорости.
Технически - всё ок.
По производительности не очень.
Вот типичная ошибка:
❌ Загружаешь всю сущность (все колонки)
❌ Потом фильтруешь и мапишь уже в памяти
Что происходит:
- лишние данные тянутся из БД
- растёт нагрузка на сеть
- увеличивается потребление памяти
- замедляется приложение
✅ Как правильно:
Используй проекцию через `.Select()` прямо в запросе:
- берёшь только нужные поля
- меньше данных из БД
- быстрее запрос
- меньше нагрузка на систему
📌 Правило простое:
Не тащи всё - бери только то, что используешь
Именно такие мелочи чаще всего дают x2–x10 к скорости.
🚨 «Нет времени на тесты» уже не работает
В 2026 писать тесты стало проще, чем придумывать оправдания
ИИ генерит шаблоны, дописывает кейсы и закрывает рутину
Если у тебя есть нормальная структура, всё остальное ускоряется в разы
Вот минимальный стек, который покрывает почти всё
• Для юнитов
• xUnit остаётся стандартом
• TUnit можно смотреть как более современную альтернативу
Для ассёртов
• Shouldly даёт максимально читаемые проверки
• FluentAssertions теперь платный, это стоит учитывать
Для интеграционных тестов
Aspire сильно упрощает жизнь
WebApplicationFactory плюс TestContainers дают реальные зависимости в тестах
Respawn чистит базу между прогонами
Для фронта
• Playwright сейчас лучший выбор
• Selenium уже больше про легаси
Для моков
• NSubstitute самый чистый по API
• Moq как дефолт, если привык
Для данных
Bogus и AutoFixture закрывают генерацию тестовых сценариев
Для перфома нса
• BenchmarkDtNet для микро-бенчей
• k6 для нагрузки
• NBomber если хочешь остаться в C#
Что по факту важно
Тебе не нужен весь этот стек сразу
Достаточно двигаться по порядку
• Сначала юнит-тесты
• Потом интеграция
• Потом нагрузка
• Потом E2E
Самый частый фейл не в инструментах
А в том, что тесты откладывают «на потом»
В 2026 это уже странное решение
Гайд по интеграционным тестам в Aspire
https://antondevtips.com/blog/dotnet-aspire-integration-testing-best-practices-for-distributed-applications?utm_source=twitter&utm_medium=social&utm_campaign=09-04-2026
В 2026 писать тесты стало проще, чем придумывать оправдания
ИИ генерит шаблоны, дописывает кейсы и закрывает рутину
Если у тебя есть нормальная структура, всё остальное ускоряется в разы
Вот минимальный стек, который покрывает почти всё
• Для юнитов
• xUnit остаётся стандартом
• TUnit можно смотреть как более современную альтернативу
Для ассёртов
• Shouldly даёт максимально читаемые проверки
• FluentAssertions теперь платный, это стоит учитывать
Для интеграционных тестов
Aspire сильно упрощает жизнь
WebApplicationFactory плюс TestContainers дают реальные зависимости в тестах
Respawn чистит базу между прогонами
Для фронта
• Playwright сейчас лучший выбор
• Selenium уже больше про легаси
Для моков
• NSubstitute самый чистый по API
• Moq как дефолт, если привык
Для данных
Bogus и AutoFixture закрывают генерацию тестовых сценариев
Для перфома нса
• BenchmarkDtNet для микро-бенчей
• k6 для нагрузки
• NBomber если хочешь остаться в C#
Что по факту важно
Тебе не нужен весь этот стек сразу
Достаточно двигаться по порядку
• Сначала юнит-тесты
• Потом интеграция
• Потом нагрузка
• Потом E2E
Самый частый фейл не в инструментах
А в том, что тесты откладывают «на потом»
В 2026 это уже странное решение
Гайд по интеграционным тестам в Aspire
https://antondevtips.com/blog/dotnet-aspire-integration-testing-best-practices-for-distributed-applications?utm_source=twitter&utm_medium=social&utm_campaign=09-04-2026
🔥 Ты не проверишь весь код, если он большой, но нужные инструменты проверят
Невозможно вручную ревьюить каждую строчку. Даже в маленьком проекте что-то ускользает.
Здесь и подключается статический анализ.
Он работает как второй слой контроля прямо во время разработки. Подсвечивает баги, плохие паттерны и потенциальные уязвимости ещё до запуска кода.
В итоге ты ловишь проблемы на раннем этапе, а не после релиза.
Для C# это особенно просто внедряется. Пара настроек и у тебя уже есть автоматический аудит кода без лишней боли.
Если пишешь на C# и не используешь статический анализ, ты реально теряешь быстрые фиксы и чистоту кода.
Начать можно отсюда
Невозможно вручную ревьюить каждую строчку. Даже в маленьком проекте что-то ускользает.
Здесь и подключается статический анализ.
Он работает как второй слой контроля прямо во время разработки. Подсвечивает баги, плохие паттерны и потенциальные уязвимости ещё до запуска кода.
В итоге ты ловишь проблемы на раннем этапе, а не после релиза.
Для C# это особенно просто внедряется. Пара настроек и у тебя уже есть автоматический аудит кода без лишней боли.
Если пишешь на C# и не используешь статический анализ, ты реально теряешь быстрые фиксы и чистоту кода.
Начать можно отсюда
Внутри SQL-движка соединение таблиц — это не магия, а конкретный алгоритм. Сравним два подхода к соединению таблиц.
Nested Loops работает буквально так, как звучит: берём строку из первой таблицы и ищем совпадения во второй. Если вторая таблица имеет подходящий индекс, поиск по нему будет очень быстрым, и такой алгоритм блестяще справляется с задачей маленькое соединяется с большим.
Hash Join подходит там, где Nested Loops захлёбывается. Он сначала строит хэш-таблицу по одной из входных таблиц, а затем пробегается по второй и ищет совпадения через хэш-функцию. Это даёт огромный выигрыш, когда нужно соединить два больших набора данных, и когда индексов для ускорения поиска нет. Цена такого подхода — расход памяти.
В итоге — если речь идёт о маленьком наборе строк против большого и есть индекс, Nested Loops окажется быстрее. Но если обе таблицы крупные и индексы не спасают, Hash Join чаще всего становится оптимальным выбором.
#dotnet_challenge
Please open Telegram to view this post
VIEW IN TELEGRAM
async void ломает ваш кодВ C# у async void есть дурная репутация, и не зря. Такой метод не возвращает Task, а значит, его нельзя await-ить, нельзя встроить в пайплайн и невозможно корректно отследить завершение.
Исключения из него не ловятся обычным образом — они пробиваются в синхронизационный контекст или в пул потоков, где легко превращаются в необработанные и могут уронить процесс.
// Контроллер ASP.NET Core
[HttpPost]
public async void Create() { await _svc.DoAsync(); } // Исключения мимо pipeline
// Библиотека
public async void SaveAsync(Item item) { await _repo.Save(item); } // Вызывающему не сконтролировать
Правильно — всегда возвращать Task:
[HttpPost]
public async Task<IActionResult> Create() { await _svc.DoAsync(); return Ok(); }
public Task Invoke(HttpContext ctx) => _next(ctx);
public Task SaveAsync(Item item) => _repo.Save(item);
Единственный сценарий, где async void уместен, — обработчики событий в UI-фреймворках вроде WPF или WinForms, где сигнатура задаётся самим фреймворком. Там приходится мириться, но даже там стоит ловить исключения локально и логировать их.
Please open Telegram to view this post
VIEW IN TELEGRAM
🚀 ASP.NET Core в 2026: если не знаешь это - ты отстал
Одна картинка закрывает почти весь стек, который реально нужен в проде. Без воды и устаревших практик.
Сейчас ASP.NET Core уже не про «сделать API», а про систему: от роутинга и DI до очередей, кешей и realtime. Если ты всё ещё пишешь просто контроллеры и думаешь, что этого достаточно - плохие новости.
Что важно:
• основа осталась той же, но усложнился прод
• логирование и мониторинг теперь обязательны
• кеш без стратегии уже не спасает
• без resilience и retry твой сервис падает при первой же проблеме
Отдельно бросается в глаза тренд:
минимум магии, максимум контроля
ручной mapping вместо AutoMapper
явная архитектура вместо «оно само работает»
И ещё момент, который многие игнорят:
• экосистема вокруг стала важнее самого фреймворка
• Redis, Kafka, OpenAPI, gRPC, SignalR - это уже не «дополнительно», это база
Если коротко:
ASP.NET Core сейчас это не про backend
это про сборку полноценной распределённой системы
И вопрос уже не в том, знаешь ли ты .NET
а в том, умеешь ли ты строить сервисы, которые живут под нагрузкой
Одна картинка закрывает почти весь стек, который реально нужен в проде. Без воды и устаревших практик.
Сейчас ASP.NET Core уже не про «сделать API», а про систему: от роутинга и DI до очередей, кешей и realtime. Если ты всё ещё пишешь просто контроллеры и думаешь, что этого достаточно - плохие новости.
Что важно:
• основа осталась той же, но усложнился прод
• логирование и мониторинг теперь обязательны
• кеш без стратегии уже не спасает
• без resilience и retry твой сервис падает при первой же проблеме
Отдельно бросается в глаза тренд:
минимум магии, максимум контроля
ручной mapping вместо AutoMapper
явная архитектура вместо «оно само работает»
И ещё момент, который многие игнорят:
• экосистема вокруг стала важнее самого фреймворка
• Redis, Kafka, OpenAPI, gRPC, SignalR - это уже не «дополнительно», это база
Если коротко:
ASP.NET Core сейчас это не про backend
это про сборку полноценной распределённой системы
И вопрос уже не в том, знаешь ли ты .NET
а в том, умеешь ли ты строить сервисы, которые живут под нагрузкой
Forwarded from C# (C Sharp) programming
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет код?
Правильный ответ: 22
Почему: выражение считается слева направо. Сначала x++ возвращает 10, но потом увеличивает x до 11. Затем ++x сначала увеличивает x до 12, потом возвращает 12. В итоге 10 + 12 = 22.
using System;
class Program
{
static void Main()
{
int x = 10;
Console.WriteLine(x++ + ++x);
}
}
Правильный ответ: 22
Почему: выражение считается слева направо. Сначала x++ возвращает 10, но потом увеличивает x до 11. Затем ++x сначала увеличивает x до 12, потом возвращает 12. В итоге 10 + 12 = 22.
Раньше в .NET для идентификаторов чаще всего использовали обычный
Guid.NewGuid().Проблема в том, что классический UUID случайный. Для уникальности это удобно, но для базы данных - не всегда.
Когда значения генерируются хаотично, новые записи вставляются в разные части индекса. Отсюда:
- больше фрагментации
- дороже вставки
- чаще перестраиваются страницы индекса
- хуже поведение на больших таблицах
Поэтому многие разработчики начали использовать ULID.
ULID состоит из двух частей:
- timestamp
- random
За счет timestamp такие ID сортируются по времени, а значит база может вставлять новые записи более последовательно.
Но начиная с .NET 9 появился встроенный вариант:
Guid.CreateVersion7()UUID v7 тоже содержит временную часть, поэтому лучше подходит для индексируемых ключей, чем полностью случайный UUID.
Главное отличие:
ULID - отдельный формат и часто сторонняя библиотека.UUID v7 - обычный Guid, который уже поддерживается в .NET.Для новых проектов это выглядит как более разумный дефолт:
- не
Guid.NewGuid()- не отдельный ULID-пакет
- а
Guid.CreateVersion7()Особенно если
Guid используется как primary key в базе.Please open Telegram to view this post
VIEW IN TELEGRAM
Представьте: через четыре месяца вы открываете чужой .NET-проект и читаете его как книгу.
IServiceCollection не вызывает ступора.
async Task<IActionResult> пишется на автомате. Вы точно знаете, почему EF Core сгенерировал именно такой SQL - и как переписать запрос, чтобы он летал.Это не фантазия. Это результат после 16 модулей, в которых каждая концепция объясняется через код и закрепляется практикой.
ООП, SOLID, LINQ, async/await, DI, EF Core, ASP.NET Core, Docker, Kubernetes - всё, что казалось магией, станет рабочим инструментом.А бонусом - портфолио проектов: от CLI-утилит и REST API до собственного SaaS с multi-tenancy, JWT и деплоем в Kubernetes под TLS.
Скидка - 58% доступна 48 часов: https://stepik.org/a/282984/
Please open Telegram to view this post
VIEW IN TELEGRAM
⏱️ Ускоряем async-код в C#: не просто await, а нормальная параллельность
Одна из частых ошибок в C# - писать независимые асинхронные операции так, будто они зависят друг от друга:
Выглядит нормально, но по факту это последовательное выполнение.
Если каждый запрос занимает примерно 1 секунду, весь блок будет выполняться около 3 секунд. Не потому что async медленный, а потому что мы сами заставили код ждать каждый шаг по очереди.
Правильнее запускать независимые операции сразу:
Теперь все запросы стартуют одновременно, а общее время будет примерно равно самому долгому из них.
Но есть важный нюанс.
Task.WhenAll не делает CPU-код магически параллельным. Он особенно полезен для I/O-bound задач:
- запросы в базу
- HTTP-вызовы
- чтение файлов
- обращения к внешним API
- независимые операции с сетью
И ещё один момент: после WhenAll лучше доставать результаты уже из завершённых задач:
Так код остаётся читаемым, ошибки нормально пробрасываются, а логика не превращается в кашу.
Async/await сам по себе не ускоряет код. Он просто даёт возможность не блокировать поток. Ускорение появляется тогда, когда вы правильно запускаете независимые операции вместе, а не ждёте их по одной.
Одна из частых ошибок в C# - писать независимые асинхронные операции так, будто они зависят друг от друга:
await GetUserAsync();
await GetOrdersAsync();
await GetRecommendationsAsync();
Выглядит нормально, но по факту это последовательное выполнение.
Если каждый запрос занимает примерно 1 секунду, весь блок будет выполняться около 3 секунд. Не потому что async медленный, а потому что мы сами заставили код ждать каждый шаг по очереди.
Правильнее запускать независимые операции сразу:
var userTask = GetUserAsync();
var ordersTask = GetOrdersAsync();
var recsTask = GetRecommendationsAsync();
await Task.WhenAll(userTask, ordersTask, recsTask);
Теперь все запросы стартуют одновременно, а общее время будет примерно равно самому долгому из них.
Но есть важный нюанс.
Task.WhenAll не делает CPU-код магически параллельным. Он особенно полезен для I/O-bound задач:
- запросы в базу
- HTTP-вызовы
- чтение файлов
- обращения к внешним API
- независимые операции с сетью
И ещё один момент: после WhenAll лучше доставать результаты уже из завершённых задач:
var user = await userTask;
var orders = await ordersTask;
var recs = await recsTask;
Так код остаётся читаемым, ошибки нормально пробрасываются, а логика не превращается в кашу.
Async/await сам по себе не ускоряет код. Он просто даёт возможность не блокировать поток. Ускорение появляется тогда, когда вы правильно запускаете независимые операции вместе, а не ждёте их по одной.
Практическое руководство по росту в C#-разработке. Материал собран для тех, кто хочет получить инженерную глубину, а не просто накликать CRUD по туториалам.
Здесь последовательность изучения, лучшие практики, ресурсы и трезвый разбор того, как работать с ИИ-инструментами и оставаться востребованным.
https://github.com/Develp10/Csharp_Roadmap/
Please open Telegram to view this post
VIEW IN TELEGRAM