.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
День 1115. #Testing
Вы Неправильно Называете Тесты! Окончание
Начало
Продолжение

Шаблон Given-When-Then
Альтернативой шаблону
[MethodUnderTest]_[Scenario]_[ExpectedResult]
является шаблон Given-When-Then (Дано-Когда-Тогда):
public class BankAccountShould
{
public void Have_balance_of_zero_when_created()
public void Have_balance_increased_after_a_deposit()

}

В этом шаблоне класс модульного теста описывает тестируемый объект (банковский счёт), тогда как сами тесты — варианты использования, связанные с этим объектом.

Этот шаблон имеет два явных преимущества:
1. Позволяет сокращать имена тестов
Тестируемый объект всегда один и тот же, поэтому вам не нужно повторять его имя в тестах; оно извлекается в имя класса.
2. Помогает организовать тесты с простой иерархической структурой
Если вы хотите протестировать банковский счёт при нескольких условиях, вы можете создать отдельный класс для каждого из этих условий (например, BankAccountForPreferredClientsShould). Затем вы можете поместить все полученные классы в одну папку, чтобы они были хорошо сгруппированы.

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

Если политика именования не вредит читаемости тестовых имён, то с ней всё в порядке. Просто не забывайте всегда отдавать предпочтение читаемости, а не политики. Будьте готовы делать исключения, когда политика препятствует читаемости.

Приведенные выше тесты также читаются как простое описание поведения системы. Но есть примеры, когда и Given-When-Then не работает:
public class PricingCalculatorShould
{
public void Return_zero_given_zero_quantity()
public void Throw_argument_OutOfRangeException_given_quantity_less_than_zero()

}

Здесь слово given присутствует в обоих тестах как часть политики именования. Это сигнал о том, что предпочтение отдали политике именования, а не читаемости. Ещё одна проблема — слово Return. Остерегайтесь терминов программирования в именах тестов. С точки зрения бизнеса неясно, к чему они относятся, где и что именно возвращает калькулятор?

Наконец, есть проблема со вторым тестом. Исключение OutOfRangeException — это деталь реализации, и оно никогда не должно быть частью имён тестов. Лучше всего перефразировать название с точки зрения клиента и показать, в чём именно проявляется это исключение:
Cannot_calculate_quantities_less_than_zero()

Итого
Политики именования сами по себе не плохи. Они становятся проблемой, только когда они препятствуют читаемости. Если вы следуете политике именования, такой как Given-When-Then, всегда будьте готовы пойти на компромисс в пользу читаемости имени теста, если по какой-то причине имя теста не соответствует политике.

Источник: https://enterprisecraftsmanship.com/posts/you-naming-tests-wrong/
👍3
День 1116. #юмор
Минимальным API посвящается.
👍26
День 1117. #ЗаметкиНаПолях #AsyncTips
Динамический параллелизм

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

