вторник, 8 декабря 2015 г.

Почему стоит заглядывать на кладбище или Систематическая ошибка выжившего


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

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

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

За каждым популярным автором стоят сотни других людей, чьи книги не продаются. За каждым из них, в свою очередь, стоят сотни тех, кто не смог найти издателя. За каждым из этих – сотни тех, чьи книги пылятся в столе. За каждым из этих – сотни тех, кто не дописал свою первую книгу до конца. И за каждым из таких уже стоят сотни людей, которые мечтают когда-нибудь написать книгу. А вы слышите только об успешных авторах, многие из которых на сегодняшний день издают книги на свои собственные деньги, и не в состоянии определить, насколько маловероятен литературный успех. То же самое у фотографов, предпринимателей, художников, спортсменов, архитекторов, лауреатов Нобелевской премии, телеведущих и победительниц конкурсов красоты. СМИ не интересуют раскопки кладбищ в поисках неудачников. Чтобы устранить систематическую ошибку выжившего, копать придётся вам.

Ошибка выжившего ждёт вас в денежных вопросах: вот, ваш друг начинает предпринимательскую деятельность. Вы его потенциальный инвестор или партнёр и чувствуете: это может быть новый Гугл. Может, удача на вашей стороне. Но реальность в том, что наиболее ожидаемый результат – это то, что компания даже не стартует. Второй наиболее вероятный исход – банкротство в ближайшие 3 года. Из тех компаний, которые переживают первые три года, большинство никогда не вырастают более, чем до десяти сотрудников. Что, получается нужно никогда не рисковать тяжело заработанными деньгами и никогда не рисковать увольняться с основной работы? Да нет, не обязательно. Но вы всегда должны распознавать ошибку выжившего, искажающую вероятность успеха, как приближающая линза.

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

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

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

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

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

пятница, 28 августа 2015 г.

Гибкая методология разработки и правдоподобное отрицание

– За последний месяц он побеседовал с каждым из своих учеников. Своего рода передача последней крупицы мудрости.
– И какой же последней крупицей мудрости он поделился с тобой? – спросил я. – Ну, то есть если это не секрет… или не что-то такое, слишком личное.
Энея улыбнулась:
– Напомнил, что заказчик непременно заплатит вдвое больше, если сообщать о дополнительных расходах постепенно и только после того, как будет заложен фундамент и конструкция начнет обретать форму. Он сказал, что тогда отступать уже некуда, и клиент не сорвется с крючка.
Дэн Симмонс, Восход Эндимиона.


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

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

Например, приходит человек и говорит, что ему нужен проект, который будет делать то и это, и будет стоить шестьдесят тысяч долларов. А мы рассказываем ему, что мы работаем по гибкой методологии. Это когда мы придумываем список задач, так, что после выполнения их всех получится проект. Потом оцениваем, сколько времени займёт каждая из них. Потом выбираем задач на первую итерацию (допустим, неделю) и делаем. Заказчик проверяет нашу работу, оценивая результат выполнения каждой из задач. Если что-то недоделано / сделано не так, просит переделать. В итоге, как мы это называем, "принимает" задачу, говоря, что тем. что мы сделали в рамках этой задачи, он доволен. Потом выбираем задач на вторую итерацию. Смыть, повторить.

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

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

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

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

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

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

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

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

Дисклеймеры:

  1. Может показаться, что я говорю это так, как будто это что-то плохое. Это не совсем так. Я понимаю, что разработчику гораздо комфортнее при такой системе, и что я разработчик я тоже понимаю. Но чем-то от этого попахивает. Наверно тем, что сначала заказчику вообще оценивают проект целиком, а не говорят сразу, что он получит неизвестно что неизвестно когда.
    Хорошее резюме из комментариев: "
    Фактически, используя аджайл и оценив матерость заказчика на этапе собеседования, можно сказать проект провалится или нет. Получается, что исполнитель заранее подписывается на проект, который в итоге окажется провальным и в этом несомненно будет виноват заказчик, потому что исполнитель матерый, а заказчик -- нет."
  2. Нет, я не знаю, как лучше. Фиксированная цена звучит честнее, но там возникают проблемы с определением понятия "сделано".
  3. Да, я знаю, что при классическом "водопаде" тоже проваливаются проекты. Но там я могу увидеть шанс, что проект состоится, если команда сделает всё правильно. А здесь посредством дисперсии ответственности любая команда не может обеспечить успешную сдачу проекта в срок – только дисциплина заказчика может дать шанс на это. С другой стороны в провале "водопадного" проекта всегда виновата команда, а в провале "гибкого" – заказчик.


