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

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1107. #ЗаметкиНаПолях
DateTime как Объект-Значение
Объекты-значения являются ключевой частью предметно-ориентированного проектирования (DDD) и моделей предметной области. Однако разработчики используют их не так часто, как следовало бы, и часто неправильно их понимают. Тип DateTime в .NET является полезным примером объекта-значения.

Объект-значение — это неизменяемый тип, экземпляры которого можно отличить только по значению их свойств. Любые два объекта-значения с одинаковыми свойствами можно считать равными.

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

Разработчики спрашивают что-то вроде: «Могу ли я иметь список объектов-значений в моей модели», - и ответ почти всегда - нет. Опять здесь может помочь DateTime. Имеет ли смысл иметь список объектов DateTime в модели? DateTime сам по себе, без контекста, не имеет значения в модели предметной области. Только когда он используется для описания чего-то (обычно Сущности) он становится полезным. Вот пример:
07.02.2022
08.02.2022
09.02.2022

Если вы обнаружите этот список в модели, что он значит? Нет способа это узнать. А вот так:
var article = new Article {
CreationDate = new DateTime(2022,2,7),
PublicationDate = new DateTime(2022,2,8),
LastModifiedDate = new DateTime(2022,2,9)
};

Те же три значения DateTime теперь имеют контекст, так как они используются для описания сущности статьи.

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

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

Если вы попытаетесь создать DateTime с недопустимыми значениями, например, с месяцем 13, вы получите исключение ArgumentOutOfRangeException:
var someDate = new DateTime(2022,13,1);

А если вы попытаетесь изменить значение свойства, вы получите ошибку компиляции, т.к. оно доступно только для чтения:
someDate.Month = 13;

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

Так как же их изменить?
Объекты-значения могут предоставлять методы, которые «кажутся» изменяющими их, но на самом деле создают новые экземпляры. Это относится как к DateTime, так и к String в .NET. AddDays(), ToLower() и т.п. возвращают новый экземпляр.

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

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

Источник: https://ardalis.com/datetime-as-a-value-object/
День 1108. #ЧтоНовенького
Новинки System.Text.Json в .NET 6. Начало

1. Игнорирование циклических ссылок
В .NET 5 при обнаружении циклических ссылок выдавалось исключение JsonException. В .NET 6 их можно игнорировать:
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles
};
string json = JsonSerializer.Serialize(myObj, options);

2. Уведомления при сериализации/десериализации
Появилось 4 новых интерфейса, которые можно реализовать в соответствии с вашими потребностями:
- IJsonOnDeserialized
- IJsonOnDeserializing
- IJsonOnSerialized
- IJsonOnSerializing

class Product : IJsonOnDeserialized, IJsonOnSerializing
{
public string Name { get; set; }
public string Test { get; set; }

void IJsonOnDeserialized.OnDeserialized()
=> Validate();
void IJsonOnSerializing.OnSerializing()
=> Validate();

private void Validate()
{
if (Name is null)
throw new InvalidOperationException(
"Name can't be null."
);
}
}

3. Порядок сериализации свойств
Атрибут JsonPropertyOrder позволяет контролировать порядок сериализации свойств:
class Product
{
// после Price
[JsonPropertyOrder(2)]
public string Category { get; set; }

// после свойств с порядком по умолчанию
[JsonPropertyOrder(1)]
public decimal Price { get; set; }

// порядок по умолчанию - 0
public string Name { get; set; }

// перед свойствами с порядком по умолчанию
[JsonPropertyOrder(-1)]
public int Id { get; set; }
}

Вывод:
{
"Id": 1,
"Name": "Surface Pro 7",
"Price": 550,
"Category": "Laptops"
}

4. Сериализация из/в поток
В .NET 6 добавлены перегрузки синхронных методов Serialize/Deserialize для потоков:
// десериализация из потока
using MemoryStream ms = new MemoryStream(bytes);
MyClass ex =
JsonSerializer.Deserialize<MyClass>(ms);

// сериализация в поток
JsonSerializerOptions options = new() {
WriteIndented = true
};

using Stream output = Console.OpenStandardOutput();

MyClass ex = new() { Value = "Test" };
JsonSerializer.Serialize<MyClass>(output, ex, options);

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

Источник:
https://blog.okyrylchuk.dev/system-text-json-features-in-the-dotnet-6
День 1109. #ЧтоНовенького
Новинки System.Text.Json в .NET 6. Окончание
Начало

5. Поддержка IAsyncEnumerable
Сериализация IAsyncEnumerable преобразует его в массив.
static async IAsyncEnumerable<int> GetAsync(int n)
{
for (int i = 0; i < n; i++)
{
await Task.Delay(1000);
yield return i;
}
}

