[clean] Напиши только 5 строчек

Viktor Love
7 min readDec 12, 2018

--

Вы способны написать пять строчек без багов. Значит вы способны повторить это 10, 100, 1000 раз и получить 50, 500, 5000 строк без багов. Но почему-то на практике не получается.

Почему?

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

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

Подпричины

Почему люди пытаются написать 50 строк, а не десять раз по 5 строк?

  • Гордыня;
  • Неопытность;
  • Лень;
  • Сложная задача;
  • Плохой тулинг;
  • Корректность не нужна;

Основная идея

Я исхожу из нескольких предположений, которые хотелось бы на всякий случай описать:

  • программист способен написать корректный код, если будет идеально понимать работу кода;
  • идеальное понимание достигается при высокой концентрации и постоянном пересмотре предположений о работе кода;
  • идеальное понимание невозможно, если программист “забывает” мелкие моменты;
  • постоянная концентрация невозможна, человеку нужен отдых;
  • при переключении между участками кода программист легко забывает детали;

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

Терминология

Для простоты восприятия я оперирую концептом “строка кода”. Это протекающий концепт, потому что строка строке рознь. Но концепт полезен тем, что рассуждать в этих терминах проще. Да, приходится игнорировать пустые строки. Да, приходится не допускать появления сложных строк. Но это проще, чем считать количество AST-нод.

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

Термин “контракт” в себя включает ограничения на входные данные; а также заявленные обязательства, как будет ввести себя код во всех обстоятельствах, включая необычные.

Определяем волшебное число

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

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

Главное: нужно не предположить, а вспомнить. У вас может быть убеждение, что вы отлично справляетесь с написанием кода в 100 строк. И оно будет ложным, потому что покопавшись в своей памяти вы обнаружите, что вы допустили серию ошибок, которые всплыли через месяц.

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

Предположим, что волшебное число = 5. В уме заменяйте 5 на ваше волшебное число. Эм, ну 10. Ну 15. Ну 20. Ну 30.

Пишем по 5 строчек

После определения волшебного числа вы должны начать писать по 5 строк кода. Не больше.

Увы, в пять строк ничего толкового не реализуешь. Значит нужно пользоваться декомпозицией и wishful thinking.

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

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

После реализации текущей функции её нужно проверить в уме. На всех возможных входах она должна отрабатывать ожидаемым образом.

И только после проверки нужно приступать к реализации фантазий. Но прежде — отдохните хотя-бы секунду-две. Увы, концентрацию не получится поддерживать постоянно.

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

Когда подводит wishful thinking

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

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

Предположим, что вы почему-то решили, что к функции-фантазии нужно предъявить другие требования. Тогда делаем так:

  • Если что-то уже реализовали в фантазии, то удаляем или комментируем;
  • Возвращаемся к основной функции и исправляем её;
  • Проверяем заново реализацию в уме
  • Отдыхаем;
  • Идем писать функцию фантазию.
Каждый раз, когда вы отвлекаетесь, к вам приходит этот дядя

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

Смотрите по себе. Здесь суть не в определенном процессе. А в том, чтобы ваш процесс гарантировал, что вы оперируете мелкими кусками.

Использование существующего кода

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

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

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

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

Если прослойку писать не целесообразно, то лучше написать свою реализацию, чем пытаться соблюсти DRY.

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

Потому что нельзя править вживую ДНК

Изменение существующего кода

Если вы хотите настоящей корректности, то код нужно читать, а не вспоминать.

Если вы не можете избавиться от воспоминаний, то нужно:

  • реализовать код заново;
  • проверить;
  • сравнить с предыдущей реализацией;
  • исправить забытые недочеты;
  • проверить снова.

Нельзя думать только о том, зачем нужен код. Что именно делает код в данный момент — это тоже очень важно.

На всякий случай: перед изменением кода убедитесь, что вы знаете, как работает все то, что используется в этом коде.

Изменение общего кода

Предположим, что по каким-то причинам вам нужно изменить контракт существующего кода.

Но увы, он используется во многих местах. Т.е вам нужно подправить все использования.

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

Здесь подходит стратегия постепенной миграции. Напишите новую функцию с новым контрактом. Потом постепенно вы сможете заменить использования старой функции на новую.

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

Явный контракт

Любое ревью (в том числе и саморевью) чаще всего представляет из себя изучение только одного уровня кода.

Это значит, что никто не видит контракты, описанные в хидере используемых классов/функций.

Таким образом полное понимание возможно, только если контракт функции очевиден из точки использования (или проверяется компилятором). Здесь очень хорошо помогает Type Driven Development.

В ряде языков (примерно половина мейнстрима) вы не сможете этого добиться без введения общих соглашений.

Примечание: частично упростить понимание кода можно введением high cohesion на уровне структуры файлов в проекте. Но это может помочь, только если разработчики будут изучать весь ближний код.

Сложные задачи

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

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

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

Ужасный тулинг

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

От авторов “C++ за 24 часа”

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

В общем, не используйте ужасный тулинг. А, ну и не болейте.

Нужна ли вам корректность?

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

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

Предостережение

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

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

Дырка есть, что еще надо?

--

--

Viktor Love
Viktor Love

Written by Viktor Love

Software Engineer from Ukraine. TypeScript, React, C#, Angular.

No responses yet