.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1084. #Курсы
Сегодня порекомендую вам пару курсов от Microsoft Learn Live.

1. Создание микросервисов в .NET и ASP.NET (Create microservices with .NET and ASP.NET).
Курс посвящён созданию независимо развертываемых, масштабируемых и отказоустойчивых сервисов на платформе .NET.

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

Предстоящие стримы:
- 18.01.2022. 12:00–13:30 Мск. Создание и развёртывание облачного микросервиса в ASP.NET Core.
- 25.01.2022. Реализация отказоустойчивости.
- 01.02.2022. Инструментарий для облачного микросервиса.
- 08.02.2022. Реализация флагов функций в микросервисном приложении.
- 15.02.2022. Использование хранилищ данных в микросервисном приложении.
- 22.02.2022. Понимание API-шлюзов в микросервисном приложении.
- 01.03.2022. Развёртывание микросервисного приложения с помощью GitHub Actions.

Помимо стримов, для каждой темы доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.

2. Обучение на сертификацию по Azure Cosmos DB (Azure Cosmos DB Certification Study Hall).
Узнайте, как разрабатывать, внедрять и отслеживать облачные приложения для хранения данных и управления ими, и получите сертификат Azure Cosmos DB Developer Specialty.

Этот курс проходит по средам и также начался 12 января. Первый модуль «Основы Azure Cosmos DB SQL API» можно посмотреть тут.

Предстоящие модули:
- 19.01.2022. 23:00–01:30 Мск. Планирование и реализация Azure Cosmos DB SQL API.
- 26.01.2022. Перемещение данных из и в базу.
- 02.02.2022. Использование SDK.
- 09.02.2022. Конфигурация SDK.
- 16.02.2022. Выполнение операций в базе данных.
- 23.02.2022. Выполнение кросс-документных транзакций.
- 02.03.2022. Обработка больших объемов данных.
- 09.03.2022. Запросы к БД.
- 16.03.2022. Создание сложных запросов.
- 23.03.2022. Определение индексов.
- 30.03.2022. Настройка индексов.
- 06.04.2022. Обработка сообщений об изменениях в SDK.
- 13.04.2022. Обработка событий в Azure Functions.
- 20.04.2022. Поиск по БД с помощью Azure Cognitive Search.

Как и для предыдущего курса, доступно и обычное обучение с практическими заданиями на Microsoft Learn. Ссылки на все темы доступны на странице курса.
👍5
День 1085. #ЗаметкиНаПолях
Использовать ли Одинаковые Идентификаторы в Разных Микросервисах
Существует распространённый и довольно интересный вопрос о том, как управлять идентификаторами в архитектуре микросервисов. Этот вопрос лучше всего описать на примере, поэтому, допустим, у нас есть два ограниченных контекста, Sales и Support, которые работают в своих соответствующих микросервисах (см. картинку ниже).

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

Отсюда вопрос: «Должен ли сервис поддержки использовать тот же ID, что и сервис продаж, или нужно назначать собственный ID своей версии клиента?»

Если вы используете UUID(GUID), вы можете легко назначить раздельные ID клиента в обоих сервисах, и идентификаторы в двух ограниченных контекстах не будут пересекаться. Но хорошо ли это?

Нет. Используйте один и тот же идентификатор во всей вашей системе, если этот идентификатор создаётся одним из ваших микросервисов. ID — это одно из свойств, представляющих клиента. Идентификатор контролируется микросервисом, который создаёт экземпляры клиента. В нашем примере это сервис продаж. Новые клиенты создаются, когда ваша компания что-то им продаёт. Поэтому Sales — главный микросервис для идентификации всех клиентов. Только сервис Sales может назначать ID. Нижестоящие сервисы должны реплицировать его в свои базы данных, аналогично тому, как они реплицируют свойства Name или Email.

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

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

Стоит ли использовать этот идентификатор Facebook как есть?
Нет, вам нужно создать собственный идентификатор клиента и сохранить идентификатор Facebook как отдельное (необязательное) поле. Разница здесь в том, что у вас нет контроля над идентификаторами, которые предоставляет вам Facebook. Facebook потенциально может изменить эти идентификаторы и испортить вашу систему.

Контракт между вашими микросервисами намного прочнее, чем между вашей системой и Facebook. Вы можете (и должны) поддерживать обратную совместимость между микросервисами в вашей системе. Нет никакой гарантии такой совместимости между вашей системой и Facebook.

