.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
День 2058. #Карьера
Категории Руководства в Технических Командах. Продолжение

Начало

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

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

Управление проектами — это не просто административная задача; для его успешного выполнения требуется значительный объём экспертных знаний в предметной области (чтобы следить за обсуждениями проекта, понимать обновления статуса, отслеживать зависимости и т. д.). Помимо этого, полезно быть организованным и ориентированным на детали, а также иметь хорошие ментальные модели людей:
- Кто будет хорош в каких типах работы?
- Какие виды координационных ритуалов полезны для этой команды?

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

Когда команды становятся большими, управление проектами проще всего делегировать и разделить. Команду в 10+ человек лучше разделить, дать им разные области проекта и отдельных менеджеров проекта.

4. Техническое руководство
Означает ответственность за качество технической работы команды.

Конкретная работа включает:
- Установку технического направления (например, программы исследований на тему или разработку архитектуры системы).
- Проверку выполнения в соответствии с этим направлением (проверку экспериментальных проектов и результатов, технических проектных документов, проверку кода и т. д.)
- Другое техническое наставничество, например, личный менторинг, парное взаимодействие и т. д.
- Частично индивидуальное выполнение, хотя это может варьироваться в зависимости от того, насколько занят технический руководитель.

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

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

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

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

Источник:
https://www.benkuhn.net/leadcats/
👍8
День 2059. #Карьера
Категории Руководства в Технических Командах. Окончание

Начало
Продолжение

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

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

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

Инженерный менеджер (ИМ) / техлид (ТЛ)
Такой тип разделения распространён в крупных технологических компаниях, где ИМ отвечает за общее руководство, людей и управление проектами, а ТЛ - за техническое руководство. ТЛ здесь не обязательно должен быть формальным званием, и иногда в команде будет несколько ТЛ в разных областях. Тех. руководство может осуществляться и несколькими сеньорами в разных частях сервиса (реализация модели, архитектура, планирование запросов, управление хранилищем и т. д.).

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

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

Менеджер по персоналу/руководитель исследований
В этом разделении, в отличие от разделения ИМ/ТЛ в инженерной команде, разумнее, чтобы руководитель исследования отвечал за общее направление, поскольку оно в значительной степени зависит от высококонтекстных интуитивных суждений о том, в каком направлении исследования следовать. Во многих (хотя и не во всех!) инженерных командах приоритеты в меньшей степени зависят от такого рода высокотехнических суждений.

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

Источник:
https://www.benkuhn.net/leadcats/
👍2
День 2060. #Оффтоп
Вы Думаете Ваша Кодовая База Плохая?
Вот откровения разработчика СУБД Oracle 12.2.

Около 25 миллионов строк кода на C…

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

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

Иногда нужно понимать значения и эффект от 20 различных флагов, чтобы предсказать, как код будет вести себя в разных ситуациях. Иногда сотни! Я не преувеличиваю.

Единственная причина, по которой этот продукт все ещё выживает и все ещё работает, — это в буквальном смысле миллионы тестов!

Вот какова жизнь разработчика СУБД Oracle:
- Начинаете работать над новой ошибкой.

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

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

- Отправляете изменения в тестовую ферму, состоящую примерно из 100-200 серверов, которые скомпилируют код, создадут новую базу данных Oracle и запустят миллионы тестов распределённым образом.

- Идёте домой. Приходите на следующий день и работайте над чем-то другим. Тесты могут занять от 20 до 30 часов.

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

- Добавляете ещё несколько флагов, чтобы попытаться исправить проблему. Отправляете изменения ещё раз на тестирование. Ждёте ещё 20–30 часов.

- Так правите и повторяете ещё недели две, пока не получаете правильное таинственное заклинание из комбинации флагов.

- Наконец, в один прекрасный день вы добиваетесь успеха, и ни один тест не падает.

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

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

- Недели через 2 (или месяца через 2), когда всё будет готово, код будет окончательно объединён с основной веткой.

Вышеизложенное — это не преувеличенное описание жизни программиста в Oracle, исправляющего ошибку. Теперь представьте, каким ужасом будет разработка новой функции. Разработка одной небольшой функции (например, добавления нового режима аутентификации, типа поддержки аутентификации AD) занимает от 6 месяцев до года (иногда 2 года!).