Ссылки:

  1. Правдоподобное отрицание.
  2. Гибкая методология разработки.
  3. Экстремальное программирование.
  4. Каскадная модель.
  5. Песни Гипериона (эпиграф).

понедельник, 27 апреля 2015 г.

Rails authentication routing constraints considered harmful

Давно не писал в блог, а надо. Попробую писать почаще.

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

Сегодня мы поговорим о реализации аутентификационных запретов посредством ограничений в маршрутном файле. Уф. В общем, о authentication routing constraints.

Проблема

Реализация аутентификации в приложении требует способ задания запретов. Способ сказать в приложении "а вот сюда можно только залогиненным пользователям".

Решение


Каждая библиотечка аутентификации в мире Рельсов, работающая на уровне контроллеров, считает своим долгом предложить своему пользователю routing constraint. Примеры:


  • Devise
    authenticated :user do
      resources :posts
    end
  • Clearance
    constraints Clearance::Constraints::SignedIn.new do
      resources :posts
    end
  • Monban
    constraints Monban::Constraints::SignedIn.new do
      resources :posts
    end
Ну, вы поняли. 

Работает это следующим образом – роутинговая система Рельсов позволяет на маршрут задавать условие, при невыполнении которого маршрут просто не сработает. Пример из Рельсов: 

class BlacklistConstraint
  def initialize
    @ips = Blacklist.retrieve_ips
  end

  def matches?(request)
    @ips.include?(request.remote_ip)
  end
end

Rails.application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: BlacklistConstraint.new
end

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

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

Почему плохо


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

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

А вот routing constraints именно что возвращают 404. И это очень неудобно для пользователя. Потёр он сессию в браузере, зашёл на привычную страницу и увидел "Страница не найдена". Вдобавок, "Страница не найдена" в Рельсах по умолчанию не содержит навигации, поэтому, нужно опять что-то набирать в адресной строке и пользователь совершенно не понимает, что происходит и куда он попал.

Заказчик просит это исправить, вы разбираетесь в проблеме, убираете routing constraints и раскидываете по контроллерам authenticate_user!.

Что делать?


На это есть два ответа: простой и правильный. Прежде всего, не используйте routing constraints и пишите authenticate_user!. Это как минимум не идёт в разрез со стандартным способом делать вещи, и не доставляет проблем пользователю. Но есть и более правильный путь.

Правильный ответ


Дело в том, что проверка полномочий "гость или залогиненный пользователь" – всего лишь частный случай проверки полномочий в приложении вообще, то есть, авторизации. 

Используйте библиотеку авторизации, такую, как CanCan или Pundit. Везде, всегда не глядя делайте authorize! из этой библиотеки. Большинство из них позволяют потребовать, чтобы authorize выполнялся и бросить исключение, если этого не произошло.

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

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

Пример для CanCan:

  • В контроллере (любом)
    class ArticlesController < ApplicationController
      load_and_authorize_resource
    
      def show
      end
    end
  • Вы можете указать ресурс и не обязаны его загружать:
    class ArticlesController < ApplicationController
      authorize_resource :articles
    
      def show
      end
    end
  • Собственно, в том самом одном файле прав доступа:
    class Ability
      include CanCan::Ability
    
      def initialize(user)
        if user # права залогиненного пользователя
          can :manage, :articles
          # ...
        else # права незалогиненного пользователя (гостя)
          can :read, :home
        end
      end
    end

    Понятно, что вы можете пользоваться CanCan'ом и по назначению, проверяя у уже залогиненного пользователя наличие определённых ролей.
  • Соль: чтобы при несоответствии полномочий незалогиненных пользователей перенаправлять на страницу логина, а если полномочия не соответствуют уже для залогиненного пользователя, показывать "доступ запрещён" (пример для Devise):
    class ApplicationController < ActionController::Base
      rescue_from CanCan::AccessDenied do |exception|
        if user_signed_in?
          redirect_to root_url, :alert => exception.message # доступ запрещён
        else
          authenticate_user! # перенаправление на страницу логина
        end
      end
    end
  • Чтобы обязать себя всегда вызывать authorize:
    class ApplicationController < ActionController::Base
      check_authorization
    end

