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

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2605. #ЗаметкиНаПолях
5 Малоизвестных Функций C#, Которые Упростят Вашу Жизнь

Фреймворк уже имеет надёжные инструменты для решения различных проблем, но многие из них мало известны. Сегодня рассмотрим некоторые.

1. OperatingSystem.IsX вместо RuntimeInformation
В течение многих лет проверка текущей ОС в .NET означала написание чего-то вроде:
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
//…
}

Теперь появились более чистые варианты:
- OperatingSystem.IsWindows,
- OperatingSystem.IsLinux,
- OperatingSystem.IsMacOS
и т.п.
if (OperatingSystem.IsWindows())
{
//…
}


2. Правильная изоляция плагинов с помощью AssemblyLoadContext
Если вы когда-либо пытались создать систему плагинов в .NET, вы знаете, насколько болезненны конфликты сборок. Загрузите две версии одной и той же зависимости, и внезапно всё перестаёт работать. AssemblyLoadContext позволяет загружать сборки изолированно, так что два плагина могут зависеть от разных версий одной и той же DLL, не мешая друг другу. Вот минимальный код для начала:
class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;

public PluginLoadContext(string path) =>
_resolver = new(path);

protected override Assembly? Load(AssemblyName name)
{
var path = _resolver
.ResolveAssemblyToPath(name);

return path != null
? LoadFromAssemblyPath(path)
: null;
}
}

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

3. Разбор зависимостей с помощью AssemblyDependencyResolver
AssemblyDependencyResolver, получив путь к плагину или сборке, определяет, откуда должны браться зависимости:
var resolver = 
new AssemblyDependencyResolver(pluginPath);
var path = resolver
.ResolveAssemblyToPath(assemblyName);

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

4. Получение версий сборок без ошибок
Часто в коде можно встретить что-то такое:
var version = Assembly
.GetExecutingAssembly()
.GetName()
.Version;

Дело в том, что это значение не всегда совпадает с версией, которую вы указали в проекте. Это версия сборки, а не обязательно версия файла или информационная версия. Какая из них вам нужна?
- AssemblyName.Version - версия сборки, полученная во время компиляции,
- FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion - версия файла,
- AssemblyInformationalVersionAttribute - семантическая версия, вроде 1.0.0-beta+sha.abc123.

Лучший подход – выражаться более явно:
var version = Assembly
.GetExecutingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion;

Так вы получите именно ту строку, которую хотели передать, а не то, что MSBuild по умолчанию присвоил вашей DLL.

5. Получение зависимостей из контейнера с помощью ActivatorUtilities
Иногда нужно создать объект, который не зарегистрирован в контейнере, но имеет зависимости, которые зарегистрированы. Используйте ActivatorUtilities:
var myService = ActivatorUtilities
.CreateInstance<MyService>(serviceProvider);

Это говорит контейнеру: «Я знаю, что MyService не зарегистрирован, но, пожалуйста, внедри все необходимые ему сервисы». Это удобный обходной путь, который избавляет вас от необходимости создавать неудобные фабрики или засорять код логикой разрешения сервисов.

Источник: https://blog.stackademic.com/if-youre-not-using-these-5-net-features-you-re-working-too-hard-0aefbf5a6fdc?gi=bb3ac273e638
👍14
День 2606. #Здоровье
Утренние Привычки, Которые Разрушают Ваш Мозг. Начало

Автор оригинала: нейробиолог Патрисия Шмидт

Утро предоставляет уникальную возможность максимально эффективно использовать возможности мозга в течение всего дня. То, что вы делаете (или не делаете) в первые 60–90 минут после пробуждения, повлияет на ваше настроение и когнитивные способности в последующие часы. Горькая правда в том, что большинство людей неосознанно саботируют работу своего мозга утром и удивляются, почему они не могут сосредоточиться или постоянно испытывают стресс. Рассмотрим распространённые утренние привычки с точки зрения нейробиологии и найдём лучшие альтернативы для каждой.

1. Проверка телефона первым делом утром
84% населения США проверяют свои телефоны в течение первых 10 минут после пробуждения.

Почему это проблема:
В первые 30–45 минут после пробуждения происходит реакция пробуждения кортизола (резкое повышение уровня). Здоровая реакция пробуждения кортизола подготавливает вас к предстоящему дню, к ожидаемым ежедневным нагрузкам, и когда вы вводите непосредственные стрессовые факторы (контент на телефоне) в этот чувствительный период, вы нарушаете свою естественную систему подготовки.

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

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

Почему это проблема:
Наибольшее влияние на ваши внутренние биологические часы оказывает воздействие света. Свет, попадающий в глаза, стимулирует область в мозге, называемую супрахиазматическим ядром (СХЯ). Эта область задаёт темп для мозга и тела, используя солнечный свет для установки суточных ритмов.

Когда свет попадает утром в глаза, начинается каскад нейрохимических событий:
- Естественным образом усиливается здоровая реакция пробуждения кортизола.
- Мелатонин («гормон сна») подавляется, что оптимизирует вечернюю выработку мелатонина, улучшая сон следующей ночью.
- Улучшается оборот серотонина в мозге. Воздействие яркого дневного света может способствовать регулированию настроения, делая вас счастливее и спокойнее.

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

Что делать:
Как можно скорее после пробуждения, в идеале в течение первых 30 минут, подвергайте глаза воздействию естественного солнечного света. Стремитесь к как минимум 10 минутам пребывания на солнце или 15–20 минутам в пасмурные дни.

Если вы не можете выйти на улицу рано или встаёте в темноте, яркое комнатное освещение может помочь. Стандартные комнатные лампы недостаточно яркие, поэтому подумайте о приобретении лампы солнечного света (яркостью не менее 10 000 люкс). Но не используйте её вечером, так как это может нарушить сон!

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

