Не надо заставлять, надо автоматизировать

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

В этом посте — о том, что это работает не только в уговорах с собой, но и в общем случае.

Порядок, правила, инструкции

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

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

Дело в том, что «создать правило» ≠ «этому правилу следовать». Просто потому что «следовать правилам» скучно и быстро надоедает — я ж ленивый.

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

Например

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

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

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

«Страх наказания» не работает. Да, человек будет тревожиться и переживать, что его накажут, но лень это не переборет. Надо автоматизировать процесс или менять контекст.

Инструкции не работают, человеческая лень — вот, что работает

…Поэтому когда я замечаю, что «пишу инструкцию» для кого‑то, я начинаю думать:

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

В жизни

Я постарался принести эту философию в разработку Тяжеловато.

Мы сейчас переезжаем на conventional commits, но поменять привычку писать комиты так, как я привык, мне было тяжеловато (no pun intended). Поэтому мы пилим ветку с линтером, который не будет пропускать комиты не по стандарту. А для мотивации писать нормальные комит‑сообщения мы будем каждый комит переносить в автоматически сгенерированный чейнджлог.

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

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

А не манипуляция ли это?

Это тонкий вопрос.

Я считаю такую стратегию манипуляцией при совпадении двух факторов:

  • она не служит безопасности и «лучшим практикам»;
  • её нельзя обсудить и изменить.

Первое понятно — сужение дороги перед переходом спасает жизни, тут не важно, бомбит водителям или нет. А вот «как называть ветки» — это более серая зона. С одной стороны есть best practice, с другой — это похоже на директивное управление.

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

Почитать на тему

У меня в блоге:

Не у меня в блоге:

Тяжеловато:

Компоненты — это организмы

Я достаточно долго пытался сформулировать для себя, что такое «Компонент».

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

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

Компонент, как изолированная сущность

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

Это важно, потому что проектировать что‑либо без чётко‑выделенных сущностей — больно и неэффективно.

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

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

ДНК компонентов

Организм развивается под действием ДНК и внешней среды. Компоненты — так же.

Для компонентов ДНК — это внутренние правила, которые определяют его цель, то есть чего компонент старается достичь, чтобы выжить. Эти же правила могут определять полезность и способ максимально‑эффективного достижения цели.

Давайте на примерах.

Чтобы Текстовое поле могло выжить, оно должно уметь показывать, что в него можно ввести текст и отображать этот введённый текст.

Кнопка должна уметь показывать, что на неё можно нажать, и по нажатию что‑то произойдёт.

Если компонент не умеет делать то, что «должен», он не нужен — то есть он не выживает.

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

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

Если присмотреться повнимательнее, то видно, что одно из таких «внутренних правил» — это принцип единственной ответственности. Этот принцип (SRP) — часть ДНК компонента, которая составляет часть исходных данных, которые определяют жизненный путь компонента.

Компонент во внешней среде

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

Снова на примерах. Форма поиска — это совокупность компонентов — сообщество:

У каждого организма в этой совокупности есть собственная роль — специализация:

  • текстовое поле должно принимать и отображать вводимый текст;
  • иконка должна объяснять, что текст будет воспринят, как текстовый запрос;
  • кнопка должна нажиматься и объяснять, что по нажатию будет отправлен поисковый запрос.

Внешняя среда может и будет меняться. Если среда меняется так, что наличия просто Текстового поля внутри Формы поиска недостаточно, поле может приспособиться и превратиться в Автокомплит, поле с Тегами, что‑то ещё, что позволит ему делать свою работу — выживать — лучше.

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

Если же на сайте не осталось форм в принципе, то Текстовое поле как компонент перестаёт быть нужным и вымирает. Это называется эволюция.

Эволюция компонентов

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

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

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

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

Если коротко — компоненты стремятся делать как можно меньше работы, но как можно лучше.

Эволюция среды

Внешняя среда тоже развивается. Сообщества растут или отмирают, условия развития компонентов и сообществ становятся жёстче или ослабевают. Всё это отражается на развитии компонентов.

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

