C# Short Posts 🔞 – Telegram
C# Short Posts 🔞
250 subscribers
111 photos
4 videos
151 links
Здесь я, Дима Афонченко @Undermove1, публикую короткие заметки о разработке (и около). Я не претендую на правильность высказываний и открыт к дискуссиям, исправлениям и конструктивной критике. С любыми деструктивными вещами можно приходить в комменты)
Download Telegram
Мутантное тестирование! Протестируй свои тесты, детка.
Блин, меня просто распирает от крутости этой фичи. В общем, если вы пишете по TDD, то знаете, что там самое главное – написать в итоге такие тесты, чтобы они были точно корректными.

А как, блин, понять, что они корректные? Раньше так мог только гуру разработки. Так сказать, “с высоты своего опыта”. А сейчас появился инструмент под названием Stryker.

Очень забавное название, учитывая что это тулза для мутантного тестирования 😊
Для непосвященных Страйкер – это чувак, который заделал Росомаху. Не в смысле, как батя сына, а в смысле, как ученый мутанта с металлическими костями.

Ладно, я как всегда растекаюсь мыслей по древу (пиздобольствую), вот суть.
Эта штука берет код ваших тестов, видит метод, который ваши тесты тестируют и начинает этот метод ломать разными способами. Если при поломке кода все ваши тесты упали, то все ок, значит вы учли все сценарии. Но если нет… Значит вы дятел 🦆Шучу, не дятел 😎

Рассмотрим пример. У вас есть вот такой метод:
public static int Subtract(int a, int b)
{
return a - b;
}

Вы написали вот такой тест для него:
[Test]
[TestCase(5,5,0)]
public void Test1(int a, int b, int expected)
{
var result = TryStrykerCalc.Subtract(a, b);

Assert.AreEqual(expected, result);
}

Ну очевидно, что тут не все тест-кейсы предусмотрены, но давайте посмотрим, что скажет страйкер.

1. Ставим его себе на тачку: dotnet tool install dotnet-stryker --global

2. Запускаем в папке с .csproj файлом теста: dotnet stryker

3. Видим отчет, в котором нам говорят о том, что мы покрыли 50 % всех тест-кейсов. Ну и там даже показано, как он мутировал строку, чтобы тест по идее сломать, но ваш тест не сломался.

4. Понимаем, что нужно накинуть еще один тест-кейс

5. Профит! 💸

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

Короче, попробуйте запустить у себя на проекте Страйкера и посмотрите, что произойдет. Всего две строки и вы знаете, где маленько проебались!
Убойный релиз: MediatR теперь могет в AsyncEnumerable
Не особо давно у MediatR случился релизец, в котором библиотеку научили работать с AsyncEnumerable и это довольно охуенно, друзья.
https://jimmybogard.com/mediatr-10-0-released/

Вообще я фан IAsyncEnumerable и всей этой фичи, поскольку она реально улучшает работу вашего приложения с потоками.

👎А еще всегда удобно делать не вот это говно:

List<object> result = new List<object>();
foreach (var value in someCrazyList)
{
var shit = await RequestShitFromDB(value);
result.Add(shit);
}

return result;

👍А просто и лаконично написать красоту:

foreach (var value in someCrazyList)
{
yield return await RequestShitFromDB(value);
}

Но перейдем к сути релиза.

Раньше вы не могли вернуть IAsyncEnumerable в хэндлере, то есть сделать так:

public async IAsyncEnumerable<WeatherResponse> Handle(WeatherRequest request, CancellationToken cancellationToken)

Но после 6-го января 2022 года (видать на новогодних Джимми Боггарт не оливьеху наворачивал! 🥗) Вы можете делать асинхронные энумераторы в хэндлерах! Выглядит оно вот так. Вид от лица хэндлера, так сказать:

public async IAsyncEnumerable<WeatherResponse> Handle(WeatherRequest request, CancellationToken cancellationToken)
{
for (int i = 0; i < request.DaysCount; i++)
{
// imitation of DB
await Task.Delay(100, cancellationToken);
yield return new WeatherResponse
{
Date = DateTime.Now.AddDays(i),
TemperatureC = Random.Shared.Next(-20, 55)
};
}
}

А вот как это выглядит в контроллере:

[HttpGet(Name = "GetWeatherForecast")]
public IAsyncEnumerable<WeatherResponse> Get([FromQuery]WeatherRequest request)
{
return _mediator.CreateStream(request);
}

Не знаю по каким причинам, но меня трясет от страха, при виде слова Stream. Ни малейшего понятия, почему так. Возможно из-за собесов (ставь лайк если тоже заебали заумные собесы). Но тут можно не бояться, ибо метод CreateStream это тот же Send, только с другим возвращаемым типом. Думаю, не нужно пояснять с каким (IAsyncEnumerable).

🅰️ Итого: Вы спросите меня, а что делать-то, Димас? А вот что! Если у вас в приложении есть MediatR и где-то вы собираете рузльтат, прежде чем отдать его, то выпиливайте эту срань к чертям и меняйте на прекрасный AsyncEnumerable!

PS. А ты поставил, кстати, звезду медиатру на гитхабе? Если нет, то помни, Джимми старался на новогодних ради тебя, возможно не ел оливьеху. Поставь ему звезду, не постесняйся, а он тебе еще апдейтов накидает 🤩 https://github.com/jbogard/MediatR
Вопросы из зала: Могу ли я использовать record’ы для описания конфигурации?
🩳 Коротко: да, но не так просто, ибо у них нет конструктора по умолчанию.

Когда видишь рекорды в первый раз (да и во второй), то задаешься вопросом, а могу ли я использовать для конфигурации?

🤟Это, детектив, правильный вопрос, ведь написать вот так:

public record BotConfiguration(string BotToken, string HostAddress);

💩Гораздо удобнее, чем вот так:

public class BotConfiguration
{
public string BotToken { get; set; }
public string HostAddress { get; set; }
}

Но вот неудобная правда. Если просто попробовать зарегать такую конфигурацию в ASP.NET:

builder.Services.Configure<BotConfiguration>(
builder.Configuration.GetSection("BotConfiguration"));

То при попытке достать конфигурацию:

var botConfig = builder.Configuration
.GetSection("BotConfiguration")
.Get<BotConfiguration>();

😬 Рантайм (угадай заблюренное слово) опиздюлит вас ошибкой:
System.InvalidOperationException: Cannot create instance of type 'TestRec.BotConfiguration' because it is missing a public parameterless constructor.

И в принципе будет прав. Но! Кого вообще устроит такая ситуация? Меня точно не устроит! Поэтому я полез искать самые извращенные решения этой проблемы и нашел, пожалуй не самое извращенное, но доступное только с версии .NET 6.0

Для этого вам понадобится пакет DevTrends.ConfigurationExtensions, у которого всего три звезды на гитхабе (включая мою, разумеется): https://github.com/devtrends-org/configuration-extensions

Этот пакет позволяет сделать такой финт ушами. Зарегать конфигурацию как синглтон:

builder.Services.AddSingleton(builder.Configuration.Bind<BotConfiguration>());

И использовать как синглтон:

var service = app.Services.GetRequiredService<BotConfiguration>();

И я проверил – все работает. Все параметры конфигурации учитываются, то есть, если запустите с параметром Development сборку, то подтянутся Development конфиги.

🅰️Итого: я бы сказал, что пока говорить о конфигах на рекордах скорее рановато, но для пет-проектов под пиво 🍻 попрёт.

P.S. Ну и, разумеется, по старой-доброй традиции, я предложу вам втащить репозиторию. Для вас это клик, а для Paul Hiles это приятная неожиданность.
Как писать интеграционные тесты без InMemoryDB или SQLite?
Этот крутой вопрос поднял мой добрый коллега Паша Кириллин. И я в очередной раз убедился, насколько же я дуболом 🤖.