Источник: https://enterprisecraftsmanship.com/
Автор оригинала: Владимир Хориков
👍9👎1
Возник вопрос в переводе термина Entity Framework Core (EF Core) - это ...
*см. первый комментарий
Anonymous Poll
79%
фреймворк (он)
21%
библиотека (она)
👍4
День 1086. #ЗаметкиНаПолях
Защитите Контейнеры с Помощью Подписи в GitHub Actions
Большинство контейнеров, доступных сегодня, уязвимы для атак цепочки поставок, потому что они могут быть опубликованы только с помощью простого ключа API. В случае утечки этого ключа злоумышленник может легко опубликовать законно выглядящий контейнер, который на самом деле содержит вредоносное ПО. Один из лучших способов защитить пользователей от таких атак — подписать образ во время создания.

GitHub является одним из основателей Open Software Security Foundation (OpenSSF), отраслевой группы, занимающейся созданием решений с открытым исходным кодом для защиты разработчиков от угроз безопасности. Один из проектов, спонсируемых OpenSSF, — это sigstore, который позволяет разработчикам безопасно создавать, распространять и проверять подписанные артефакты ПО.

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

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

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

jobs:
build:
steps:
# ... шаги сборки

- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key

- name: Sign container image
run: |
cosign sign --key cosign.key \
-a "repo=${{ github.repository }}" \
-a "workflow=${{ github.workflow }}" \
-a "ref=${{ github.sha }}" \
ghcr.io/your-org/your-repo:some-tag
env:
COSIGN_PASSWORD: ""

Заметьте, что в подпись добавлены аннотации репозитория, рабочего процесса и ссылки на коммит. Вот и всё.

Затем проверить подпись после получения образа можно с помощью команды:
cosign verify --key cosign.pub ghcr.io/your-org/your-repo:some-tag

А используя инструменты fulcio и Rekor от sigstore вы можете подписывать свои образы контейнеров с помощью токена OIDC, предоставленного GitHub, без необходимости предоставления и поддержки собственного закрытого ключа.

Источник: https://github.blog/2021-12-06-safeguard-container-signing-capability-actions/
👍4
День 1087. #ЗаметкиНаПолях
Зачем Использовать DateTimeOffset
DateTime повсеместно используется как тип свойств сущностей и полей базы данных, но если вас действительно волнует временная часть значения, она часто неоднозначна. В каком часовом поясе указана дата? Она сохраняется как UTC? Всегда? Вы уверены? DateTimeOffset решает эту проблему.

Хранение значений DateTime
Тип DateTime всегда подразумевает время локальной машины. Когда вы запрашиваете .Today или .Now, он использует локальные системные часы. Это может легко вызвать проблемы между машинами с разным временем и/или часовыми поясами:
var now = new DateTime(2022,1,20,18,11,30);
Console.WriteLine(now);
// 1/20/2022 6:11:30 PM

Console.WriteLine(now.ToLocalTime());
// 1/20/2022 9:11:30 PM

Console.WriteLine(now.ToUniversalTime());
// 1/20/2022 3:11:30 PM

Изначальное время — 18:11 20 января 2022 года на моем компьютере в московском часовом поясе. Переменная now не содержит данных о часовом поясе. Функции ToLocalTime и ToUniversalTime предполагают, что данные, с которыми они работают, представлены в формате UTC или по местному времени соответственно. В результате время, полученное с помощью ToLocalTime, неверно, так как оно добавляет 3 часа к фактическому времени 18:11.

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

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

По всем этим причинам может оказаться целесообразным хранить даты в вашем приложении, используя тип DateTimeOffset.

DateTimeOffset является типом как в .NET, так и в SQL Server (в других базах данных тоже есть эквиваленты). Основное отличие между ним и более простым DateTime в том, что он включает смещение часового пояса от UTC. Таким образом, при взгляде на DateTimeOffset всегда ясно, какое время имеется в виду, будь то UTC или местное время.

Добавим к примерам выше аналоги с DateTimeOffset:
var nowHere = new DateTimeOffset(now);

Console.WriteLine(nowHere);
// 1/20/2022 6:11:30 PM +03:00

Console.WriteLine(nowHere.ToLocalTime());
// 1/20/2022 6:11:30 PM +03:00

Console.WriteLine(nowHere.ToUniversalTime());
// 1/20/2022 3:11:30 PM +00:00

Мы видим 2 изменения:
- значение ToLocalTime правильное,
- строковые представления даты включают смещение часового пояса.

