[Перевод] Как программисту снизить когнитивную нагрузку: три способа
Предотвращение когнитивной перегрузки программиста — это ключ к недопущению ошибок и ускорению разработки.
Среди подкастов, которые я обычно слушаю, один называется «Никаких дурацких вопросов». В минувший понедельник я с большим интересом прослушал выпуск «Когда простота становится избыточной?». В нем авторы бросили вызов пресловутой «бритве Оккама» и продемонстрировали предвзятое отношение людей к простейшему объяснению по сравнению с более сложным. Несмотря на то, что в физике зачастую самое простое объяснение оказывается единственно верным, в других науках, таких как экономика или психология, ситуация может измениться. Почему пала Римская империя, или отчего преступность в последние десятилетия пошла на спад — причин тому не одна, а несколько. Авторы подкаста ясно показали, что, несмотря на любые факты, люди предпочитают иметь дело только с одним, а не с несколькими объяснениями случившегося. Это поразило меня.
Месяц назад я опубликовал методику снижения когнитивной нагрузки на программиста. При этом я изложил ее не в виде нотации «только так и больше никак», а в формате «вот еще один способ борьбы с нагрузкой, вы можете его попробовать». В основе этого материала лежало открытие, которое я сделал совсем недавно и которым хотел поделиться с остальными. В целом, статью приняли весьма тепло, однако некоторые читатели ни с того ни с сего ополчились против моего метода. Причина их враждебности заключалась в том, что им был известен некий альтернативный метод, который они почему-то считают единственно верным и правильным. Любопытно, что все они при этом ссылались на один и тот же метод. По сути, они все были правы в отношении этого метода, но ошибались, предполагая, что он единственный. На сегодняшний день мне удалось найти уже целых три метода борьбы с перегрузками, не считая ряда техник, которые в паре с ними доказали свою эффективность.
Выходит, когнитивная нагрузка — это комплексная проблема, у которой может быть сразу много причин и последствий.
Известные мне три базовых способа решения проблемы когнитивной перегрузки программиста можно резюмировать так: «Разделяй», «Упрощай» и «Делегируй».
Разделяй
Именно это мой инструмент номер один для снижения когнитивной нагрузки. Вместо того чтобы заставлять программиста решать сразу все проблемы, их стоит разбить на несколько областей, с которыми можно разбираться последовательно. Если софт по-настоящему серьезный, он наверняка включает сотни, а то и тысячи различных компонентов и концепций. Вряд ли вам захочется, чтобы какая-то одна команда лезла сразу во все части проекта. Следует разделить большую задачу на несколько частей.
Есть два отличных друг от друга способа разделения: вертикальное и горизонтальное.
Вертикальное разделение — наиболее распространённое. Оно происходит в четком соответствии с бизнес-правилами и бизнес-требованиями. Его проще всего применять, потому что оно может даже соответствовать естественной структуре организации по отделам. Здесь в игру вступают техники а-ля Domain-Driven Design, позволяющие вокруг каждой проблемы построить свой четко очерченный домен.
Горизонтальное разделение заключается в том, что вместо разбивки команд по бизнес-концепциям, они делятся по задачам и технологиям. Именно так создаются группы, например, по инфраструктуре или производительности. Такой вид разбивки нередко применяется на практике, но, увы, часто происходит без учёта когнитивной нагрузки.
Общая рекомендация — разбивать команды, как только они разрастутся, а внутри групп появятся свои подгруппы узкой специализации. Логику, лежащую в основе техники разделения, совсем недавно озвучили авторы книги «Топологии команд». У них нашлись весомые аргументы в пользу создания команд вокруг концепции когнитивного воздействия. Я предлагаю вам посмотреть видео, в котором изложены некоторые основные концепции:
Упрощай
Именно об этом варианте, как мне показалось, думает большинство людей, когда речь заходит о попытках снизить когнитивную нагрузку. Суть концепции состоит в том, что правильная архитектура и структура кода может нивелировать возможные последствия локальных изменений, поэтому программист может изолированно работать строго в рамках своей текущей узкой задачи.
Это очень хорошо сочетается с концепцией связанности. Вот едва ли не самое примитивное определение связанности: А и В связаны, если изменение в А может повлечь за собой изменение в В. Если архитектура кода подобрана правильно, связанность между объектами низка, и программисту не нужно рассматривать больший набор компонентов, чем необходимо; таким образом, когнитивная нагрузка снижается.
Можно сказать, что обратной стороной связанности является связность. Связность состоит в том, что код, относящийся к одним и тем же функциональностям, находится в одном месте. При высокой связности программисту не надо искать «хвосты» по всей кодовой базе — всё, что связано между собой, находится в одном месте, поэтому не нужно вспоминать, где еще требуется внести изменения, чтобы свежая версия кода работала без ошибок. Это также сокращает когнитивную нагрузку.
Зачастую многие забывают, что связность выходит за рамки только структуры кода. Так, структура папок тоже играет немалую роль. Часто попадаются проекты, в которых папки классифицированы по типу логики (контроллеры, модели, …), а не функциональности (пользователи, посты, …). Такая структура обладает низкой связностью и увеличивает когнитивную нагрузку.
Кроме того, существует так называемый принцип наименьшего удивления. Недостаточно одной только качественно спроектированной архитектуры, необходимо также, чтобы код во всем проекте выглядел единообразно. Принцип наименьшего удивления гласит, что код всегда должен выглядеть так, как ожидает программист, с как можно меньшим количеством сюрпризов. Это означает, что он должен везде следовать одним и тем же паттернам, должен избегать фильдеперсовых решений, если стандартных вполне достаточно, и повторно использовать существующие конструкции, даже если они просто хороши, а не идеальны. Это позволяет снизить когнитивную нагрузку.
По сути, код следует писать и организовывать таким образом, чтобы программист мог внести любое изменение в любом месте, не задумываясь о том, что что-то пойдет не так где-то еще.
Делегируй
Именно об этом способе я рассказывал в своей предыдущей статье. Я открыл его для себя совсем недавно, но, как оказалось, ему уже больше двадцати лет! Суть концепции состоит в следующем:, а что, если мы можем снять с программиста всю лишнюю когнитивную нагрузку и делегировать ее непосредственно коду?
В работе программиста на самом деле не так много видов когнитивных нагрузок, с которыми он должен справляться собственными силами. Даже если мы разбиваем домен на команды, даже если у нас самая лучшая в мире архитектура, у программиста все равно остается несколько концепций, которые он должен держать в голове одновременно. Эта нагрузка связана не столько с кодом, сколько с бизнес-процессами компании, в которой он работает. Программист должен четко знать, как новый функционал изменит программу, как он повлияет на существующие возможности, что еще требуется дописать и в каком месте может выползти коварный баг. А следовательно, он должен жонглировать всеми этими концепциями и придумать решение, при котором всё будет работать как единый слаженный механизм.
Оказывается, даже то, что мы считали минимальной когнитивной нагрузкой, поддается оптимизации. Техника под названием TDD позволяет еще сильнее облегчить жизнь вашим программистам. Я не стану объяснять TDD прямо здесь и сейчас, но, если кратко, это техника, при которой программист выполняет автоматизацию тестов для проверки корректности поведения всей программы. Сначала он реализует один тест, затем добивается его успешного прохождения, пишет новый тест и новый код, и так по кругу. Этот итеративный процесс похож на сборку мебели из Ikea: ты соблюдаешь порядок сборки страница за страницей, вместо того чтобы пытаться понять, как собрать все сразу. Это снижает когнитивную нагрузку, потому что программисту нужно думать только о следующем шаге, и он уже знает, получился ли предыдущий или что-то пошло не так.
Меня удивило то, что как раз-таки это самое свойство TDD делает его актуальным и по сей день. Кент Бек, автор TDD, говорил, что после того, как он впервые написал программу, руководствуясь TDD, он почувствовал себя обманутым: «то героическое, доступное одним лишь кодерам чувство, что всё вот-вот выйдет из-под контроля, но лишь благодаря огромной силе разума и воли на месте хаоса воцаряется порядок, полностью исчезло».
В начале статьи я упомянул о подкаст-шоу «Никаких дурацких вопросов», в котором популярно объяснялось, что зачастую у проблемы не может быть одного-единственного верного решения. Я скажу вам больше: нашей с вами проблеме и целых трех решений будет мало.
Есть и другие способы снизить когнитивную нагрузку. Может быть, не такие действенные, как описанные выше, но все они важны по-своему. Например, в эпизоде «Если все ненавидят собрания, почему у нас их так много?» говорится о том, что «Есть два типа расписаний […] расписание руководителя и расписание исполнителя». Выходит, что если менеджеры разбивают дни на часовые интервалы, то программисты и прочие «настоящие» работники разбивают дни на половинки. Поэтому нам не стоит отвлекать программистов совещаниями, это позволит не перегружать их лишней информацией в тот момент, когда они занимаются решением конкретных проблем.
Еще одна техника, которую часто связывают с когнитивной нагрузкой, — это парное программирование, или моб-программирование. Как ни странно, но на самом деле написание кода — это социальная деятельность. Вместо того чтобы писать в одиночку и думать, как другие люди поймут твой код, можно поговорить с ними и избежать лишних волнений. Кроме того, есть плюс в том, что любую внешнюю помеху может обработать кто-то один, в то время как его напарник продолжит работать в прежнем темпе. И по какому-то странному общечеловеческому принципу, когда прервавшийся возвращается к работе, он без заминок возвращается в контекст.
Вероятно, когнитивная нагрузка — это самая большая проблема, с которой сталкивается программист. Снизить ее до минимума жизненно важно, и для этого необходимо использовать все возможные техники. Это не простая проблема, но у нас есть решения — именно решениЯ, во множественном числе.