💩 Обычно, если я хочу написать тесты, которым зачем-то нужна реальная база, то я втупую напишу docker-compose.yaml с конфигурацией этой базы. И каждый раз при запуске дергаю docker compose up. В общем, довольно грубый метод.

А вот Паша посидел разок и задумался, а как бы это сделать автоматизированно, чтобы не пришлось делать docker compose up? И сразу нашел решение в виде такого репозитория: https://github.com/HofmeisterAn/dotnet-testcontainers

Вы только посмотрите, как это работает:

using System.Threading.Tasks;
using DotNet.Testcontainers.Containers.Builders;
using DotNet.Testcontainers.Containers.Configurations.Databases;
using DotNet.Testcontainers.Containers.Modules.Abstractions;
using DotNet.Testcontainers.Containers.Modules.Databases;
using MySql.Data.MySqlClient;
using NUnit.Framework;

namespace TryTestContainers;

public sealed class UnitTest1
{
private readonly TestcontainerDatabase _testcontainers = new TestcontainersBuilder<MySqlTestcontainer>()
.WithDatabase(new MySqlTestcontainerConfiguration
{
Database = "db",
Username = "mysql",
Password = "mysql",
})
.Build();

[SetUp]
public async Task SetUp()
{
await _testcontainers.StartAsync();
}

[TearDown]
public async Task TearDown()
{
await _testcontainers.DisposeAsync().AsTask();
}

[Test]
public void TestMethod()
{
using MySqlConnection connection = new MySqlConnection(_testcontainers.ConnectionString);
using var command = new MySqlCommand();
connection.Open();
command.Connection = connection;
command.CommandText = "SELECT 1";
command.ExecuteReader();
}
}

Если вы, как и я, пропускаете большие блоки кода при чтении (да-да, ты не один такой ленивый, нас как минимум двое 👬), то вот вам текстовое описание происходящего:

1. В переменной _testcontainers мы создаем конфигурацию контейнера, также как мы это обычно делаем в docker-compose.yaml
2. В методе SetUp мы запускаем эти контейнеры. Они запускаются в вашем локальном Docker
3. В методе TestMethod мы подключаемся к базе данных и делаем запрос.
4. В методе TearDown мы их гасим, после всех тестов

Звучит достойно! Но еще более достойно звучит тот факт, что таким же макаром вы можете поднимать RabbitMq, Kafka и еще всякие штуки!

🅰️Итого: Если вам хочется делать тесты на уровне классов, которые ходят в инфраструктурные сервисы, вы можете использовать эту штуку. Но кажется надо посмотреть, как такие тесты поднимать в Github Actions? Как запускать их в параллель? Я попробую ответить на эти вопросы завтра, а вам пореомендую не сцать и тоже попробовать эту штуку!

P.S. Естественно, я въебал звезду на гитхабе этому проекту. И ты тоже не сиди, пойди и въеби звезду! У проекта уже 994 , так что это серьезно!
Проверяем TestContainers в Github Actions

Да уж после долгого перерыва, приятно вернуться в родную гавань. А я наконец попробовал запустить test-containers в Github Actions.

Что же выяснилось. Выянилось, что все работает из коробки! Я ожидал, что нужны будут дополнительные телодвижения, но оказалось, что хватает стандартного запуска тестов.

Пример можно посмотреть тут, но в нем ничего не изменилось, кроме добавления файлов wofrkflow.
https://github.com/Undermove/TryTestContainers/runs/5623573070?check_suite_focus=true

🅰️Итого: dotnet-testcontainers однозначно заслуживает вашего внимания. Если хотите поднимать базы в докере, но не хотите писать docker-compose то это ваш вариант. Думаю, что если захотите параллелизации, то можно придумать какой-нибудь общий сетап для тестов, который будет поднимать контейнеры единожды.
Span: Как быстро уменьшить количество аллокаций? (кликбейт)
📹 Посмотрел недавно на все том же славном канале Ника Чапсаса видос про Span<T>. Рекламируется, как штука, которая позволяет избавиться от лишних упаковок при нарезке массивов. Примерно так:

1️⃣ Вот в этом примере есть упаковка при создании строки dayAsText:

string _dateAsText = "08 07 2021";
string dayAsText = _dateAsText.Substring(0, 2);

2️⃣А вот в этом примере при создании dayAsSpan аллокации нет:

ReadOnlySpan<char> dateAsSpan = "08 07 2021";
ReadOnlySpan<char> dayAsSpan = dateAsSpan.Slice(0, 2);

Потому что dayAsSpan просто ссылается на кусочек памяти, размещенный в dateAsSpan.

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

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

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

🅰️ Так что, на вопрос в заголовке можно уверенно ответить: для web-разрабочтиков не через Span. Но вот в каких-нибудь библиотечных историях, вещь хорошая. А, ну и на собесах (ебучих) теперь вы можете ответить на какие-нибудь каверзные вопросы про этот Span.
Вопросы, которые вам не задают на собесах: Что если заэвэйтить заэвэйченное?
Недавно я знатно объебался при написании кода. Прод не пострадал только благодаря тому, что нагрузка выявила рост потребления памяти и времени ответа. Время ответа выросло в два раза, а память на 20% подросла.

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

Предположим у вас есть такой метод:

public async Task ExecuteAsync(Func<Task> action)
{
await action();
}

Нормальный вариант вызова этого метода выглядит так:

public async Task Bar()
{
await ExecuteAsync(() => Task.Delay(1000));
}

Но вы можете вызвать его вот так:

public async Task Foo()
{
await ExecuteAsync(async () =>
{
await Task.Delay(1000);
});
}

😬И это будет ошибкой, но компилятор об этом никому не скажет и спокойно все прожует. Только вот вопрос, а что же тогда будет?

ℹ️ А будет вот что. Как вы (может быть) знаете async/await при использовании генерирует класс IAsyncStateMachine, в котором менеджерится продолжение работы вашего кода после оператора await. Так вот во втором случае таких классов сгенерируется не один, как в первом случае, а два! Отсюда и рост потребления памяти! Соответсвтенно увеличится и количество исполняемых инструкций после оператора await, что приводит и к замедлению работы метода. Такие дела!

🅰️Итого: не будьте тупыми, как я, и не передавайте асинхронные лямбды в методы с названием Async.

P.S.: А вот и ссылка на диффы между разными вариантами вызовов https://quickdiff.net/?unique_id=84CB0FCA-A15D-7D87-5747-80E13C087198
Нужен ли async await в однострочных методах?
🗣 Третьего дня сели с Пашей Кириллиным перетереть за жизнь и как-то зацепились за предыдущий пост. И соответственно возник такой вопрос. Предположим есть у вас репозиторий, который достает там что-то из базы. вы его можете написать вот так:

public class Repository
{
public Task<string> GetAsync()
{
return Task.FromResult("123");
}
}

Вопрос, нужОн ли здесь async await? Думаю вы уже догадались, что не нужОн. Но вот вопрос, а почему? На самом деле ответ был постом выше – такое решение приведет к генерации лишней стейт машины при вызове метода.

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

Ну и вот диффы для сравнения: https://quickdiff.net/?unique_id=BE81872D-7947-BF93-D398-3B6D0E321A05

🅰️Итого: избегаем async await в однострочных методах. Но, сеньоры, давайте уже признаемся, что мы все давно уже пользуемся этими async await-ами просто потому что они прикольные! Так что если очень хочется, то оставляйте – никто вас не поругает)))
Напишу-ка я про всякое, а не только про C#
Я тут понял, что увлечение C#-ом у меня носит сезонный характер – летом почему-то похуй на новые релизы .NET