Источник:
https://medium.com/write-a-catalyst/as-a-neuroscientist-i-quit-these-5-morning-habits-that-destroy-your-brain-3efe1f410226
👍22
День 2607. #Здоровье
Утренние Привычки, Которые Разрушают Ваш Мозг. Окончание

Начало

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

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

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

4. Сладкий завтрак
Сладкий завтрак очень распространённое явление.

Почему это проблема:
Это приводит к резкому скачку уровня глюкозы в крови, за которым следует его значительное падение через несколько часов. Такая нестабильность влияет на мозг, поскольку он использует глюкозу в качестве топлива.

Что делать вместо этого:
Сосредоточьтесь на белке. Богатый белком завтрак (25–35 граммов белка) поддерживает когнитивные функции, чувство сытости и обеспечивает энергией на протяжении всего дня. То есть не только улучшает работу мозга, но и регулирует аппетит.

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

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

Что делать:
Пейте воду сразу после пробуждения. Достаточно 250–350 мл. Держите бутылку или стакан с водой на прикроватной тумбочке, чтобы они были под рукой, когда вы проснётесь.

Итого
Эти 5 привычек могут показаться незначительными, но их влияние накапливается. Ваша утренняя рутина создаёт условия либо для успеха, либо для провального дня. То, что вы делаете в первые 60–90 минут после пробуждения, влияет на вашу когнитивную работоспособность, настроение, энергию и концентрацию внимания в течение всего дня, и даже на качество сна следующей ночью.

Источник: https://medium.com/write-a-catalyst/as-a-neuroscientist-i-quit-these-5-morning-habits-that-destroy-your-brain-3efe1f410226
👍14
Код с каким вариантом блокировки на картинке в первом комментарии успешно выполнится?
#Quiz #CSharp
Anonymous Quiz
24%
В строке 1 (object)
19%
В строке 2 (Lock)
42%
Оба
14%
Ни один
👍11👎2
День 2608. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

27. Инструменты управления проектами
«Опишите ваш опыт использования инструментов управления проектами. Как вы использовали эти инструменты для повышения эффективности проекта и улучшения взаимодействия в команде?»

Хороший ответ
В своих предыдущих проектах на .NET я активно использовал такие инструменты управления проектами, как JIRA для управления задачами и Confluence для документирования. Они сыграли решающую роль в поддержании организованности проекта и повышении эффективности и взаимодействия команды.

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

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

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

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

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

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

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

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍2👎1
День 2609. #ЧтоНовенького #NET11
Обновления
ASP.NET Core в Превью 2 .NET 11

1. ASP.NET Core теперь нативно добавляет атрибуты семантического соглашения OpenTelemetry к активности HTTP-сервера, что соответствует спецификации трассировки HTTP-сервера OpenTelemetry. Все необходимые атрибуты теперь включены по умолчанию, что соответствует метаданным, ранее доступным только через библиотеку OpenTelemetry.Instrumentation.AspNetCore.
Для сбора встроенных данных трассировки подпишитесь на источник активности Microsoft.AspNetCore в конфигурации OpenTelemetry:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore")
.AddConsoleExporter());

Дополнительных библиотек инструментирования (например, OpenTelemetry.Instrumentation.AspNetCore) не требуется. Теперь фреймворк напрямую заполняет атрибуты семантических соглашений, такие как http.request.method, url.path, http.response.status_code и server.address, в активности запроса.

Если вы не хотите, чтобы атрибуты OpenTelemetry добавлялись к активности, вы можете отключить это, установив параметр AppContext Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData в true.

2. В Blazor Server-Side Rendering (SSR) теперь поддерживается TempData — механизм хранения данных, сохраняющийся между HTTP-запросами. TempData идеально подходит для таких сценариев, как всплывающие сообщения после отправки формы, передача данных при перенаправлениях (шаблон POST-Redirect-GET) и одноразовые уведомления.

3. Представлен новый шаблон проекта dotnet new webworker, позволяющий приложениям Blazor WebAssembly переносить ресурсоемкие вычисления на Web Worker без блокировки UI-потока, обеспечивая отзывчивость приложений во время ресурсоемких операций:
dotnet new webworker -n MyWebWorker

Шаблон генерирует класс WebWorkerClient с фабрикой для создания экземпляров исполнителей. Методы исполнителя используют [JSExport] и могут быть вызваны из компонентов:
await using var worker = 
await WebWorkerClient.CreateAsync(JSRuntime);
var result = await worker
.InvokeAsync<string>("MyApp.MyWorker.Greet", ["World"]);


4. Поддержка OpenAPI 3.2.0 (Ломающее изменение)
Microsoft.AspNetCore.OpenApi теперь поддерживает OpenAPI 3.2.0 через обновлённую зависимость в Microsoft.OpenApi 3.3.1. Это обновление включает ломающие изменения в зависимых библиотеках. Детали в гайде по обновлению Microsoft.OpenApi. Чтобы сгененировать документ OpenAPI 3.2.0, укажите версию при вызове AddOpenApi():
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion =
Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_2;
});

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

5. Парсер HTTP/1.1 запросов Kestrel был переработан, чтобы избежать генерации исключений при некорректных запросах. В сценариях с высокой интенсивностью некорректного трафика, таких как сканирование портов или неправильно настроенные клиенты, отмечено повышение пропускной способности на 20–40 процентов.

Источники:
-
https://www.infoq.com/news/2026/03/dotnet-11-preview-2/
-
https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview2/aspnetcore.md
👍1
День 2610. #Testing
TUnit – Новый Фреймворк Тестирования в .NET. Начало

