В какой-то момент я стал замечать, что пишу более качественный: оптимальный, логичный и читаемый код, но пишу его медленно. По крайней мере, куда медленнее, чем лет 15 назад. Да, на старый код без слёз не взглянешь: всё можно сделать оптимальнее и, по крайней мере, читабельнее. Но код был написан быстро.

Проблема в контроле. Я стал жестче и глубже контролировать код и… стал тратить на этот контроль слишком много ресурсов.

Основной принцип прост. Если контролю подлежит 10-20% случаев, то на контроль надо тратить… 0% ресурсов. Баги и переписывания всё равно будут. Но лучше разделить кодирование и отладку и на время кодирования об ошибках вообще забыть.


Немного теории и аналогий


Выдающийся психолог Лев Семёнович Выго́тский, показал, что действия человека итерационны. И состоят из его действия, контроля над этим действием и исправления ошибок.

Например. Когда ребенок тянется рукой к игрушке – это происходит за несколько итераций:
  • Выдвинуть руку вперед
  • Оценить расстояние до игрушки
  • Еще выдвинуть
  • Проверить тактильный контакт
  • Дать команду пальцам на сближение.
  • И т.д.
Для взрослых пример – движение мышки. Когда мы «тыкаем» в ссылку, то первым быстрым движением подводим курсор в область ссылки, оцениваем положение курсора и доводим до движение конца. Либо дополнительным быстрым рывком, либо чуть менее быстро и более плавно, чем в начале движения. Это получается настолько быстро и привычно, что движение кажется слитным.

Наработка автоматизма – это отключение контроля. Или перевод его в более оптимальные, подсознательные формы.
  1. Контроль по реперным точкам – пример с мышкой. Мы не контролируем движение курсора всегда, мы контролируем его в одной контрольной точке рядом с целью. Мы делаем это очень быстро и на автомате, поэтому движение выглядит слитным.
  2. Подсознательный контроль. Если на раздражитель есть стандартная реакция, то быстро нарабатывается подсознательный ответ. Когда мы промахиваемся по ссылке, мы не останавливаемся и не начинаем анализировать «кто виноват и что делать?». У подсознания достаточно информации, чтобы рассчитать завершающую фазу движения в фоновом режиме (и сразу же отправить на исполнение).
  3. Контроль по прерыванию. Когда движение «набито до автоматизма», то контроль включается, если что-то идёт не так. Например, заглючил драйвер мышки или комп подвис. Расчёт траектории стал сложен. В этом случае мы, включаем речевой аппарат для «слива» эмоций и несколькими сознательными итерациями доводим мышь. Поскольку драйвер глючит редко (а колесики с намотанным на них мусором уже почти история), то и контроль нужен редко.
А самое главное, мы даже и не думаем, что можем промахнуться по ссылке. (Не напоминайте мне про мобильные браузеры).

Цена вопроса


В «Аквариуме» Суворова-Резуна упоминаетcя различие в подготовке спецназовцев и цирковых артистов.

«Я уже грамотен и понимаю, что человек может ходить и по телеграфному проводу над бездонной пропастью. Все дело в психологической закалке. Человек должен быть уверен, что ничего плохого не случится, и тогда все будет нормально. Цирковые артисты тратят годы на элементарные вещи. Они ошибаются. У них нет научного подхода. Они базируют свою подготовку на физических упражнениях, не уделяя достаточно внимания психологии. Они тренируются много, но не любят смерть, боятся ее…»


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

Технология удержании равновесия проста (пусть не на проволоке, где специфичные движения, а, скажем, по узенькой доске). Нужно идти с постоянной скоростью четко ставя ноги. Небольшая ошибка в постановке ноги или отклонении корпуса не страшна. Страшно её исправление. Попытка восстановить якобы потерянное равновесие, приводит к резонансу, каждая новая попытка раскачивает ещё сильнее. Чем выше доска, тем выше цена ошибки, и тем раньше включается контроль. А с ним смертельно опасная «работа над ошибками».

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

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

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

Как писать код, зная всё это?


С теоретической частью закончено. Давайте, не расслабляясь, к практике.

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

Скажете бред? А что какова наша реакция, если код заработал с первой попытки: WTF?!!! Что я не так делаю?!!!

С возрастом опытом мы всё более уверены, что должны ошибаться. И заранее ждем этого. Написали три строчки? Там может (!) быть ошибка. Надо проверить. Нет ошибки? Это как-то подозрительно. Надо покурить и подумать.

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

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

Дисклаймер


Эти рекомендации относятся к опытным программистам.

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

Чем тщательнее вы себя контролируете, тем быстрее нарабатывает автоматизм. По сути именно для этого контроль и нужен… для более эффективной наработки автоматизма. На любой стадии развития.

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

Итак…

Работайте большими итерациями


Разделяйте фазы написания, проверки и отладки. Считайте, что вы не ошибаетесь в коде. По крайней мере пока его пишите, вы не может ошибаться. Вообще.

