В C# 15 появились union types. И это не косметика, это смена подхода к обработке ошибок.
Вы можете явно описать результат как “либо значение, либо ошибка”, прямо на уровне языка.
Без самодельных Result-классов, без лишних обёрток, без попыток использовать исключения там, где они неуместны.
Раньше, ты либо городишь свой Result и работаешь с лишним бойлерплейтом.
Либо тащишь сторонние библиотеки.
Либо кидаешь исключения и теряешь контроль над потоком.
С union types эта проблема решается .
Pattern matching начинает работать так, как и должен. Ты не гадаешь, что вернул метод, а явно разбираешь все варианты.
Код становится проще читать, проще тестировать и сложнее сломать.
Вы можете явно описать результат как “либо значение, либо ошибка”, прямо на уровне языка.
Без самодельных Result-классов, без лишних обёрток, без попыток использовать исключения там, где они неуместны.
Раньше, ты либо городишь свой Result и работаешь с лишним бойлерплейтом.
Либо тащишь сторонние библиотеки.
Либо кидаешь исключения и теряешь контроль над потоком.
С union types эта проблема решается .
Pattern matching начинает работать так, как и должен. Ты не гадаешь, что вернул метод, а явно разбираешь все варианты.
Код становится проще читать, проще тестировать и сложнее сломать.
This media is not supported in your browser
VIEW IN TELEGRAM
Unity выкатила в открытую бету AI-помощника, который работает не как «чатик сбоку», а как полноценный агент внутри проекта. Он видит контекст сцены, понимает структуру кода и может сам вносить изменения.
Что умеет:
— Plan Mode: разбивает задачу на шаги, пишет код, ищет ошибки и помогает выстроить архитектуру
— Figma-интеграция: можно дать ссылку на макет, а Unity попробует собрать по нему интерфейс
— Генерация ассетов: текстуры, звуки и 3D-объекты можно создавать по текстовому описанию
— Откат изменений: если агент накосячил, правки можно быстро вернуть назад
Для Pro и Enterprise функции уже доступны. В Personal-версии бету можно попробовать через trial.
Геймдев постепенно превращается в работу не только с кодом и сценами, но и с агентами, которые собирают часть проекта по описанию.
А вместе с этим, похоже, нас ждёт новая волна нейрослоп-игр в Steam.
https://unity.com/ru/features/ai?utm_campaign=unity-ai-beta
Please open Telegram to view this post
VIEW IN TELEGRAM
project_final_v2_FINAL_truly_final - знакомо?
Значит, пора.
Большинство разработчиков знают 5 команд Git и боятся шестую. Коммитят в main, гуглят «how to undo» и копируют папку «на всякий случай».
Это не работа - это выживание.
После курса вы:
— делаете rebase, не задерживая дыхание;
— разбираете конфликт на 200 файлов по алгоритму;
— возвращаете «потерянные навсегда» коммиты за 30 секунд через reflog;
— пишете историю, которую не стыдно показать на code review.
Git Flow, trunk-based, Pull Request, защита веток, CI/CD-хуки — всё, что отличает джуна от senior в командной работе.
Скидка 53%, 48 часов: https://stepik.org/course/284799/
Please open Telegram to view this post
VIEW IN TELEGRAM
Хватит копировать один и тот же unit test
Если тест отличается только входными данными, не нужно плодить десятки одинаковых методов.
В xUnit для этого есть parameterized tests, они же data-driven tests. Вы пишете один тест, а потом прогоняете его на разных наборах данных.
Самый простой вариант -
Если данных больше или нужна нормальная типизация, можно вынести их в
Смысл простой: меньше копипасты, чище тесты, проще добавлять новые кейсы.
Вместо пяти почти одинаковых тестов - один понятный сценарий и набор входных данных.
Для xUnit это один из тех приемов, который быстро окупается в любом C# проекте.
Если тест отличается только входными данными, не нужно плодить десятки одинаковых методов.
В xUnit для этого есть parameterized tests, они же data-driven tests. Вы пишете один тест, а потом прогоняете его на разных наборах данных.
Самый простой вариант -
[InlineData(...)], когда параметры можно прямо указать над тестом.Если данных больше или нужна нормальная типизация, можно вынести их в
TheoryData<T> и подключить через [ClassData(typeof(...))].Смысл простой: меньше копипасты, чище тесты, проще добавлять новые кейсы.
Вместо пяти почти одинаковых тестов - один понятный сценарий и набор входных данных.
Для xUnit это один из тех приемов, который быстро окупается в любом C# проекте.
Одна функция в C однажды положила заметную часть раннего интернета.
В 1988 году в стандартной библиотеке C была функция `gets()`. Она читала ввод пользователя в буфер, но не проверяла его размер. Если данных приходило больше, чем помещалось, они просто начинали затирать соседнюю память.
Именно это использовал Robert Morris в знаменитом Morris Worm. Червь атаковал `
Итог: около 6000 машин были заражены за считанные часы. По оценкам того времени, это была примерно десятая часть всего интернета.
Самое безумное здесь не сам червь, а то, насколько маленькой была причина. Один небезопасный вызов. Один буфер без проверки. Одна функция, которую годами использовали как обычный инструмент.
Позже `gets()` признали настолько опасной, что её фактически выкинули из современного C.
Урок простой: в системном программировании безопасность часто ломается не на сложной криптографии, а на банальном «а что будет, если ввод окажется длиннее, чем мы ожидали?»
#history
В 1988 году в стандартной библиотеке C была функция `gets()`. Она читала ввод пользователя в буфер, но не проверяла его размер. Если данных приходило больше, чем помещалось, они просто начинали затирать соседнюю память.
Именно это использовал Robert Morris в знаменитом Morris Worm. Червь атаковал `
fingerd`, переполнял буфер и заставлял систему выполнять чужой код.Итог: около 6000 машин были заражены за считанные часы. По оценкам того времени, это была примерно десятая часть всего интернета.
Самое безумное здесь не сам червь, а то, насколько маленькой была причина. Один небезопасный вызов. Один буфер без проверки. Одна функция, которую годами использовали как обычный инструмент.
Позже `gets()` признали настолько опасной, что её фактически выкинули из современного C.
Урок простой: в системном программировании безопасность часто ломается не на сложной криптографии, а на банальном «а что будет, если ввод окажется длиннее, чем мы ожидали?»
#history
Представьте: через четыре месяца вы открываете чужой .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
Раньше в .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
⚡️ Один SQL-запрос выполнялся за 298 мс.
Почти такой же - за 0,66 мс.
Разница в 451 раз из-за одной строки.
Ситуация обычная: cursor pagination, сортировка по
Но
То есть индекс есть, но запрос все равно тащит слишком много лишнего.
Проблема в условии:
Для разработчика это выглядит логично: сначала сравниваем дату, потом id.
Но для оптимизатора такой
Правильнее записать условие через tuple comparison:
Смысл тот же, но для Postgres это уже понятный диапазон по составному индексу.
И результат: 298 мс превращаются в 0,66 мс.
Индекс сам по себе ничего не гарантирует.
Важно не только создать индекс, но и написать запрос так, чтобы оптимизатор реально смог его использовать.
Почти такой же - за 0,66 мс.
Разница в 451 раз из-за одной строки.
Ситуация обычная: cursor pagination, сортировка по
date DESC, id DESC, лимит на 1000 записей и composite index по (date, id). На первый взгляд, все должно работать быстро.Но
EXPLAIN ANALYZE показывает другое: Postgres вроде бы использует Index Scan, но после этого выкидывает 900 000 строк через Filter.То есть индекс есть, но запрос все равно тащит слишком много лишнего.
Проблема в условии:
`date < @date OR (date = @date AND id <= @lastId)`Для разработчика это выглядит логично: сначала сравниваем дату, потом id.
Но для оптимизатора такой
OR плохо ложится на composite index. В итоге база не может сразу пойти по нужному диапазону и вынуждена фильтровать огромный кусок данных.Правильнее записать условие через tuple comparison:
`(date, id) <= (@date, @lastId)`Смысл тот же, но для Postgres это уже понятный диапазон по составному индексу.
И результат: 298 мс превращаются в 0,66 мс.
Индекс сам по себе ничего не гарантирует.
Важно не только создать индекс, но и написать запрос так, чтобы оптимизатор реально смог его использовать.
Что выведет на экран этот код:
Anonymous Quiz
42%
1, 2
3%
1, 0
7%
0, 0
15%
0, 2
24%
Код не скомпилируется
9%
Практическое руководство по росту в C#-разработке. Материал собран для тех, кто хочет получить инженерную глубину, а не просто накликать CRUD по туториалам.
Здесь последовательность изучения, лучшие практики, ресурсы и трезвый разбор того, как работать с ИИ-инструментами и оставаться востребованным.
https://github.com/Develp10/Csharp_Roadmap/
Please open Telegram to view this post
VIEW IN TELEGRAM
15 проезный .NET-библиотек, которые используют senior-разработчики
Open-source библиотеки, которые делают код чище, тесты надёжнее, а разработку быстрее.
**HTTP, устойчивость и DI**
**1. Refit**
Превращает REST API в типизированные C# интерфейсы. Меньше boilerplate вокруг
GitHub: https://github.com/reactiveui/refit
2. Polly
Retry, circuit breaker, timeout и resilience-политики для исходящих вызовов.
GitHub: https://github.com/App-vNext/Polly
3. Scrutor
Автосканирование и регистрация сервисов в DI по конвенциям.
GitHub: https://github.com/khellang/Scrutor
Тестирование
4. Bogus
Генератор реалистичных fake-данных для тестов и сидинга.
GitHub: https://github.com/bchavez/Bogus
5. Verify
Snapshot-тесты для .NET: один раз утвердил вывод, дальше ловишь регрессии.
GitHub: https://github.com/VerifyTests/Verify
6. Testcontainers for .NET
Поднимает реальный PostgreSQL, SQL Server, Redis и другие сервисы в Docker для интеграционных тестов.
GitHub: https://github.com/testcontainers/testcontainers-dotnet
API и фоновые задачи
7. FastEndpoints
Быстрые Minimal API по паттерну REPR без раздутых контроллеров.
Сайт: https://fast-endpoints.com
GitHub: https://github.com/FastEndpoints/FastEndpoints
8. TickerQ
Нативный планировщик фоновых задач без Hangfire, Quartz и лишнего оверхеда.
GitHub: https://github.com/Arcenox-co/TickerQ
9. HotChocolate
Мощный GraphQL-сервер для .NET, когда один гибкий endpoint удобнее десятков REST-маршрутов.
Сайт: https://chillicream.com/docs/hotchocolate
GitHub: https://github.com/ChilliCream/graphql-platform
Микросервисы и messaging
10. Dapr
Service discovery, pub/sub и state management для микросервисов без лишней инфраструктурной сантехники.
Сайт: https://dapr.io
GitHub: https://github.com/dapr/dotnet-sdk
11. Wolverine
Mediator и messaging в одном фреймворке. Как MediatR, только шире по возможностям.
Сайт: https://wolverinefx.net
GitHub: https://github.com/JasperFx/wolverine
Утилиты и работа с данными
12. UnitsNet
Безопасная работа с единицами измерения вместо сырых
GitHub: https://github.com/angularsen/UnitsNet
13. Humanizer
Превращает строки, даты, числа и enum-ы в читаемый вид одной строкой кода.
GitHub: https://github.com/Humanizr/Humanizer
14. ImageSharp
Обработка, ресайз и конвертация изображений в .NET. Кросс-платформенно, без GDI+.
Сайт: https://sixlabors.com/products/imagesharp
GitHub: https://github.com/SixLabors/ImageSharp
Архитектура
15. ArchUnitNET
Тесты для архитектурных правил. Нарушения слоёв и Clean Architecture падают прямо в CI.
GitHub: https://github.com/TNG/ArchUnitNET
Open-source библиотеки, которые делают код чище, тесты надёжнее, а разработку быстрее.
**HTTP, устойчивость и DI**
**1. Refit**
Превращает REST API в типизированные C# интерфейсы. Меньше boilerplate вокруг
HttpClient. GitHub: https://github.com/reactiveui/refit
2. Polly
Retry, circuit breaker, timeout и resilience-политики для исходящих вызовов.
GitHub: https://github.com/App-vNext/Polly
3. Scrutor
Автосканирование и регистрация сервисов в DI по конвенциям.
GitHub: https://github.com/khellang/Scrutor
Тестирование
4. Bogus
Генератор реалистичных fake-данных для тестов и сидинга.
GitHub: https://github.com/bchavez/Bogus
5. Verify
Snapshot-тесты для .NET: один раз утвердил вывод, дальше ловишь регрессии.
GitHub: https://github.com/VerifyTests/Verify
6. Testcontainers for .NET
Поднимает реальный PostgreSQL, SQL Server, Redis и другие сервисы в Docker для интеграционных тестов.
GitHub: https://github.com/testcontainers/testcontainers-dotnet
API и фоновые задачи
7. FastEndpoints
Быстрые Minimal API по паттерну REPR без раздутых контроллеров.
Сайт: https://fast-endpoints.com
GitHub: https://github.com/FastEndpoints/FastEndpoints
8. TickerQ
Нативный планировщик фоновых задач без Hangfire, Quartz и лишнего оверхеда.
GitHub: https://github.com/Arcenox-co/TickerQ
9. HotChocolate
Мощный GraphQL-сервер для .NET, когда один гибкий endpoint удобнее десятков REST-маршрутов.
Сайт: https://chillicream.com/docs/hotchocolate
GitHub: https://github.com/ChilliCream/graphql-platform
Микросервисы и messaging
10. Dapr
Service discovery, pub/sub и state management для микросервисов без лишней инфраструктурной сантехники.
Сайт: https://dapr.io
GitHub: https://github.com/dapr/dotnet-sdk
11. Wolverine
Mediator и messaging в одном фреймворке. Как MediatR, только шире по возможностям.
Сайт: https://wolverinefx.net
GitHub: https://github.com/JasperFx/wolverine
Утилиты и работа с данными
12. UnitsNet
Безопасная работа с единицами измерения вместо сырых
double для температуры, скорости и расстояний. GitHub: https://github.com/angularsen/UnitsNet
13. Humanizer
Превращает строки, даты, числа и enum-ы в читаемый вид одной строкой кода.
GitHub: https://github.com/Humanizr/Humanizer
14. ImageSharp
Обработка, ресайз и конвертация изображений в .NET. Кросс-платформенно, без GDI+.
Сайт: https://sixlabors.com/products/imagesharp
GitHub: https://github.com/SixLabors/ImageSharp
Архитектура
15. ArchUnitNET
Тесты для архитектурных правил. Нарушения слоёв и Clean Architecture падают прямо в CI.
GitHub: https://github.com/TNG/ArchUnitNET