xUnit, NUnit и MSTest многие годы хорошо служили командам для модульного, интеграционного и даже приёмочного тестирования. Однако по мере развития .NET с появлением новых версий среды выполнения, AOT-компиляции, требований кроссплатформенности, всё более крупных наборов тестов и более быстрых конвейеров CI/CD ограничения устаревших фреймворков стали более очевидными. TUnit стремится решить многие из этих проблем, или, по крайней мере, заявляет об этом. В этой серии рассмотрим, что может предложить TUnit.

Проблема существующих фреймворков
1. Накладные расходы на рефлексию
xUnit, NUnit и MSTest в значительной степени полагаются на рефлексию для обнаружения тестов, создания экземпляров фикстур и вызова методов тестирования. В больших наборах тестов накладные расходы на рефлексию могут значительно влиять на время выполнения.
Обнаружение тестов на основе рефлексии происходит во время выполнения, заставляя разработчиков ждать, прежде чем тесты начнут выполняться. Это особенно раздражает в конвейерах CI/CD.

2. Асинхронный подход был второстепенным
async/await был добавлен в существующие фреймворки тестирования. В результате иногда получаются неуклюжие утверждения, непоследовательная обработка асинхронных операций и не всегда понятный код.

3. Отсутствие поддержки AOT
Поддержка AOT становится необходимой для облачных приложений и контейнеризированных рабочих нагрузок. Интенсивное использование рефлексии в устаревших фреймворках затрудняет или делает невозможной поддержку AOT.

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

Заявления TUnit
Философия проектирования TUnit основана на скорости, современности, гибкости, простоте и лёгкости внедрения.

Ключевые концепции:
1. Производительность прежде всего — использование генерации кода во время компиляции вместо рефлексии, с параллельным выполнением тестов, включённым по умолчанию.
2. Совместимость с современным .NET — полная поддержка .NET 8 и более поздних версий, нативный AOT, тримминг и новые возможности среды выполнения.
3. Управление и гибкость — рабочие процессы на основе атрибутов и конфигурации, пользовательские источники данных, расширяемое поведение, цепочки зависимостей и тонкое управление планированием.
4. Широкая применимость — подходит не только для модульных тестов, но и для интеграционных, приёмочных и даже сквозных сценариев (включая поддержку таких библиотек, как Playwright).
5. Прочие отличительные особенности — асинхронные утверждения, Fluent API, различные источники данных (аргументы, методы, классы, матрицы), диагностика во время компиляции, скорость выполнения на 30% выше xUnit.

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

Далее подробнее рассмотрим функции, которые предлагает TUnit.

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

Источник:
https://trailheadtechnology.com/tunit-the-new-sheriff-in-town-for-net-testing/
👍10
День 2611. #Testing
TUnit – Новый Фреймворк Тестирования в .NET. Продолжение

Начало

Ключевые особенности TUnit

1. Нативная поддержка асинхронности
Утверждения ожидаются, обработчики инициализации асинхронны, и весь жизненный цикл теста учитывает, что современная .NET является асинхронной платформой.
xUnit:
[Fact]
public async Task TestAsync()
{
var result = await GetDataAsync();
Assert.Equal(5, result.Items.Count);
}


TUnit:
[Test]
public async Task TestAsync()
{
var result = await GetDataAsync();
await Assert.That(result.Items)
.Count()
.IsEqualTo(5);
}

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

2. Генерация кода и проверка во время компиляции
TUnit использует генераторы кода C# для обнаружения и компиляции тестов во время сборки, полностью исключая необходимость рефлексии во время выполнения. Такой подход позволяет выявлять многие ошибки во время компиляции, а не во время выполнения, и значительно сокращает время запуска тестов. Это также улучшает общее качество отладки, поскольку сгенерированный код обеспечивает более понятные пути выполнения и более предсказуемое поведение по сравнению с традиционными фреймворками, основанными на рефлексии.
[Test]
[Arguments(1, 2, 3)]
[Arguments(4, 5, 9)]
public async Task Add_WithVariousInputs(
int a, int b, int expected)
{
var result = Add(a, b);
await Assert.That(result).IsEqualTo(expected);
}

// Упрощённый сгенерированный код:
// [CompilerGenerated]
// public async Task Add_WithVariousInputs_1()
// {
// var result = Add(1, 2);
// await Assert.That(result).IsEqualTo(3);
// }
// [CompilerGenerated]
// public async Task Add_WithVariousInputs_2() { … }


3. Модель параллелизации
// Тесты по умолчанию распараллеливаются 
[Test]
[Parallel] // Явно распараллелить
public async Task Test1() { }

// Последовательное выполнение при необходимости
[Test]
[Sequential]
public async Task Test2() { }

// Общие ресурсы для параллельных тестов [ClassDataSource<DatabaseFixture>(Shared = SharedType.PerClass)]
public class DatabaseTests
{
[Test]
public async Task Test1(DatabaseFixture db) { }

[Test]
public async Task Test2(DatabaseFixture db) { }
}

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

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

Источник:
https://trailheadtechnology.com/tunit-the-new-sheriff-in-town-for-net-testing/
👍10
День 2612. #Testing
TUnit – Новый Фреймворк Тестирования в .NET. Продолжение

Начало
Особенности 1-3

4. Передача данных в тесты
1) Аргументы уровня компиляции
[Test]
[Arguments(1, 2, 3)]
[Arguments(4, 5, 9)]
public async Task Addition(
int a, int b, int expected)
{
await Assert.That(a + b).IsEqualTo(expected);
}


2) Метод-источник (динамические данные)
[Test]
[MethodDataSource(nameof(TestCases))]
public async Task WithDynamicData(
int value, string expected)
{
var result = ProcessValue(value);
await Assert.That(result).IsEqualTo(expected);
}