DateTimeOffset в SQL Server
DateTimeOffset использует переменную точность и поэтому может занимать больше места, чем DateTime. Если вы хотите увидеть разницу, выполните следующие запросы:
select GetDate()
select SYSDATETIME()
select SYSDATETIMEOFFSET()

Результаты (заметьте разницу в точности):
2022-01-20 18:30:18.227
2022-01-20 18:30:18.2292271
2022-01-20 18:30:18.2292271 -05:00

EF Core прекрасно поддерживает DateTimeOffset.

Итого
Значения DateTime не имеют сведений о смещении часового пояса или его отсутствии (в UTC). Если вам нужно знать, когда что-то произошло на самом деле, с большей точностью, чем просто приблизительная дата, и вы не можете быть на 100% уверены, что ваши даты ВСЕГДА хранятся в формате UTC, вам следует рассмотреть возможность использования DateTimeOffset для представления значений даты и времени. Этот тип указывает смещение, таким образом, может устранить множество ошибок, от которых страдают многие системы реального мира.

Источник: https://ardalis.com/why-use-datetimeoffset/
👍26
День 1088. #ВопросыНаСобеседовании #Карьера #Юмор
Расскажите, Как Вы Провалили Собеседование
Наткнулся недавно на замечательный тред в Твиттере, который начался с такого текста:
«Давайте расскажем всем, что провал на интервью – это нормально. Это не является желаемым результатом, но это случается. И когда это действительно происходит, охота провалиться сквозь землю.
Расскажите о случае, когда вы реально завалили интервью. Бонус, если вы его завалили, когда уже были сеньором.
»

Приведу здесь переводы лучших ответов, а полный тред смотрите по ссылке внизу.

cube_drone: «Я предпочитаю думать об этом не как о провале интервью. Просто по ходу вы и интервьюер приходите к выводу, что вы, вероятно, не подходите друг другу.
Возможно, потому что они обнаружили, что вы высмеивали основателя компании в социальных сетях.
»

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

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

b0rk: «Однажды я проходил собеседование на должность разработчика ядра в Apple и описывал, как исправил действительно запутанную ошибку. Интервьюер спросил, как я это диагностировал, и я сказал: «Э-э… я попросил помощи в Твиттере, и кто-то сказал мне, что не так». Они не впечатлились ответом :)»

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

DeborahKurata: «Я была менеджером по программному обеспечению более 5 лет. Чтобы лучше управлять, я выучила C++.
Проходила собеседование на должность менеджера в другой компании. Прошла все собеседования, кроме последнего, с командой. Самоучка, я не знала разницы между «свойством» и «методом», поэтому они меня завалили…
После этого я углубилась в эту тему и написала несколько книг по ООП.
Незнание нескольких терминов не должно быть причиной провала собеседования, особенно если вы знаете понятия.
В итоге все получилось… Я открыла свою компанию.
»

GameDevMum: «Совсем недавно у меня было интервью, первое за долгое время после того, как я стала матерью во время пандемии. Муж должен был наблюдать за нашим ребёнком, она ускользнула от него, пробежала голой позади меня, вскочила мне на колено и… показала голый зад интервьюеру. Я не получила работу.»

EricFishor: «Помню, первая часть прошла как по маслу, мы поладили, они были впечатлены моими ответами и т. д.
Затем они попросили меня, написать код алгоритма сортировки на доске. Я посыпался на нём. Зачем кодировать что-то встроенное, а доски — ужасные редакторы кода.
Я ушел в нехорошем настроении, а они решили выбрать кого-то другого, кто «лучше справился с технической частью интервью». «Технической частью» был тест на доске. (Я добавил несколько функций и исправлений ошибок в сам фреймворк, на котором они меня тестировали)
»

iandouglas736: «Хорошо прошёл все технические части, но мне начали задавать вопросы про американский футбол, о котором я НИЧЕГО не знал. Я из Канады, спросите меня о хоккее, но кому, чёрт возьми, нужен ваш Хайсман трофи?
Команда фентези-футбола из Yahoo – вот кто меня собеседовал.
»

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

Источник: https://twitter.com/engineering_bae/status/1395447011902636033
👍12
День 1089. #юмор
👍3
День 1058. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 6
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Предыдущие части: 1, 2, 3, 4, 5

Поддержка интеграционных тестов в WebApplicationFactory
В предыдущем посте мы рассмотрели изменения, внесённые в HostBuilder для поддержки HostFactoryResolver, используемого инструментами EF Core. Это было достигнуто за счёт добавления дополнительных событий DiagnosticSource в HostBuilder. Они позволяют HostFactoryResolver получить доступ к HostBuilder без необходимости использовать соглашения предыдущих версий.

