вторник, 19 февраля 2019 г.

Я выгорел?

Или почему я намного меньше пишу в блогах.

Мне недавно задали два вопроса: 1) не выгорел ли я, поскольку стал совсем мало блогать, и 2) стоит ли вести блог вообще и какие в этом преимущества?

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

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

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

Хоть аналогия и не идеальна она прекрасно иллюстрирует главную сложность в жизни: поиск баланса между разными аспектами жизни и правильное распределенные нашего времени и энергии.

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

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

Когда у нас появляется наша собственная семья распределение энергии меняется.

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

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

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

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

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

Рассмотрим донорство органов в Германии и Дании. Эти страны достаточно похожи с точки зрения уровня жизни и культуры, но в Германии донорами органов становятся на порядок меньше людей, чем в Дании. Вопрос: почему? Дело в том, что в Дании донорство осуществляется по принципу opt-out, а в Германии – по принципу opt-in. Т.е. в Дании нужно отказаться от донорства, а в Германии – наоборот, с ним нужно согласиться.

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

Теперь давайте возьмем такое когнитивное искажение как «якорение» (anchoring). Вот небольшой пример. Давайте проведем опрос о том, в каком возрасте умер, ну, не знаю, Ницше. И для первой группы вопрос будет таким: «В каком возрасте умер Ницше, при учете, что он пережил свое тридцатилетие?», а второй – «В каком возрасте умер Ницше, при учете, что он не дожил до 100 лет?». Мы увидим, что ответы будут сильно отличаться. В первом случае средний возраст будет существенно ниже, чем во втором случае.

Я на личном опыте столкнулся с проблемой якорения. Мы были с семьей в ресторане и семья за соседним столиком, услышав, что мы говорим по-русски, рассказала о возможной скидке в 10% с чека, если мы зачекинимся на фейсбуке. Мы их поблагодарили, но «трюком» не воспользовались. Но в момент выписывания чаевых, вместо 20% я неосознанно выписал 10%.

Якорение часто проявляется во время планирования задач. Если один из разработчиков первым сказал, что фичу можно сделать за 2 дня, то все будут отталкиваться от этой «оценки» и будут стараться лишь ее скорректировать. Именно с этим борется planning poker, когда каждый член проекта записывает свою оценку на листочке и лишь когда все готовы, «карты вскрываются».

Напоследок, мы можем взять экономическое понятие – sunk cost fallacy. Идея этого явления в том, что чем больше мы вкладываем сил и средств во что-то, тем тяжелее нам быть по этому поводу объективным. Это явление проявляется на уровне человека, который спорит, что старый язык программирования или технология все еще лучшее средство для решения всех проблем. На уровне команды, которая уперлась рогом в текущие архитектурные ограничения и не в состоянии сделать шаг назад и посмотреть на проблему с другой точки зрения. Или на уровне организации, которая не может уйти от старой бизнес-модели, которая уже не применима.

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

Мне очень понравилась идея изучения чего-то нового кусками по 20 часов (The first 20 hours – how to learn anything): ты берешь какую-то тему, находишь вменяемые источники по ней и изучаешь ее 20 часов, применяя всякие там deliberate practice и т.п.

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

Ну а теперь мы можем вернуться к вопросу выгорания. Так что же такое?

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

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

вторник, 12 февраля 2019 г.

Не очень занимательный C#

Я как-то не особо лезу со своими комментариями к другим людям, а уж тем более к постам на хабре. Но вчера вот листал RSS-ленту и увидел интригующее название поста – "Занимательный C#. Пять примеров для кофе-брейка".

Итить, думаю, дай-ка зайду, посмотрю, что да как.

И вот первая загадка – что выдаст следующий код:

using System;

public struct SDummy : IDisposable
{
    private bool _dispose;
    public void Dispose() => _dispose = true;

    public bool GetDispose() => _dispose;

    private static void Main(string[] args)
    {
        var d = new SDummy();
        using (d)
        {
            Console.WriteLine(d.GetDispose());
        }
        Console.WriteLine(d.GetDispose());
    }
}

 

Ну, думаю, ок. Странно начинать с изменяемых структур и особенностей блока using, ну, ничего.

Открыл объяснение, а в нем говорится, что причина странного поведения в упаковке, дескать. Компилятор зовет Dispose метод через каст: ((IDisposable)myStruct).Dispose(), ну а каст структуры к интерфейсу, как известно, приводит к упаковке.

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

Я добавил комментарий, после чего началось небольшое обсуждение. Дескать, сам Эрик "уже давно в фейсбуке работает" Липперт писал, что упаковка в блоке using быть должна и компилятор нарушает спеку и все такое... (хотя сегодня это и не так).

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

Что в этом плохого?

Структуры в C# имеют две особенности – они являются "значениями", и могут располагаться напрямую в памяти контейнера (в стеке, регистрах и напрямую в других объектах).

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

Второе же (место жизни структуры) может привести к упаковке т.е. к созданию копии структуры в куче.

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

Вот, например, семантика значения и защитные копии стали гораздо более распространенной бедой с выходом C# 7x с их модификаторами ‘in’ и возвратом по неизменяемой ссылке (readonly refs) (вот, например, много буков по этому поводу - The ‘in’-modifier and the readonly structs in C#).

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

Упаковка же происходит совсем в других местах, при кастах к объектам/интерфейсам и в более экзотических случаях, типа при вызове методов из System.Object или System.ValueType (когда, например, Equals/GetHashCode не переопределены). А проявляется она путем увеличения давления на сборку мусора, что может аукнуться за счет тормозов сборщика мусора.

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