public static
IEnumerable<(int, string)> TestCases()
{
yield return (1, "one");
yield return (2, "two");
yield return (3, "three");
}


3) Матричные тесты (комбинаторные)
[Test]
public async Task MatrixCombinations(
[Matrix(1, 2, 3)] int x,
[Matrix("a", "b")] string y,
[Matrix(true, false)] bool z)
{
// 3 × 2 × 2 = 12 комбинаций
await Assert.That(Validate(x, y, z)).IsTrue();
}


4) Класс-источник
[ClassDataSource<UserFixture>(Shared = SharedType.PerClass)]
public class UserServiceTests
{
[Test]
public async Task CreateUser(UserFixture fixture)
{
var user = await
fixture.Service.CreateAsync("John");
await Assert.That(user.Name).IsEqualTo("John");
}
}

public class UserFixture
{
public IUserService Service { get; }
= new UserService();
}

Как видите, TUnit поддерживает практически все функции, предлагаемые существующими фреймворками, часто усовершенствованные и модернизированные.

5. Жизненный цикл тестов и фикстур
public class TestLifecycleExample
{
private DatabaseConnection _conn;

// Перед каждым тестом
[Before(Test)]
public async Task SetupTest()
{
_conn = new DatabaseConnection();
await _conn.OpenAsync();
}

[Test]
public async Task DatabaseQuery()
{
var result =
await _conn.QueryAsync("SELECT * FROM Users");
await Assert.That(result).IsNotNull();
}

// После каждого теста
[After(Test)]
public async Task CleanupTest()
{
await _conn.CloseAsync();
_conn?.Dispose();
}

// Однажды на тестовый класс
[Before(Class)]
public static async Task SetupClass()
{
// Инициализация общих ресурсов
}

[After(Class)]
public static async Task CleanupClass()
{
// Очистка общих ресурсов
}

// Однажды на сборку
[Before(Assembly)]
public static async Task SetupAssembly()
{
// Инициализация данных уровня приложения
}
}


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

Источник:
https://trailheadtechnology.com/tunit-the-new-sheriff-in-town-for-net-testing/
👍6
День 2613. #Testing
TUnit – Новый Фреймворк Тестирования в .NET. Окончание

Начало
Особенности 1-3
Особенности 4-5

6. Нативный Fluent API
TUnit поддерживает цепочку fluent утверждений «из коробки», без необходимости установки каких-либо дополнительных пакетов или настройки.
[Test]
public async Task FluentAssertions()
{
var user = new { Name = "Alice",
Age = 30, Email = "alice@example.com" };

// Цепочка утверждений
await Assert.That(user.Name)
.IsNotNull()
.IsNotEmpty()
.StartsWith("Al");

// 'And' для логической группировки
await Assert.That(user.Age)
.IsGreaterThan(18)
.And.IsLessThan(65);

// Несколько утверждений в области
// (сообщение сразу о всех неудачах, а не о первой)
using var scope = Assert.Multiple();
await Assert.That(user.Name).IsEqualTo("Alice");
await Assert.That(user.Age).IsEqualTo(30);
await Assert.That(user.Email).IsNotNull();
}


7. Нативная поддержка внедрения зависимостей
Для использования внедрения зависимостей не требуется сложная настройка, TUnit поддерживает его «из коробки».
[Test]
public async Task WithDependencyInjection(
IUserService userService, ILogger logger)
{
var user = await userService.GetUserAsync(1);
logger.LogInformation($"Loaded user: {user.Name}");

await Assert.That(user).IsNotNull();
}

// Конфигурация через ServiceCollection
public void ConfigureServices(
IServiceCollection services)
{
services.AddScoped<IUserService, UserService>();
services.AddLogging();
}


Итого
Использовать ли TUnit?
TUnit - шаг вперёд в тестировании .NET. Существующие фреймворки остаются надёжными, стабильными и зрелыми, но они отражают проектные решения, принятые задолго до AOT, генераторов кода или современных ожиданий производительности.

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

Источник: https://trailheadtechnology.com/tunit-the-new-sheriff-in-town-for-net-testing/
👍11
Что будет выведено в результате работы кода из первого комментария?
#Quiz #CSharp
Anonymous Quiz
15%
2, 4, 6, 4, 5
50%
1, 4, 6, 8, 5
16%
1, 2, 3, 4, 5
13%
Ошибка компиляции
6%
Ошибка времени выполнения
👍18
День 2514. #Карьера #Оффтоп
Миф о «Хорошем Программисте»
Новичкам кажется, что быть хорошим программистом означает быть быстрым. Быстро печатать. Быстро решать задачи. Быстро отвечать. Но по мере накопления опыта оказывается, что все громко кричат о скорости, но молчат о глубине понимания. А глубина понимания — редкость.

Где-то в ИТ мы перепутали производительность с пониманием. Если кто-то решает 300 задач на структуры данных и алгоритмы, мы называем его сильным программистом. Если кто-то знает пять языков, мы называем его талантливым. Если кто-то может мгновенно объяснить временную сложность, мы называем его умным.

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

Программирование перестаёт быть просто написанием строк кода. Оно становится проблемой ясности мышления. А ясность мышления — это сложно.

В интернете программирование выглядит как соревнование. Аккаунты на GitHub полные зелёных квадратиков. Люди хвастаются сериями успехов: «Решил 500 задач», «Освоил 8 языков». Но никто не пишет: «Я переделывал свою логику 6 раз, потому что она не масштабировалась». Никто не хвастается отладкой в течение четырёх часов подряд. Никто не радуется удалению 200 строк ненужного кода.

