Искусство «отключать мозг»


Перевод статьи Дэна Норта - "The Art of Misdirection"

Взгляните, как работает фокусник. Он кидает монетку на ладонь одной руки, сжимает руку в кулак, размахивает кулаком – и резко открывает ладонь. Монетка исчезла. Он улыбается, а вы смотрите на другую его руку, он машет ею, поворачивает ладонью вверх и разжимает. Там тоже нет ничего! И тут он берет вашу руку, сжимает ее пальцы в кулак, затем разжимает – и вот она, монетка!
А сейчас смотрите еще раз. Только теперь – на другую руку. Как только фокусник поворачивает руку, находящуюся сверху, он дает вам возможность увидеть, как монета скользит в ту руку, которая находится снизу, при этом движения почти нет. Затем он открывает пустую руку и перемещает наверх ту, что была снизу. Не отвлекайтесь на очевидное движение. Вместо этого обратите внимание, как он зажимает монетку между средними пальцами. Не смотрите на открытую ладонь, следите за монеткой, которая снова переместилась. И, наконец, попытайтесь почувствовать, когда монетка скользнет в вашу ладонь, именно в тот момент, когда фокусник сжимает вашу руку в кулак. Магия? Конечно, нет. Это – классическая иллюзия.
Магия работает, используя «отключение мозга». Фокусник пользуется вашей слабостью следить за тем, что очевидно, в то время как главное действие происходит совсем в другом месте. Когда очевидное сделано достаточно убедительно, трудно даже представить, что можно посмотреть еще куда-то. Экономисты придумали название для этого стремления смотреть только в одно место: они называют это альтернативной стоимостью. Что бы вы ни делали в эту минуту, оно стоит именно столько, сколько бы стоило любое другое, что вы могли бы сделать в это время. Но вы не учитываете то, другое, потому что в данный момент вы сконцентрировали все внимание на очевидном.
Скрытая стоимость разработки программного обеспечения
Как это можно применить к деятельности по разработке программного обеспечения? Все новые виды деятельности начинаются с доброго совета: «Мы попробовали сделать это (стэндапы, парное программирование, TDD, диаграммы сгорания, итерации, автоматическую сборку) и у нас это работает замечательно. Может быть, и вам стоит попробовать это сделать?»
И к этому добавляют: «Мы это попробовали и мы считаем, что можно брать деньги с людей за то, что мы им показываем, как это сделать. Вы сможете применить это, потому что оно работает. Вот бумага, которая может подтвердить».
Между прочим, если вы пробежали глазами список практик и думаете: «делать такие вещи действительно здорово», тогда я обращаюсь к вам. А где эти методы не работают? А что другое вы могли бы сделать вместо этого? Что мы теряем, выбрав именно это, а не другое действие?
В течение последних пары лет я работал с командами, продуктивность которых превышала все, что я видел ранее. Их методы представляли собой микс «традиционных» ловких приемов и совершенно сумасшедших, нелогичных действий которые ввергали меня в культурный шок. Мне пришлось забыть все премудрости, которым меня учили, прежде чем я смог начать думать, как они.
Попробуйте провести следующий эксперимент, состоящий из двух частей: думайте о действии или методе, который вы обычно используете при разработке программного обеспечения, возможно, о своем любимом действии. Думаете? Отлично, теперь часть первая: Почему вы это делаете? Какие выгоды это вам дает? Возможно, их несколько. Запишите их. Теперь часть 2: Где вы это используете и какие имеются альтернативы? Запишите несколько ЗА и ПРОТИВ для каждой. Я подожду.
Думаю, выполнение второй части оказалось более сложным. Если вы находите, в основном, негативные или ироничные аргументы, характеризующие альтернативные методы, вы просто перечислили больше причин выбрать оригинальный метод. Вы не увидели альтернативу. Вы обмануты!
В центре внимания - TDD
В качестве примера я выбрал TDD, поскольку это самый авторитетный из распространенных методов, с которыми я сталкивался, но вы можете применить этот подход к чему угодно. Мы собираемся рассмотреть TDD – действительно рассмотреть его – и увидеть можем ли мы определить, где он работает блестяще, а где – не так уж и полезен.
Если вы приверженец TDD, воспользуйтесь моментом и подумайте, где бы вы не стали использовать его, и чем бы смогли его заменить. Недавно я видел пару человек, задающих этот вопрос в качестве риторического или вызывающе, с иронией, как будто только безумный может принять такое решение – не использовать TDD.
Защитники TDD обычно говорят: «TDD позволяет вам делать постоянные устойчивые изменения». Да, так оно и есть. «TDD делает возможным проектировать на лету». Может быть. «Автоматические тесты работают как регрессионый тестовый набор и не дают вам повторять ошибки». Да, часто так и происходит. «Тесты работают как живая документация». Могут. «ПО, разработанное по TDD, чище и его проще изменить, чем не-TDD». На самом деле я поднимаю одну проблему. Я видел шокирующие базы кодов, полностью сделанные по TDD, и вполне жизнеспособные базы кодов совершенно без тестов.
А теперь давайте-ка взглянем на альтернативную стоимость – компромиссы – каждого из этих заявлений. Каждый из них может быть отдельной статьей. Я просто хочу дать вам настроение для нахождения компромиссов своего рода крепким пинком.
Альтернативная стоимость постоянного, устойчивого изменения
Что не нравится в постоянном устойчивом изменении? Иногда вы можете описать проблему, но не заметить очевидный ответ. Большинство финансовых торговых приложений похожи: вы должны попробовать несколько подходов и посмотреть, как они работают, что-то вроде серии экспериментов. Вы хотите, чтобы каждый эксперимент был не слишком дорогим, потому что хотите попробовать сразу несколько. При использовании TDD каждый вариант, несомненно, будет работать, но стоить это будет дороже, чем просто сделать скетч, чтобы глянуть, насколько это будет хорошо.
Насколько же больше времени затрачивают при использовании TDD по сравнению со скетчами? Вы можете набросать половину идей за время, которое вы затратите на разработку через тестирование всего одной из них. И это еще не все. Результат одной попытки может изменить ваше понимание проблемы, поскольку предлагает потенциальное решение, направляющее вас новым неожиданным путем. Некоторые защитники TDD скажут, что это просто ряд пиков и применять ТDD здесь нет необходимости. Однако я здесь говорю о введении ПО в полной мере в «продакшн», и закреплении успешных экспериментов в качестве рабочего варианта ПО, что в действительности не выполняется.
TDD закрывает от вас конечную цель. Этот способ предполагает, что вы знаете, куда вы направляетесь или, в конце концов, с чего хотите начать. Если же вы представления не имеете, как выглядит решение, такая стратегия нежелательна. Возможно, вам следует не тратить время на решение, пока вы не узнаете о проблеме побольше.
Альтернативная стоимость быстрого проектирования
Иногда правильное архитектурное решение не очевидно. Оно может меняться в перспективе, может быть пересмотрена изначальная предпосылка, чтобы увидеть неочевидную простоту сквозь довлеющую сложность. TDD – это путь возрастающих изменений и улучшений. Это отлично при поиске локальных максимумов, но наилучшее решение может потребовать радикального пересмотра. Альтернативная стоимость в данном случае – это то, что мы будем пойманы в ловушку локального максимума, и упустим бОльшую победу. В терминах Lean – это разница между kaizen, непрерывным совершенствованием, и kaikaku, резкой трансформацией.
TDD является воплощением kaizen программирования, чтобы блистать, оно нуждается в окружающей среде, где вы можете сделать шаг назад, совершить ментальную прогулку вокруг блока, вернуться назад и, возможно, изменить все. Вы нуждаетесь в панорамном обзоре, «большой картинке» дизайна или архитектуры. TDD вам этого не даст. Однако, четкая реализация сложного поведения принесёт выгоду в случае применения TDD-подхода. Заметьте, я не предлагаю вам не применять TDD вовсе, я советую квалифицировать, где и когда эта технология эффективна.
Вы не пишете ПО ради ПО, вы пишете ПО, чтобы предоставить возможности. В нескольких недавних приложениях мы дошли до точки, когда нам пришлось переписать всю систему, чтобы добавить новую возможность. Это звучит безрассудно, не говоря уже, что расточительно, но то, что мы узнали, попав туда, куда нам было сказано, показало что переписать все заново будет эффективнее – часто в другой технологии – всего или части приложения, содержащего реальную ценность. Одну систему начали разрабатывать в Scala и кусками мигрировали на Java (конечно, ошибочное направление!). Другую начали на Python и портировали на JavaScript и node.js. В то же время ещё одну начали на Python и потом переписали в другое приложение на том же Python. Каждая переделка была произведена после того, как написанное ПО было представлено пользователям или операторам, с учетом их замечаний. В данном контексте не важно, каким был исходный код, написан в лоб или на основании тестов, потому что большую часть его мы выбросим.
Мы не слишком много вкладывали в разработку ПО, не окружали его со всех сторон автотестами, потому что это были бы слишком дорогостоящие упражнения. Но что делать с ПО которое осталось? Мы обнаружили, что можно установить качество уровня TDD постфактум. Мы знаем, как должно выглядеть хорошо организованное ПО, и на этой стадии мы знаем, что мы готовы инвестировать в это ПО: оно доказало свою ценность. И тогда мы начинаем вводить тесты в духе TDD для более сложных или критических частей кода, что приводит нас к рефакторингу, чтобы сделать код более пригодным для тестирования. Это приводит к проработке стыков и подсистем в коде, и дальше к еще большему рефакторингу, и так далее. Я описываю этот метод как Spike and Stabilize (пронзать и стабилизировать): получать нечто, что угодно, в производство, что требует быстрой обратной связи, и инвестировать в то, что уцелеет.
Альтернативная стоимость автоматических тестов
Автоматические тесты дают нам уверенность, что код делает то, что должен. Это предполагает два вопроса: Есть ли иной путь, чтобы в этом удостовериться? И так ли важна эта уверенность? Автоматические тесты хороши для выполнения конкретных частей кода. Сможете ли вы определить ошибки в коде без тестов? Вы не сможете заметить потерянную (пропущенную) или неподключенную кнопку. Если приложение терпит крах на половине пути по причине оборванной выборки данных, что оно должно делать? После того как вы запрограммировали его делать это, вероятно ли, чтобы оно когда либо прекратило это делать? Дадут ли автоматические тесты вам большую уверенность? Что вы можете сделать вместо этого?
Ценность автоматических тестов – это функция многих вещей: критичности кода, последствий того, что Плохие Вещи случатся, стоимость исправления последствий Плохих Вещей, как для репутации, так и для эксплуатации, вероятность того, что вы не выявите его при систематическом использовании в разработке, ваше знакомство с предметной областью. Код на стыках системы зачастую труднее тестировать, чем внутренний код. UI-виджеты, наружные точки интеграции, сторонние сервисы, автоматизация тестов их всех может быть слишком дорогой. Будет ли их ценность оправдывать стоимость? И снова повторю: я не призываю отказаться от тестов, я предлагаю поставить вопрос о компромиссах и решить, имеет ли это смысл. Я видел команды, угробившие недели усиленной работы для создания прекрасных тестовых наборов, которые не давали никакой дополнительной уверенности или обратной связи по сравнению с использованием приложения. Потери для репутации в этом случае были велики: они утратили доверие заинтересованных лиц, которые предпочли бы видеть реализованный функционал. Кроме того, вы должны подбить баланс, понимая, где автоматизация полезна, а где это просто дань традициям.
Альтернативная стоимость тестов, как существующей документации
Есть много видов документации. Полезная документация воспитывает вас. Она рассказывает вам то, что вы не узнали бы никаким иным способом, потому что это неочевидно. Вы хотите узнать, что делает эту систему отличной от других сходных с ней систем, которые вы видели (вы можете также задаться вопросом, почему вы документируете эти выкрутасы, вместо того, чтобы удалить их из системы и снизить уровень сюрпризов для новичков, но это уже совсем другая история).
Некоторые автоматические тесты при чтении похожи на исторические документы, давая нам понимание прошедших времен. Возможно, в одном месте была глупая ошибка, где обновление текущего статуса выстрелило е-мэйлом в back office. Ее сразу же заметили, и разработчики написали тест под названием «не отправлять мэйл в бэк офис при обновлении текущего состояния». И что? Конечно же, не отправляет. Но через некоторое время накапливается много таких «конечно же нет» тестов, увеличивается уровень «шума» в сигнале, и таким образом становится труднее найти действительно важную живую документацию. Живая не значит – полезная. Наблюдение за живой документацией - автоматизированными тестами - такая же важная деятельность, как и курирование любой другой документации. Слишком часто это упускают из виду.
Выводы
Хотя мы в этой статье заостряли внимание на TDD, выявление альтернативной стоимости и компромиссов важно при использовании любых практик и активностей. Я рассматривал TDD как наиболее полезный и важный метод разработки, в некоторых ситуациях он работает блестяще, тогда как в иных является помехой. Так что не следует сходу принимать все на веру, надо вместо этого «включать мозг» и искать компромиссы, потому что они есть, неважно, видите ли вы их или нет. И если вы научитесь определять их, волшебство сработает.
Chief technology officer "Один Сервис.ВЦ" - Денис Олейник