Инструментам EF Core просто необходимо получить доступ к построенному IHost, чтобы извлечь из него IServiceProvider, поэтому запущенное фоновое приложение останавливалось. Однако WebApplicationFactory должна иметь возможность модифицировать HostBuilder вашего приложения до вызова Build(). Кроме того, ей требуется, чтобы приложение продолжало работать для отправки в него тестовых запросов.

WebApplicationFactory предоставляет несколько способов настройки вашего приложения в интеграционных тестах, но по своей сути он предоставляет способ запуска экземпляра хоста вашего приложения в памяти. Один из основных методов в этом процессе — EnsureServer(). Он отвечает за создание тестового сервера, предварительно получая экземпляр IHostBuilder. IHostBuilder он пытается получить через Program.CreateHostBuilder(), обычно используемый в ASP.NET Core 3.x/5. Если это не удаётся, он ищет метод Program.CreateWebHostBuilder(), используемый в ASP.NET Core 2.x. Если и это не удается, он прибегает к подходу .NET 6.

В этом подходе используется новый тип DeferredHostBuilder. DeferredHostBuilder предназначен для «захвата» вызываемых в нём методов конфигурации (например, ConfigureServices()), а затем «воспроизведения» их для IHostBuilder реального приложения, как только он станет доступен. Методы «отсрочки» собирают методы конфигурации в виде мультикаст-делегата, а делегаты затем применяются к IHostBuilder, когда вызывается событие HostBuilding экземляра DiagnosticSource.

Затем запускается процесс, описанный в предыдущем посте, в котором приложение выполняется в отдельном потоке, с помощью событий DiagnosticSource вызывается настройка ConfigureHostBuilder() и возвращается экземпляр IHost. При этом приложение в отдельном потоке не перестаёт работать, потому что нам нужно, чтобы остальной код в Program.cs приложения выполнился. DeferredHostBuilder сохраняет IHost в новый тип DefferedHost.

DeferredHost отвечает за ожидание правильного запуска приложения. Ему нужно подождать, пока будут настроены все конечные точки, а также исполнится любой дополнительный стартовый код. Это достигается через существующие события IHostApplicationLifetime, которые вызываются в обычном приложении на универсальном хосте при запуске. В частности, вызов NotifyStarted() вызывает событие ApplicationStarted, которое DeferredHost использует для определения того, что приложение запущено, и можно безопасно запускать тесты. См. рисунок ниже.

После этого WebApplicationFactory создаёт HttpClient, как и в предыдущих версиях, и вы можете выполнять вызовы к приложению в памяти, как и раньше. Стоит знать, что (в отличие от предыдущих версий ASP.NET Core) в ваших тестах будет исполнено всё, что содержится в Program.cs приложения. Но, помимо этого нюанса, всё в вашем тестовом коде останется прежним.

Подробный перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-4-building-a-middleware-pipeline-with-webapplication/
👍4
День 1091. #ЗаметкиНаПолях
Упрощённые API Криптографии в .NET5/6
За последние несколько выпусков в .NET улучшили сценарии использования разработчиками криптографических примитивов, таких как AES, SHA и т. д.

«Лучше» — интересное понятие, когда мы говорим о дизайне криптографического API. Для разработчика «лучше» может означать большую пропускную способность, меньшее количество аллокаций или просто менее громоздкий API. Для автора фреймворка или библиотеки это означает думать о том, как разработчики могут использовать или неправильно использовать API.

Давайте посмотрим на шифрование AES, каким оно было во времена .NET Framework:
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;

using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
byte[] encrypted = transform.TransformFinalBlock(data, 0, data.Length);
}

Тут от силы десяток строк кода, но не все из них очевидны. Что такое transform? Что за финальный блок?

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

Простые API важны для защиты от неправильного использования, и .NET стал лучше в этом отношении за последние несколько выпусков, предложив для Aes, TripleDES, и т.п. алгоритмов API с вызовом единственного метода, вроде EncryptCbc, EncryptEcb или DecryptCbc и DecryptEcb:
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;
aes.Key = key;

// Шифруем все данные за раз
byte[] encrypted = aes.EncryptCbc(data, iv);
}
Здесь нет ICryptoTransform и нет необходимости беспокоиться о блоках, отступах и т. д.

Кроме того, некоторые методы сделали статическими. Хеширование до .NET 5 это выглядело примерно так:
byte[] data = …; 
using (SHA256 hash = SHA256.Create())
{
byte[] digest = hash.ComputeHash(data);
}
Теперь вместо получения экземпляра алгоритма, есть статический метод:
byte[] digest = SHA256.HashData(data);