Кнопка — отличный пример. Когда на сайте нет никаких форм кнопка мимикрирует под ссылки — она выглядит как кнопка, нажимается как кнопка, по нажатию что‑то происходит — но это не кнопка.

И это я ещё даже не говорю о компонентах‑паразитах; это, пожалуй, на какой‑нибудь другой раз.

Дизайн‑системы Экосистемы

Компоненты собираются в сообщества; сообщества — в группы; группы сообществ — в экосистемы. Экосистема — набор внешних и внутренних правил, по которым живёт проект.

Например, SRP — часть экосистемы, потому что диктует, как компоненты определяют свою специализацию. Наличие функции поиска на сайте — тоже часть экосистемы, потому что определяет, будет ли существовать, например, Форма поиска. Любое отношение между компонентами (симбиоз, паразитарные отношения) — всё это части экосистемы.

Экосистема (дизайн‑система) — это не «плакат со всеми сущностями в нашем проекте», нет. Это набор принципов и правил, которые определяют ценность проекта в целом и набор правил, по которым живут и развиваются сущности в этом проекте.

Капец ты двинутый

Как ни странно, всё это помогает проектировать системы с учётом таких старых друзей, как например SOLID.

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

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

Ссылки

Сообщение об ошибке, от которого не горит

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

Сижу, значит, программирую, внезапно консоль заливает красным, и вылезает вот это:

Uncaught Error: Bad dependency path or symbol.
  // стектрейс из скомпилированного (!) кода...
  // стектрейс из внутренних библиотечных функций...
  //
  // скомпилированного... напомню, это dev-режим.

Да, это сообщение об ошибке.
Да, это всё сообщение.
И да, это плохое сообщение.

Дальше — простыня, почему оно плохое.

Как надо

Хорошее сообщение об ошибке следует двум главным принципам:

  1. Оно опирается на факт, что всё горит, и починить ошибку надо немедленно.
  2. Заботится о разработчике, а не обвиняет его в глупости.

Из этих принципов я бы вывел 4 правила, что хорошее сообщение:

  1. Говорит, что именно сломалось — какой модуль, функция и т. д.
  2. Где именно сломалось — как ошибку найти.
  3. Почему оно сломалось — как ошибку воспроизвести, что не сходится.
  4. Как это починить — что на что заменить, чтобы заработало.

Говорит, что именно сломалось

— Ваше приложение не работает.
— Да, но почему?
— Потому что оно сломалось
¯\_(ツ)_/¯

То, что приложение не работает, я уже знаю — оно, блин, не работает. Мне как бы нужно понять из‑за чего.

В ошибке выше, вроде, даже написано, что сломалось, вот — Bad dependency path or symbol. Но что именно‑то: bad dependency path или bad symbol? Если символ, то какой? Если путь до зависимости, то до какой?

Почему бы, например, не сделать так?

Bad dependency path.
  Cannot find module "superModule".

Указывает, где сломалось

Чтобы что‑то починить, надо исправить код. Чтобы в коде что‑то исправить, надо найти, что
— Ваш Кэп

В эпоху до сборки фронтенда (помните, IE7, FTP, jQuery, эх!..) всё было просто. Стектрейс сам говорил, на какой строке беда. Сейчас браузерный стектрейс может не помочь.

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

Bad dependency path at line 42, character 88 in "entryPoint.js".
  Cannot find module "superModule".

Объясняет, почему оно сломалось

С любовью, вечно ваш «undefined is not a function»

Да, если произошла низкоуровневая ошибка, синтаксическая например, надо об этом написать. Но undefined is not whatever как бы так сказать, is not enough. Ткните, пожалуйста, носом в то место, которое не работает: в поле объекта, название функции, метода.

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

Bad dependency path at line 42, character 88 in "entryPoint.js".
  Cannot find module "superModule".
  Expected one of extensions: "js", "jsx", "ts", "tsx", "mjs", but tried to import "".

Инструктирует, как починить

— Хм, а как это починить?
— Ну вы держитесь здесь, вам всего доброго, хорошего настроения и здоровья