Тот факт, что этот продукт вообще работает, — просто чудо!

Я больше не работаю в Oracle. Никогда больше не буду работать в Oracle!

Источник: https://news.ycombinator.com/item?id=18442941
👍28
День 2061. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 24. Не давайте оценок наугад
Вы — бизнес-аналитик или владелец продукта. По дороге на работу вы встречаете представительницу клиентов вашего проекта:
- Я хотела бы добавить кое-что в наш проект», — говорит она. Вы выслушиваете её описание.
- Как думаете, сколько времени потребуется, чтобы сделать это?
- Около трех дней.
- Отлично, спасибо!

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

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

«По нашим оценкам, это займёт Х» часто воспринимается как «Мы обязуемся закончить через Х». Оценки и обязательства важны, но не следует путать их.

Часто есть соблазн дать быструю оценку, но старайтесь подавлять это искушение. Быстрые ответы не основаны на анализе, это догадки. Убедитесь, что точно знаете, о чём вопрос, затем оцените, что реально потребуется для решения. После анализа часто оказывается, что проблема более обширная, чем предполагалось. Если вы дадите реалистичную оценку, заказчик может отказаться от запроса. Лучше узнать об этом до того, как вы начнёте реализовывать новую функциональность, чем отказаться от неё, когда она уже будет наполовину проработана и вдруг выяснится, что изменение оказалось неоправданно большим.

Вопросы для оценки
- Какие предположения повлияли на оценку? Как можно проверить их достоверность?
- Известно ли, кто будет выполнять эту работу? У разных членов команды разные навыки; не все одинаково продуктивны. Если вы не знаете, кто справится с работой, считайте некий средний уровень производительности.
- Кто будет писать и выполнять тесты, ревью кода, регрессионное тестирование и т.п.? Касается ли ваша оценка всего объёма работ или только собственно разработки.
- Учли ли вы неочевидные последствия и дополнительную работу, которая может потребоваться помимо реализации новой функциональности? Она может повлиять на другие функции, негативно отразиться на некоторых атрибутах качества. Может понадобиться изменить дизайн или интерфейс либо обновить документацию.
- Есть ли риски или вы просчитали только идеальный сценарий?

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍12👎1
Вам нужно "округлить" заданное положительное целое число до ближайшей степени двойки, например, 3 -> 4, 10 -> 16, 191 -> 256. Что вы используете в C#?
Anonymous Quiz
5%
цикл
44%
комбинацию битовых сдвигов вправо (>>)
23%
комбинацию методов Math.Pow и Math.Log2
28%
метод RoundUpToPowerOf2
👍12👎3
День 2062. #Оффтоп
Сегодня посоветую вам видео из серии Deep .NET «Let's Build Our Own ArrayPool».

Скотт Хансельман спрашивает, а Стивен Тауб объясняет, как работает ArrayPool (да и прочие виды пулов) в .NET, когда его стоит и не стоит использовать, а также создаёт упрощённую реализацию пула массивов.

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

ЗЫ: и на тему вчерашнего опроса там тоже есть)))
👍18
День 2063. #ЧтоНовенького
.NET 9 RC1: Приближаемся к Релизу

Microsoft выпустили первый релиз-кандидат .NET 9, которая включает ряд обновлений основных компонентов, таких как .NET Runtime, SDK, библиотеки, C#, а также ASP.NET Core и .NET MAUI.

1. В ClientWebSocketOptions и WebSocketCreationOptions добавлены новые API, позволяющие разработчикам настраивать пинги WebSocket и автоматически завершать соединения, если в течение указанного периода времени не получен ответ:
using var cws = new ClientWebSocket();
cws.Options.HttpVersionPolicy =
HttpVersionPolicy.RequestVersionOrHigher;
cws.Options.KeepAliveInterval =
TimeSpan.FromSeconds(5);
cws.Options.KeepAliveTimeout =
TimeSpan.FromSeconds(1);


