.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
День 2122. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 6: Связывание. Начало

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

Связанные токены отмены
Связанные токены отмены позволяют вашему коду создавать связанный CancellationTokenSource, который отменяется другим токеном отмены, в дополнение к тому, что сам может запросить отмену:
async Task DoAsync(
CancellationToken ct)
{
using var cts =
CancellationTokenSource
.CreateLinkedTokenSource(ct);
var task = DoOtherAsync(cts.Token);
… // Что-то делаем
… // возможно вызываем cts.Cancel()
await task;
}

Метод DoAsync принимает токен ct — «внешний» токен отмены. Затем он создает CTS, который связан с этим внешним токеном. Когда он вызывает DoOtherAsync, он передает «внутренний» токен из этого связанного CTS.

Если внешний токен когда-либо отменяется, то связанный cts и его внутренний токен (cts.Token) также отменяются. Более того, метод DoAsync имеет возможность явно отменить связанный CTS — в этом случае будет отменён только внутренний токен отмены, оставив внешний отмены неизменным.

То же самое можно сделать с помощью регистраций:
async Task DoAsync(
CancellationToken ct)
{
using var cts = new CancellationTokenSource();
using var reg = ct.Register(cts.Cancel);
var task = DoOtherAsync(cts.Token);
… // Что-то делаем
… // возможно вызываем cts.Cancel()
await task;
}

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

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

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

Источник:
https://blog.stephencleary.com/2024/10/cancellation-6-linking.html
👍8
День 2123. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 6: Связывание. Продолжение

Начало

Варианты использования
Внешний токен и внутренний источник отмены могут на самом деле представлять что угодно; связанные токены отмены полезны, когда вам нужно отменить код, если «A или B».

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

Одним из естественных мест, где используется этот тип кода, является Polly. Polly позволит передать внешний токен, который находится под вашим контролем. Затем он передаёт другой токен отмены вашему делегату выполнения; этот внутренний токен контролируется Polly. Конвейеры Polly (например, тайм-аут) могут отменить внутренний токен для отмены вашего делегата. Естественно, если ваш код отменяет внешний токен, переданный Polly, это также перейдет во внутренний токен. То есть, они связаны:
async Task ExecuteRetryTimeoutAsync(
CancellationToken ct)
{
var pipeline = new ResiliencePipelineBuilder()
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();

await pipeline.ExecuteAsync(async token =>
{
/* ваш код тут */
}, ct);
}

ExecuteRetryTimeoutAsync принимает внешний токен ct и передаёт его Polly. Затем Polly создаёт связанный внутренний токен (который включает поведение конвейера, такое как тайм-аут), и передаёт внутренний токен (token) вашему делегату.

Делегаты, которые вы передаёте Polly, должны следить за токеном, который они получают от Polly, а не за какими-либо другими! Это может оказаться ловушкой, когда вы добавляете конвейеры Polly в существующий код, например, при добавлении тайм-аутов в этот код:
async Task ExecuteAsync(CancellationToken ct)
{
for (int i = 0; i != 10; ++i)
await Task.Delay(1000, ct);
}

Частая ошибка – забыть обновить использованный токен:
async Task ExecuteWithTimeoutAsync(
CancellationToken ct)
{
var pipeline = new ResiliencePipelineBuilder()
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();

await pipeline.ExecuteAsync(async token =>
{
// ПЛОХОЙ КОД!!!
for (int i = 0; i != 10; ++i)
await Task.Delay(1000, ct);
}, ct);
}

Здесь делегат по-прежнему следит за внешним токеном отмены ct, а должен следить за токеном token:
async Task ExecuteWithTimeoutAsync(
CancellationToken ct)
{
var pipeline = new ResiliencePipelineBuilder()
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();

await pipeline.ExecuteAsync(async token =>
{
for (int i = 0; i != 10; ++i)
await Task.Delay(1000, token);
}, ct);
}


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

Источник:
https://blog.stephencleary.com/2024/10/cancellation-6-linking.html
👍8
День 2124. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 6: Связывание. Окончание

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

Не используйте OperationCanceledException.CancellationToken
Рассмотрим снова исходный пример:
async Task DoAsync(
CancellationToken ct)
{
using var cts =
CancellationTokenSource
.CreateLinkedTokenSource(ct);
var task = DoOtherAsync(cts.Token);
… // Что-то делаем
… // возможно вызываем cts.Cancel()
await task;
}

Предположим, код вызывает DoAsync и реагирует на отмену:
async Task MainAsync()
{
using var cts =
new CancellationTokenSource();
cts.CancelAfter(2000);

try
{
await DoAsync(cts.Token);
}
catch (OperationCanceledException ex)
// ПЛОХОЙ КОД!!!
when (ex.CancellationToken == cts.Token)
{
Console.WriteLine("Timeout!");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

Замысел кода — сделать что-то особенное, если код отменяется из-за этого конкретного источника отмены. К сожалению, этот код проблематичен в реальном мире; DoAsync может использовать связанный источник токена отмены, в этом случае OperationCanceledException.CancellationToken не будет соответствовать cts.Token, даже если он был источником отмены!

Поэтому не используйте OperationCanceledException.CancellationToken. Правильное решение — проверять, запросил ли этот источник отмену:
async Task MainAsync()
{

catch (OperationCanceledException)
when (cts.IsCancellationRequested)
{
Console.WriteLine("Timeout!");
}

}


То же касается и проверки связанных источников отмены:
async Task DoAsync(
CancellationToken ct)
{
using var cts =
CancellationTokenSource
.CreateLinkedTokenSource(ct);
cts.CancelAfter(1000);

try
{
await DoOtherAsync(cts.Token);
}
catch (OperationCanceledException ex)
// ПЛОХОЙ КОД!!!
when (ex.CancellationToken == cts.Token)
{
… // делаем что-то при таймауте
throw;
}
}

С этим кодом та же проблема! Возможно, что DoAsync может сам использовать связанный токен отмены (или будет использовать в будущем). Решение — не использовать OperationCanceledException.CancellationToken:
async Task DoOtherAsync(
CancellationToken ct)
{

catch (OperationCanceledException ex)
when (cts.IsCancellationRequested)
{
… // делаем что-то при таймауте
throw;
}
}


Итого
В большинстве случаев не приходится использовать связанные токены отмены, но они бывают полезны. Вот, что следует запомнить:
1. Освобождайте (dispose) источники токенов отмены, включая связанные источники токенов отмены.
2. Не используйте OperationCanceledException.CancellationToken; вместо этого используйте IsCancellationRequested.
3. Для любого кода, который имеет несколько токенов в области действия, будьте внимательны к тому, на какой из них вы реагируете.

Источник: https://blog.stephencleary.com/2024/10/cancellation-6-linking.html
👍9
Что выведет код с картинки в первом комментарии (при стандартных настройках культуры)?
#Quiz #CSharp
Anonymous Quiz
12%
23.11.2024 11.23.2024
40%
23.11.2024 11/23/2024
6%
23/11/2024 11/23/2024
13%
2024-11-23 11/23/2024
23%
23.11.2024 00:00:00 11/23/2024
7%
23.11.2024 00:00:00 11.23.2024
👍13👎2
День 2125. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 33. Клиент не всегда прав
Утверждение «Клиент всегда прав» подразумевает, что если клиент что-то просит, то вы обязаны дать ему это.

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

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

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

3. Заместитель представителя
Клиент может представить требования от имени класса пользователей, к которому он не принадлежит. Его понимание ожиданий от системы может быть устаревшим или неполным. Например, если в роли представителя сообщества пользователей при обсуждении требований выступает руководитель. Он может не знать всех подробностей повседневной работы простых пользователей. Его опыт может устареть.

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

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

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

7. Изменения не бесплатны
Типичный пример, когда клиент не всегда прав, — когда он просит добавить новые возможности или внести другие изменения, но ожидает, что цена и дата готовности останутся прежними. Клиенты относятся к этому так: «Изменения должны вноситься бесплатно; просто сделайте это». Это больше похоже на шутку, но такое случается в реальной жизни.

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍3
День 2126. #Холивар
Композиция или наследование

Возникли у нас как-то с коллегой разногласия по поводу реализации одной функциональности. Лучшие практики объектно-ориентированного проектирования советуют нам предпочитать композицию наследованию. Но всегда ли это верно?

Коротко о домене. Есть у нас сервис поиска поставщиков электронных деталей, и сайт, на котором его можно осуществлять по подписке. А ещё есть сервис, который мы предоставляем производителям этих деталей. Заключается он в том, что мы даём им доступ к результатам нашего поиска «на их сайте». Чтобы на их сайте покупатель мог легко найти, какие поставщики их деталей есть рядом. Да, в наше время уже 99,9% читателей подумали бы, что мы предоставляем API, но нет. В смысле, API тоже есть, но поскольку сервису уже почти 20 лет и не у каждого производителя есть отдел разработки, который мог бы прикрутить этот API к их сайту, есть у нас и другая опция. Мы создаём страницы с функциональностью (поиск, результаты, возможность заказа и т.п.) и подгоняем их под внешний вид сайта производителя.

К сути. Конечно, каждый наш клиент хочет, чтобы функциональность максимально соответствовала его сайту. И стандартная реализация не всегда подходит. Кто-то хочет разную логику поиска: «с начала», «содержит», «равно». Кто-то разные регионы: по умолчанию результаты делятся на Америка, Европа и Азия, но некоторые просят поделить по-другому. И так далее, там настроек выше крыши. Большинство из них легко сохраняется в JSON, но не все. Например, кто-то хочет, чтоб на странице результатов поиска повторялась форма поиска, а кто-то, чтоб результаты определённого поставщика показывались сверху. Тут уже не избежать кастомной бизнес-логики.

Мы сошлись на том, что должен быть базовый класс со стандартным поведением, реализующий интерфейс. Вот его упрощённый код. Только методы, каждый из которых реализует нужный кусок функциональности (допустим, форматирует вывод):
puclic class SearchResults : ISearchResults
{
public Header GetHeader(…) { … }
public Rows GetRows(…) { … }
public Footer GetFooter(…) { … }
}

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

Так вот, моя реализация предполагала, что спец-класс просто наследовал бы от базового и переопределял нужную функциональность:
puclic class CustomSearchResults
: SearchResults, ISearchResults
{
public override Header GetHeader(…)
{
// кастомная логика
}
}

Остальные методы автоматически наследовались бы. Коллега настаивал на предпочтении композиции наследованию, и что мы должны делать декоратор базового класса. Как-то так:
puclic class CustomSearchResults 
: ISearchResults
{
private SearchResults _base;
public CustomSearchResults(SearchResults sr)
{
_base = sr;
}
public Header GetHeader(…)
{
// кастомная логика
}

public Caption GetRows(…)
=> _base.GetRows(…);
public Caption GetFooter(…)
=> _base.GetFooter(…);
}

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

Так мы к согласию и не пришли. А вы что скажете? Может есть вообще какой-то третий, «более лучший», вариант?
👍2
День 2127. #ЗаметкиНаПолях
Проверяем Утверждения в Коде

Иногда нам приходится делать предположения в коде. Например, что какое-то свойство или переменная имеет определённое значение. Либо у нас есть определённое представление о значении, но мы не уверены на 100%, верно ли оно. Сегодня познакомимся с Debug.Assert.

Сразу оговоримся, есть TDD, где вы можете записать свои предположения в виде тестов https://t.iss.one/NetDeveloperDiary/2471. Сегодня не об этом. Иногда у вас есть предположения об определённых характеристиках данных или среды, которые вы не хотите тестировать по какой-то причине (например, потому что вы думаете, что переменная никогда не должна иметь это значение в этом пути кода). Так что рассматривайте это как дополнительный инструмент разработки.

Debug.Assert
Рассмотрим следующий код:
return view
.Where(s => s.TimeSignal is not null)
.GroupBy(v => v.SomeField)
.Select(v =>
{
var signals = v.ToList();
Debug.Assert(signals.Count == 2);
return new Model
{
Id = v.Key,
First =
CreateFromSignal(signals[0].TimeSignal),
Last =
CreateFromSignal(signals[1].TimeSignal),
};
})
.ToArray();

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

Отличительная особенность Debug.Assert, как следует из названия, в том, что он работает только в режиме отладки. Поэтому вы можете усеять свой код этими утверждениями, и они будут работать только в режиме отладки (надеюсь, в проде у вас код работает в режиме RELEASE). Если условие не выполняется, вы получите исключение.

Мне нравится такой подход, чтобы записывать некоторые из моих предположений в код. Это как небольшая заметка для меня и моих коллег. Если мы точно знаем, что предположение должно быть верным в 100% случаев, тогда мы утверждаем это тем или иным способом. В противном случае удаляем Debug.Assert и позволяем коду работать. Часто я запускаю свой код локально с включенным режимом DEBUG.

Microsoft тоже использует эту технику повсеместно: https://grep.app/search?current=2&q=Debug.Assert&case=true&filter[lang][0]=C%23

Trace.Assert
Также есть Trace.Assert, который похож на Debug.Assert, но выполняется, когда в проекте определён символ условной компиляции TRACE.

В проектах Visual Studio и Rider по умолчанию символ условной компиляции DEBUG определяется для отладочных сборок, а TRACE — для всех сборок. Однако, если вы его удалите, либо создадите новую конфигурацию без него, то Trace.Assert не будет выполняться.

Источник: https://steven-giesel.com/blogPost/0c8009eb-0bb6-4716-ae6c-fcb3de4a26e6/how-to-assert-assumptions-in-code-that-might-not-be-true
👍2
День 2128. #Книги
«Предметно-ориентированное проектирование (DDD): структуризация сложных программных систем» (Эванс Э. — СПб.: ООО «Диалектика», 2020).

«Библия DDD», и я наконец-то её осилил. Ох! Ни с одной другой книгой у меня не возникало таких трудностей. Даже Рихтера в начале карьеры я прочитал не с первого, так со второго раза. Но тут я принимался, бросал, продолжал, понимал, что забыл, о чём читал раньше, начинал сначала, снова бросал, читал другие книги, снова возвращался к этой, снова понимал, что всё забыл, пролистывал начало по диагонали и продолжал… В общем, книга у меня уже года три, и последние полтора я пытался её осилить.

Дело даже не в сложности темы, а скорей в подаче. Среди отзывов в начале книги встретился такой: «Книгу легко читать. У Эрика в запасе много интересных историй, и языком он владеет хорошо». Но, то ли моих умственных способностей недостаточно, то ли «легко читать» - это не об этой книге. Во-первых, на мой взгляд, очень сложные для понимания примеры. Да, автор описывает реальные проекты, в которых он участвовал. Но всё-таки, ПО для компании грузоперевозок с грузами, видами контейнеров, маршрутизаторами, маршрутами, участками пути и манипуляциями с грузами – не самый очевидный домен. А уж финансовая программа с займами, долями участия, графиками платежей и начислений и прочими синдицированными кредитами – тут вообще, если с этой сферой не знаком, разобраться по тексту крайне сложно.

А во-вторых, это перевод. Я занимался переводами нескольких книг, и по себе знаю, что такое «русифицировать» какой-то английский термин. Иногда подходящих слов в русском языке просто нет, а часто англицизм настолько вошёл в обиход, что не использовать его – значит специально вводить читателя в заблуждение. Но НАСТОЛЬКО академический перевод вроде бы не самой академической темы я вижу впервые. Я лично сквозь текст просто продирался, как через джунгли, размахивая мачете. Вроде все слова по отдельности знаю, но какая мысль заложена в предложение – решительно не понятно. То ли переводчик привык работать с научными текстами, то ли издательство посчитало, что тема книги подойдёт лишь людям с научными степенями. Но, например, что словосочетание «деловые регламенты» - это то же самое, что «бизнес-правила», я понял далеко не сразу.

Показательна фраза в сноске, которая должна пояснить (sic!) смысл перевода выражения «алгоритмическая часть программы»: «Это словосочетание входит и в название книги “Tackling Complexity at the Heart of Software”, которое можно по смыслу перевести как “систематическое упрощение кода алгоритмически сложных систем”». ЧТО??? Да мне английская фраза гораздо понятнее, чем перевод, хотя, согласен, её не просто перевести дословно.

Попробуйте сами догадаться, что означают следующие понятия:
1. Регламентное правило
2. Объекты-показатели
3. Уровень прикладных операций
4. Техническая архитектурная среда

Убеждён, что «рефакторингу» удалось остаться в тексте книги «рефакторингом» исключительно по причине того, что это название книги Мартина Фаулера.

Несмотря на всё это, книга довольно полезная. И хорошо раскрывает особенности DDD, лучшие практики и подходы к проектированию программных систем. Читать именно её или найти какой-то другой источник, решать вам.
👍18
День 2129. #Оффтоп
Почему Разработчики Любят Чистый Код, но Ненавидят Писать Документацию? Начало

В Developer Coefficient, исследовании, заказанном финтех-гигантом Stripe, разработчики сообщили, что они тратят более 17 часов в неделю на задачи по обслуживанию, такие как отладка и рефакторинг — работа, классифицируемая ими как «мытарство».

Опрос разработчиков 2024 на StackOverflow выявил множество тех же проблем и жалоб. Наибольшим разочарованием, с большим отрывом, был технический долг. И наоборот, больше всего разработчиков радовало улучшение качества их кода и среды разработки. И заглядывая в будущее, две области, в которых разработчики чувствовали, что они получат наибольшую выгоду от инструментов GenAI, — это тестирование и документирование.

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

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

В исследовании PLOS ONE 2023 года была разработана модель для проверки того, какие методы окажут положительное или отрицательное влияние на процесс рефакторинга. Авторы пишут, что «документация помогает в адаптации новых членов команды и обеспечивает согласованность методов рефакторинга во всей команде».

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

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

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

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

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

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

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

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

Источник:
https://stackoverflow.blog/2024/11/11/developers-hate-documentation-ai-generated-toil-work/
👍6
День 2130. #Оффтоп
Почему Разработчики Любят Чистый Код, но Ненавидят Писать Документацию? Окончание

Начало

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

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

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

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

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

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

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

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

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

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

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

Источник: https://stackoverflow.blog/2024/11/11/developers-hate-documentation-ai-generated-toil-work/
👍4
День 2131. #ЧтоНовенького
Отладка Тестов с Помощью GitHub Copilot
Новая функция Visual Studio 2022, работающая на основе GitHub Copilot, предлагает кнопку Debug with Copilot (Отладка с Copilot) в обозревателе тестов.

Отладка неудачных модульных тестов часто может быть утомительным и разочаровывающим процессом. Разработчики тратят много времени на выявление основной причины сбоя и выяснение шагов по её устранению. Этот трудоемкий процесс может задержать сроки разработки и снизить производительность. Visual Studio 2022 с GitHub Copilot предлагает инновационное решение для оптимизации этого процесса.

Как это работает
После того, как вы нажмёте на кнопку Debug with Copilot, GitHub Copilot предоставит подробный план отладки, адаптированный к вашему конкретному тесту. Он устанавливает необходимые точки останова и отслеживает переменные, которые имеют решающее значение для определения следующих шагов. По мере того, как сеанс отладки продвигается и достигает точки останова, GitHub Copilot анализирует значения отслеживаемых переменных и даёт рекомендации относительно того, следует ли продолжать отладку или приступить к исправлению проблемного кода. См. скриншот.

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

Источник: https://devblogs.microsoft.com/visualstudio/transform-your-debugging-experience-with-github-copilot/
👍4
День 2132. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 34. Мы слишком часто принимаем желаемое за действительное


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

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

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

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

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

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

Ещё одна фантазия — вера в то, что члены команды могут посвящать работе над проектом 100% времени и при этом находить время для самообучения, инноваций и расширения возможностей команды. Руководитель, который думает, что полное использование рабочего времени над проектом осуществимо и желательно, отрицает реальность. Не имея возможности учиться, исследовать и улучшать предложения, команда не будет совершенствоваться.

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

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

Надежда на то, что мир не такой, какой есть на самом деле, может быть утешительной, но неконструктивной. Иногда мы не в восторге от реальности, но она — всё, что у нас есть, и мы должны с этим жить.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍5
День 2133. #ЗаметкиНаПолях
Пример Реализации Паттерна Цепочка Обязанностей

Паттерн «Цепочка Обязанностей» —позволяет создавать цепочку объектов для обработки запроса или выполнения задачи. Рассмотрим, как реализовать его в .NET на практическом примере.

Допустим, у нас есть ряд правил скидок для приложения электронной коммерции. В зависимости от клиента мы хотим применять различные проценты скидок к его заказам:
— VIP - 20%,
— постоянный клиент - 10%,
— новый клиент - 5%.
— иначе не применять скидку.

Изначально мы можем обрабатывать эту логику с помощью ряда операторов if:
public decimal CalculateDiscount(
Customer customer,
decimal orderTotal)
{
if (customer.IsVIP)
return orderTotal * 0.8m;

if (customer.IsRegular)
return orderTotal * 0.9m;

if (customer.IsNew)
return orderTotal * 0.95m;

return orderTotal;
}


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

1. Создадим абстрактный класс обработчика DiscountHandler, который определяет общий интерфейс для всех обработчиков скидок:
public abstract class DiscountHandler
{
protected DiscountHandler _next;

public DiscountHandler SetNextHandler(
DiscountHandler next)
{
_next = next;
return next;
}

public abstract decimal CalculateDiscount(
Customer customer, decimal orderTotal);
}


2. Реализуем конкретные обработчики скидок, производные от DiscountHandler. Каждый обработчик будет отвечать за определённое правило и решать, применять ли скидку или передавать запрос следующему обработчику:
public class VIPDiscountHandler
: DiscountHandler
{
public override decimal
CalculateDiscount(
Customer customer,
decimal orderTotal)
{
if (customer.IsVIP)
return orderTotal * 0.8m;

return _next?
.CalculateDiscount(customer, orderTotal)
?? orderTotal;
}
}

Остальные реализуем аналогично.

3. Создадим цепочку связанных обработчиков:
var vipHandler = new VIPDiscountHandler();

vipHandler
.SetNextHandler(new RegularDiscountHandler())
.SetNextHandler(new NewCustomerDiscountHandler())
.SetNextHandler(new NoDiscountHandler());

Наконец, вызовем цепочку, обратившись к методу CalculateDiscount первого обработчика:
decimal discountAmount = vipHandler
.CalculateDiscount(customer, orderTotal);


Плюсы
1. Гибкость
Позволяет динамически изменять или расширять цепочку (добавлять или удалять обработчики), не затрагивая другие части кода.
2. Слабая связанность
Каждому обработчику нужно знать только о своем непосредственном преемнике, что минимизирует зависимости.
3. Единая ответственность
Отделяет классы, которые вызывают операции, от классов, которые выполняют операции.

Минусы
1. Запрос может остаться необработанным
Если ни один из обработчиков в цепочке не может обработать запрос, он может остаться необработанным, что приведет к неожиданному поведению. Важно иметь обработчик по умолчанию или способ обработки таких сценариев.
2. Потенциальное влияние на производительность
Если цепочка становится очень длинной, это может привести к накладным расходам производительности из-за обхода нескольких обработчиков.

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

Источник: https://thecodeman.net/posts/chain-responsibility-pattern
👍19👎1
День 2134. #ЧтоНовенького
Значительное Ускорение Восстановления NuGet-Пакетов в .NET 9

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

Внутри Microsoft большой репозиторий содержит тысячи проектов на .NET Core. Это непростая задача, в настоящее время более 2500 отдельных проектов перенесены и оптимизированы для современных стандартов. Восстановление NuGet-пакетов занимало более 30 минут, что приводило к значительным задержкам в сотнях сборок каждый день. Эти задержки быстро накапливались, что приводило к потере времени и разочарованию разработчиков.

Переосмысление разрешения пакетов
Старый алгоритм разрешения зависимостей NuGet начинался как временное решение; доказывая, что, как сказал Милтон Фридман, «Ничто не является столь постоянным, как временное решение, которое работает». Хотя он долгое время служил своей цели, он не был разработан для обработки масштаба и сложности больших репозиториев. Первоначальный сопоставитель зависимостей создавал огромный граф с миллионами узлов, представляющих все возможные отношения между зависимостями. Этот подход просто не был масштабируемым; он требовал огромных объёмов памяти и вычислительной мощности, и по мере роста проектов росли время и усилия, необходимые для разрешения графов.

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

Результаты
Новый подход дал впечатляющие результаты. Исходный граф зависимостей, который создал бы 1,6 миллиона узлов для сложного проекта, был сокращён до всего 1200 узлов. Благодаря меньшему количеству узлов для обработки время восстановления значительно сократилось: до всего 2 минут.

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

Источник: https://devblogs.microsoft.com/dotnet/dotnet-9-nuget-resolver/
👍21
День 2135. #ЗаметкиНаПолях
Избегаем Null в Строках при Отправке Форм
ASP.NET Core
Если перенести решение с ASP.NET Framework на ASP.NET Core 8, можно внезапно столкнуться с ошибкой 500. Ошибка происходит из-за исключения NullReferenceException. Рассмотрим метод действия:
[HttpPost("/")]
public ActionResult Index(
[FromForm] MyForm myForm)
{
if(myForm.Input1.Length > 0)
{
// Что-то делаем…
}
return Ok();
}
public class MyForm
{
public string Input1 { get; init; }
}

Вроде ничего странного, правда? Только обработку ошибок убрали для краткости. А вот код, который отправлял форму:
const formData = new URLSearchParams();
formData.append('Input1', '');
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData.toString()
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});

Здесь важно то, что Input1 установлен в пустую строку ''. Но при отправке пустой строки ASP.NET Core не устанавливает соответствующее свойство в пустую строку. Вместо этого он устанавливает его в значение null.

Это можно исправить несколькими способами.

Для конкретной конечной точки
Использовать атрибут DisplayFormat:
public class MyForm
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Input1 { get; init; }
}


Глобально
Написать реализацию IDisplayMetadataProvider, которая устанавливает ConvertEmptyStringToNull в false для всех строковых свойств:
public class EmptyStringDisplayMetadataProvider :
IDisplayMetadataProvider
{
public void CreateDisplayMetadata(
DisplayMetadataProviderContext context)
{
if (context.Key.ModelType == typeof(string))
context.DisplayMetadata
.ConvertEmptyStringToNull = false;
}
}

Использование:
// …
builder.Services.Configure<MvcOptions>(opts =>
{
opts.ModelMetadataDetailsProviders
.Add(new EmptyStringDisplayMetadataProvider());
});
// …


Источник: https://josef.codes/dont-let-asp-net-core-set-empty-strings-to-null-when-posting-forms/
👍21
День 2136. #Оффтоп
async2 — Завершение Эксперимента с Асинхронностью в .NET. Начало

Команда .NET работала над новым экспериментом под названием async2, который представляет собой новую реализацию шаблона async/await, разработанную для большей эффективности и гибкости, чем текущая реализация. Он начался с зелёных потоков и закончился экспериментом, который перемещает async и await в среду выполнения. Рассмотрим путь async2 и то, чем закончился эксперимент.

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

В настоящее время для потоков и в некоторой степени для async/await создаётся новый стек. Вы можете легко увидеть это в IDE, при отладке (см. картинку).

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

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

Это привело к выводу, что зелёные потоки — неверный путь для среды выполнения .NET, и породило эксперимент async2.

async2
Очевидно, это просто кодовое имя. Целью эксперимента было переместить async и await в среду выполнения. Главной мотивацией этого было сделать async более эффективным и гибким. Поскольку async уже используется как идентификатор в C#, команда решила использовать async2 в качестве кодового имени для эксперимента. Если эта штука когда-нибудь попадёт в среду выполнения, она будет называться async — то есть это будет замена текущей реализации async. В следующем посте разберём, что же это такое.

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

Источник:
https://steven-giesel.com/blogPost/59752c38-9c99-4641-9853-9cfa97bb2d29/async2-the-net-runtime-async-experiment-concludes
👍25
День 2137. #Оффтоп
async2 — Завершение Эксперимента с Асинхронностью в .NET. Окончание

Начало

async — функция компилятора
Текущая реализация async и await — это функция компилятора. Компилятор генерирует конечный автомат для async -метода. Среда выполнения ничего не знает об async и await. Нет никаких следов ключевого слова async в IL или в JIT-скомпилированном коде. И вот с этого начался эксперимент, подробно описанный здесь.

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

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

Сегодня AsyncLocal используется для хранения данных, которые передаются с логическим контекстом вызова. Они копируются в новый контекст. Тем не менее, если функция глубоко в стеке вызовов изменяет значение AsyncLocal, вызывающий метод не увидит обновлённое значение, его увидят только методы ниже по логическому асинхронному потоку. Например:
await new AsyncLocalTest().Outer();

public class AsyncLocalTest
{
private readonly AsyncLocal<string>
_al = new();

public async Task Outer()
{
_al.Value = "Out";
Console.WriteLine($"Outer: {_al.Value}");
await Inner();
Console.WriteLine($"Outer: {_al.Value}");
}

private async Task Inner()
{
_al.Value = "In";
Console.WriteLine($"Inner: {_al.Value}");
await Task.Yield();
Console.WriteLine($"Inner: {_al.Value}");
}
}

Вывод:
Outer: Out
Inner: In
Inner: In
Outer: Out

В async2 изменения не «откатываются», что приводит к другому выводу:
Outer: Out
Inner: In
Inner: In
Outer: In


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

Полный документ здесь.

Что дальше?
Пока это всего лишь эксперимент, который может привести к замене async через несколько лет. Да, может пройти некоторое время, прежде чем это будет выпущено. А для переходной фазы должен быть interop для async <-> async2. В любом случае - очень хорошая отправная точка. Ждём async2, как юнионов.

Источник: https://steven-giesel.com/blogPost/59752c38-9c99-4641-9853-9cfa97bb2d29/async2-the-net-runtime-async-experiment-concludes
👍20
День 2138. #УрокиРазработки
Уроки 50 Лет Разработки ПО


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

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

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

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

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

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

В своей книге Software Teamwork Джим Броссо рекомендует каждой команде выработать свой контракт. Контракт должен отражать специфику конкретной команды, но быть связан с другими командами и компанией в целом. Он может содержать заявления, подобные этим:
- Для принятия решений на основе консенсуса мы проводим дебаты, уважая при этом мнения друг друга.
- Мы вовремя приходим на встречи и другие мероприятия и соблюдаем повестку дня.
- Члены команды должны стремиться завершить к установленным срокам все задачи, которые взяли на себя.
- Мы можем расходиться во мнениях внутри команды, но вовне представляем единую позицию.
- Мы приветствуем различные точки зрения, чтобы максимизировать коллективное творчество.

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍10
День 2139. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Командная работа. Окончание

Начало

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

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

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

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

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

2. Попробуйте перечислить предпринимаемые руководителями или членами команды модели поведения и действия, которые усиливают внимание к культуре, ориентированной на качество.

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

4. Насколько хорошо вы понимаете культуру компании? Соответствует ли культура вашей команды культуре компании? Если нет, то что можно сделать, чтобы уменьшить разрыв?

5. Определите любые проблемы (болевые точки), которые можно отнести к недостаткам вашей культуры и того, как люди и команды взаимодействуют между собой. Каковы материальные и нематериальные издержки этих проблем?

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍8
День 2140. #МоиИнструменты
Создаём и Тестируем Устойчивые Приложения в .NET с Помощью Dev Proxy
При создании приложений, подключающихся к API, мы обычно фокусируемся на том, чтобы приложение работало. Но что, если API работает медленно, возвращает ошибки или становится недоступным? Сложно смоделировать, как ваше приложение будет справляться с этими сценариями, если вы не контролируете API, с которыми вы интегрируетесь.

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

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

Dev Proxy — это веб-прокси, который вы запускаете локально на своей машине разработки. Перед запуском вы настраиваете его для отслеживания запросов на определённые URL. Затем определяете, как он должен обрабатывать эти запросы: возвращать предопределённый ответ, выдавать ошибку, задерживать ответ или имитировать ограничение скорости или другие поведения? Когда вы запускаете Dev Proxy, он регистрируется как ваш системный прокси и перехватывает все запросы, которые соответствуют настроенным вами URL. Затем он применяет определённые вами поведения. Ваше приложение не знает, что оно не общается с реальным API. Оно просто получает ответы. Это отличный способ проверить, как ваше приложение обрабатывает различные поведения API.

По умолчанию Dev Proxy имитирует ошибку в ответ на запрос с вероятностью 50%. Если запрос не возвращает ошибку, Dev Proxy передаёт его реальному API.

Как улучшить устойчивость приложения для обработки сценария с ошибкой API? Во-первых, мы должны рассмотреть возможность перехвата исключения API и отображения его в удобном для пользователя виде. Это поможет обрабатывать как ошибки API, так и, например, ограничения частоты запросов (ошибка 429 Too Many Requests). Мы также должны рассмотреть возможность обработки ошибки 429 отлично от остальных ошибок, чтобы гарантировать, что приложение корректно сделает паузу и даст API время на восстановление.

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

Подробный пример работы с Dev Proxy рассмотрен в этом видео.

Источник: https://devblogs.microsoft.com/dotnet/build-test-resilient-apps-dotnet-dev-proxy/
👍10