Допустим, стало понятно, что именно сломалось. Если сообщение об ошибке рассказывает, как чинить — это прекрасно.

Самый человечный, но и самый ресурсозатратный способ — рассказать по шагам, что надо сделать, чтобы ошибка исчезла. Так, например, делает React в режиме разработки.

Ещё один хороший способ — показать примеры работы функции, что она вернёт при каких аргументах. Так делает Lodash в jsdoc и многие модули Python в docstring.

Да, это запарно. Но разработчики скажут спасибо.

Bad dependency at line 42, character 88 in "entryPoint.js".
  Cannot find module "superModule".
  Expected one of extensions: "js", "jsx", "ts", "tsx", "mjs", but tried to import "".
  Check your import extension and make sure file exists.
  Note that this lib supports only imports with direct link to a file with its extension.

Опирается на факт, что всё горит

Тут всё как у Ситника в докладе о продвижении проектов.

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

Заботится о разработчике

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

Разработчики не тупые. Им может не хватать контекста, знаний, опыта. А ещё бывает, что другой third‑party код конфликтует с вашим, или браузер лагает, или интерпретатор, или сеть, или железо… ну вы поняли.

Ну и вообще…

Это всё — просто эффективная коммуникация и желание помочь. Ильяхов в Правилах деловой переписки писал подобное. А сообщение об ошибке — чем не переписка?

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

Ссылки в конце поста

Из моего блога:

Из внешнего интернета:

С кодом:

Когда последовательность важнее правильности.

Пост будет холиварный, читайте с осторожностью.

Представьте ситуацию: приходит задача дополнить валидацию данных пользователя, которую писали до вас. Вы открываете код и видите:

const validateUserData = (userData) => {
  const {socialNumber, email} = userData
  return validateSocialNumber(socialNumber)
    || validateEmail(email)
}

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

Вы открываете код функций validateSocialNumber и validateEmail и видите, что они возвращают истину, если в данных есть ошибка. Откуда делаете вывод, что и validateUserData возвращает истину, если есть ошибка (наверное, на то были причины ¯\_(ツ)_/¯). Беглый просмотр кодовой базы показал, что таких функций много, они встречаются в разных модулях, и быстро исправить всё не получится.

Ваша задача — добавить проверку почты и сделать это «ВЧЕРА!!!». Из‑за сроков отрефактироить код перед этим не успевается (напомню, подобных функций много). Вы понимаете, что правильнее — написать новую функцию так, чтобы она возвращала истину, если ошибок нет…

Но прямо сейчас последовательность — важнее правильности, и первый шаг — сделать по аналогии, сделать «неправильно»

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

Когда задача решена, уже можно (и нужно) отправиться в рефакторинг‑поход и всё переделать. Но до этого момента между последовательностью и правильностью, стоит выбрать первое.

Болеть за продукт

Я, наверное, невероятно очевидную вещь сейчас скажу, но очень надо, ужизвинити.

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

Без веры в продукт задачи становятся просто какими‑то штуками, работать с которыми скучно, неинтересно, субъективно бессмысленно. Чё‑то там сбоку происходит, ну и ладно.

С ней же задачи — это боль пользователей, которую видно и которая зудит. От этого зуда и появляется желание поправить баги, обкатать фичу, проверить гипотезу.

Но что делать, если не получается болеть?

Зависит от причины. Ведь можно просто устать, и тогда отпуск это исправит. Вернётесь, почувствуете охоту фигачить — круто, значит, действительно просто устали, и надо было отдохнуть.

А можно устать от продукта. Так бывает, люди меняются, всё нормально. Осознайте, насколько сильно просели мотивация и польза (вам и от вас) и примите решение, что делать дальше.

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

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

P.S. Я сейчас не говорю об аспектах, связанных с командой, комьютом, офисом, плюшками и проч. Речь именно о том, над чем вы работаете.

P.P.S. Примерно о том же, но гораздо подробнее, я писал во «Фронтенд — это не больно!».

Раньше