2. Добавлены новые типы — ZLibCompressionOptions и BrotliCompressionOptions, которые обеспечивают больший контроль над уровнями и стратегиями сжатия. Эти дополнения обеспечивают большую гибкость по сравнению с предыдущей опцией CompressionLevel.

3. Для работающих с TAR-архивами, в System.Formats.Tar.TarEntry добавлено свойство DataOffset, позволяющее получить доступ к положению данных в потоке. Это упрощает управление большими файлами TAR, включая функции одновременного доступа.

4. Начиная с этой версии, события LogLevel.Trace, сгенерированные HttpClientFactory, теперь по умолчанию исключают значения заголовков. Разработчики могут регистрировать определённые значения заголовков с помощью вспомогательного метода RedactLoggedHeaders, что повышает конфиденциальность и безопасность:
services.AddHttpClient("myClient")
.RedactLoggedHeaders(name => name != "User-Agent");


5. Добавлена команда dotnet workload history. Она отслеживает историю установок или изменений рабочей нагрузки в установке .NET SDK, предлагая информацию об изменениях версии рабочей нагрузки с течением времени. Она предназначена для того, чтобы помочь пользователям более эффективно управлять версиями рабочей нагрузки аналогично функциональности reflog в Git.

6. В ASP.NET Core представлены такие обновления, как:
- тайм-аут keep-alive для WebSockets
app.UseWebSockets(new WebSocketOptions { 
KeepAliveTimeout = TimeSpan.FromSeconds(15) });

- поддержка FromKeyedServices в промежуточном ПО как в конструкторе, так и в Invoke/InvokeAsync
- улучшения распределённой трассировки SignalR, направленные на повышение производительности и упрощение разработки.

7. Что касается .NET MAUI, релиз фокусируется на решении проблем и стабильности платформы. Среди новых функций:
- HorizontalTextAlignment.Justify предоставляет дополнительные параметры выравнивания текста для меток.
- обновления HybridWebView, особенно при вызове методов JavaScript из C#.

Источник: https://www.infoq.com/news/2024/09/dotnet-9-release-candidate-1/
👍12
День 2064. #ЗаметкиНаПолях
Частичные Классы в C#
Частичные классы находятся в одном пространстве имён и имеют одинаковое имя класса. Их можно использовать для разделения функциональности между несколькими файлами.

Если вы создадите два класса MyClass в одном пространстве имён, вы получите ошибку компиляции:
CS0101: The namespace 'x' already contains a definition for 'MyClass'. You can't have two classes of the same namespace and class name. (Пространство имён 'x' уже содержит определение для 'MyClass'. У вас не может быть двух классов с одинаковым пространством имён и именем класса.)

Можно пометить их как частичные. Тогда компилятор объединит все элементы из каждого из частичных классов в один класс:
public partial class MyClass
{
public bool Ok { get; set; }
}

public partial class MyClass
{
public bool IsItOk()
{
return Ok;
}
}

Ключевое слово partial также работает для структур, записей и интерфейсов.

Правила для частичных классов
1. Все классы должны иметь ключевое слово partial перед объявлением класса
Ключевое слово partial должно быть перед ключевым словом class (struct, record или interface). Если вы попытаетесь добавить abstract после partial, возникнет исключение:
CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. (Модификатор 'partial' может появляться только непосредственно перед 'class', 'record', 'struct', 'interface' или возвращаемым типом метода.)

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

3. Модификаторы доступа должны быть одинаковыми
Если изменить один из частичных классов на internal, а другой оставить public, получится ошибка компиляции:
CS0262: Partial declarations of 'MyClass' have conflicting accessibility modifiers (Объявления частичного класса 'MyClass' имеют конфликтующие модификаторы доступа)

4. Абстрактные и запечатанные классы и атрибуты
Если один из частичных классов помечен как абстрактный, класс будет абстрактным, даже если он не объявлен абстрактным в других случаях. То же касается ключевого слова sealed. Попытка создать экземпляр абстрактного частичного класса выдаст ошибку компиляции.
Атрибуты класса следуют тому же правилу. Например, если пометить одно из объявлений частичного класса атрибутом [Obsolete], весь класс будет объявлен как устаревший.