А вот к осени и весне почему-то тянет на солененькое заумное.

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

В общем, буду теперь и про это писать. Буду надеяться, что получится хотя бы в течение месяца это делать плюс-минус регулярно.
Удаляйте задачи
И первое, что хочется рассказать – это про задачи в бэклоге. За свою карьеру я видел мнго бэклогов. Больших и маленьких, кривых и прямых, с двумя и десятью колонками. Но все их объединяло одно – команды боятся удалять старые задачи.

Причем удалять их боятся именно разработчики. Бизнесу в общем-то индиферентно (хуекладительно) на то, что творится в бэклоге у этих задротов, но сами задроты свой бэклог стерегут.

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

А вместе с тем: неактуальные задачи мешают видеть, что важно в данный момент.

💡 Пример: пришел в команду. В бэклоге есть задача: "Поправить клиентские логи" Делать ее 5 минут, польза приходит практически моментально. Но выше нее скопилось 56 задач. Из них 38 завели люди, которые уже уволились. Но у задач стоит какой-то высокий приоритет, поэтому они закрывают свежую задачу.

🅰️ Что делать? Удаляем все задачи, что лежат в бэклоге дольше месяца, если не хочется удалять, то берем и сразу делаем. Если сразу сделать не получилось, то удаляем.

Но что происходит на самом деле: при попытке удалить задачу все вокруг взрываются и шипят – ты что, а как же техдолг! Ты его не выплачиваешь! Потом его придется отдавать вдвойне! (летят слюни впереммешку с камнями)

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

В общем, удаляйте задачи.
#бэклог
🔥2
Телега и боты.
Если вы ищете непаханные поля, то боты в телеге это именно они. Во первых их мало, во вторых в них пока нет общепринятых правил для оформления UI. Очень много ботов с хорошими полезными функциями, но с дерьмовым юзабилити.

Так что можно брать существующего бота, улучшать его юзабилити и захватывать рынок!

Кстати из-за того что в этой области пока что нет индустрии - появляются очень прикольные штуки типа текстовых ММО для дружеских чатов! К примеру @toadbot позволяет вам с друзьями растить своих жаб, собирать из них кланы и нападать на других жаб!

Если у вас есть полудохлый дружеский чатик в телеге, то вы можете оживить его с помощью жабок 🐸

Или вот чел сделал текстовый вариант игры Wastelands с другими игроками, преферансом и радиоактивными куртизанками! 🌆

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

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

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

🅰️ Но можно так – дать возможность пройти испытательный срок. При этом сразу поднять зп, но несильно, к примеру на 10% от стандартного повышения. Если человеку комфортно в новой должности и он чувствует себя в ней ок, то выдать полный пакет денег и считать эксперимент удачным, а если нет, то ничего страшного, можно оставить эту слегка поднятую зарплату - так как человек оброс новым опытом за этот срок.

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

Хотя мне, конечно легко распоряжаться деньгами, которых у меня нет 🌚 Но вот такое мыслительное упражнение.
🤸Горизонтальная мобильность.
Короче, я тут прочитал книгу Гуриева "Мифы экономики" и как любой пиздюк, узнавший новое слово, вворачиваю её в разговор по делу и без дела. Вот и сейчас вверну.

Там в этой книге основная мысль, что для того чтобы поднять Россию с колен нужно
1️⃣ Обеспечить справедливое рыночное взаимодействие в каждой из отраслей. Это типа чтобы частное лицо могло подать в суд на корпорацию и имело справедливый шанс выиграть.
2️⃣ Обеспечить населению страны горизонтальную мобильность. Это чтобы чуваки из депрессивных регионов могли свободно гонять по стране в поисках счастья в менее депрессивных регионах (физическая мобильность, она же миграция). Ну и там еще попутно решается проблема моногородов – если в городе закрылся металлургический завод, то оплати людям курсы переквалификации (социальная мобильность)

Первый пункт меня зацепил, но с ним у нас все грустно.

