Как я делал торговую площадку. Часть 2

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

Технолог тоже дизайнер

Ни один план не выдерживает встречи с реальностью, с дизайном та же история.

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

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

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

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

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

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

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

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

Тоже бекендер и девопс

Код писало двое человек: я и серверный программист. У нас не было ресурсов на отдельного человека для инфраструктуры, поэтому окружение, интеграцию и доставку настраивали мы сами.

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

У меня не получилось на 100% создать структуру с сине‑зелёным продакшеном и выделенным сервером под тестирование. Получилось примерно наполовину: мы ограничились CI/CD и прогонкой тестов в Travis.

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

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

Автоматизация также спасает от человеческой невнимательности. Если надо сделать несколько рутинных операций, человек всегда будет тупить проигрывать компьютеру. Люди ленятся идти по списку, делают что‑то неправильно, случайно (или неслучайно) пропускают пункты. Компьютер всегда выполняет инструкцию полно и правильно. Перестраховывайтесь от человеческого фактора.

Тоже редактор и маркетолог

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

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

Мы думали, как объяснить пользу этого экрана, показать принцип работы, как подписать «включатель» этой фичи. Потом то же — но для участников. Если бы за каждой кнопкой я бежал к дизайнеру за макетом, то мы, скорее всего, зарелизились бы году в 3019. Умейте работать с текстом, экономьте время и запускайте проекты быстрее.

Тоже менеджер

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

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

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

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

Тоже эйчар

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

Проводить собеседования для меня было в новинку. Я сделал вывод, что проводить именно технические собеседования не имеет смысла. Вместо этого я фокусировался на вопросах о видении человека своей работы, на том, насколько кандидаты подходят нам, а наша компания — им.

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

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

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

Мы с Костей проговорили цели, задачи и проблемы и решили, как именно надо искать нового человека. Снизили напряг, сократили ресурсы на поиск и фильтр кандидатов. В итоге я нашёл двух отличных ребят: Тимофея Аксенова и Романа Макарова. Сейчас весь фронт на них.

Тоже тимлид

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

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

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

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

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

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

Параллельно учился описывать задачи более понятно. Скудное описание демотивирует, потому что непонятно откуда брать нужные данные. Слишком объёмное — лень читать. Учился находить баланс.

И вообще кто угодно

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

Конечно, у меня тоже бывают моменты, когда я думаю «уф, это же не моя работа, почему я вообще должен этим заниматься?», но чаще всего — это от усталости. Работа может приносить удовольствие, если найти отчего прёт. Меня пёрла огромная амбициозная задача. Вроде, справился.

Список литературы, кек

О площадке:

Статьи, посты, доклады:

Я награфоманил:

Крутые ребята:

Как я делал торговую площадку. Часть 1

Последние джва полтора года я работал в закрытом режиме над фронтендом для последнего проекта. Мы не могли себе позволить до релиза пилить фичи итеративно и выкатывать их по мере готовности. Нам нужно было полностью переделать сервис и выкатить новую версию разом.

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

Контекст

Задача была в том, чтобы переписать фронт, обновить дизайн и добавить новые типы торгов.

Проект оказался сложным — я с таким уровнем до этого не сталкивался. Чтобы как‑то справиться, старался искать рекомендованные подходы, читать книги об организации кода и архитектуре и применять это всё по ходу дела.

Теперь вот рассказываю о граблях, которые насобирал.

Проблема 1. Один в поле

Я начинал писать фронт один. Писать сервис одному — значит страдать от недостатка ревью и обратной связи. Рефакторить трудно, спросить совета не у кого (исходники‑то под NDA).

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

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

Проблема 2. Проседающая мотивация

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

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

Но иногда и дробление переставало работать. Мне тогда помогало сесть и начать хоть что‑то делать. Начав с малого, можно втянуться и продолжать работать. Но именно «сесть и начать» бывает сложно.

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

Проблема 3. Отсутствие обратной связи

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

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

Проблема 4. Страх запуска

Итеративная разработка — это делаем фичу и деплоим её, когда она готова. Если что‑то пошло не так, узнаем об этом сразу, будет понятно, что вызвало проблему и как её исправлять.

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

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

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

Преодолевать страх — трудно.
Деплоить итерациями — спокойнее.
Понять это на своём опыте — бесценно.

Проблема 5. Фичеризм

Если пилите проект в закрытую, очень велик соблазн добавить как можно больше фич. «Раз уж мы не выкатываемся по частям, давайте сделаем всё‑всё‑всё.» Это — путь в никуда.

Единственное решение, которое помогло нам, — назначить конечный срок запуска и не сдвигать его. Всё, что не успели, перенесли на версию 2.1.

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

Проблема 6. Размер и сложность

Помнить всё — нереально; слишком дофига деталей и нюансов.

Важно решения документировать, но важнее всего — описывать причины, по которым это решение принимается. Иначе велика вероятность, что потом вы не вспомните, почему какая‑то фича работает именно таким образом.

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

Проблема 7. Собственно запуск

В итеративной разработке релиз — это вполне обыденное дело. Сделали, зарелизили, работаем дальше.

В закрытой разработке релиз — это веха. Жизнь делится на до и после. После релиза, как правило, работы становится больше.

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

Это может стать стресс‑тестом и для вашего продукта, и для вас. А там — привет, пункт 2 ↑

Итог

Это было трудно. Наверное, это был самый сложный проект и по сложности системы, и по поддержанию ритмичной работы с самодисциплиной.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Потерянная абстракция

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

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

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

Вопрос: что не так с этим условием в конце?

class Reader {
  constructor(user, shelf) {
    this.user = user
    this.shelf = shelf
  }

  getBooksFromShelf = () =>
    this.shelf.getBooks()
}

const reader = new Reader(
  someUserData, 
  someBookShelfData)

// вот тут проблема ↓
if (reader.getBooksFromShelf().contains(book.id)) {
  // ...
}

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

Смысл вместо реализации

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

Но как только мы…

// заменим условие на метод-предикат canRead,
// в который вынесем всю техническую реализацию...
if (reader.canRead(book)) {
  // ...
}

class Reader {
  // ...
  canRead = book => 
    this.getBooksFromShelf().contains(book.id)
}

…код станет гораздо понятнее.

Говорящий метод

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

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

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

В книге “97 Things Every Programmer Should Know” есть глава “Code in the Language of Domain”. Она как раз описывает подобные случаи.

Ссылочки, ссылочки, ссылочки

Ютуб, книга, два конспекта и статья:

Раньше ↓