5. Можно наследовать только один класс
Если в разных объявлениях частичного класса вы наследуете разные классы, вы получите следующее исключение:
CS0263: Partial declarations of 'MyClass' must not specify different base classes (Объявления частичного класса 'MyClass' не должны указывать разные базовые классы)
public class MyBaseClass {…}
public class MyBaseClass2 {…}

public partial class MyClass : MyBaseClass //CS0263
{ … }
public partial class MyClass : MyBaseClass2
{ … }

Однако в разных объявлениях можно указывать наследование от одного и того же класса.

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

Источник:
https://www.roundthecode.com/dotnet-tutorials/what-are-partial-classes-csharp-why-do-we-use-them
👍13
День 2065. #ЗаметкиНаПолях
Частичные Классы в C#. Окончание

Начало

6. Можно реализовать несколько интерфейсов
И не важно, в каком объявлении будет реализация интерфейса, главное, чтобы все интерфейсы были реализованы:
public interface IBase {
void MyMethod();
}
public interface IBase2 {}

public partial class MyClass : IBase
{…}

public partial class MyClass : IBase2
{

public MyMethod()
{
// …
}
}


7. Конструкторы должны иметь разные сигнатуры
Не может быть нескольких конструкторов с одинаковой сигнатурой, даже если они появляются в разных частичных классах. Иначе будет ошибка компиляции
CS0111: Type 'MyClass' already defines a member called 'MyClass' with the same parameter types (Тип 'MyClass' уже определяет член с именем 'MyClass' с теми же типами параметров).
public partial class MyClass
{
public MyClass() {…}
}

public partial class MyClass
{
public MyClass() {…} //CS0111
}


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

В следующем примере один из частичных классов добавляет частичный метод с именем Abc без реализации. Вы можете добавить эту реализацию в другой частичный класс:
public partial class MyClass
{
partial void Abc();
}

public partial class MyClass
{
partial void Abc()
{
// Реализация
}
}


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

Источник: https://www.roundthecode.com/dotnet-tutorials/what-are-partial-classes-csharp-why-do-we-use-them
👍6
День 2066. #ЗаметкиНаПолях
Использование Mutex<T> для Синхронизации Доступа к Общему Ресурсу
Когда вам нужно получить доступ к общему ресурсу, вы можете использовать оператор lock или примитив синхронизации, такой как System.Threading.Mutex, для синхронизации доступа.

Однако в сложном коде его легко забыть:
var obj = new object();
var value = 42;
lock (obj)
{
// Надо убедиться, что lock используется везде, где происходит доступ к ресурсу
Console.WriteLine(value);
}

// ⚠️ Здесь доступ получен без блокировки
value = 43;


Можно сделать это более явным и менее подверженным ошибкам, создав класс Mutex<T>, который инкапсулирует общий ресурс и примитив синхронизации:
var m = new Mutex<int>(42);
using (var scope = m.Acquire())
{
// доступ к ресурсу
Console.WriteLine(scope.Value);

// изменение ресурса
scope.Value = 43;
}
// ✔️ Вы не можете использовать общий ресурс вне области мьютекса

Вот реализация класса Mutex<T> и его области MutexScope<T>:
sealed class Mutex<T>
{
internal T _value;
private readonly Lock _lock = new();

public Mutex() { _value = default!; }
public Mutex(T initialValue)
=> _value = initialValue;

public MutexScope<T> Acquire()
{
_lock.Enter();
return new MutexScope<T>(this);
}

internal void Release()
=> _lock.Exit();
}

sealed class MutexScope<T>
: IDisposable
{
private readonly Mutex<T> mutex;
private bool disposed;

internal MutexScope(Mutex<T> mutex)
{
this.mutex = mutex;
}

public ref T Value
{
get
{
ObjectDisposedException
.ThrowIf(disposed, this);
return ref mutex._value!;
}
}

public void Dispose()
{
mutex.Release();
disposed = true;
}
}