А вот второй пункт я переложил на АйТи. Потому что в АйТи сейчас как мне кажется мало кто вообще озабочен вопросами горизонтальной мобильности. Народ строит всякие ассесменты по уровням Джуниор-Миддл-Синьор-Эльф 80-го уровня и так далее.

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

Чем вообще полезен такой переход? Дохрена чем:
1. К примеру у вас человек застрял на каком-то уровне. Не может он вытянуть дата-сайнетизм на сеньора и все тут. В общем, начал человек кукситься и в конечном итоге скурвился и осатанел. Хочет сменить работу. В обычном случае он от вас уйдет куда-то и унесет пару кило экспертизы, которую успел накопить. А тут вы со своей горизонтальной мобильностью! И он остается с вами! (и пару кило его компетенций тоже)
2. Или вот предположим, что человек для общего развития захотел посмотреть, а чем живут те, кто делают его таски? То ему надо на какие-то курсы для IT идти, платить туда деньги и так далее. А тут вы раз и его на три месяца в ссылку к этим ебаным ойтишникам. И вот он уже через три месяца возвращается как огурчик и спятнцами в подмышках! И все с минимальными потерями.

🅰️У кого же подсмотреть примеры того что это работает? На самом деле горизонтальная мобильность очень хорошо развита у стртапов. Там на начальном этапе развития многие работники – многостаночники. То есть программисты работают продакт-оунерами, продакт-оунеры сис-админами. Из этого всего вырастают вполне себе классыне лидеры, которые разбираются в нескольких обласятх, от чего могут нанимать качественный персонал и в те и другие области. Ну кстати, чем не пример – Билл Гейтс.

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

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

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

Так как же сделать горизонтальную мобильность просто и справедливо?
К примеру у нас есть синьор продакт оунер с зарплатой 160к и он хочет стать джуниор фронтенд разработчиком, где зп, 80к. Какие очевидные варианты у нас есть?

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

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

🅰️Я придумал похожий вариант, как в посте про принцип Питера: Оставляем зп на уровне 160к + 3к за смелость и даем испытательный срок. В случае провала мы получим продакта с подросшей компетенцией в новой области, который лучше понимает проблематику разработки (и еще у него ЗП станет 163к). А в случае успеха мы получим фронтенд разработчика, который может помочь с бизнесовым видением задачи (и тоже с зп в 163к). И то и другое потенциально может не сработать, но если сработает, то может позволить вам создать что-то чего на рынке пока нет.

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

Главный принцип в том, что инициативу необходимо поощрять, независимо от результата, но при этом минимизировать потери на случай неудачи.
1
Так это порт у вас или адаптер?
Не понимаю как можно жить без пет-проектов. Есть вот к примеру такая статья.
https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/
 
Там есть красивая картинка, что мол вот у тебя есть ядро и вот оно такое красивое, только с бизнес-логикой и все такое прочее, а все остальное это порты и адаптеры.

Но как это должно выглядеть в итоге в коде? Что значит вот это ядро? Это папка с таким названием? Или отдельный репозиторий? Или проект? Или класс такой?

Благо есть репозиторий от Джейсона Тейлора, который проливает свет на это дело. Там это все уже как надо расписано: https://github.com/jasontaylordev/NorthwindTraders
 
Круто! Но вот я сел за код и у меня задача – написать телеграм-бота. А Джейсона Тейлора он для веб-приложений. Возникает вопрос. Часть, которая отвечает за взаимодействие с телегой – это часть ядра? Или это уже какой-то порт? А может вообще адаптер? А может еще что-то третье?
 
По итогу вышло, что это на самом деле это зависит от твоего желания.
 
Если ты пишешь чисто телеграм-бота и ты решил, что никаких больше других соц сетей ты поддерживать не хочешь, то это будет частью ядра. 
 
Если ты выяснил, что будут еще какие-то соц-сети, то это будет адаптер, а из ядра тебе надо будет прокидывать порты для него, ну медиатры там всякие.

