Привет, Хабр!

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

Приятного чтения.

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

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

Однажды мы решили: хватит! Выделили для всех вещей свое место и навели порядок с нашим инвентарем. Подвойные инструменты сдали в благотворительность. Мы прилагали всяческие усилия, чтобы воспитать самодисциплину и всегда класть инструменты только на свое место после того как поработаем с ними. Качество и объемы работы в единицу времени резко пошли вверх. Стресса, впустую потраченных денег и испорченного настроения стало гораздо меньше.

Наводим порядок в корпоративном гараже


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

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

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

Наводим порядок в разработке ПО


Разберем конкретный пример: инкрементный рефакторинг.

Многих это выражение озадачивает. Не знаю, какое именно слово ставит их в тупик: «инкрементный» или «рефакторинг».

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

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

Постепенный рефакторинг


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

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

Правило бойскаута


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

Именно так работает и постепенный рефакторинг. Перед нами – фрагмент кода, и нам нужно добавить новое условие в список имеющихся конструкций. Замечаем, что список реализован в виде большого блока if/else. Решаем, что его нужно переоформить в виде switch или оператора выбора, одновременно добавляя условие. Можно возразить: а разве это намного лучше, чем блок if/else? Да, вы правы: ненамного. Но чуть-чуть лучше. Когда кто-то следующий будет читать этот код после вас, он сможет еще лучше его доработать его, поскольку вы улучшили для него исходную позицию. Если вам доводилось делать что-то подобное ранее, то вы, вероятно, замечали, что, просто внимательно прочитав все условия, вы иногда находили то дублирующиеся конструкции, то неоптимальный порядок выражений; возможно, вам попадалось какое-нибудь условие, которое никогда не может быть выполнено, а то и логическая «дыра», через которую вся управляющая последовательность провалится через блок if/else, а некоторый случай вообще обработан не будет. Просто радуйтесь, что обнаружили это сейчас, а не глубокой ночью, когда вам прилетит соответствующий тикет от службы техподдержки. В такой момент, когда глаза слипаются, нет совершенно никакого желания разбираться в путаном коде.

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

“Долгосрочный” –это обычно не так долго, как кажется


Часто слышу от разных людей, что рефакторинг приносит «долгосрочную» пользу. Хотя, в теории так и есть, здесь, в Реальном Мире постоянно приходится сдавать работу в жесткие сроки. Однако, если говорить о поддержании чистоты кода путем мелкого постепенного рефакторинга, «долгосрочное» длится ровно до того момента, когда кто-то еще притронется к базе кода. Ровно до следующего раза.

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

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

Что такое быстро, что такое медленно?


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

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

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

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

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

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

Технический долг как метафора


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

Те, кто так говорит, не понимают эту метафору, а возможно – и никаких метафор вообще.

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

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

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

Срезание углов ради того, чтобы сдать работу побыстрее – это не накопление технического долга. Это обычная халтура.

Кто ответственный?


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

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

Комментарии (17)


  1. 7ft
    01.12.2018 03:09

    Странно, что delivery в нескольких местах переведено как «доставка», на мой взгляд, «выдача» удачнее. Или уже в жаргоне устоялась «доставка», а я проспал?


  1. olegshutov
    01.12.2018 03:50

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


    1. caffeinum
      01.12.2018 08:09

      Нельзя просто так вот захотеть и с завтрашнего дня перестать говнякать.

      Мне кажется, что можно. Я помню момент, когда в моей голове произошел этот переворот. Когда я планировал сделать пуш, а потом подумал «почему вместо того, чтоб оставлять комментарий // TODO, я не могу сразу сделать то, что там написано?».


      1. olegshutov
        01.12.2018 11:33

        Фундаментальное понимание говняканья возникло у меня, когда я решил спустя лет 10 после универа снова поучаствовать в олимпиаде по программирониваю. такое участие подразумевает говняканье. посмотрите на код победителей — ничего хуже его не бывает. и я вдруг понял, что не могу снова заставить себя говнякать. мне пришлось вдумчиво и медленно говнякать, вместо того, чтобы инлдуитивно писать, как привык.
        Короче, мой пойнт в том, что каждый пишет как умеет. просто некоторые люди обманывают себя, мол, «я так написал, потому что было мало времени, а так-то я знаю, как надо было сделать нормально». если дать такому человеку сколько угодно времени, он начнет с «прототипа по-быстрому», а потом будет точно так же будет переписывать его и переписывать и все равно будет то же самое, но вид сбоку
        Любой программисто считает, что он может сделать хорошо, если ему дать достаточно времени. По факту же не любой. Для того, чтобы уметь делать хорошо, нужен достаточный опыт деланья хорошо, а если его нет, то, извините, ничего не получится.


      1. kinall
        01.12.2018 13:53

        «почему вместо того, чтоб оставлять комментарий // TODO, я не могу сразу сделать то, что там написано?»

        Например, потому, что уже девять вечера, кофе давно кончился, а завтра релиз бета-версии?


      1. sshikov
        01.12.2018 18:17

        Насчет todo: нет, не всегда можете. Для этого может потребоваться анализ. И не на 5 минут, а на 5 дней. Всяко бывает.


      1. Amper
        02.12.2018 06:10

        почему вместо того, чтоб оставлять комментарий // TODO, я не могу сразу сделать то, что там написано?

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


  1. caffeinum
    01.12.2018 08:07

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

    Тут есть огромная опасность, в которой сам себя часто ловил: отвлекаешься и вместо выполнения основной задачи весь день занимаешься перекладыванием автотестов из папки в папку и расстановкой запятых на правильные строчки. Как с этим быть? (:


    1. marshinov
      01.12.2018 10:53

      Исправлять косяки только в рамках задачи и не трогать остальные запятые и тесты, не относящиеся к задаче


  1. ssurrokk
    01.12.2018 08:30

    Лепить гауно, это не уважать себя же в будущем — сам же потом вернёшься к тому что наваял и будешь плеваться)


  1. 3cL1p5e7
    01.12.2018 11:30

    Замечу, что «правило туриста» это, наверное, неверно интерпретированное «правило бойскаута» из Чистого кода Боба Мартина. Оно гласит примерно следующее: «оставь после себя место чище, чем оно было до тебя».


  1. azomas
    01.12.2018 11:30

    «Philips-head screwdrivers» переводится как «крестовые отвертки».


  1. yannmar
    01.12.2018 12:36
    -1

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

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


  1. akurilov
    01.12.2018 12:45

    По своему опыту — изменения "за компанию" и "за рефакторинг" — древнее и страшное зло. Любые изменения подвержены ошибкам и опечаткам. Ибо нельзя написать 10 строк кода, на набажив по самое не могу.


    1. ZaEzzz
      01.12.2018 16:13

      Я разрываюсь между "Ваше сообщение прекрасно" и "Кому-то это действительно не дано".


      Простите, не удержался.


    1. psFitz
      01.12.2018 16:58

      странный у вас опыт, можно тесты написать раз такое дело.


    1. sshikov
      01.12.2018 18:21

      >Ибо нельзя написать 10 строк кода, на набажив по самое не могу.

      Это неправда. Точнее нет, не так — код бывает разный. Бывает вполне рутинный, где все очевидно, и где вам понятно, что вы делаете, и зачем делаете. А бывает нечеткая постановка задачи, и другие неопределенности.

      И 10 строк одного кода совсем не равны 10 строкам другого.