Источник: https://www.meziantou.net/using-mutex-t-to-synchronize-access-to-a-shared-resource.htm
👍27👎2
День 2067. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 25. Айсберги всегда больше, чем кажутся
Практически все проекты выходят за рамки первоначальной оценки после более тщательного анализа проблем и запросов на изменение во время разработки. Чем дольше длится проект, тем большего его роста можно ожидать. Требования, предъявляемые к крупным проектам, обычно увеличиваются на 1–3% в месяц во время разработки. Если в ваших планах не предусмотрен такой рост, то вы наверняка отстанете от графика.

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

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

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

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

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍8
День 2068. #юмор
А как проходят ваши презентации демок?
👍20👎1
День 2069. #ЗаметкиНаПолях
Тестируем HttpClient, Вызывающий API

API можно тестировать с помощью WebApplicationFactory, как было рассмотрено здесь. Но что, если нужно «имитировать» вызовы HttpClient внутри тестов? Например, у вас нет контроля над API, но нужно протестировать реакцию приложения на получение определённого ответа от API.

Допустим, у нас такой минимальный API:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddHttpClient<GreeterService>(
o => o.BaseAddress =
new Uri("https://www.website.com"));

var app = builder.Build();
app.MapGet("/", async (GreeterService svc)
=> await svc.GetSomethingAsync());

app.Run();

public sealed class GreeterService
{
private readonly HttpClient _client;

public GreeterService(HttpClient client)
=> _client = client;

public async Task<string> GetSomethingAsync()
=> await _client.GetStringAsync("/api");
}


Мы используем HttpClient внутри GreeterService. Как нам теперь протестировать этот класс? Мы бы не хотели делать настоящие HTTP-запросы в тестах (по крайней мере, к внешним системам), поскольку они делают тест нестабильным и ненадёжным. Кроме того, сложно проверить, как наш сервис будет обрабатывать отказы API.

HttpMessageHandler / DelegatingHandler
DelegatingHandler — это «промежуточное ПО» для HttpClient. Каждый раз, когда вызывается SendAsync, этот обработчик будет вызываться. Обычно его используют для ведения журнала, аутентификации и т. д. Но мы также можем использовать его в тестовых сценариях!

Обработчик для нашего теста будет выглядеть следующим образом:
public class FakeDataHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage>
SendAsync(
HttpRequestMessage request,
CancellationToken ct)
{
if (request.RequestUri is
{ AbsoluteUri: "https://www.website.com/api" })
{
return Task.FromResult(
new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello World"),
});
}

return base.SendAsync(request, ct);
}
}

Теперь используем FakeDataHandler:
public ApiTests(
WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(s =>
s.ConfigureTestServices(sc =>
{
sc.AddTransient<FakeDataHandler>();
sc.ConfigureHttpClientDefaults(d =>
d.AddHttpMessageHandler<FakeDataHandler>());
}));
}

И следующий тест пройдёт:
[Fact]
public async Task ShouldGreet()
{
using var client = _factory.CreateClient();

var response = await client.GetStringAsync("/api");

Assert.NotNull(response);
Assert.Equal("Hello World", response);
}

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

Источник: https://steven-giesel.com/blogPost/47e4a351-0f1b-4bb5-a3d5-0ce4d7c1fa61/how-to-test-httpclient-inside-api-tests
👍16
День 2070. #ЗаметкиНаПолях
Экономим Память с RecyclableMemoryStream

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

В Microsoft создали библиотеку RecyclableMemoryStream для обеспечения пула для объектов MemoryStream с целью повышения производительности приложения, особенно в области сборки мусора.

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

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

Затем мы сериализуем коллекцию в zip-поток. Вот пример с MemoryStream:
List<User> users = PersonGenerator.Generate();

using (var stream = new MemoryStream())
{
using (var gzip = new GZipStream(
stream, CompressionLevel.Optimal, true))
{
await JsonSerializer.SerializeAsync(gzip, users);
await gzip.FlushAsync();
}
}

Теперь сравним с реализацией, использующей RecyclabeMemoryStreamManager:
List<User> users = PersonGenerator.Generate();
var msMgr = new RecyclableMemoryStreamManager();

using (var stream = msMgr.GetStream("memory"))
{
using (var gzip = new GZipStream(
stream, CompressionLevel.Optimal, true))
{
await JsonSerializer.SerializeAsync(gzip, users);
await gzip.FlushAsync();
}
}