И всё же именно здесь происходит рост. Быть «хорошим программистом» — это не значит решать задачи быстрее всех. Это значит сохранять спокойствие, когда ничего не работает. Это значит разбивать большую, некрасивую проблему на более мелкие, понятные части. Это значит проектировать, прежде чем писать. Это значит понимать «почему» настолько глубоко, что даже если язык меняется, ваше мышление остаётся неизменным. Языки развиваются. Фреймворки меняются. Синтаксис обновляется. Ясное мышление остаётся.

То, чем по-настоящему стоит гордиться
Создание чего-то с нуля, что действительно работает. Не идеально. Не красиво. Но логично. А затем улучшение этого. Снова. И снова. И снова. Возможно, вопрос был задан неправильно. Не: «Хорошо ли я разбираюсь в программировании?», а: «Могу ли я создавать? Могу ли я мыслить? Могу ли я упорствовать, когда всё идёт наперекосяк?»

Потому что программирование — это не представление. Это архитектура мышления. А для этого требуется не только скорость. Требуется терпение.

Источник:
https://medium.com/@shriya73spark2008/the-myth-of-being-good-at-coding-a9d15cbd689c
👍44
День 2615. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

28. Методы оценки
«Можете ли вы описать методы оценки, которые вы использовали в своих проектах, и объяснить, как они помогли вам обеспечить выполнение проектов в установленные сроки?»

Хороший ответ
В моём опыте управления проектами .NET я использовал комбинацию методов оценки для обеспечения точного прогнозирования и эффективного управления проектом. Среди них экспертная оценка (Expert Judgment), покер планирования (Planning Poker) и анализ точек использования (Case Point Analysis).

1. Экспертная оценка предполагает консультации с опытными членами команды для получения оценок на основе их прошлого опыта. Например, при работе над веб-сервисом, включающим ASP.NET Core и Entity Framework Core, я обсуждал задачи со старшими разработчиками, имеющими аналогичный опыт работы над проектами, чтобы получить реалистичное представление о трудозатратах и сроках.

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

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

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

Часто встречающийся плохой ответ
«Я примерно по опыту оцениваю время, которое потребуется на написание кода и добавляю к нему некоторый процент на непредвиденные проблемы. Обычно этого достаточно.»

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

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

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

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍3
День 2616. #SQLServer
SQL Server Незаметно Переименовывает Пользователя
Автор оригинала: Bart Wullems

Учитывая кучу существующих ИИ-помощников, можно было бы ожидать, что мы больше не будем тратить время на глупые проблемы. К сожалению, до этого ещё далеко. Сегодня я потерял немало времени из-за поведения SQL Server, которое кажется очевидным, если вы об этом знаете, а если не знаете —ужасно раздражает.

У меня был скрипт, который должен был быть идемпотентным: создать пользователя БД, если его не существует, или обновить его, если существует. Стандартная процедура. Вот упрощённая версия:
USE [ONT_SampleDB];
GO
IF EXISTS (SELECT * FROM sys.database_principals WHERE name = N'usr_DB_reader')
BEGIN
ALTER USER [usr_DB_reader] WITH LOGIN = [lg_DB_dev_reader];
END
ELSE
BEGIN
CREATE USER [usr_DB_reader] FOR LOGIN [lg_DB_dev_reader];
END
GO
ALTER ROLE [db_datareader] ADD MEMBER [usr_DB_reader];
GO

Выглядит нормально. Запустил один раз — работает. Запустил второй раз, и SQL Server выдаёт ошибку, что не может найти пользователя usr_DB_reader. Пользователя, которого мы только что создали. В той же базе данных. Этим же скриптом.

Что происходит на самом деле?
Когда вы запускаете ALTER USER […] WITH LOGIN = […], SQL Server переименовывает пользователя в соответствии с логином — по умолчанию, без предупреждений.

Таким образом, после первого запуска пользователя usr_DB_reader больше не существует. Он переименован в lg_DB_dev_reader, чтобы соответствовать логину. При втором запуске проверка IF EXISTS ищет usr_DB_reader, ничего не находит и переходит в ELSE — где CREATE USER завершается с ошибкой, потому что такой логин уже сопоставлен с пользователем c другим именем.

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

Решение
Явно укажите SQL Server сохранить имя пользователя как есть, добавив NAME = к оператору ALTER USER:
ALTER USER [usr_DB_reader]
WITH NAME = [usr_DB_reader],
LOGIN = [VLM\lg_DB_dev_reader];

Добавление WITH NAME = [usr_DB_reader] говорит SQL Server: да, измени логин, но не трогай имя пользователя. Теперь скрипт действительно идемпотентный.

Почему SQL Server так делает?
SQL Server различает логины (субъекты уровня сервера) и пользователей (субъекты уровня БД). Когда вы связываете пользователя с логином с помощью команды ALTER USER … WITH LOGIN, не указывая имя, SQL Server предполагает, что вы хотите синхронизировать их — поэтому он переименовывает пользователя в соответствии с логином. Это следует соглашениям, но это не то, чего вы ожидаете, если привыкли рассматривать имена пользователей как стабильные идентификаторы.

Источник: https://bartwullems.blogspot.com/2026/03/sql-server-silently-renames-your-user.html
👍4
День 2617. #Шпаргалка
Виды Классов в C#

Сегодня рассмотрим различные виды классов в C# и как они работают.

Абстрактный
Базовый класс, экземпляр которого нельзя создать. Он может содержать абстрактные и неабстрактные члены и предназначен для наследования от него.
public abstract class Vehicle
{
public abstract int Wheels { get; }
public abstract void TurnOn();
public bool Started { get; protected set; }
}