Для .NET 6 API хеширования также были перенесены в классы HMAC, предлагая такие же улучшенные API и более высокую производительность. PBKDF2 также улучшил производительность в .NET 6:
byte[] salt = RandomNumberGenerator.GetBytes(32);
byte[] prk = Rfc2898DeriveBytes.Pbkdf2(
userPassword,
salt,
iterations: 200_000,
HashAlgorithmName.SHA256,
outputLength: 32);

Обновлённые API позволяют работать с ReadOnlySpan<byte> для входных данных и возможность записи в Span<byte> для выходных данных.

Источник: https://vcsjones.dev/one-shot-crypto/
👍1
День 1092. #CodeReview
10 Советов по Написанию Эффективных Обзоров Кода. Начало
Существует несколько хороших практик, которые рецензент может использовать, чтобы сделать процесс рецензирования менее скучным и более ясным. Это выигрыш для всех, потому что:
- автор будет более четко понимать ваши отзывы, что приведёт к меньшему количеству итераций обзоров кода;
- вы будете давать автору советы и замечания, которые повлияют на его подход к кодовой базе в целом, чтобы в следующий раз было меньше «ошибок».
Вот несколько советов и практических правил относительно подхода к процессу рецензии.

1. Всегда сообщайте «почему»
Рассмотрим некоторые типичные комментарии из обзора:
- «неправильное имя функции»,
- «этого не должно быть здесь»,
- «думаю, это может сломаться».
По этим комментариям автор кода может только предположить, что не так с его кодом. Но может прийти к неправильному выводу о том, почему он не верен, и сделать неправильное исправление. Это приведёт к дополнительной итерации обзора.
Вот пример хорошего комментария:
«Это имя функции не идеально; оно должно начинаться с глагола действия, и в этом случае иметь указание аргумента, типа '...ByUserId'. Подробнее см. документацию по стилю кода.»
Да, вы написали кучу слов, зато ясно объяснили автору, что не так, и как следует поступать в будущем.

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

3. Не выдавайте конечный результат
Как рецензент изменений кода, вы не должны исправлять его или придумывать решение, когда обнаружите проблему. Да, вы можете помочь или предложить свою идею, но это не значит давать полное решение проблемы. Следующий комментарий мало чему научит автора:
«Этот метод не совсем подходит, он должен быть примерно таким:
<кусок кода>
»
Скорее всего, автор скопирует и вставит его себе, не задумываясь. И в дальнейшем не будет сильно беспокоиться о качестве своего кода, если будет знать, что вы за него всё исправите.
Правильно в этом случае было бы описать, как должен выглядеть метод, объяснить, почему код не сработает, и как бы вы подошли к решению проблемы. Иногда допустим псевдокод.

4. Не откладывайте обзор
Очень важно не блокировать работу друг друга. Переключение контекста — непростая задача для разработчика. И в ожидании обзора кода по одной из своих задач автору придётся переключиться на другую, и переключаться туда и обратно, по мере получения комментариев и ответа на них. Это в любом случае произойдет, даже если проверка пройдёт очень быстро. Однако, чем дольше затянется обзор, тем больше таких переключений будет происходить.
Кроме того, вы можете и фактически блокировать чью-то работу. Т.е. люди не смогут продолжать работу, пока эта функция не будет завершена и изменения кода не будут объединены. Всё это неэффективно и раздражает обе стороны.
Чтобы убедиться, что такого не произойдёт, всегда полезно отдавать приоритет проверкам кода в своем расписании. Конечно, у вас могут быть более срочные задачи, но вы обязаны найти правильный баланс между собственной работой и обзорами.

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

Источник:
https://betterprogramming.pub/10-tips-to-write-effective-code-reviews-c25c25aa22c5
👍6
День 1093. #CodeReview
10 Советов по Написанию Эффективных Обзоров Кода. Окончание
Начало

5. Следуйте рекомендациям
Если ваша команда достаточно большая, у вас наверняка есть некоторые рекомендации по написанию кода проекта: общие подходы к организации кода, соглашения об именах и т.п. Если вы активно пишете код и просматриваете код других, вы, вероятно, привыкли к ним. Однако полезно возвращаться к рекомендациям и пробегать их глазами каждые пару недель или каждый месяц. Вы будете удивлены, увидев, сколько моментов вы забыли или на которые не обращали внимания в обзорах.
Если у вашей команды нет этих правил, было бы неплохо написать их во время очередной проверки кода. Такой список будет служить единственным источником правды в вашей команде, гарантируя, что у всех авторов кода и рецензентов есть ресурс, на который можно обратить внимание при написании/обновлении/чтении кода.