Очень похоже, за исключением того, что вместо MemoryStream используется RecyclabeMemoryStreamManager.

Теперь измерим производительность. Генерируем коллекцию и создаём экземпляр RecyclabeMemoryStreamManager в глобальной настройке, измеряем только сериализацию и сжатие.
|       Method |     Mean |   Allocated |
| Recyclable | 496.1 ms | 37.59 KB |
| MemoryStream | 506.2 ms | 16422.09 KB |


Как видите, у RecyclableMemoryStreamManager огромное преимущество в использовании памяти.

RecyclableMemoryStreamManager имеет множество настроек, провайдер EventSource для трекинга, а также хуки событий, куда вы можете добавлять свой код.

Источник: https://medium.com/@matias.paulo84/recyclablememorystream-vs-memorystream-c4aa341324a9
👍27
День 2071. #ЗаметкиНаПолях
Реалистичные Фейковые Данные с Помощью Bogus. Начало

Во вчерашнем посте я упомянул библиотеку Bogus для генерации случайных данных. Сегодня рассмотрим её поподробнее.

Иногда нужно поэкспериментировать с некоторыми функциями или создать демо проект, не создавая при этом реальный экземпляр БД. Также хотелось бы использовать более-менее реалистичные данные, а не «test1», 123 и т. д.

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

Вы можете определить так называемые «фейкеры», целью которых является генерация фиктивных экземпляров классов путем автоматического заполнения их полей. Вот простой пример. Пусть у нас есть POCO-класс Book:
public class Book
{
public Guid Id { get; set; }
public string Title { get; set; }
public int Pages { get; set; }
public Genre[] Genres { get; set; }
public DateOnly PublicationDate { get; set; }
public string Author { get; set; }
}

public enum Genre
{
Thriller, Fantasy, Romance, Biography
}

И нам необходимо создать коллекцию книг. Для начала создадим:
Faker<Book> bookFaker = new Faker<Book>();

Мы можем добавить один или несколько методов RuleFor для создания правил, используемых для генерации каждого свойства. Самый простой подход — использовать перегрузку, где первый параметр — это функция, указывающая на заполняемое свойство, а второй — функция, которая вызывает методы, предоставляемые Bogus, для создания фиктивных данных. В виде псевдокода это выглядит так:
faker.RuleFor(
sm => sm.SomeProperty,
f => f.DataGenerator.Generate());

Другой вариант – использовать строковое имя свойства:
faker.RuleFor(
"SomeProperty",
f => f.DataGenerator.Generate());

Также можно определить генератор для определённого типа данных, указав, например, что «каждый раз, когда надо создать данные для этого типа, используй этот генератор»:
bookFaker.RuleForType(
typeof(DateOnly),
f => f.Date.PastDateOnly());


Далее рассмотрим генерацию данных для основных типов.

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

Источник:
https://www.code4it.dev/blog/seed-inmemory-entityframework-bogus/
👍20
День 2072. #ЗаметкиНаПолях
Реалистичные Фейковые Данные с Помощью Bogus. Продолжение
Начало

Генерация данных
1. Случайные ID

bookFaker.RuleFor(b => b.Id, 
f => f.Random.Guid());


2. Случайный текст
Мы можем генерировать случайный текст по шаблону «Lorem Ipsum». Можно генерировать текст разной длины в буквах, словах, предложениях, параграфах и т.п.:
bookFaker.RuleFor(b => b.Title, 
f => f.Lorem.Text());


3. Перечисления
Для перечислений можно использовать свойство Random, чтобы получить несколько случайных значений перечисления (в данном случае – 2, если не задать параметр – случайное количество):
bookFaker.RuleFor(b => b.Genres, 
f => f.Random.EnumValues<Genre>(2));

Если нужно получить одно значение, используйте f.Random.Enum<Genre>().

4. Фиктивные данные людей
Одной из самых захватывающих функций Bogus является возможность генерировать реалистичные данные для обычных сущностей, таких как люди. Можно использовать свойство Person для генерации данных человека: имени, фамилии, пола, логина, телефона, веб-сайта и других:
bookFaker.RuleFor(b => b.Author, 
f => $"{f.Person.FirstName} {f.Person.LastName}");