Создать экземпляр класса Vehicle нельзя, поэтому унаследуем от него. При этом класс-наследник обязан переопределить абстрактные члены:
public class Car : Vehicle
{
public override int Wheels => 4;
public override void TurnOn()
=> Started = true;
}


Запечатанный (sealed)
Специальный тип класса, который ограничивает иерархию наследования. Это предотвращает создание производных типов, что повышает безопасность кода и позволяет компилятору применять оптимизации производительности.
public sealed class Vehicle
{
}


Статический
Экземпляр статического класса нельзя создать, и от статического класса нельзя унаследовать. Все члены должны быть помечены статическими.
public static class SpeedConverter
{
public static decimal ToMph(decimal kph)
=> return kph / 1.6093m;
}


Частичный
В одном и том же пространстве имён нельзя создавать несколько классов с одним именем. Частичный класс позволяет разделить объявление класса на несколько файлов. При компиляции объявления объединяются в один класс. Нельзя дублировать члены с одной сигнатурой в разных объявлениях частичного класса:
public partial class Team
{
public Team() { }
public string Name { get; set; }
}

public partial class Team
{
public int Players { get; set; }
}


Небезопасный
Небезопасный класс позволяет использовать код с указателями:
public unsafe class MemoryReader
{
public void Read(int* value)
{
Console.WriteLine(*value);
}
}

Если вам необходимо работать с указателями, нужно включить небезопасный код в файле .csproj, установив параметр AllowUnsafeBlocks в значение true.

Запись
Запись — ссылочный тип, предназначенный для данных, а не для поведения, и по умолчанию является неизменяемой:
public record class Team(string Name, int Players);

См. подробнее про записи.

Модификаторы доступа
- public – доступен отовсюду;
- internal – доступен внутри сборки;
- private – доступен только внутри включающего его типа;
- protected – доступен внутри включающего его типа и всех его наследников;
- file – доступен только внутри содержащего его файла исходного кода.

Источник: https://www.roundthecode.com/dotnet-tutorials/c-sharp-class-types-explained-examples
👍15👎5
День 2618. #МоиИнструменты #PG
Инструменты Оптимизации Запросов в PostgreSQL. Часть 5


5. EXPLAIN ANALYZE (для всех SQL баз данных)
Что даёт: точное понимание, как БД выполняет запрос.
Тип: встроенная команда (для всех основных БД)
Зачем: показывает план выполнения запроса — как БД фактически обрабатывает SQL-запрос. Без этого оптимизация — это гадание.

Использование в разных базах данных:
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT … FROM orders WHERE …;

-- MySQL
EXPLAIN ANALYZE
SELECT … FROM orders WHERE …;

-- SQL Server
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
SELECT … FROM orders WHERE …;


Вывод (пример Postgres):
EXPLAIN (ANALYZE, BUFFERS) 
SELECT * FROM orders
WHERE customer_id = 12345
AND order_date >= '2024-01-01';


Seq Scan on orders  (cost=0.00..185234.25 rows=1 width=120) 
(actual time=0.045..2341.234 rows=247 loops=1)
Filter: ((customer_id = 12345) AND (order_date >= '2024-01-01'::date))
Rows Removed by Filter: 9847234
Buffers: shared hit=47234 read=138000
Planning Time: 0.234 ms
Execution Time: 2341.567 ms

Что это значит:
- "Seq Scan" – полное сканирование таблицы (ПЛОХО – не используется индекс)
- "Rows Removed by Filter: 9847234" - Просканировано 9.8млн строк, возвращено 247
- "Execution Time: 2341ms" - 2.3 секунды

Решение – добавить индекс на поля customer_id и order_date:
CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);

После:
Index Scan using idx_orders_customer_date on orders  
(cost=0.43..8.45 rows=1 width=120)
(actual time=0.023..0.087 rows=247 loops=1)
Index Cond: ((customer_id = 12345) AND (order_date >= '2024-01-01'::date))
Buffers: shared hit=5
Planning Time: 0.123 ms
Execution Time: 0.112 ms

Что это значит:
- "Index Scan" – Используется индекс (ХОРОШО)
- "Buffers: shared hit=5" – только 5 блоков прочитано (против 185234 в предыдущем случае)
- "Execution Time: 0.112ms" – в 20000 раз быстрее

Основные шаги оптимизации запросов
1. Выполнить EXPLAIN ANALYZE
2. Обнаружить узкое место (seq scan, дорогой join или сортировку и т.п.)
3. Исправить (добавить индекс, переписать запрос, изменить порядок объединения)
4. Ещё раз выполнить EXPLAIN ANALYZE для проверки

Когда использовать
- Анализ любого медленного запроса;
- Перед написанием сложных запросов (прогноз производительности);
- После изменений схемы (проверка влияния);
- При добавлении индексов (обоснование использования).

Когда отказаться
Особых причин не использовать нет.

Скрытая функция
Сравнение планов запросов
1. Сохранить в файл
psql -c "EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT …" > plan_before.json

2. Сделать изменения
3. Сохранить новый план
psql -c "EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT …" > plan_after.json