Короче, я ХЗ как после чтения этой статьи без пет-проекта можно все это прохавать. Может есть кто-то кто может, но мне вот прям никак.
👏1
🐈 Маленький победоносный пет-проект.
Вот пет-проекты это круто, но жутко порой задалбывают. Ты их начинаешь писать, чтобы отвлечься от работы, но потом тебе уже хочтся отвлечься от самих пет-проектов, и ты заводишь новый пет-проект, чтобы отвлечься от предыдущего пет-проекта. Ну а когда тебя задолбает этот пет-проект, то понятно, что ты заведешь новый и так далее.

🅰️ Короче, я придумал выход из этой смачной ситуации! Нужно, чтобы отвлекающие пет-пргоекты были на пару дней всего.

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

Вот так вот просто!

P.S. Начал тут такой пет-проект, а он растянулся на неделю. Вот же сука!
🔥1
Быстрое знание: console.table()
💨 Смотрел тут сторис в инстаграмме и между фотками из чужих путешествий и чужих котов проскочила инфа, что помимо console.log() в браузере можно использовать console.table()

🅰️ Так удобнее смотреть большие объекты. Сначала не поверил. Но! Проверил – работает! Юзайте.

P.S.: Хотя может я один такой не знал 😕
🤯1
😐Ресайленс и самообучение
Расскажу сейчас про охуенную штуку для изучения ресайленса!

Год назад я думал, как нам заятнуть на работу Chaos Testing. Ничего особо не получилось. Но! Удалось посмотреть такую штуку как Simmy https://github.com/Polly-Contrib/Simmy.

Simmy это версия Polly из темной всеенной 🌚. Если в Polly вы настраиваете все так чтобы у вас ничего не упало по итогу, то в Simmy вы настраиваете рандомные падения так, чтобы все деградировало каждый раз по-разному, но чтобы эти деградации выглядели реалистично. (Наконец в этом канале хоть что-то про деградацию! Услада для тех, кто пришел деградировать)

💆 Я если честно не допер, как в итоге это применить в работе. Подумал, что пока туповат. Потом посидели с чуваками на работе, потупили в код этой штуки. Он выглядит кстати вот так:

var isEnabled = true;
var chaosPolicy = MonkeyPolicy.InjectLatency(with =>
with.Latency(TimeSpan.FromSeconds(5))
.InjectionRate(0.1)
.Enabled(isEnabled)
);

В этом коде делается так чтобы 10% ваших запросов проходили с задержкой в 5 секунд.

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

Но! Но! Память об этой штуке всплыла недавно вот в этом О-ХУ-И-ТЕЛЬ-НОМ репозитории https://github.com/bjartnes/bounded-disturbances

Что это за репозиторий такой? Это полноценный готовый к запуску курс по обучению ресайленсу! Если вы каждый раз, как и я тупите, когда думаете о том сколько ретраев вам надо впихнуть в какой-либо запрос. Или просто вы не знаете ничего про Polly, то этот репозиторий это ваш абсолютный БРО!

📶 В нем есть 10 быстрых задач, в которых вам дают ненадежный канал (настроенный как раз с помощью Simmy), дают прометей и задачу – с помощью правильных настроек ресайленса (кол-ва ретраев и правил таймаута) добиться заданного SLO!

И это прям максимально прикладная задача! Это как лабы в универе, если бы все преподы в нем вылечились от маразма!

🅰️ Итого: Быстро берите эту репу, скачивайте проходите все 10 занятий и становитесь богом ресайленса! 👼 Вы никогда больше не будете смотреть на вашего httpClient'a прежними глазами, только ресайленс, только усиление!

P.S.: Ну и въебите звезду этой штуке! Давайте. Это всего лишь клик, а авторам это будет приятно!