Вывод


Для аутентификации не нужно использовать routing constraints. Если же всё же хочется отделить проверку полномочий от конкретных контроллеров и вытащить её в отдельный файл, всё встаёт на свои месте, если пользоваться библиотекой для авторизации. Что неудивительно, поскольку это – авторизация.

Дополнительно

  • Routing constraints – штука сама по себе хорошая. Пользуйтесь. Но не для аутентификации.
  • Библиотеки аутентификации, которые предлагают routing constraints – сами по себе хорошие. У них просят, они и предлагают. Вы не пользуйтесь. Они же не только это предлагают.

вторник, 24 марта 2015 г.

Первая RubyConfBY – впечатления

Дорога

Однажды я с тремя моими коллегами – Сашей, Вовой и Серёжей – собрались на на RubyConfBY в Минске. Поднялись мы рано утром в воскресенье, попрыгали в машину и поехали за триста километров послушать умных людей.

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

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

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

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


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


Конференция

Мы заранее знали, что мы опаздываем на первое выступление, но в итоге оказалось, что мы пришли аж на середину второго. Выступление было про статический анализ кода в Руби, средствами специальных гемов для метрик, делали это ребята из JetBrains, соответственно, с позиций разработчиков RubyMine и PyCharm. Что запомнилось, это отвага молодого человека, выходящего на конфереции рубистов, и заявляющего "Я работаю на Питоне, и хотел бы обрисовать разницу между этими языками и подходами". Однако же обошлось без крови.

За ними вышел представитель главного спонсора этой конференции – компании Toptal – и в краткой приветственной речи стал зазывать к себе работать. Ну что делать, кто девушку ужинает, тот её и танцует.

Следующим был небезызвестный в Руби сообществе Божидар Батсов, активно продвигающий свой стандарт форматирования кода в Руби. Тут стоит сказать, что я уже давно хочу, чтобы в Руби сообществе появилось что-то похожее на PEP8 и столь же авторитетное, но думаю, что это безнадёжно. Одна из ценностей Руби, в отличие от Питона, гласит "должно быть много способов сделать это" – и по определению, любые подобные стандарты обречены быть уделом отдельных команд и проектов. А жаль.

На выступление "Rails in production" мы возлагали массу надежд. Однако, оно оказалось некоей версией урока "Как оно в продакшене устроено" для чайников. Вот прямо капитан очевидность у кафедры. Как же я был удивлён, когда вопросы из зала вскрыли в выступающем профессионального и эрудированного девопса и администратора, работавшего с кучей интересных и сложных проблем и инструментов. Жалко, что он решил сделать выступление "Rails in production 101", а не сделать выступление из ответа на любой из этих вопросов.

Выступление "Почему мы увольняемся с работы" порадовало прикольным разбором ценностей agile manifesto.


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

Вторая половина конференции

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

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

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

Но до него пришлось послушать социальное выступление "ребята, давайте жить дружно". Оно было обо всём и ни о чём, и должно было убедить нас участвовать в жизни сообщества – делиться кодом, временем, ходить на конференции и выступать на конференциях. Нашлось место и RailsGirls и SummerOfCode, и многим другим подобным явлениям. В целом было позитивно, бодро и приятно. Запомнилась просьба встать и обнять камеру (Friday hug в воскресенье) ну и самым чистым английским на всей конференции.


Апотоник!

Наконец-то мы дождались звезду конфереции – Ника Саттерера. Для меня было самое полезное выступление на всей конференции, потому что я думал примерно о том же последние две недели.

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

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

Домой

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

Расчёт времени возвращения оказался верным.

Мысли и выводы

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

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

Ну а вообще посещать подобные мероприятия приятно и полезно.

Благодарности

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