4. Использовать утилиту визуализации плана:
- https://explain.dalibo.com/
- https://explain.depesz.com/
5. Использовать утилиту визуального сравнения (например, https://winmerge.org/)

С осторожностью
EXPLAIN с параметром ANALYZE на самом деле выполняет запрос.
-- Удалит данные!!!
EXPLAIN ANALYZE DELETE FROM orders WHERE …;

-- Безопасное тестирование
BEGIN;
EXPLAIN ANALYZE DELETE FROM orders WHERE …;
ROLLBACK;
END;

-- Либо без ANALYZE (только оценка)
EXPLAIN DELETE FROM orders WHERE …;


Источник: https://medium.com/@reliabledataengineering/15-sql-optimization-tools-that-make-queries-10x-faster-8629ac451d97
👍17
День 2619. #ЗаметкиНаПолях
Паттерн Адаптер: Упрощаем Интеграцию со Сторонними Сервисами. Начало

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

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

Сценарий
Есть приложение, использующее следующий интерфейс:
public interface IPaymentProcessor
{
void ProcessPayment(decimal amount);
}

Всё приложение использует эту абстракцию. Однако, есть существующий сервис, производящий оплату:
public class LegacyPaymentService
{
public void MakePayment(string amount)
{
// обработка оплаты
}
}

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

Решение
1. Создаём адаптер
public class PaymentAdapter(
LegacyPaymentService legacyService) :
IPaymentProcessor
{
public void ProcessPayment(decimal amount)
{
var amntStr = amount.ToString("F2");
legacyService.MakePayment(amntStr);
}
}

Адаптер:
- реализует интерфейс приложения (IPaymentProcessor);
- преобразует десятичное число в строку;
- делегирует вызов устаревшему сервису.
Приложение остаётся чистым и не знает об устаревшей реализации.

2. Используем адаптер
LegacyPaymentService legacySvc = new();
IPaymentProcessor processor =
new PaymentAdapter(legacySvc);
processor.ProcessPayment(123.4567868m);


Результат:
- Приложение использует только IPaymentProcessor;
- Адаптер обрабатывает все преобразования;
- Устаревшая система полностью изолирована.

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

Источник:
https://thecodeman.net/posts/simplifying-integration-with-adapter-pattern
👍18
День 2620. #ЗаметкиНаПолях
Паттерн Адаптер: Упрощаем Интеграцию со Сторонними Сервисами. Окончание

Начало

Пример из реальной жизни: интеграция с облачным хранилищем
Представьте себе систему, поддерживающую несколько облачных провайдеров:
- Amazon S3
- Azure Blob Storage
- Google Cloud Storage
У каждого провайдера свои SDK и API. Без адаптера код становится тесно связанным с конкретной реализацией.

1. Определение общего интерфейса
public interface ICloudStorage
{
Task UploadAsync(string container,
string file, Stream stream);
Task<Stream> DownloadAsync(
string container, string file);
Task DeleteAsync(string container,
string file);
}

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

2. Реализуем адаптер (пример для Google Cloud)
public class GoogleCloudStorageAdapter 
: ICloudStorage
{
private readonly StorageClient _client;

public GoogleCloudStorageAdapter(
StorageClient client)
{
_client = client;
}

public async Task UploadAsync(
string container, string file, Stream stream)
{
await _client.UploadObjectAsync(
container, file, null, stream);
}

public async Task<Stream> DownloadAsync(
string container, string file)
{
MemoryStream stream = new();

await _client.DownloadObjectAsync(
container, file, stream);
stream.Position = 0;

return stream;
}

public async Task DeleteAsync(
string container, string file)
{
await _client.DeleteObjectAsync(container, file);
}
}

Адаптер:
- оборачивает SDK от Google,
- приводит вызовы к вашему интерфейсу,
- скрывает детали реализации.

3. Настройка внедрения зависимостей
builder.Services
.AddTransient<Func<string, ICloudStorage>>(
sp => provider =>
{
return provider switch
{
"Azure" => sp.GetRequiredService<AzureBlobStorageAdapter>(),
"Google" => sp.GetRequiredService<GoogleCloudStorageAdapter>(),
"AWS" => sp.GetRequiredService<S3StorageAdapter>(),
_ => throw new ArgumentException("Неподдерживаемый провайдер")
};
});

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

Когда использовать Адаптер
- интеграция сторонних API,
- работа с устаревшими системами,
- стандартизация нескольких провайдеров,
- переключение реализаций.

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

Источник: https://thecodeman.net/posts/simplifying-integration-with-adapter-pattern
👍11
День 2621. #ЧтоНовенького #МоиИнструменты
Bookmark Studio Закладки на Стеройдах в Visual Studio

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

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

Bookmark Studio - новое экспериментальное расширение Visual Studio от Мэдса Кристенсена, которое развивает существующий опыт работы с закладками. Вот его основные функции.

1. Навигация по слотам. Закладки можно назначать слотам с 1 по 9 и переходить к ним напрямую с помощью простых сочетаний клавиш, таких как Alt+Shift+1Alt+Shift+9. Это делает закладки более продуманными и удобными для быстрого доступа к нескольким важным разделам. Новые закладки автоматически назначаются следующему доступному слоту, если это возможно, поэтому быстрая навигация часто работает без дополнительной настройки. Bookmark Studio также интегрируется с существующими командами закладок Visual Studio, т.е. текущие сочетания клавиш продолжат работать.

2. Менеджер закладок (см. картинку). Отображает все закладки в одном месте и упрощает просмотр, поиск и навигацию между ними. Вы можете фильтровать по имени, файлу, местоположению, цвету или слоту и переходить непосредственно к закладке двойным щелчком или навигацией с помощью клавиатуры. Он разработан для того, чтобы упростить повторное обращение к закладкам, особенно при переключении контекста или возвращении к коду позже.

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

4. Экспорт. Закладки часто наиболее ценны, когда они отражают намерение, а не просто местоположение. Bookmark Studio позволяет легко экспортировать закладки в виде обычного текста, Markdown или CSV. Т.е. вы можете включать закладки в пул-реквесты, делиться с коллегами или перемещать полезные наборы закладок между репозиториями.

5. Отслеживание. Bookmark Studio отслеживает закладки по мере перемещения текста во время редактирования, поэтому они остаются прикрепленными к соответствующему коду, а не смещаются на неправильную строку.

Итого
Если вы уже используете закладки в Visual Studio, Bookmark Studio покажется вам знакомым за считанные минуты. А если вы когда-либо хотели, чтобы закладки могли делать немного больше, это расширение стоит посмотреть.

Источник: https://devblogs.microsoft.com/visualstudio/bookmark-studio-evolving-bookmarks-in-visual-studio/
👍8
День 2622. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.

29. Командная работа
«Расскажите, как вы развивали командную работу в ваших проектах? Какие стратегии и инструменты вы использовали для обеспечения эффективной коммуникации и интеграции между членами команды?»

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

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

- Проверки кода: внедрение надёжного процесса проверки кода имеет жизненно важное значение. Мы используем пул-реквесты, которые не только облегчают коллегиальную проверку, но и интегрируют проверки в конвейер CI/CD, где код автоматически собирается и запускаются тесты. Этот процесс гарантирует, что код соответствует стандартам качества до слияния, и способствует обмену знаниями и наставничеству внутри команды.

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

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

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

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

Часто встречающийся плохой ответ
«Пока каждый выполняет свою работу и соблюдает сроки, нет особой необходимости в дополнительном сотрудничестве или частых встречах. Мы просто отправляем дневные отчёты через e-mail или общаемся в чатах, чтобы держать всех в курсе».

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

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

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

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
День 2623. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Начало

Путь от провала на собеседовании до проектирования масштабируемых производственных систем.
Автор оригинала: Soma Sharma

Собеседование, которое изменило всё
Google. Второй раунд. Собеседование по системному проектированию: «Спроектируйте ленту Instagram для 2 миллиардов пользователей». У меня в голове всё помутнело. Я начал мямлить что-то о REST API. Упомянул MySQL. Поговорил о кэшировании… А потом тишина. Я понятия не имел, как это делать.
Шесть месяцев спустя я успешно прошёл собеседования по проектированию систем в Meta, Amazon и Netflix. Вот путь от полной растерянности до уверенного в себе системного проектировщика.

1. Я признал, что ничего не знал (и это нормально)
Годами я избегал проектирования систем. Я видел видео под названием «Разрабатываем Твиттер» и думал: «Это для архитекторов, а не для разработчиков, как я». Нет. Проектирование систем — это для всех, кто хочет понять, как ПО работает в масштабе. Я перестал спрашивать: «Достаточно ли я умён для этого?» и начал спрашивать: «Что нужно изучить в первую очередь?»

Проектирование систем — это набор концепций:
1) Инфраструктура:
- Как запросы проходят через интернет;
- Что происходит, когда вы вводите URL-адрес;
- DNS, балансировщики нагрузки, CDN.