Решение
Класс Parallel и Parallel LINQ, которые рассматривались ранее, — всего лишь удобные обертки для мощного типа Task. Если требуется реализовать динамический параллелизм, проще использовать тип Task напрямую.
В примере ниже для каждого узла бинарного дерева необходимо выполнить некоторую затратную обработку. Структура дерева неизвестна до стадии выполнения. Метод Traverse обрабатывает текущий узел, а затем создаёт две дочерние задачи, по одной для каждой ветви (в данном примере предполагается, что родительские узлы должны быть обработаны до перехода к дочерним узлам):
void Traverse(Node current)
{
DoActionOnNode(current);
if (current.Left != null)
Task.Factory.StartNew(
() => Traverse(current.Left),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);

if (current.Right != null)
Task.Factory.StartNew(
() => Traverse(current.Right),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
}

Метод ProcessTree начинает обработку, создавая родительскую задачу верхнего уровня и ожидая её завершения:
void ProcessTree(Node root)
{
Task task = Task.Factory.StartNew(
() => Traverse(root),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);

task.Wait();
}

Флаг AttachedToParent гарантирует, что задача для каждой ветви связывается с задачей своего родительского узла. Таким образом создаются отношения «родитель/потомок» между экземплярами Task. Родительские задачи выполняют своего делегата, после чего ожидают завершения своих дочерних задач. Исключения от дочерних задач распространяются к родительской задаче. Таким образом, ProcessTree может ожидать задач для всего дерева, для чего достаточно вызвать Wait для одной задачи в корне.

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

Если ваша ситуация не относится к категории «родитель/потомок», вы можете запланировать запуск любой задачи после другой задачи, используя продолжение (continuation). Оно представляет собой отдельную задачу, которая выполняется после завершения исходной:
Task task = Task.Factory.StartNew(
() => Thread.Sleep(TimeSpan.FromSeconds(2)),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);

Task continuation = task.ContinueWith(
t => Trace.WriteLine("Task is done"),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);

Замечание: всегда лучше явно задавать планировщик TaskScheduler, используемый в StartNew и ContinueWith.

Использование Task для параллельной и асинхронной обработки принципиально отличаются:
1. Параллельные задачи:
- могут использовать блокирующие методы, такие как Task.Wait, Task.Result, Task.WaitAll и Task.WaitAny,
- обычно используют AttachedToParent для создания отношений «родитель/потомок» между задачами,
- следует создавать методами Task.Run или Task.Factory.StartNew.

2. Асинхронные задачи:
- следует использовать с await, Task.WhenAll и Task.WhenAny вместо блокирующих методов,
- не должны использовать AttachedToParent, но могут формировать неявные отношения «родитель/потомок», используя ожидание других задач.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 4.
👍11
День 1118. #ЧтоНовенького
Диаграммы на GitHub с помощью Mermaid
GitHub недавно объявил о поддержке диаграмм, встроенных непосредственно в файлы markdown. Новая функция использует инструмент построения диаграмм и графиков Mermaid, который основан на JavaScript и поддерживает множество распространённых форматов диаграмм.

Использование Mermaid в GitHub Markdown
Чтобы начать использовать Mermaid, всё, что вам нужно сделать, это создать область кода, используя синтаксис тройной обратной кавычки, и указать, что ваш язык — mermaid. Затем вам нужно указать, какую диаграмму вы создаете в первой строке. Например:
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```

Поддерживаются следующие диаграммы:
- classDiagram – диаграмма классов для общего концептуального моделирования структуры приложения и для детального представления модели в программном коде.
- erDiagram – ER-диаграмма или диаграмма Сущность-Связь описывает взаимосвязанные вещи, представляющие интерес в конкретной области знаний.
- flowchart – базовый тип диаграмм в виде объектов и стрелок разной формы.
- graph – блок-схемы.
- gantt – тип гистограммы, который иллюстрирует график проекта и количество времени, которое потребуется для завершения любого проекта.
- journey – диаграмма пути пользователя, которая описывает с высоким уровнем детализации, какие именно шаги предпринимают разные пользователи для выполнения конкретной задачи в системе, приложении или на веб-сайте.
- pie – стандартная круговая диаграмма.
- requirementDiagram – диаграмма требований, которая обеспечивает визуализацию требований и их связей друг с другом и другими документированными элементами.
- sequenceDiagram – диаграмма взаимодействия, показывающая, как процессы взаимодействуют друг с другом и в каком порядке.
- stateDiagram-v2 – диаграмма состояний, описывающая поведение системы.

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

Обратите внимание, что вы можете задавать подсказки для типа диаграммы, например TB (top-to-bottom - cверху вниз) или TD (top-down), либо LR (left-to-right – слева направо).

Кроме того, поддерживаются темы и множество настроек цветовой схемы.

IDE также поддерживают Mermaid в предпросмотре файлов Markdown:
- В Visual Studio для предпросмотра файлов Markdown установите плагин Markdown Editor
- В Rider зайдите в Settings > Languages & Frameworks > Markdown (Настройки > Языки и Фреймворки > Markdown), в блоке Markdown Extensions установите и поставьте галочку у пункта Mermaid.

Источники:
-
https://ardalis.com/github-diagrams-with-mermaid/
-
https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/
-
https://mermaid-js.github.io/mermaid
👍5
День 1119. #Карьера
Как Задавать Умные Цели
Если вы занимались профессиональной деятельностью, скорее всего, вы сталкивались с акронимом SMART. Посмотрим, что это, и как это работает.

SMART — это система постановки целей. Разработана в 1981 году консультантом Джорджем Дораном совместно с Артуром Миллером и Джеймсом Каннингемом и быстро стала стандартом для определения организационных целей и задач.

Цели и задачи: в чем разница?
Цели (goals)
относятся к желаемому результату, который должен быть достигнут в какой-то момент в будущем либо отдельным лицом, либо организацией. Это общая картина, пространное утверждение или видение того, куда вы хотите прийти.
Задачи (objectives) же являются конкретными, краткосрочными действиями с измеримыми результатами.

Цели в SMART считаются задачами, потому что они заставляют вас разбивать большие стремления на более целенаправленные и детализированные этапы.

Что означает акроним S.M.A.R.T.?
Цели SMART должны быть:
S = Specific (Конкретными)
Ваши цели должны быть конкретными, иначе как вы узнаете, над чем работаете? Задайте себе следующие вопросы, чтобы определить специфику той или иной цели:
- Что?: Чего вы хотите достичь?
- Кто?: Кто участвует?
- Где?: Где вы это сделаете?
- Когда?: Когда это произойдет?
- Почему?: Каковы конкретные причины или преимущества цели? Почему вы хотите этого добиться?

M = Measurable (Измеримыми)
Как говорится, нельзя управлять тем, что нельзя измерить. А для измерения у вас должны быть метрики, то есть количественные данные и контрольные показатели, которые объективно определяют, находитесь ли вы на пути к достижению цели.
- Сколько чего-либо вы хотите достичь?
- Как вы будете измерять прогресс?
- Как вы узнаете, что достигли цели?
Как только метрики будут установлены, вы сможете отслеживать прогресс, видеть, что работает (а что нет), и вносить необходимые коррективы по ходу.

A = Achievable (Достижимыми)
Цель не должна быть настолько недостижимой, что вы с самого начала настроены на неудачу. Но установка слишком низкой планки может сделать цель бессмысленной, а её достижение неинтересным. Стремитесь к середине: сложно, но достижимо.
- Реальна ли эта цель с нынешними ресурсами? Если нет, то чего не хватает?
- Есть ли ограничения по времени?
- Есть ли другие ограничения, которые могут помешать достичь цели?
- Нужно ли повысить уровень каких-либо навыков или знаний, чтобы добиться успеха? Потребуется ли дополнительная поддержка со стороны?
- Достигали ли другие люди в аналогичном положении этой цели раньше?
Ответы на эти вопросы помогут вам оценить, действительно ли ваша цель реалистична или её нужно скорректировать.

R = Relevant (Актуальными)
Нужно убедиться, что все находятся на одной волне и работают над достижением одних и тех же целей, а не мешают успехам друг друга. Поэтому важно, чтобы индивидуальные цели напрямую согласовывались с более широкими целями команды и компании.
- Противоречит ли эта цель целям какой-либо команды или компании?
- Как эта цель соотносится с квартальными целями команды? Со стратегией компании?
- Сейчас подходящее время, чтобы сосредоточиться на этой конкретной цели?

T = Time-bound (Ограниченными по времени)
Если вы хотите добиться успеха в достижении целей, они должны быть привязаны к определённой дате или временным рамкам. Это удерживает вас в графике, а также требует от вас ответственности: без крайнего срока вам может потребоваться 10 лет, чтобы достичь того, что вы могли бы сделать за месяц. При определении правильного количества времени, которое нужно выделить на конкретную цель, спросите себя:
- Чего вы можете достичь в этом месяце? Квартале? Году?
- Реальны ли установленные сроки?
- Можно ли сделать быстрее или нужно больше времени?

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

Источник: https://hypercontext.com/blog/work-goals/smart-goals
👍7
День 1120. #ЧтоНовенького
Построчный Git Stage в Visual Studio 2022
Построчный или интерактивный стейджинг – одно из самых популярных предложений нововведений в VS. Он может быть полезен, когда вам нужно разделить изменения в одном файле между разными коммитами.

Обновления в VS 17.2 Preview 1
1. Цветовое выделение изменений
Редактор теперь визуализирует изменения Git как на полосе прокрутки, так и на полях. Это позволяет легко различать сохранённые и несохранённые добавления, удаления или изменения. См. в верхней части картинки ниже.
Кстати, вы можете изменить цвета полей по умолчанию, перейдя в Tools > Options > Fonts and Colors (Инструменты > Параметры > Шрифты и Цвета) и выбрав предпочитаемые цвета в разделе Track additions, deletions and modifications in documents under source control (Отслеживание добавлений, удалений и изменений в документах под контролем изменений).

2. Редактор Изменений
Цветные поля являются интерактивными, и нажатие на них запускает Редактор Изменений (Peek Difference), где вы можете просматривать изменения прямо в редакторе, не переключая контекст. Редактор также доступен по щелчку правой кнопкой мыши на строке с изменениями и пункту меню Peek Difference или через сочетание клавиш Alt+F8. См. в средней части картинки ниже.
В заголовке Редактора Изменений приводится сводка о количестве добавленных и удаленных строк и упрощённая навигация по изменениям с помощью стрелок вверх/вниз. Также, нажав на иконку с шестерёнкой справа, вы можете выбрать способ отображения изменений: два документа «до и после», либо все изменения в одном документе построчно.

3. Построчный Git Stage
Вы можете выполнить Stage любого фрагмента кода в Редакторе Изменений, наведя указатель мыши на изменение, которое вы хотите добавить, и выбрав Stage Change. В качестве альтернативы вы можете использовать кнопку Stage для всех изменений в документе. См. в нижней части картинки ниже.
Вы можете развернуть Редактор Изменений на полный экран, щелкнув на иконку закладки в заголовке редактора справа, либо с помощью сочетания клавиш Ctrl+Alt+Home.

Фиксация изменений
После того, как вы переместили в Stage все нужные изменения, в окне Git Changes в выпадающем списке под полем сообщения выберите Commit Staged для фиксации только нужных изменений.

Источник: https://devblogs.microsoft.com/visualstudio/line-staging-interactive-staging/
День 1121. #Оффтоп
Давно ничего не рекомендовал вам посмотреть. Сегодня порекомендую плейлист «Computer Stuff They Didn't Teach You» от Скотта Хансельмана. Это сборник видео, на самые разные темы, которые призваны помочь заполнить пробелы во всех тех мелочах, которые мы должны были узнать в школе или институте, но так и не узнали. Возможно, некоторые из вас узнали об этих концепциях сами, прочитав статью в википедии или наткнувшись на онлайн курс! А если нет, эти лёгкие видео помогут вам в этом. Список тем очень обширный: от основ Git и горячих клавиш в VS Code до понятия каретки и кодировки, от процессов и портов до облачных вычислений, от настройки домашней сети до интернета вещей, советы по продвижению в карьере и многое другое.

Приятного просмотра.
👍9
День 1122. #Карьера
Внёс Серьёзную Ошибку в Код Системы. Как Теперь Быть?
Вынес это из вопросов на Stack Exchange. Довольно распространённая ситуация, в которую, думаю, каждый попадал не раз в своей карьере, и, к сожалению, ещё попадёт в будущем.

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

Приведу пару лучших ответов.

Jorge Córdoba:
Я работаю разработчиком ПО больше 20 лет и менеджером не меньше 10.
Ошибки появляются постоянно. Вы это сделали специально? Если нет, то дальше извиняться не нужно. Только не-разработчики могут ожидать безупречного кода, потому что они ничего не понимают.

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

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

Люди совершают ошибки, но в этом виноваты недостатки процесса, а не сами люди. Если ваши менеджеры этого не понимают, это тревожный сигнал.

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

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

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

Один руководитель совершил огромную ошибку, стоившую компании около $10 млн. Он написал заявление об уходе и передал его генеральному директору. Разговор был таким:
ГД: Что это за хрень?
Р: Моё заявление об уходе.
ГД: Я только что заплатил $10 млн. за твоё обучение. Думаешь, я позволю этим деньгам пропасть зря? Возвращайся к работе и больше не поднимай эту тему!

Вы только что стали программистом. Вы правильно себя повели:
- признали свою ошибку,
- исправили её,
- извлекли из неё урок.
Не зацикливайтесь на этом. Усвойте то, чему вы на этом научились, и развивайтесь как программист.

Источник: https://workplace.stackexchange.com/questions/182655/made-a-bad-code-change-that-introduced-a-bug-into-our-system-how-do-i-rebound
👍12
День 1123. #ЧтоНовенького
Новинки Entity Framework Core 6. Часть 1
1. Атрибут Unicode
Новый атрибут Unicode позволяет сопоставлять строковое свойство со столбцом, отличным от Unicode, без прямого указания типа базы данных. Атрибут Unicode игнорируется, если система баз данных поддерживает только типы Unicode.
public class Book
{
// …
[Unicode(false)]
public string Title { get; set; }
}

2. Атрибут Precision
До EF Core 6.0 вы могли настроить точность с помощью Fluent API. Теперь вы можете сделать это также с помощью аннотаций:
public class Product
{
// …
[Precision(precision: 10, scale: 2)]
public decimal Price { get; set; }
}

3. Атрибут Column
Помогает указать порядок столбцов в создаваемых таблицах. Также это можно сделать с помощью нового Fluent API — HasColumnOrder().
public class EntityBase
{
[Column(Order = 1)]
public int Id { get; set; }
[Column(Order = 99)]
public DateTime CreatedOn { get; set; }
}
public class Person : EntityBase
{
[Column(Order = 2)]
public string FirstName { get; set; }
[Column(Order = 3)]
public string LastName { get; set; }
}

4. Атрибут EntityTypeConfiguration
Помогает EF Core найти и использовать подходящую конфигурацию. До этого нужно было создавать экземпляр конфигурации класса и вызывать его из метода OnModelCreating:
public class ProductConfig : 
IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> b)
{
b.Property(p => p.Name).HasMaxLength(250);
b.Property(p => p.Price).HasPrecision(10, 2);
}
}

[EntityTypeConfiguration(typeof(ProductConfig))]
public class Product
{
// …
}

5. Предварительная конфигурация модели
Позволяет вам указать конфигурацию сопоставления один раз для данного типа. Это может быть полезно, например, при работе с объектами-значениями.
public class ExampleContext : DbContext
{
public DbSet<Person> People { get; set; }

protected override void
ConfigureConventions(ModelConfigurationBuilder mcb)
{
mcb.Properties<string>()
.HaveMaxLength(500);
mcb.Properties<decimal>()
.HavePrecision(12, 2);
mcb.Properties<Address>()
.HaveConversion<AddressConverter>();
}
}

Здесь для поля типа Address будет использоваться класс конвертера, сериализующий значение в JSON:
public class AddressConverter : 
ValueConverter<Address, string>
{
public AddressConverter()
: base(
v => JsonSerializer.Serialize(v,
(JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<Address>(v,
(JsonSerializerOptions)null))
{
}
}

Источник: https://blog.okyrylchuk.dev/entity-framework-core-6-features-part-1
👍5
День 1124. #юмор
👎5👍3
День 1125. #Estimates
5 Законов Оценки Сроков Разработки
Оценки обычно являются необходимым злом в разработке ПО. Люди склонны считать, что написание ПО похоже на строительство дома, поэтому подрядчик должен предоставить оценку работы до того, как заказчик одобрит её. Однако в мире ПО большая часть системы создается с нуля, и обычно то, как она создаётся, что и как должна делать, когда она будет готова, — всё это туманные цели. Далее описаны некоторые аспекты оценок, которые являются (почти) универсальными.

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

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

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

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

5. Оценки необходимы
Компании не могут принимать решения о создании ПО, не имея представления о затратах и ​​времени. Часто они должны предоставлять оценку сроков как часть любого предложения продукта. То, что приведённые выше законы верны, не означает, что оценки должны исчезнуть волшебным образом. Но можно лучше управлять ожиданиями и временем, затрачиваемым на оценку, если все участники проекта понимают эти истины.
По закону Гудхарта, когда мера становится целью, она перестаёт быть хорошей мерой. Если желательны точные оценки, их не следует использовать в качестве обязательств или сроков.

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

Источник: https://ardalis.com/the-5-laws-of-software-estimates/
👍8
День 1126. #ЗаметкиНаПолях #AsyncTips
Parallel LINQ

Задача:
требуется выполнить параллельную обработку последовательности данных, чтобы сгенерировать другую их последовательность или обобщение этих данных.

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

PLINQ хорошо работает в потоковых сценариях, которые получают последовательность входных значений и производят последовательность выходных значений. Следующий простой пример просто умножает каждый элемент последовательности на 2 (в реальных сценариях выполняются гораздо более серьезные вычисления):
IEnumerable<int> Multiply(
IEnumerable<int> vals)
{
return vals.AsParallel()
.Select(value => value * 2);
}

Пример может генерировать свои выходные значения в любом порядке; это поведение используется по умолчанию в Parallel LINQ. Также можно потребовать, чтобы сохранялся исходный порядок. В следующем примере обработка ведется параллельно, но с сохранением исходного порядка:
IEnumerable<int> Multiply(
IEnumerable<int> vals)
{
return vals.AsParallel()
.AsOrdered().Select(value => value * 2);
}

Другое логичное применение Parallel LINQ — агрегирование или обобщение данных в параллельном режиме. В следующем примере выполняется параллельное суммирование:
int ParallelSum(IEnumerable<int> values)
{
return values.AsParallel().Sum();
}

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

PLINQ предоставляет параллельные версии многих операторов, включая фильтрацию (Where), проекцию (Select) и разные виды агрегирования, такие как Sum, Average и более общую форму Aggregate. В общем случае всё, что можно сделать в обычном LINQ, также можно сделать в параллельном режиме в PLINQ. В результате PLINQ становится отличным кандидатом для обработки существующего кода LINQ, который выиграл бы от выполнения в параллельном режиме.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 4.
👍6
День 1127. #DDD
Раскрытие Реализации в Именах Классов
Эта тема возникла в ответ на вопрос о паттерне CQRS, но он в равной степени применима и к другим паттернам. Вопрос:
Почему DatabaseRetryDecorator раскрывает свою структуру и детали реализации в имени класса? Что, если я изменю реализацию и заменю декоратор чем-то другим? Придется ли мне переименовывать его только потому, что я изменил его реализацию?

Просто для контекста, декораторы — это классы, которые позволяют вам вводить сквозную функциональность в вашем приложении.

Вот пример использования декоратора для повторных попыток при обращении к базе данных:
[DatabaseRetry]
public class EditPersonalInfoCommandHandler :
ICommandHandler<EditPersonalInfoCommand>
{
public Result Handle(EditPersonalInfoCommand command)
{
/* Изменяем данные клиента */
}
}

Приведенный выше атрибут DatabaseRetryAttribute соединяет обработчик команд с DatabaseRetryDecorator. Если обработчик команды выдаёт исключение базы данных, декоратор повторно его запускает (как вариант, через некоторое время). Идея аналогична тому, как промежуточное ПО работает в ASP.NET.

Действительно, почему вы должны называть класс DatabaseRetryDecorator, а не просто DatabaseRetry? Тот же аргумент можно применить и к другим классам, например, CustomerRepository, CustomerFactory или даже к EditPersonalInfoCommandHandler. Так ли им нужно указывать на паттерны, которые они реализуют, в своих именах (репозиторий, фабрика и обработчик команд)?

Здесь есть 2 совета.
1. Никогда не используйте имена паттернов при именовании классов на уровне предметной области (Domain layer). Вы должны использовать только термины из общего языка. Так что, не CustomerEntity или CustomerAggregate, а просто Customer.

2. Допустимо использовать имена паттернов при именовании классов на прикладном уровне (Application layer), потому что прикладной уровень находится за пределами действия общего языка.
Кроме того, имена паттернов помогают лучше понять назначение классов на прикладном уровне, поскольку эти классы не имеют прямой связи с вашей моделью предметной области.

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

Самый распространенный пример нарушения YAGNI — вызов репозитория CustomerSqlRepository. Если вы не используете несколько парадигм хранения, таких как SQL и NoSQL, нет необходимости указывать, что ваш репозиторий работает поверх базы данных SQL.

Источник: https://khorikov.org/posts/2021-08-16-exposing-implementation-details-in-class-names/
👍9
День 1128. #ЧтоНовенького
Новые Функции C#11
Команда .NET уже начала работу над функциями, которые будут включены в C# 11 и .NET 7. Сегодня рассмотрим некоторые из них.

1. Усовершенствования интерполированных строк
В настоящее время поддерживается два типа интерполированных строк:
- Обычная интерполяция: $""
- Дословная интерполяция: $@""

Основное отличие состоит в том, что дословно интерполированные строки могут содержать несколько строк, и экранировать нужно только кавычки. В обычных интерполированных строках для новой строки нужно использовать спец символы /r/n. Теперь в обычных интерполированных строках можно будет использовать что-то вроде:
var v =
$"Результат: { this.Is.Something()
.That.I.Should(
be + able)[
to.Wrap()] }.";

2. Шаблоны списков
Они позволят нам сравнивать с массивами и списками, иметь возможность сопоставлять различные элементы или даже включать шаблоны, которые соответствуют нулю или более элементам.
Например, под шаблон [1, 2, .., 10] подходят все следующие массивы:
int[] arr1 = { 1, 2, 10 };
int[] arr2 = { 1, 2, 5, 10 };
int[] arr3 = { 1, 2, 5, 6, 7, 8, 9, 10 };

3. Проверка параметра на null
Эта новая функция создана из-за того, что стандартный код проверки параметра на null встречается слишком часто:
public static void M(string s)
{
if (s is null)
throw new ArgumentNullException(nameof(s));
// …
}

Теперь мы сможем использовать сокращённую запись с помощью !!:
public static void M(string s!!)
{
// …
}

Это также может быть использовано для проверки параметров индексаторов:
public string this[string key!!] 
{ get { ... } set { ... } }

В конструкторах эта функция будет действовать немного по-другому. Проверка параметров конструктора на null с помощью !! будет происходить после инициализаторов полей, однако до вызова конструктора базового класса. То есть:
public record Base(int len);
public record Derived(string s!!)
: Base(s.Length);
При передаче null в конструктор Derived, будет выброшено ArgumentNullException из класса Derived, а не NullReferenceException из класса Base.

Если мы применим !! к имени параметра обнуляемого ссылочного типа, компилятор выдаст предупреждение:
void WarnCase<T>(
string? name!!,
// CS8995 Nullable type 'string?' is null-checked and will throw if null
// (Обнуляемый тип 'string?' проверяется на null и выбросит исключение при null)
T value1!! // OK
)

Источник: https://dev.to/dotnetsafer/c-11-is-coming-5-features-that-will-blow-your-mind-3o7h
👍9
День 1129. #ЗаметкиНаПолях
Улучшенная Обработка Исключений с EntityFrameworkCore.Exceptions
При выполнении практически любых действий с Entity Framework, включая обновления, удаления и вставку, если что-то пойдёт не так, вам выдаётся стандартное исключение:
Microsoft.EntityFrameworkCore.DbUpdateException: ‘An error occurred while saving the entity changes. See the inner exception for details.’ (Возникла ошибка при сохранении изменений сущности. См. детали во внутреннем исключении)

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

Допустим, мы получили исключение с таким внутренним исключением:
SqlException: Cannot insert duplicate key row in object ‘…’ with unique index ‘…’. The duplicate key value is (…).

Если мы хотим обработать его отдельно, нам пришлось бы писать что-то вроде этого:
try
{
context.SaveChanges();
} catch(DbUpdateException e) when
(e?.InnerException?.
Message.Contains("Cannot insert duplicate key")
?? false)
{
// …
}

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

Использование EntityFrameworkCore.Exceptions
Библиотека EntityFrameworkCore.Exceptions чрезвычайно проста в использовании. Чтобы использовать её, всё, что нам нужно сделать, это установить соответствующий NuGet пакет:
Install-Package EntityFrameworkCore.Exceptions.SqlServer

Доступны также версии для Postgres, MySQL, Oracle и Sqlite.

Затем добавьте вот эту строку в настройки DB Context:
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseExceptionProcessor();
}

Теперь при том же исключении БД вместо стандартного DbUpdateException мы получим:
EntityFramework.Exceptions.Common.UniqueConstraintException: ‘Unique constraint violation’

Таким образом, можно заменить наш код try/catch на следующий:
try
{
context.SaveChanges();
} catch (UniqueConstraintException ex)
{
// …
}
Гораздо чище, компактнее и намного более подходит для повторного использования.

Источник: https://dotnetcoretutorials.com/2022/01/29/better-exception-handling-with-entityframeworkcore-exceptions/
👍15
День 1130. #ЧтоНовенького
Microsoft Выпустили в Превью Инструменты Разработчика Edge для Visual Studio
Это расширение для Visual Studio 2022 позволяет разработчикам просматривать проекты ASP.NET и ASP.NET Core. Оно дополняет аналогичный инструмент для Visual Studio Code, который доступен уже много лет и установлен более 460 000 раз.

Помимо предварительного просмотра вашего веб-приложения, вы можете использовать инструмент Edge Developer Tools для изменения CSS. Изменения будут автоматически применены к вашим исходным файлам.

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

Изменения CSS можно вносить на вкладке Elements и автоматически применять к исходным файлам. Вкладка Network позволяет разработчикам видеть веб-запросы, сделанные при обновлении страницы.

В нынешней превью редакции не хватает некоторых функций, включая поддержку Blazor, которая, как говорят, появится в ближайшее время.
Помимо этого, вот другие известные проблемы:
- Изменения CSS требуют сохранения файла
При изменении CSS обновления не будут применяться по мере ввода, вам нужно будет сохранить файл, чтобы изменения были перенесены в окно предварительного просмотра.
- Сброс прокрутки после внесения изменений CSS в инструментах разработчика Edge
При редактировании CSS с помощью инструментов разработчика Edge, если вы изменяете исходный код файла CSS, который в данный момент открыт в редакторе Visual Studio, редактор открутит файл вверх после внесения изменений в CSS.
- Предупреждение об окончаниях строк
Если вы вносите изменения в файл CSS с помощью инструментов Edge, при следующем открытии этого файла CSS вы можете получить предупреждение о несовместимых окончаниях строк.

Инструмент доступен для Visual Studio 2022 17.1 или более поздней версии.

https://marketplace.visualstudio.com/items?itemName=ms-edgedevtools.VisualStudioEdgeDevTools

Источник: https://visualstudiomagazine.com/articles/2022/03/02/edge-tools-visual-studio.aspx
👍4👎3
День 1131. #Карьера
Как Быть Продуктивным, Когда Вокруг Столько Отвлекающих Факторов
Мы определённо живем в эпоху постоянного отвлечения внимания. А сейчас ситуация совсем вышла из-под контроля. Неудивительно, что многие из нас чувствуют себя хронически истощёнными. Нужно обрабатывать так много информации и так много вещей, к которым нужно приспособиться. И вдобавок ко всему, большая неопределённость в экономике, здравоохранении и рост стоимости жизни.

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

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

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

3. Меньше потребления контента
Происходит столько всего, что вы просто не можете удержаться от пролистывания ленты новостей. Однако это негативно сказывается на вашем мышлении и продуктивности. Постарайтесь не особо интересоваться новостями. Если произойдёт что-то важное, вы узнаете об этом от семьи или друзей. Это действительно экономит час или два в день. Вместо потребления контента займитесь чем-нибудь активным. Банально говорить что-то вроде: «Проводите больше времени на природе». Но все знают, что это хорошо.

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

Источник: https://dariusforoux.com/productivity-distraction-age/
👍15
День 1132. #юмор
👍6
День 1133. #DDD
Коллекции и Одержимость Примитивными Типами. Начало
Одержимость примитивными типами (Primitive Obsession) — это антипаттерн, который заключается в чрезмерном использовании примитивных типов, особенно для моделирования предметной области.

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

Обычно одержимость примитивными типами проявляется в использовании строк, целых чисел и других простых типов. Распространённые примеры:
- Использование строки для представления адресов электронной почты. Специально созданный объект-значение Email был бы здесь намного лучше.
- Использование double для моделирования концепции денег вместо введения объекта-значения Money.

Конечно, не все концепции в вашей предметной области должны быть представлены как объекты-значения, необходимо провести некоторый анализ, чтобы выяснить, стоит ли создавать новый объект-значение. Но недостаточное использование объектов-значений — гораздо более серьёзная проблема, чем чрезмерное их использование.

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

Действительная строка может быть или не быть действительным адресом электронной почты; концепция строки слишком широка и сама по себе не может учитывать правила валидности электронного адреса.

Другими словами, множество строк больше, чем множество e-mail адресов. Чтобы правильно представить концепцию e-mail адреса, нам нужно создать пользовательский класс, который будет соответствовать множеству действительных e-mail адресов.

Помимо инкапсуляции, есть ещё принцип абстракции. Класс Email абстрагирует все бизнес-правила, связанные с e-mail адресами, так что клиентский код может работать с этими объектами, не обращая внимания на детали реализации, связанные с проверкой e-mail.

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

Источник:
https://enterprisecraftsmanship.com/posts/collections-primitive-obsession/
👍16
День 1134. #DDD
Коллекции и Одержимость Примитивными Типами. Окончание
Начало

Пользовательские классы коллекций
А что насчёт коллекций? Допустим, у нас есть следующий класс:
public class Customer
{
public IReadOnlyList<Order> Orders { get; }
}
Может коллекция должна быть представлена специальным классом, например:
public class Customer
{
public OrderList Orders { get; }
}

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

Рассмотрим примеры выше.
1. Коллекция сущностей

public IReadOnlyList<Order> Orders { get; }

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

Нам нужно, чтобы свойство Orders не могло быть изменено напрямую, а это уже сделано путем представления этого свойства как IReadOnlyList. Значит нужно изменить класс Customer, добавив метод с необходимой проверкой:
public class Customer
{
private List<Order> _orders;
public IReadOnlyList<Order> Orders => _orders;

public void AddOrder(Order order)
{
if (_orders.Contains(order))
throw new Exception();

_orders.Add(order);
}
}
В некотором роде Customer уже представляет собой пользовательский класс, инкапсулирующий коллекцию Orders.

2. Пользовательский класс коллекции
Если коллекция не принадлежит никакой другой сущности, то имеет смысл создать для неё отдельный класс. Например, если вам нужно отслеживать всех пользователей, которые в данный момент находятся в сети, лучше всего представить их в виде пользовательского класса:
public class OnlineUsers
{
private List<User> _users;
public void ForceLogOff(long userId)
{
// …
}
}

Здесь подразумевается, что, помимо собственно коллекции, необходимы некоторые дополнительные функции (например, описанный выше метод ForceLogOff), в противном случае класс OnlineUsers не нужен.

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

Источник: https://enterprisecraftsmanship.com/posts/collections-primitive-obsession/
👍14