Код, в котором мы живем
Традиционно процесс разработки ПО сравнивается со строительством. Термин «архитектор» только укрепляет ассоциативную связь между этими процессами. Но современные реалии сделали эту модель неактуальной, потому что есть механизмы, которые она объяснить не может:
- Если мы делаем какой-то физический объект или продукт, то почему ПО никогда не считается законченным?
- Если мы говорим об инженерных решениях, то почему мы никогда не можем уверенно планировать наперед?
- Если мы — архитекторы или строители, то почему так много проектов заканчиваются полным провалом?
- И наконец, при наличии множества советов, best practices, принципов, книг и докладов о разработке ПО, почему так много проектов превращаются в невыносимое место для работы?
Предлагаю близкий к тексту перевод блестящего доклада Сары Мэй «Livable code», где она рассматривает все эти вопросы.
Почему важно иметь модель
Модель позволяет провести аналогию между незнакомым или сложным для понимания процессом и чем-то привычным, понятным для нас. При этом она не только должна точно подходить для описания ситуации, когда все хорошо, но и, как любая хорошая аналогия, модель должна направлять нас в пограничных ситуациях.
Процесс разработки ПО — это совокупность двух связанных, взаимодействующих, но, тем не менее, раздельных сущностей: код и команда.
Подтверждение этому — закон Конвея. Связь этих сущностей говорит нам о том, что проблемы в коде связаны с проблемами в команде и наоборот. Поэтому у нас есть два варианта их решения: со стороны кода и со стороны команды.
Некоторые проблемы кажутся проблемами исключительно с кодом, некоторые — только с командой. На деле все они — это проблемы с обеими составляющими. Поэтому новая модель должна подходить для современных реалий и отражать связь обеих составляющих процесса разработки.
Описание новой модели
Пространство, в котором живет приложение
Возведение здания имеет три четких стадии: проектирование, строительство и обслуживание. Раньше разработка ПО выглядела аналогично: итогом разработки являлся законченный продукт. Он устанавливался пользователю и передавался для поддержки отдельной группе, а разработчики начинали заниматься чем-то другим.
Но в мире веб-приложений, деплоев и релизов по нескольку раз за день, по сути, ничего не является законченным. Аналогичная ситуация наблюдается в мобильных приложениях и играх, когда они автоматически обновляются на консоли через Steam (чаще всего в самый неподходящий момент). Кроме того, в изменчивых условиях бизнеса приложения должны быть гибкими и способными меняться. При разработке приходится прикладывать огромные усилия, чтобы обеспечить эту гибкость. Того же нельзя ждать от зданий.
На здание больше похоже та инфраструктура, которую мы используем при разработке приложений. То, что мы создаем, — это не сами здания, потому что большинство прикладных приложений занимают место в предварительно возведенных структурах. Это звучит как понижение из архитекторов до дизайнеров интерьера, но в такой модели заключена большая сила. В ней здания — это основа для наших приложений: фундамент — ОС, уровень парковки — язык программирования, само здание — используемые фреймворки, а мы разрабатываем приложения уже внутри них.
Внутреннее пространство (структура), обеспечиваемое зданиями, является изменяемым, однако изменить его сложнее, чем свой собственный код. Настолько же, как снос стены сложнее перестановки мебели. Таким образом, само здание (фреймворк) налагает определенные ограничения на возможности и вид вашего интерьера (приложения).
Каждое здание оптимизировано для своих целей. Приложение может быть размещено в любом из них, но какие-то пространства будут лучше подходить для него, а какие-то — хуже. Поэтому код, который вы собираетесь поместить в заранее сформированное пространство будет определенным образом сформирован этим пространством.
В нашей индустрии все еще есть огромное количество работы по возведению зданий, но большинство из нас все же работают над интерьерами (прикладными приложениями).
Новая цель при разработке
Если добавить к всему этому то, что проекты сейчас никогда не считаются законченными, то приходишь к мысли, что код — это не то, что мы строим, а то, где мы живем. Это заставляет посмотреть на саму цель разработки под другим углом. Место, где мы живем, нужно сделать livable: пригодным, комфортным и удобным для жизни. Причем не только для нас самих, но и для тех, кто живет вместе с нами. В случае с кодом мало просто закончить продукт и пойти дальше. Его также нужно сделать livable: понятным, поддерживаемым, расширяемым — для команды, которая в нем «проживает».
Что такое Livable Code
Livable в отношении дома значит то, что вы живете в пространстве, где можно делать повседневные дела с комфортом и легкостью. В спальне есть светильник для чтения, на журнальном столике лежат салфетки, в гостиной стоит коробка для джойстиков, а в прихожей есть крепление для велосипеда.
Но то, что значит livable для группы людей, зависит от них самих. Удобный дом для молодых родителей с ребенком не похож на дом для студентов или для пожилых людей, или для одинокого родителя с подростком.
Livable для кода означает то же самое: вы можете изменять или добавлять то, что хотите, но без чрезмерной сложности и раздражения. Вы понимаете что-где находится, поэтому с легкостью переходите нужное место в коде и работаете с ним. Здесь принцип тот же: то что livable для одной команды, не подходит для другой. То, что является livable для одного сеньора и четырех джуниоров отличается от livable для пяти сеньоров, и сильно отличается от понятия livable для команды на аутсорсе.
Люди приходят и уходят, поэтому создание и поддержание кода в состоянии livable — это постоянный процесс. Появление нового члена команды сродни новому соседу по квартире: у него странный диван, который он почему-то очень любит, поэтому вы нехотя соглашаетесь поставить его в гостиной. А через пару дней сосед предлагает заменить шторы и жалюзи, потому что есть более эргономичные. И самое ужасное — он моет посуду табами вместо пробелов!
Получается так, что ваше настроение и состояние больше зависит от тех, с кем вы живете, чем от конкретных вещей в квартире и еще меньше от ее планировки. Также и с кодом: ваше благополучие в основном зависит от тех, с кем вы работаете, и гораздо меньше от самого кода или фреймворка.
Как проект становится захламленным
Но что если ваш код выглядит как эта комната? Как сделать его пригодным для команды? С чего начать?
Согласитесь, здесь передвигаться-то сложно, не говоря уже о том, чтобы понять, как навести порядок. Почему дома доходят до такого состояния? Часто говорят, что это лень. Психологи думают иначе: дом приходит в такое состояние из-за череды казалось бы незначительных неверных решений.
Представьте, с чего могло все начаться. Есть небольшая комната, в которой вы попытались оптимизировать пространство: добавили несколько полок и ящиков, использовали подоконник. Какое-то время все было хорошо: вы ежедневно поддерживали порядок. Но наступил вечер после трудного дня, когда вы не убрались. Утром вы проспали, а вернувшись домой вы поужинали прямо за рабочим столом и забыли убрать тарелку. На выходных родители привезли ваши старые вещи. Разбирать их не было времени и вы поставили коробки как есть. И вы чувствуете, что комната уже не в порядке, но ее состояние настолько угнетающее, что вы не можете понять откуда начать исправлять ситуацию и вскоре она превращается в ЭТО. Череда маленьких неверных решений: в комнате уже жуткий беспорядок, почему не бросить книги на пол?
То же самое происходит и с кодом. Вы открываете файл, который уже плох, полдюжины паттернов, которые больше не работают. А еще менеджер стоит у вас за спиной, ожидая починки бага. Первое, что приходит на ум, — это починить баг скотчем насколько это возможно и убраться отсюда поскорее.
Вот такие, вроде бы небольшие, локальные решения — это именно то, что приводит код в состояние unlivable. Впрочем, есть пути, чтобы выбраться из этой ямы, которую мы сами для себя и роем.
Спасение через изменение привычек
Вы не замечали, что в большинстве захламленных и неухоженных домов часто есть множество журналов, газетных вырезок с красивыми и уютными интерьерами? Там же лежат фотографии, проекты, советы по грамотному обустройству жилого пространства.
Люди, живущие в захламленных домах, хотят жить иначе, они смотрят на все эти журналы и чертовски хотят себе такой же интерьер, но не знают как этого добиться. И они думают, что только какое-то кардинальное решение, событие позволит им сделать их дом таким же, как на картинке.
На американском ТВ есть шоу «Hoarders». Люди, живущие в вечном бардаке, соглашаются на участие, чтобы команда профессионалов навела порядок и сделала дизайнерский интерьер в их доме. Ну, и как всегда, в конце программы красивые фото ДО и ПОСЛЕ. Интересно, что бОльшая часть участников признается, что в конце концов их жилище вернулось к изначальному состоянию. Все просто: та же последовательность маленьких неверных решений привела их к прежнему результату.
Оказывается более эффективным способом является постепенная продолжительная работа работа не с содержимым дома, а с самими людьми. Цель — изменить их привычки, чтобы они сделали поддержание порядка частью своей повседневной жизни. К сожалению, продюсеры посчитали бы такой вариант слишком скучным для реалити-шоу.
Захламленный код ведет себя также: мы боремся с ним день ото дня, только чтобы решить текущую задачу, и мечтаем все переписать. Вот если бы мы только могли все переделать, мы бы точно не сделали тех же ошибок! Мы должны переписать jQuery на Ember! Мы должны распилить монолит на микросервисы!
Иногда это работает, по крайней мере достаточно долго, чтобы написать триумфальный пост в блоге. Но потом свет гаснет, камеры перестают работать, съемочная группа покидает площадку, а вы, если не поменяли привычек, снова оказываетесь в беспорядке и даже быстрее, чем предполагаете.
Люди думают, что большие ошибки убивают нас: дом слишком маленький, планировка неудачная, не тот фреймворк, монолит, недостаточно ресурсов. Это не так. Нас убивают те самые маленькие решения, которые обусловлены привычками и образом мышления.
Вы можете переписать все, что угодно. Но, если ваша команда не изменит свои привычки, то вы окажетесь в запутанной сети микросервисов, точно также, как у вас была запутанная сеть классов и модулей в монолите.
Командные привычки
Нужно отметить, что мы говорим не об индивидуальных, а о командных привычках и нормах. Это объясняет то, почему образ разработчика как индивидуального профессионала, ремесленника не отражает реальность. Это не отдельный разработчик ленивый и непрофессиональный. Это команда с хромающей культурой совместной работы.
Точно также, как с соседями по комнате: все должны проводить ежедневную уборку, мыть посуду за собой, периодически чистить ванную и туалет, наводить порядок в общей гостиной. Но есть и более сложные занятия для повышения livability, которые можно оставить «только для заинтересованных». Например, перевесить кухонные шкафы или выбрать подходящий столик в гостиную вместо слишком громоздкого.
Но ежедневная работа — это то, что обязан делать каждый. То есть вы можете иметь людей в команде, заинтересованных в глобальном архитектурном рефакторинге, разбиении слишком больших модулей, оптимизации взаимодействий между ними и тех, кто не хочет этого делать. Каждый не обязан участвовать в перестановке мебели, но каждый должен мыть за собой посуду.
Две крайности являются unlivable или почему книги не работают
Замечательный интерьер, не правда ли? Все идеально подобрано и продумано, ничего лишнего. Одним словом — мечта. Совсем как описание паттернов, показывающих красивую версию абсолютно согласованного, идеально абстрактного кода. Так прекрасно, что глаз радуется.
Да, квартира на картинке прекрасна, но вы не сможете жить в ней. Квартиры со страниц журналов намеренно лишены беспорядка, потому что беспорядок — это что-то индивидуальное.
То, из чего состоит этот беспорядок — разные вещи для каждого. Если вы любите читать, то это книги, если любите компьютерные игры —это приставка и джойстики, а если вы любите активный отдых — это велосипед или байдарка. Вещи, которые вы используете, должны быть под рукой, поэтому приходится мириться с определенным беспорядком. Главное, чтобы его не было слишком много.
Журналы о красивых интерьерах в мире разработки ПО показывают нереалистично чистый код и поэтому устанавливают неверные ориентиры. Таковы книги по разработке ПО, паттерны проектирования, советы из блогов и статей. Код, удовлетворяющий всем их требованиям, идеально согласованный и абстрактный — в реальном мире нежизнеспособен. Даже если вам удалось добиться этого, вы не сможете в нем жить.
Нам нужен небольшой беспорядок, чтобы чувствовать себя комфортно. Это справедливо как для квартиры, так и для кода.
Обе крайности являются unlivable. Если перед вами кусок кода, который вы не понимаете и не знаете, как с ним работать, значит он в какой-то из этих крайностей. Либо он слишком замусоренный, что невозможно различить подходящие идеи от неподходящих, либо он настолько чистый, что подходящие идеи просто невозможно найти.
Причем, эти две крайности могут существовать одновременно в одном и том же проекте. Иногда даже по соседству друг с другом. Livable код находится где-то посередине. Также как и комфортное пространство для жизни находится где-то между фотографиями с обложек журналов и захламленными квартирами.
Поэтому нужно уметь принимать небольшое количество беспорядка, потому что он делает пространство livable.
Манифест
Вы можете сказать: «Это все, конечно, очень интересно, но что же делать, если мой код уже находится в полном беспорядке? Или что делать, если проект не так уж плох, но я хочу быть уверенным, что он не движется в неправильном направлении?»
Допустим, ваш код похож на захламленную legacy-гостиную с узенькими козьими тропами понимания, идущими через нее. Не переживайте, вы не плохой человек — такое с каждым может случится.
Если смотреть на проблему с инженерной точки зрения, то она просто кричит: «Перепиши! Как же возможно что-то организовать в таком месте без свободного пространства? Просто вынеси все отсюда и начни заново!» Такое планирование сверху-вниз хорошо подходит для определенных инженерных задач, но не для большинства ПО. Это не потому, что мы — плохие инженеры или плохо стараемся, а потому, что в нашей области инженерный подход не работает. А работает рассмотрение кода как пространства, в котором живут люди. Нужно понять, что люди — именно то, над чем нужно работать.
Вот как это можно сделать. Есть четыре правила, которые справедливы для любого языка и фреймворка.
Не навреди
Почти как в медицине. Открывая уже захламленный файл, обещайте, что вы не будете делать его еще хуже. Даже если у вас нет времени на исправление тех вещей, которые уже ужасны. Если, проводя ревью кода, вы видите, как человек нарушает это правило, просто напомните ему, о чем вы договаривались.
Поверьте в последовательные улучшения
Куча из пяти книг на полу и одна убранная в шкаф лучше, чем куча из шести книг. Возможно, тот, кто коснется этого кода в следующий раз, переложит еще одну книгу из кучи на полку. Если вы будете ждать, когда найдется время поставить их все сразу, то этого никогда не произойдет.
Использование такого подхода может стать проблемой. Для многих последовательные улучшения даются трудно, поэтому они продолжают жить со жгучим желанием переписать полностью модуль, подсистему или приложение. В итоге, куча с книгами не уменьшается и остается на полу из итерации в итерацию.
Делайте поддержание порядка частью рабочего процесса
Не заводите user stories для рефакторинга. Не ждите две недели, чтобы исправить один файл. Научитесь встраивать простые правила в ежедневную работу. Правило бойскаута гласит: оставляй лагерь чище, чем он был до твоего прихода. Если вы будете следовать ему постоянно, то постепенно избавитесь от «вредных привычек».
Поддерживайте связь
Будьте готовы всегда общаться с каждым членом команды о том, что вы делаете. Предыдущий пункт не значит, что нужно скрывать то, чем вы занимаетесь. Просто нужно рассматривать рефакторинг и наведение порядка как часть всего, что вы делаете.
Когда вы занимаетесь рефакторингом:
- Не спрашивайте разрешения. Это часть вашей работы. Но при этом будьте открыты к обсуждению ваших решений.
- Не уклоняйтесь от ответственности за свои ошибки и выносите уроки из каждой. Ошибки — это тоже часть работы. Именно через них мы приходим к пониманию, над чем работаем. Вы будете их делать, смиритесь. Иногда вы можете не доделывать рефакторинг, иногда, наоборот, — слишком увлекаться, но главное — учитесь на своих ошибках раз за разом.
- Спрашивайте советов, но не всегда следуйте им. Ваша задача, как разработчика, определять, что нужно отрефакторить, а что — нет. У вас должна быть свобода, чтобы принимать эти решения и делать ошибки.
- Делайте все вместе, потому что вам здесь жить. Это очень важно. Мы должны изменить тот код, в котором нам приходится жить, на код, в котором нам нравится жить.
Заключение
Самая важная часть разработки ПО — не код и не люди. Самая важная часть — система. Чем больше мы думаем о разработке, как о взаимосвязанной системе кода и людей, тем ближе мы к революции, и к проектам, в которых хочется работать.
Вы тоже можете к этому прийти. Не важно насколько плохо обстоят дела сейчас — вы все сможете. Если вы построите доверительные отношения и укрепите связи внутри команды, если каждый будет относиться с уважением к коду, в котором он работает, если вы примете реальность и откажетесь от иллюзий переписать все заново и пообещаете друг другу выполнять правила, то у вас все получится.