6. Хвалите хороший код
Проверка кода не всегда связана с криткой. Иногда вы будете натыкаться на код, который заставит вас отдать должное автору. Кусок логики, очень эффективная оптимизация запросов или блестящий UX. Сообщите об этом.
Можно оставить простой комментарий, типа, «неплохо!» или более длинный с рассказом, что вам в нём понравилось. В любом случае, человек почувствует, что его ценят, и это определенно улучшит командную работу. А если остальная часть кода не так хороша, автору будет не так обидно. Кроме того, подтверждение качества куска кода побудит автора пытаться создавать больше подобного кода в будущем или выполнять аналогичные действия для других проблем.

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

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

9. Это не вы, это они
При чтении кода важно, чтобы вы могли легко его понимать. Если какая-то часть кода кажется непонятной, это не значит, что вы невнимательно прочитали код. Это означает, что код недостаточно ясен.
Если у вас есть проблемы с пониманием того, что делает этот фрагмент, другим разработчикам, вероятно, будет трудно понять его в будущем, когда им понадобится изменить эту часть кода. Когда вы обнаружите такой код, не стесняйтесь сообщить об этом. И не просто просите автора объяснить его, попросите переписать его. Если нельзя написать более понятно, хотя бы попросите автора добавить комментарии.

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

Источник: https://betterprogramming.pub/10-tips-to-write-effective-code-reviews-c25c25aa22c5
👍15
День 1094. #Карьера
Стрижка Яка: Краткий Урок, Как Оставаться Сосредоточенным
«Стрижка яка» — это термин, придуманный Карлин Вьери, доктором философии MIT, в 1991 году после просмотра эпизода «The Ren and Stimpy Show». Это то, чем вы занимаетесь, когда выполняете какую-то глупую, занудную задачу, не имеющую очевидного отношения к тому, над чем вы должны работать, но тем не менее цепочка из множества причинно-следственных связей связывает то, что вы делаете, с выполнением исходной метазадачи.

Вот отличный пример в исполнении Брайана Кренстона https://www.youtube.com/watch?v=AbSehcT19u0

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

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

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

Не поймите меня неправильно, рефакторинг — это хорошо, когда речь идет о грязном коде. (Хотя, было бы лучше не допускать грязного кода вообще, но я отвлекся.) У рефакторинга есть время и место, но посреди простого исправления ошибок ему не время и не место.

Короче говоря, сосредоточьтесь на поставленной задаче и не стригите яка!

Источник: https://betterprogramming.pub/10-tips-to-write-effective-code-reviews-c25c25aa22c5
👍6
День 1095. #юмор
День 1096. #Карьера
Лучший Совет в Вашей Карьере
Снова возьму контент из твиттера))) На этот раз очень известный в узких кругах Ник Чапсас задал простой вопрос: «Какой лучший совет вам когда-либо давали как программисту?»
Лучшие ответы (вне всякого порядка) привожу ниже. А вы пишите ваши варианты в комментариях.

- Компьютер делает то, что вы ему скажете, и только то, что вы ему скажете.

- Когда залезаешь в какой-то код, всегда оставляй его в лучшем виде, чем он был, когда ты его нашёл.

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

- Если ты не знаешь, как это назвать, ты не понимаешь, для чего это нужно.

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

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

- Часто ясно сообщать о том, что вы НЕ поддерживаете, и гарантировать, что люди НЕ смогут использовать ваш код для этого, на самом деле важнее, чем описывать то, что вы поддерживаете.

- Не переживай из-за того, что не знаешь всего, не жди, что сможешь всё запомнить и не бойся потерпеть неудачу.

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

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

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

- Пишите предложение WHERE перед DELETE.

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

Источник: https://twitter.com/nickchapsas/status/1486768911622320131
👍28
День 1097. #BestPractices
Лучшие Практики Разработки в C#. Начало
Написанию кода можно научиться из онлайн документации или учебника. Однако навыки анализа и проектирования так просто не получить. В этой серии постов рассмотрим некоторые передовые методы проектирования, которые на практике доказали свою эффективность.

1. Унифицированный возвращаемый объект
Иногда нужно как-то описать данные, возвращаемые методом. Мы называем это метаданными, так как это не собственно данные, а некоторая важная информация, которая может быть использована позже.