Старайтесь написать как можно больше кода не останавливаясь, как минимум закончить функциональный блок (Upd: но оставьте время, чтобы тестирование/отладку закончить «сегодня», «до обеда» (до любого большого перерыва), не откладывайте её ненадолго и вообще не откладывайте). Остановка для проверки и отладки кода – это переключение контекста, на которое тратятся ресурсы и сбивается ритм. Проверку кода можно и нужно вынести в отдельную фазу перед отладкой. А отладку начать «сверху вниз». Начните так делать, и вы удивитесь, как часто код может заработать сразу или сразу после исправления синтаксических ошибок. (Upd: Я попробовал и был так удивлён, что написал эту статью).

Главное, не останавливайтесь для проверок. Не надо проверять порядок аргументов вызова функции по справочнику, если мы его примерно представляем. Это можно будет сделать потом (а возможно и не придется никогда). (Upd: Если использует ASSERT'ы, и не можете придумать условие сразу — поставьте в коде метку и идите дальше. Если используете строгую методологию, напишите _ASSERT(false) и забудьте про это место до ревью/отладки.)

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

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

Upd: Большой кусок кода не должен означать, что качеством кода надо жертвовать без причин. Пишите сперва комментарии, потом блоки, потом код. Сделайте этот процесс привычным, чтобы он не вызывал каких либо заминок.

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

Да, иногда стоит оставить некрасивую конструкцию. Если её переделка нас задерживает, а мы готовы рваться впёрёд на всех парах. В этих случаях ставьте метки в комvентариях. Я использую TODO и HACK. Они сильно помогают перед финальным ревью (естественно, в альфе их уже быть не должно, как кода, который они помечали).

Проверка до отладки


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

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

Однако если что-то пошло не так, то…

Не спешите с дебагером


Итак, Хьюстон, у нас проблемы.

Втыкаем брек пойнт или отладочный вывод в код и на исполнение? Стоп!

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

На самом деле, когда мы выбрали точку для отладки – мы сделали 60% работы. Мы же не просто так её выбрали (ну я на жто надеюсь). Давайте сделаем оставшиеся 40%. Ну, или хотя бы 30%, а 10% оставим на вероятность эпик фейла.

Всё что нам нужно – это сделать шаг назад. Назад по коду. Мы ждем, что в этом месте будут проблемы. Почему именно здесь? Что здесь может быть не так? Часто достаточно посмотреть код, чтобы это увидеть.

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

А если мы ничего не нашли «методом пристального взгляда»? Эээ… Вы по-прежнему уверены, что репер надо поставить именно в этом месте?

Если нужна точка для диагностики проблем, то, по крайней мере, эффективно использовать двоичное деление. Давайте сразу проверим, есть ли ошибка в середине функционального блока? Разобьём код пополам и поставим репер.

Нет, мы всё-таки хотим плясать от начала. Ладно. Но вот в чём проблема. Если ошибка есть в начальном фрагменте, будем ли мы «копать глубже»? То есть писать ещё какой-то отладочный вывод, позволяющий более чётко идентифицировать ошибку. Будем? Так давайте сразу его и напишем. Зачем запускать программу два раза. Возможно, до этого и не дойдёт, просто подумав какая именно дополнительная информация нам нужна, мы часто сразу поймем где затыка.

По той же логике имеет смысл проверить ещё несколько реперных точек сразу. 80% ошибок – это отсутствие сигнала (действия, результата), а не его искажение. Более того, искажение результата – это, скорее всего, его отсутствие на предыдущем этапе. Расставляя несколько реперных точек, мы ещё раз волей-не волей просматриваем программу. И «волшебным образом» находим ошибки, до запуска дебагера.

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

Не придумывайте себе проблемы заранее



Известный (возможно самый известный) игрок в покер Дойл Брансон, знаменитый весьма агрессивной игрой, говорил (не ручаюсь за достоверность цитаты), «Когда меня спрашивают, что я буду делать, если соперник поднимет мою ставку, я отвечаю: Не знаю, но пусть он сперва поднимет. А вот потом уже я буду думать над этим.»

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

На самом деле, это происходит не так чтобы часто и часто не совсем так. Профессионал знает, что написав 300 строк кода из, скажем, требуемых 400, он столкнётся с проблемой. И он даже знает с какой. Он не начинает писать код, а пытается сразу найти решение будущей проблемы. Находит решение (если профи не STFW, то он не профи, так ведь?), но выясняется что это решение неполное и у него есть недостатки. Которые можно исправить, если найти решение для вот этого проблемного участка… И такое решение находится, только вот,… есть нюанс…

Как вы догадываетесь – это прямой путь в депрессию. Хабр, баш, анекдоты, новости, ЖЖ и даже порносайты политические дебаты. Депрессия конечно отступит, но работа стоит. Что же делать?

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

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

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

Также в лес проблемы связанные с производительностью. Я как-то поймал себя на том, что пытаюсь более оптимально переписать цикл на 1000 итераций. Вроде дело полезное. Но я перед этим разбираю мегабайтный JSON файл в памяти. Кого волнует, что я вложенность временного хэша снижу на уровень. Пользователь и так секунду ждёт. Ему 20 миллисекунд погоду сделают?

Да-да, производительность тоже в лес, а лес в топку До разумного предела, разумеется… Если у нас сейчас тысяча пользователей, не надо писать приложение, которое потянет миллион. Плохо конечно писать приложение только на тысячу пользователей, но и перезакладываться более чем на порядок – мягко говоря, не оптимально. С ростом «клиентуры» придется менять и функционал и возможно движок. И зачем лишний контроль за идеальностью кода, который будем переписывать?

Да, если есть возможность, сразу сделать приложение с большим запасом производительности, то надо именно так и сделать. Но если есть возможность сделать приложение только на три, две ну или хотя бы на полторы тысячи (да чёрт с ним, на текущую) тысячу, то всё равно надо садиться и писать именно такое приложение. А уж потом попытаться его обточить напильником. Разогнать готовое приложение, с которым ясно как оно работает и почему работает медленно проще, чем с нуля сделать мощный движок. (Если вы мне не верите, вы не прочитали дисклаймер).

Просто садитесь, и пишите код. Пусть не и идеальный. Я верю, что вы его доведёте в процессе.

Не спешите искать новую лопату


Помню, как меня не по детски бесил python после perl. Такое впечатление, что гонщика заставили сесть в трамвай. Да трамвай скоростной, а рельсы гладкие и блестящие, и скорости те же, но… Там где я привык осознанно использовать умолчания, python выкидывал exception. Вдруг я не знаю, что такого элемента в хэше может и не быть. Это ж катастрофа!

Меня стали контролировать, я стал злиться и терять время. Терять время и злиться. Мне показалось, что лучшее решение это ruby (помните анекдот про уток?).

С одной стороны хорошо. Разобрался пусть и «на пальцах» с рельсами. Заодно лучше понял, как работает перловый Catalyst. С другой стороны на три месяца забросил проект, который сейчас обратно пишу на питоне.

На самом деле, на питоне надо писать «pythonic», а не пытаться применять перловую парадигму: беспорядок в источниках – управляемый беспорядок в памяти – порядок на выходе.

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

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

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

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

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

Не бойтесь выкидывать


Я не помнил точно фамилию психолога, на которого ссылался в начале статьи. Если бы я не нашёл в своём бардаке «Введение в психологию» Б.Ю. Гиппенрейтер, то я бы просто удалил этот абзац, чтобы не откладывать публикацию. Я в черновую вычистил статью от ошибок, но их осталась пачка. Я чувствую. Не могу понять только где ;)