5. Генерация итогового класса
После определения правил мы можем сгенерировать реальные экземпляры. Нужно вызвать метод Generate, указав количество элементов для генерации:
List<Book> books = bookFaker.Generate(2);

Для генерации случайного количества элементов:
List<Book> books = bookFaker.GenerateBetween(2, 5);

Вот полный пример генерации списка книг:
private List<Book> GenerateBooks(int count)
{
Faker<Book> bookFaker = new Faker<Book>()
.RuleFor(b => b.Id,
f => f.Random.Guid())
.RuleFor(b => b.Title,
f => f.Lorem.Text())
.RuleFor(b => b.Genres,
f => f.Random.EnumValues<Genre>())
.RuleFor(b => b.Author,
f => $"{f.Person.FirstName} {f.Person.LastName}")
.RuleFor(nameof(Book.Pages),
f => f.Random.Number(100, 800))
.RuleForType(typeof(DateOnly),
f => f.Date.PastDateOnly());

return bookFaker.Generate(count);
}

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

Источник:
https://www.code4it.dev/blog/seed-inmemory-entityframework-bogus/
👍12
День 2073. #ЗаметкиНаПолях
Реалистичные Фейковые Данные с Помощью Bogus. Окончание

Начало
Продолжение

Заполнение БД в памяти фиктивными данными
Используем Entity Framework для БД в памяти. Для этого нужно установить NuGet-пакет Microsoft.EntityFrameworkCore.InMemory.

Теперь добавим класс DbContext и зададим БД в памяти:
public class BooksDbContext : DbContext
{
public DbSet<Book> Books { get; set; }

protected override void
OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseInMemoryDatabase("BooksDatabase");
}
}


Теперь можно заполнить БД данными, сгенерированными Bogus, переопределив метод OnModelCreating:
protected override void 
OnModelCreating(ModelBuilder mb)
{
base.OnModelCreating(mb);

var books =
BogusBookGenerator.GenerateBooks(15);
mb.Entity<Book>().HasData(books);
}

Здесь мы используем метод GenerateBooks, описанный во вчерашнем посте, и задаём полученный список книг в качестве содержимого набора данных Books.

Теперь можно прочитать данные из нашей БД:
using var ctx = new BooksDbContext();
ctx.Database.EnsureCreated();

var allBooks = await ctx.Books.ToListAsync();

var thrillers = ctx.Books
.Where(b => b.Genres.Contains(Genre.Thriller))
.ToList();

Изучите все потенциальные возможности Bogus здесь. Это поможет сделать ваши тесты и эксперименты осмысленными и более понятными.

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

Источник: https://www.code4it.dev/blog/seed-inmemory-entityframework-bogus/
👍14
День 2074. #Карьера
Упрощатели Заходят Далеко, Усложнятели Застревают

«Если вы не можете объяснить это шестилетнему ребенку, вы сами этого не понимаете».
– Альберт Эйнштейн

В бизнесе есть два типа людей:
1. Упрощатели - делают сложные вещи простыми.
2. Усложнятели - делают простые вещи сложными.

Короткая шутка
- Как усложнятель называет упрощателя?
- Босс.

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

«Что ж, это интересное предложение, и я не обязательно против него, поэтому позвольте мне поднять его на флагшток, чтобы мы могли побить его палками, как пиньяту. Поскольку я слышал, что эта идея имеет некоторую поддержку в этой области, позвольте мне связаться с ребятами наверху, и мы посмотрим, хватит ли у нас ресурсов, чтобы продолжить работу над этим. Если стоимость выше $100 тыс., мне, возможно, придётся отложить эту идею, потому что нам нужно сохранить немного сухого пороха в ожидании результатов стратегической встречи — где, как я знаю, мы рассматриваем серьёзные изменения. Так что давайте оставаться на связи. Честь и хвала команде за то, что они придумали такое отличное ценностное предложение, но сейчас, боюсь, не могу его принять.»

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