Иногда понятно, что метод возвращает какие-то данные, например коллекцию объектов, и некоторые метаданные (общее количество объектов и текущую страницу). Но иногда наличие метаданных не очевидно. Например, метод void RenameFile(string newName) принимает новое имя файла и ничего не возвращает. В реальном мире это может быть проблемой, потому что операция может завершиться неудачей, и тогда метод должен что-то вернуть, чтобы сообщить об этом вызывающей стороне.

Я уже слышу ваши варианты: вернуть булевый результат, вернуть новое имя файла в случае успеха или пробросить исключение вызывающему коду. Эти решения могут работать, однако лучше всего здесь вернуть унифицированный объект с некоторыми данными, описывающими, что на самом деле произошло:
public enum FileRenameFailureReason {
None = 0,
ExistingName = 1,
PathTooLong = 2
}

public class FileRenameResult
{
public FileRenameFailureReason FailureReason { get; }
public string FinalName { get; }
public bool Succeeded =>
FailureReason == FileRenameFailureReason.None;
}

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

2. Разбиение на страницы
Разбиение на страницы — один из широко используемых паттернов, но почти никто не говорит о нём с точки зрения практики проектирования. Когда вы создаёте API, вы должны контролировать объём данных, проходящих через него. Поэтому при разработке API полезно спроектировать стратегию регулирования объема трафика. Вы можете сказать, что у вас нет лимитов, но скорее всего они просто настолько большие, что вы их не осознаёте. Например, если у вас есть метод GetAllEmployees, вы можете разрешить вызывающей стороне получать данные всех сотрудников. А что, если когда-нибудь в системе будет 10 000 сотрудников. Сейчас самое подходящее время вернуть только первые 1000 или 5000 и предоставить вызывающей стороне объект метаданных, сообщающий, что произошло и как получить следующую порцию.

3. Делегаты вместо Func<>
Когда нужно определить ссылку на метод, иногда полезнее явно определить делегат, а затем его использовать:
public delegate bool MessageHandler(int id, string msg);

public class Module1
{
public MessageHandler Handler { get; set; }
}
public class Module2
{
public Func<int, string, bool> Handler { get; set; }
}

Отличие в том, что вы получите более информативные подсказки в IntelliSense в виде имён параметров вместо arg1, arg2 в случае использования Func<>.

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

Источник:
https://levelup.gitconnected.com/design-best-practices-in-net-c-8e37b7c3500a
👍4
День 1098. #BestPractices
Лучшие Практики Разработки в C#. Продолжение
Начало

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

Ответ прост: Car не должен зависеть от ILogger. Вы можете возразить, что даже если он не полностью зависит от него, он все равно нуждается в нём. Не совсем так. На самом деле ILogger нужен основному приложению, которое знает и о Car, и о ILogger. Основное приложение должно получить некоторую информацию от класса Car, а затем использовать ILogger для регистрации этой информации. Поэтому правильной реализацией будет удалить эту зависимость и реализовать события. В нашем примере класс Car должен определить событие SpeedChanged, а основное приложение должно подписаться на него:
public delegate void SpeedChangedEventHandler(object sender, double speed);

public class Car
{
public event SpeedChangedEventHandler SpeedChanged;

public void Accelerate()
{
var speed = …;


OnSpeedChanged(speed);
}

protected virtual void OnSpeedChanged(double speed)
{
SpeedChanged?.Invoke(this, speed);
}
}
Заметьте, что мы не вызываем событие напрямую из метода Accelerate, а оборачиваем вызов в виртуальный метод, который, во-первых, безопасно вызывает событие, а во-вторых, может быть переопределён (например, если для каких-то типов наследников не нужно вызывать это событие).

5. Использование моментальных снимков (Snapshot)
Продолжая пример выше, допустим, мы захотели отображать значение скорости на экране. Создадим объект трекера, который будет подписан на событие SpeedChanged:
public class Tracker
{
private double currentSpeed = 0;

public Tracker(Car car)
{
car.SpeedChanged += (o, speed) =>
{
currentSpeed = speed;
ShowOnScreen(speed);
};

ShowOnScreen(currentSpeed); // здесь будет 0
}

}
Проблема в том, что к моменту создания трекера объект Car уже может быть создан, запущен и иметь некоторую постоянную скорость. Тогда до изменения скорости трекер будет выводить на экран 0. Здесь поможет шаблон «Моментальный снимок» (Snapshot). Это неизменяемый объект-значение, хранящий состояние объекта Car. Для него как нельзя лучше подойдёт тип записи:
public record CarState(double speed, double temperature, …);