2) Данные:
- Где хранить различные типы данных;
- Как сделать БД быстрыми;
- Когда использовать SQL, а когда NoSQL.

3) Масштабирование:
- Обработка 1 тыс пользователей против 1 млн;
- Стратегии кэширования;
- Горизонтальное и вертикальное масштабирование.

4) Надёжность:
- Что происходит при сбоях серверов;
- Как избежать единой точки отказа;
- Стратегии резервного копирования и восстановления.

2. Я разбил «проектирование систем» на части
1) Основы (недели 1–2)
Что происходит, когда вы набираете google.com?
- Поиск DNS (домен → IP-адрес);
- Установление TCP-соединения;
- Отправка HTTP-запроса;
- Обработка запроса сервером;
- Отправка ответа;
- Отображение страницы браузером.

Изученные концепции:
- DNS (телефонная книга интернета);
- Балансировка нагрузки (распределение трафика);
- CDN (сети доставки контента);
- Протоколы HTTP/HTTPS.

2) Данные и хранилища (недели 3–4)
SQL или NoSQL — когда что использовать?
Изученные концепции:
- SQL: структурированные данные, связи, ACID-транзакции;
- NoSQL: гибкая схема, горизонтальное масштабирование, итоговая согласованность;
- Индексирование: ускорение запросов в 100 раз;
- Репликация: копирование данных для повышения надёжности;
- Шардинг: разделение данных между несколькими БД.
Вывод: нет «лучшей» БД, есть подходящая для конкретного случая.

3) Методы масштабирования (недели 5–6)
Как системы обрабатывают миллионы пользователей?
- Вертикальное масштабирование: большие серверы (ограниченно, дорого);
- Горизонтальное масштабирование: больше серверов (неограниченно, сложно).

Кэширование:
- Хранение часто используемых данных в памяти (Redis, Memcached);
- 90% запросов попадают в кэш, а не в БД;
- Ускоряет работу систем в 100 раз.

Балансировка нагрузки:
- Распределение запросов между несколькими серверами: по очереди, по наименьшему количеству соединений, по хэшу IP;
- Предотвращает перегрузку какого-либо отдельного сервера.

Очереди сообщений:
- Разделение сервисов;
- Обработка пиковых нагрузок;
- Асинхронная обработка задач.

4) Архитектурные шаблоны (недели 7–8)
Монолитная архитектура:
Простота разработки и развёртывания;
Лёгкость понимания;
Сложность масштабирования отдельных частей независимо друг от друга;
Сбой в одном месте ведёт к сбою всей системы.

Микросервисы:
Независимое масштабирование компонентов;
Разные команды отвечают за разные сервисы;
Сложное развёртывание;
Проблемы распределённых систем.

Архитектура, управляемая событиями:
- Сервисы взаимодействуют посредством событий;
- Слабая связанность;
- Kafka, RabbitMQ для передачи сообщений.

Архитектура — это не вопрос «что лучше», а вопрос «что подходит для вашего масштаба и команды».

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

Источник:
https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍16