.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2049. #юмор
👍17👎2
День 2050.
Извините за повтор, но есть хорошие новости.

DotNext 2024 стартует уже сегодня.
Я выступаю в 10:30 по Москве с докладом «Что нового в .NET 9». А посмотреть мой (и ещё несколько) докладов можно абсолютно бесплатно в рамках Community Day. Всё, что нужно – это зарегистрироваться.

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

До встречи!
👍53
День 2051. #Testing
Введение в WebApplicationFactory. Начало

Сегодня рассмотрим, что такое WebApplicationFactory и как она помогает в тестировании.

Мотивация
Все мы знаем про юнит-тесты, интеграционные тесты и End-to-end тесты. Чёткого разграничения нет. Спросите 10 разработчиков, и они дадут вам 10 разных определений. Но несомненно, что есть код, если модули кода, и их как-то надо тестировать.

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

Преимущества
1. Если контракт поменяется (как в WebAPI), тест не пройдёт.
2. Вы тестируете всю цепочку и пишете тесты с точки зрения конечного пользователя (не полагаясь на технические детали).
3. Вы получаете хороший обзор ваших функций, таким образом создавая «живую документацию» своего кода.

WebApplicationFactory
Это упрощённый сервер в памяти, который запускает ваш WebAPI ASP.NET Core. Она применит все настройки приложения (включая application.json), DI и будет работать с реальной БД (если не настроено иначе).
Вот простейший минимальный API для примера:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/", (HelloRequest request) =>
$"Hello {request.Name}!");

app.Run();

И простая запись для запроса:
public record HelloRequest(string Name); 


Настроим тестовый проект (xUnit, nUnit, MSTest – какой хотите).
Добавим NuGet-пакет Microsoft.AspNetCore.Mvc.Testing и ссылку с тестового проекта на основной.

Теперь можно написать код настройки тестов. В xUnit используется конструктор:
public class MyApiTests :
IClassFixture<WebApplicationFactory<Program>>
{
public void MyApiTests(
WebApplicationFactory<Program> factory)
{
}
}

Здесь IClassFixture<WebApplicationFactory<Program>> используется, чтобы указать xUnit передать WebApplicationFactory<Program> в конструктор.

Замечания:
1. Называйте классы и методы тестов как модули и их функции, которые вы хотите протестировать. Это важно, если вы хотите иметь живую документацию внутри своего кода.
2. Этот код не скомпилируется. Причина в том, что класс Program является внутренним (internal). В минимальных API больше нет метода Main, компилятор создаёт его за вас. Но теперь там используется модификатор internal. Одним из возможных исправлений было бы добавление InternalsVisibleTo, но это влечёт за собой много исправлений. Тогда тестовые методы также должны быть внутренними. Но внутренние методы не могут быть выполнены тестовой средой. Есть более простой «хак»: с помощью partial класса сделать класс Program публичным. Добавьте в конец файла Program.cs:
public partial class Program
{
}

Не идеально для минимальных API, но просто.

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

Источник:
https://steven-giesel.com/blogPost/cd62475b-2c7d-4ce2-bd97-9670f91ebac8/introduction-to-webapplicationfactory
👍18
День 2052. #Testing
Введение в WebApplicationFactory. Окончание
Начало

Теперь добавим тест. Для простых сценариев можно использовать HttpClient.
public class MyApiTests :
IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient client;

public MyApiTests(
WebApplicationFactory<Program> factory)
{
client = factory.CreateClient();
}

[Fact]
public async Task
PassingNameShouldReturnWelcomeMessage()
{
var resp =
await client.PostAsJsonAsync("/", new
{
Name = "Jon Smith"
});

Assert.True(resp.IsSuccessStatusCode);
var content =
await resp.Content.ReadAsStringAsync();
Assert.Equal("Hello Jon Smith!", content);
}
}

Почему мы передали анонимный объект, а не HelloRequest (он ведь публичный)? Несколько причин:
1. Он публичный, но не чтобы использовать в тестах. Помните, мы являемся пользователем API. Пользователь не знает внутреннего представления наших доменных объектов.
2. Тесты не должны пострадать, если мы поменяем доменную модель.
3. Мы также тестируем сериализацию и десериализацию.
Поэтому анонимный объект идеален.

Конфигурация
Есть много настроек WebApplicationFactory, которые можно сделать в конструкторе. Рассмотрим основные:
public MyApiTests(
WebApplicationFactory<Program> factory)
{
this.factory = factory.WithWebHostBuilder(
builder =>
{
// Изменить настройки из application.json
builder.UseSetting(
"ConnectionString",
"file=:memory:");

// Если в appsettings.json объект:
// MyObject {
// MyProp: 123
// }
// Используем нотацию ":"
builder.UseSetting("MyObject:MyProp", 234);

// Изменить среду и загружать
// настройки из appsettings.tests.json
builder.UseEnvironment("tests");

// Перенастроить сервисы
builder.ConfigureServices(
services => …
);
});
}


Источник: https://steven-giesel.com/blogPost/cd62475b-2c7d-4ce2-bd97-9670f91ebac8/introduction-to-webapplicationfactory
👍16
День 2053. #TipsAndTricks
Объединения по Нескольким Столбцам в Entity Framework

Я столкнулся с очень раздражающей «проблемой» с объединениями LINQ (left join) в Entity Framework.

Исходный запрос
var query = dbContext.Entity.Where(...);

var result = from e in query
join j in dbContext.JoinEntity
on new { e.Id, e.OtherId }
equals new { j.WId, j.OtherId } into group
from g in group.DefaultIfEmpty()
select new { e, g };


Этот запрос приводит к неприятной ошибке компилятора:
The type arguments cannot be inferred from the query. Candidates are: …
(Типы аргументов не могут быть выведены из запроса. Кандидаты: …) далее очень длинный текст.

Решение
Проблема в том, что я использовал несколько столбцов для объединения, но ВСЕ они должны иметь одинаковое имя, иначе компилятор не сможет вывести типы аргументов. Поэтому простое решение — что-то вроде:
var result = from e in query
join j in dbContext.JoinEntity
on new { WId = e.Id, OId = e.OtherId }
equals new { WId = j.WId, OId = j.OtherId } into group
from g in group.DefaultIfEmpty()
select new { e, g };

То есть оба столбца должны иметь одинаковое имя в анонимных типах. И да, можно было просто написать чистый SQL, но это отдельный вопрос.

Источник: https://steven-giesel.com/blogPost/78753461-d80f-4f81-9b4d-6484066aa43e/linq-joins-on-multiple-columns-in-entity-framework
Автор оригинала: Steven Giesel
👍15
День 2054. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 23. При планировании работ нужно учитывать «силу трения». Начало

Менеджер: «Вы сейчас тестируете один проект, но их на очереди несколько. Сколько времени вы тратите на тестирование этого проекта?»
Тимлид: «Около восьми часов в неделю».
Менеджер: «Тогда вы могли бы тестировать пять проектов в неделю».


Есть ли ошибки в рассуждениях менеджера? 40/8=5. На первый взгляд логично. Но здесь не учтены многие факторы, сокращающие время, которое люди могут потратить на работу над проектом в течение каждого дня. Есть разница между количеством рабочих часов и временем, фактически потраченным на выполнение работы. Это лишь один из множества факторов, которые должны учитывать и руководители проектов, и отдельные члены команды при переводе объёмов или усилий в календарное время.

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

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

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

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

2. Выработайте привычку решать только одну задачу в течение дня, на протяжении которой не будете отвлекаться на звонки или сообщения.

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

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

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

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

Урок 23. При планировании работ нужно учитывать «силу трения». Окончание

Начало

Эффективные часы
На работе время утекает в самых разных направлениях: встречи, видеочаты, ответы на e-mail, обзоры кода и т.п. Удалённая работа из дома сопряжена с риском отвлечения на другие занятия, более увлекательные, чем работа над проектом. Даже работая 40 часов в неделю, вы едва ли будете тратить все их до минуты на проект.

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

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

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

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

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

Руководители редко учитывают влияние времени, потерянного из-за чрезмерной многозадачности. Руководитель любит перераспределять рабочее время людей между задачами: 50% сюда и 50% туда, или 50, 25 и 25. Но, когда наступает время подвести итоги, он забывает о процентах и думает, что все в команде заняты полный рабочий день, а потом удивляется тому, как много времени тратится «впустую». Кроме того, работа в нескольких командах означает необходимость тратить больше времени на собрания и меньше — на программирование.

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 4.
👍10
День 2056. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 5: Регистрация
Регистрация — это способ для вашего кода выполнить метод обратного вызова (callback) немедленно при запросе отмены. Этот метод может выполнить некоторую операцию (часто вызов другого API) для отмены асинхронной операции.

Внимание: из-за множества способов вызова обычно рекомендуется, чтобы callback-методы не выдавали исключения.

Как регистрировать
Ваш код может зарегистрировать callback-метод с любым токеном отмены, вызвав его метод Register. Callback-метод вызывается, когда (если) токен отменяется. Метод Register возвращает регистрацию токена отмены (структуру IDisposable).

Практически все асинхронные API в .NET поддержку отмены через токен, поэтому код ниже в качестве примера использует устаревший WebClient, который не принимает токена отмены:
async Task DownloadAsync(CancellationToken ct)
{
using var wc = new WebClient();
using var ctr =
ct.Register(() => wc.CancelAsync());

await wc.DownloadStringTaskAsync(
new Uri("http://www.google.com"));
}

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

Состояние гонки
Что произойдёт, если токен отмены будет отменён примерно в то же время, когда зарегистрирован обратный вызов?
Если callback-метод добавляется к токену отмены, который уже отменён, то он немедленно и синхронно вызывается.

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

Внимание: синхронные вызовы callback-методов
Как обсуждалось в запросе отмены, источники токенов могут быть отменены путём вызова метода Cancel. Важно отметить, что любые зарегистрированные обратные вызовы немедленно (и синхронно) запускаются методом Cancel до того, как он вернёт управление. Это может стать источником взаимоблокировок или другого неожиданного поведения. Это неудобно, поэтому в .NET 8.0 был добавлен метод токена CancelAsync, который вызывает обратные вызовы отмены в потоке из пула.

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

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

Источник:
https://blog.stephencleary.com/2024/08/cancellation-5-registration.html
👍15
День 2057. #Карьера
Категории Руководства в Технических Командах. Начало

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

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

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

Важнейшим навыком является прогнозирование (как в масштабе команды, так и в масштабе компании), т.к. расстановка приоритетов сводится к вопросу: «Какой эффект будет от реализации нашей командой этого проекта?» Также важно уметь хорошо доносить эти прогнозы, приоритеты и цели команды до других заинтересованных сторон.

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

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

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

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

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

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

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

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

Источник:
https://www.benkuhn.net/leadcats/
👍5
День 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
👍29
День 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
👍28👎2
День 2067. #УрокиРазработки
Уроки 50 Лет Разработки ПО


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

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

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

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

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

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

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

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

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