#ресайленс
👍2
🕐Задачка: Увеличиваем надежность канала с помощью ретраев.
Окай, я на сотку готов поспорить, что хрена с два вы пойдете смотреть репозиторий из прошлого поста. Да я бы и сам подзабил, будем солидарны в этом.🦥

Но раз уж Магомед не идет к ресайленсу, то я принесу ему ресайленс сам. В виде серии микрозадачек.

Итак первая микро-ЗАДача. РазминОЧКА, так сказать:
💡 Условие: У вас есть нестабильный кАНАЛ (ладно, обещаю, что таких КАЛамбуров больше не будет), который падает в q = 15% случаев.

Ну то есть вы обращаетесь в своем коде к какому-то сервису, и в 15% случаев он возвращает ошибку (Internal Server Error).

Вопрос: Сколько ретраев минимально нужно сделать при обращении к этому сервису, чтобы обеспечить успех работоспособности вашего кода в > 99%?
 
Ответ тут (сразу скажу, что итоговая формула простая, но путь к ней надо будет вспомнить):
Вспоминаем формулу Бернулли:
Pn(k)=Cknpk⋅(1−p)nk=Cknpkqnk.
Ckn=n!/(nk)!⋅k!. -считается вот так

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

После всех упрощений и с учетом, что нам необходимо всего одно попадание она превращается в простую:
1 - q^n, где q - это процент ошибок, а n - количество ретраев.

Итого:
Для одного ретрая вероятность успеха: 1 - 0.15^2 = 0.9775
- маловато будет!
Делаем два: P = 1 - 0.15^3 = 0.9966
- уже подходит!
Попробуем три: P = 1 - 0.15^4 = 0.9995 - ого, аж три девятки! Если ваш SLA нацелен на такое количество девяток, то выбирайте большее!

Но минимально необходимое количество ретраев будет: 2


#ресайленс
3
Задача 2: улучшаем latency своего сервиса с помощью таймаутов
Во второй задаче у нас есть запрос, который в 10% случаев выдает высокую задержку в 1 секунду.

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

Ответ тут (на этот раз без тервера):
По первой прикидке тут все просто – таймаут в 200мс нам точно даст требуемое значение. И это правильное предположение! Но лучше взять с запасом, к примеру в 180 мс. Практика показывает что теоретические значения при столкновении с реальностью являются лишь опорой, а не точными параметрами.

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


#ресайленс
🤯1
🦮 Задача 3: Ускоряем тормозящий канал.
Мы научились работать с ретраями и таймаутами по отдельности. Что ж давайте
скомбинируем эти знания! 🔥

Теперь у нас все так же есть сервис, задержка до которого в 10% случаев составляет 1 секунду.

Но теперь вопрос такой: какую минимальную задержку выбрать и сколько минимально ретраев нужно, чтобы в итоге 95й перцентиль задержек был меньше 200мс и при этом чтобы > 98% запросов возвращали успех?
 
Обратите внимание, что при этом сервис не выбрасывает ошибок, просто иногда подвисает на секунду.

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

Ответ как всегда тут:
Для начала вычислим необходимый таймаут. Это мы сделаем по методу из прошлой статьи. Получится таймаут в 180 мс
 
Этот таймаут будет отсеивать примерно 10% запросов и выбрасывать ошибку TimeoutException. А теперь нам и пригодится формула из первой статьи!
Высчитываем для одного ретрая:
q = 0.1
- процент ошибок
n = 2
- количество попыток получить ответ вовремя (один изначальный запрос + один ретрай)
1-q^n = 1 - 0.1^2 = 0.99
– вполне достаточно по нашим требованиям

P.S.:
Кстати, пока считал тут все, проверял результаты на практике и перепутал
retryPolicy и timeoutPolicy местами. Из-за этого ничего не работало. Так что не путайте политики местами, надо вот так:
Policy.WrapAsync(retryPolicy, timeoutPolicy);
А не так
Policy.WrapAsync(timeoutPolicy, retryPolicy);


#ресайленс
🤔1