Но если вам нравится статья – то ошибки фактор маловажный. А если не нравится,… то ещё более маловажный.

Если хотите что либо сделать, делайте сразу. И публикуйте сразу.

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

Отполировать бархоткой можно в процессе. Есть продукты эппла. Где немалая часть цены – технологическая эстетика. Но… в них просто пожертвовано было другое. Например, первый макинтош ради законченности корпуса был лишен слотов для плат расширения, что стало одним из факторов его проигрыша IBM PC (по крайней мере, по объему продаж).

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

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

Большинство изящных решений находится именно в процессе эксплуатации. Главное её начать.

Шлите на…


Если кто-то говорит, что ваша работа не нужна, что она сделана плохо, что сейчас придет кто-то и сделает лучше… В общем, любой неконструктив.

Шлите его на…

Сразу. Без интеллигентостей и попыток объяснить, почему вы сделали именно так. Ну, если он конечно не ваш начальник. В этом случае… я пару раз рисковал послать начальника. И в итоге выигрывал я, особенно если начальник менялся.

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

Вы что-то сделали. Вы получили опыт. Вы получили удовольствие от сделанной (по крайней мере доведённой до конца) работы.

Upd. Что же считать конструктивной критикой? Это предложение как улучшить вашу работу. Особенно просто реализуемые и обоснованные. Это ссылки на подобные проекты из которых вы можете почерпнуть новые идеи. Это технологии (да-да те самые новые лопаты), которые пригодятся, если придётся делать качественный рывок, например по производительности.

А неконструктивные критики… Эта система контроля. Используемая в 10% случаем. А, следовательно, не нужная.

Upd И ещё раз подведём итоги


Если следовать всем моим вредным советам, то следует:
— писать большой, функционально целостный кусок кода не обращая на возможность ошибок и минимально используя документацию;
— перед компиляцией пробежать по коду, наметить возможные улучшения и план тестирования;
— не использовать дебаггер… сразу после появления ошибки, а попытаться её/их обнаружить логически;
— не поддаваться излишнему перфекционизму;
— разделать кодирование, ревью и отладку;
— переводить в эксплуатацию сразу по достижению функциональной закончености (и стабильности разумеется);
— приветствовать конструктивную критику и резко останавливать неконструктивную (простите, пожалуйста, кажется я с последним переборщил в комментариях);
— не сомневаться в собственных силах.

И самое главное


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

Всё идёт хорошо.