public class Car
{
public CarState Snapshot { get; private set; } = new CarState(0,0,…);

public void Accelerate()
{
var speed = …;

Snapshot = Snapshot with { Speed = speed };

OnSpeedChanged(Snapshot.Speed);
}

}
Тогда при создании трекера его можно инициализировать данными из моментального снимка объекта Car:
public Tracker(Car car)
{
currentSpeed = car.Snapshot.Speed;
car.SpeedChanged += (o, speed) =>
{

}
}

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

Источник:
https://levelup.gitconnected.com/design-best-practices-in-net-c-8e37b7c3500a
👍10
День 1099. #BestPractices
Лучшие Практики Разработки в C#. Окончание
Начало
Продолжение

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

7. Неиспользование IoC-контейнеров — не оправдание
Допустим, по какой-то причине вы не используете контейнеры внедрения зависимостей в своём проекте. Но это не оправдание раскидывать создание объектов через new по всему коду.
Разработчики иногда путают внедрение зависимостей (DI), инверсию управления (IoC) и IoC-контейнеры. Это три разные, независимые вещи. Здесь не подходит принцип использовать либо всё, либо ничего. IoC-Контейнеры — это лишь способ сопоставления абстракции зависимости с её реализацией.
Поэтому, если вы не используете IoC-контейнеры, это не означает, что ваши зависимости должны создаваться беспорядочно, без какого-либо планирования и проектирования. Да, в итоге вам придётся использовать new, но есть большая разница между использованием его в некоторых изолированных местах (корне композиции) и ​​по всему коду проекта.

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

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

Источник: https://levelup.gitconnected.com/design-best-practices-in-net-c-8e37b7c3500a
День 1100. #ЧтоНовенького
Новые Функции Visual Studio 2022

Очистка кода при сохранении
Начиная с VS 2022 Preview 2 разработчики могут автоматически выполнять очистку кода при сохранении файла. Это поможет очистить ваш файл кода, правильно отформатировать его и применить ваши настройки стиля кодирования. Некоторые настраиваемые параметры включают: форматирование документа, сортировку и удаление ненужных директив using и т.п. Чтобы включить эту функцию сначала перейдите в раздел Analyze > Code Cleanup > Configure Code Cleanup (Анализ > Очистка кода > Настроить очистку кода), чтобы персонализировать желаемые настройки в профилях очистки кода. Затем перейдите в Tools > Options > Text Editor > Code Cleanup (Инструменты > Параметры > Текстовый редактор > Очистка кода) и отметьте флажок "Run Code Cleanup profile on Save" (Выполнять очистку кода при сохранении). Убедитесь, что при этом вы выбрали нужный профиль очистки.

Ускорение поиска по коду
Поиск в файлах в Visual Studio 2022 уже более чем в 2 раза быстрее для 95% поисковых запросов по сравнению с Visual Studio 2019. В VS 2022 Preview 3 представлен индексированный поиск в файлах, который ещё быстрее! Предварительные замеры производительности показывают, что индексированный поиск в 95% случаев выполняется чуть дольше 1 секунды!
Чтобы убедиться, что индексированный поиск включен, перейдите в Tools > Options > Environment > Preview Features (Инструменты > Параметры > Среда > Предварительный просмотр функций) и убедитесь, что установлен флажок "Enable indexing for faster find experience" (Включить индексирование для более быстрого поиска).
С этого момента при загрузке решения или открытии папки Visual Studio будет запускать вспомогательный процесс ServiceHub.IndexingService.exe и передавать ему список файлов для индексации. Затем индексатор будет просматривать файлы и создаст индекс всех n-грамм, содержащихся в каждом файле.
Когда пользователь выполняет поиск, этот индекс используется для удаления файлов из результатов, чтобы поиск выполнялся быстрее. Процесс индексирования не влияет на загрузку решения, сборку и действия пользователей, поскольку выполняется с приоритетом операционной системы ниже нормального и вне основного процесса Visual Studio.

Источники:
-
https://devblogs.microsoft.com/visualstudio/bringing-code-cleanup-on-save-to-visual-studio-2022-17-1-preview-2/
-
https://devblogs.microsoft.com/visualstudio/code-search-in-visual-studio-is-about-to-get-much-faster/
👍2
$1 млн. - и вы больше никогда не трогаете код (не писать, не ревьюить, не читать и т.п.). Согласитесь?
Anonymous Poll
53%
да
47%
нет