JsonSerializerOptions options = new() {
WriteIndented = true
};
using Stream output =
Console.OpenStandardOutput();

var data = new { Data = GetAsync(5) };

await JsonSerializer.SerializeAsync(
output, data, options);

Для десериализации JSON-массивов корневого уровня добавлен метод DeserializeAsyncEnumerable:
// оборачивает текст в IAsyncEnumerable<T>
using MemoryStream stream =
new(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));

await foreach (int item in
JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
{
Console.WriteLine(item);
}

6. Работа с JSON как с DOM
.NET 6 предоставляет типы для произвольного доступа к элементам JSON в структурированном представлении данных. Новые типы из пространства имён System.Text.Json.Nodes:
- JsonArray
- JsonNode
- JsonObject
- JsonValue

// Парсим объект JSON
var node = JsonNode.Parse(
"{\"Value\":\"Text\",\"Array\":[1,5,13,17,2]}"
);

string value = (string)node["Value"];
// либо
value = node["Value"].GetValue<string>();
Console.WriteLine(value); // Text

int item = node["Array"][1].GetValue<int>();
// либо
item = (int)node["Array"][1];
Console.WriteLine(arrayItem); // 5

// Создаём JsonObject
var obj = new JsonObject
{
["Value"] = "Text",
["Array"] = new JsonArray(1, 5, 13, 17, 2)
};
Console.WriteLine(obj["Value"]); // Text
Console.WriteLine(obj["Array"][1]); // 5

// преобразуем в строку
string json = obj.ToJsonString();
Console.WriteLine(json);
// {"Value":"Text","Array":[1,5,13,17,2]}

Источник: https://blog.okyrylchuk.dev/system-text-json-features-in-the-dotnet-6
День 1110. #юмор
День 1111.
20 Лет .NET
Сегодня исполняется 20 лет с выпуска Visual Studio .NET и первой версии платформы .NET.

20 лет инноваций
Корни разработки ПО в Microsoft уходят в DOS и BASIC в 90-х годах. Тогда был большой набор инструментов разработчика со множеством различных утилит и языков для создания самых разных приложений. Каждый инструмент был хорош для решения своих задач. Однако приложениям было трудно обмениваться данными, особенно между машинами.

С появлением интернета мир увидел более простой способ обмена информацией. Технология сместилась в сторону распределённых систем, которые обменивались данными через сеть. .NET был создан под эту интернет-революцию. Несколько языков, одна среда выполнения и набор совместимых библиотек и API.

.NET всегда был в авангарде преобразований Microsoft и когда в Microsoft провели ещё одну крупную трансформацию, на этот раз в сторону открытого исходного кода. К 2012 году исходный код веб-фреймворка ASP.NET MVC был полностью открыт и стали приниматься предложения сообщества. В 2014м началось создание кроссплатформенного .NET с открытым исходным кодом на GitHub, и он получил невероятную поддержку и вклад сообщества разработчиков открытого исходного кода. Первая версия была представлена на конференции Red Hat DevNation в 2016 году и её работа была продемонстрирована в Red Hat Enterprise Linux, что было неслыханно в первые дни. .NET не только для Windows. Установились прочные партнерские отношения с такими компаниями, как Red Hat и IBM, чтобы внедрить .NET в RHEL, IBM Z и IBM LinuxONE.

Что дальше?
.NET 6 выпущен в ноябре 2021 года и полным ходом идёт работа над созданием .NET 7. Первая предварительная версия .NET 7 выйдет на этой неделе. В скором времени будет выпущена .NET Multi-platform App UI (.NET MAUI). Она позволит вам создавать нативные приложения для Windows, macOS, iOS и Android с единой кодовой базой. Сейчас самое время попробовать предварительную версию и оставить отзыв.

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

Присоединяйтесь к Скотту Хантеру, Скотту Хансельману и их гостям, которые поделятся своими историями и отправят нас в путешествие в прошлое, настоящее и будущее .NET на www.dot.net сегодня в 20:00 по Москве.

Источник: https://devblogs.microsoft.com/dotnet/happy-20th-anniversary-net/
День 1112. #ЧтоНовенького
Правило Анализатора для Улучшенной Конкатенации Строк
В SDK .NET 6 добавили новое правило анализатора CA1845. Правило находит выражения конкатенации строк, которые содержат вызовы Substring.

Чтобы избежать ненужного выделения строк, предлагается заменить конкатенацию строк методом Concat и заменить Substring методом AsSpan.

Чтобы включить последнюю версию набора рекомендаций, добавьте следующий параметр в свой проект:
<AnalysisLevel>latest-Recommended</AnalysisLevel>

Источник: https://twitter.com/okyrylchuk/status/1491861969070338049
День 1113. #Testing
Вы Неправильно Называете Тесты! Начало
Важно давать тестам выразительные имена. Правильное наименование помогает понять, что проверяет тест и как ведёт себя система.

Существует множество соглашений именования тестов. Одним из наиболее известных и, вероятно, одним из наименее полезных является:
[MethodUnderTest]_[Scenario]_[ExpectedResult]

Здесь
- MethodUnderTest – имя тестируемого метода,
- Scenario – условия проведения теста,
- ExpectedResult – ожидаемый в этих условиях результат.

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

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

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

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

Рекомендации по именованию модульных тестов:
1. Не вводите жёсткую политику именования. Вы просто не сможете втиснуть высокоуровневое описание сложного поведения в узкие рамки такой политики. Разрешите свободу самовыражения.
2. Назовите тест так, как если бы вы описывали сценарий непрограммисту, знакомому с предметной областью.
3. Разделяйте слова символами подчеркивания. Это помогает улучшить читаемость, особенно длинных имен.

Использование символов подчёркивания не касается имён тестовых классов, они обычно не такие длинные. Также обратите внимание, что хотя часто используется шаблон [ClassName]Tests при именовании тестовых классов, это не означает, что тесты ограничены проверкой только этого ClassName. Модуль в модульном тестировании — это модуль поведения, а не класс. Эта единица может охватывать один или несколько классов, фактический размер не имеет значения. Тем не менее, нужно откуда-то начинать. Рассматривайте класс в [ClassName]Tests просто как точку входа, API, с помощью которого вы можете проверить единицу поведения.

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

Источник:
https://enterprisecraftsmanship.com/posts/you-naming-tests-wrong/
День 1114. #Testing
Вы Неправильно Называете Тесты! Продолжение
Начало

Для примера попробуем улучшить название теста, используя приведённые выше рекомендации. Вот тест, проверяющий, что доставка с истекшей датой недействительна. Название теста написано с использованием жёсткой политики именования, что не способствует читабельности теста:
[Fact]
public void IsDeliveryValid_InvalidDate_ReturnsFalse()
{
var sut = new DeliveryService();
var pastDate = DateTime.Now.AddDays(-1);
Delivery delivery = new Delivery
{
Date = pastDate
};

var valid = sut.IsDeliveryValid(delivery);

Assert.False(valid);
}

Тест проверяет, правильно ли DeliveryService идентифицирует доставку с неправильной датой как недействительную. Выразим название на простом языке:
Delivery_with_invalid_date_should_be_considered_invalid()

Обратите внимание на две вещи в новой версии:
1. Имя теперь имеет смысл для непрограммиста. Это означает, что и программистам будет легче понять его.
2. Имя метода IsDeliveryValid больше не является частью имени теста.

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

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

Вернёмся к нашему примеру. Имя теста всё ещё можно улучшить. Что именно означает, что дата доставки недействительна? Глядя на тест, мы видим, что недействительной датой является любая дата в прошлом. То есть должно быть разрешено выбирать дату доставки только в будущем. Отразим это в имени теста:
Delivery_with_past_date_should_be_considered_invalid()

Слишком длинно. Уберём лишние слова без потери смысла. Кроме того, формулировка should_be - ещё один распространённый анти-паттерн. Тест проверяет поведение: правильное оно или нет. Здесь нет места желаниям или стремлениям:
Delivery_with_past_date_is_invalid()

Готово. Окончательная версия представляет собой прямое изложение факта, который описывает один из аспектов поведения тестируемого приложения. В данном случае аспект определения того, может ли быть осуществлена доставка.

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

Источник:
https://enterprisecraftsmanship.com/posts/you-naming-tests-wrong/
День 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/
День 1116. #юмор
Минимальным API посвящается.
День 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.
День 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
День 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
День 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 до понятия каретки и кодировки, от процессов и портов до облачных вычислений, от настройки домашней сети до интернета вещей, советы по продвижению в карьере и многое другое.

Приятного просмотра.
День 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
День 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
День 1124. #юмор
День 1125. #Estimates
5 Законов Оценки Сроков Разработки
Оценки обычно являются необходимым злом в разработке ПО. Люди склонны считать, что написание ПО похоже на строительство дома, поэтому подрядчик должен предоставить оценку работы до того, как заказчик одобрит её. Однако в мире ПО большая часть системы создается с нуля, и обычно то, как она создаётся, что и как должна делать, когда она будет готова, — всё это туманные цели. Далее описаны некоторые аспекты оценок, которые являются (почти) универсальными.

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

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

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

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

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

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

Источник: https://ardalis.com/the-5-laws-of-software-estimates/