Другой способ спрятаться за сложностью — не лингвистический, а концептуальный: всегда находить проблему вышестоящего уровня или более масштабную проблему, которая будет блокировать прогресс на нижнем уровне. Рассмотрим такое утверждение:
«Я хотел бы перейти на новый процесс, но мы ещё не завершили обучение.»

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

Есть старая поговорка:
«Когда вы спрашиваете время, некоторые люди скажут вам, как собрать часы. Другие расскажут вам, как построить целую швейцарскую деревню.»

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

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

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

Источник: https://kellblog.com/2015/05/13/career-advice-simplifiers-go-far-complexifiers-get-stuck/
👍12
День 2075. #УрокиРазработки
Уроки 50 Лет Разработки ПО

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

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

Предположим, я — руководитель проекта. Клиент спрашивает, сколько времени потребуется и сколько это будет стоить. Основываясь на своём понимании проекта и опыте, я разрабатываю и представляю смету. Если спонсору не нравится моя оценка, возможны два исхода:
1) Я пытаюсь защитить свою оценку, не имея данных, а спонсор настаивает на сокращении затрат. Это не переговоры, а дебаты, которые я, скорее всего, проиграю.
2) Я, основываясь на данных, а не на эмоциях, описываю спонсору, как получена оценка, например:
- Я посоветовался с опытными членами команды, которые понимают, какого объёма работы требует новый проект.
- Мы оценили размер проекта на основе имеющейся информации и при этом учли фактор неопредёленности и ожидаемого разрастания, основываясь на прошлом опыте.
- Мы пересмотрели наш прошлый опыт работы с аналогичными проектами и т.д.

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

Принципиальные переговоры
Каждый раз, когда возникает разрыв между ожиданиями или требованиями заинтересованной стороны и вашим прогнозом в виде оценки, необходимо провести переговоры. Принципиальные переговоры — это метод достижения взаимоприемлемых соглашений. Они базируются на четырёх принципах:
1) Отделите людей от задач. Если обсуждение сводится к личной борьбе между вами и кем-то обладающим большей властью, то вы гарантированно проиграете, если не сможете привести убедительные доводы в свою пользу.
2) Сосредоточьтесь на интересах, а не на позициях. Не упорствуйте бездумно в защите своей оценки. Постарайтесь понять интересы другой стороны. Возможно, есть способ удовлетворить и их, и ваши интересы, изменив формулировку задачи или предлагаемое решение.
3) Придумывайте варианты для взаимной выгоды. Если вас вынуждают дать обещание, которое, как вы знаете, команда не сможет выполнить, ищите альтернативные взаимоприемлемые исходы с реалистичными целями, которых вы действительно сможете достичь. По результатам большинства успешных переговоров ни одна из сторон не получает всего, чего хочет, но обе стороны могут смириться с результатом — компромиссом.
4) Настаивайте на использовании объективных критериев. Здесь пригодятся ваши данные. Используйте данные для обоснования своих оценок и уважайте данные, представленные партнёром по переговорам.

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍14
День 2076. #ЧтоНовенького
Организуем Точки Останова
Если в вашем проекте множество точек останова, новая функция группировки точек останова в Visual Studio 2022 поможет легко их классифицировать, включать и отключать по группам.

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

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

Чтобы создать группу точек останова, щелкните правой кнопкой мыши на любую точку останова в окне Breakpoints и выберите Add to Group > New Group (Добавить в группу > Новая группа). Вы можете назвать группу и добавить описание, если хотите. Добавлять точки останова в группу можно простым перетаскиванием или через контекстное меню.

Чтобы установить группу точек останова в качестве группы по умолчанию, просто щелкните правой кнопкой мыши по группе и выберите Set as Default Group (Установить как группу по умолчанию). Группа по умолчанию будет обозначена жирным шрифтом. Если у вас есть группа точек останова по умолчанию, все новые точки останова, которые вы добавляете в код, будут автоматически добавляться в эту группу.

Вы можете получить доступ ко всем действиям группы точек останова из контекстного меню или панели инструментов в окне Breakpoints.

Источник: https://devblogs.microsoft.com/visualstudio/organize-your-breakpoints-like-a-pro/
👍23