fuse8Talks – это серия разговоров с людьми из fuse8. Мы часто рассказываем, как устроены процессы и продукты. Но в этой серии материалов сместим фокус на тех, кто делает эти продукты, принимает решения, ошибается, спорит и находит баланс между бизнесом и инженерией.
В пилотном выпуске — Федя Киселев, тимлид и разработчик с десятилетним опытом в компании. Мы поговорили о коде и людях, о ревью как «единой точке развития человека», о том, почему простые решения самые сложные, и о том, что чувствует разработчик, когда возвращается к собственному коду спустя годы.
Привет, Федя. Давай начнём по-классике. Расскажи немного о себе — кто ты, чем занимаешься, из чего состоит твоя работа?
Я работаю тимлидом в команде fuse8. В компанию пришёл уже лет десять назад. Это была моя первая работа. Я тогда учился на пятом курсе универа: вторая половина курса, оставалось только сделать диплом, учебной нагрузки мало, и как раз освободилось время.
Изначально я пришёл джуном на бэкенд. Года два–три работал на этой позиции, дорос примерно до мидла. А потом случайно оказался тимлидом: на текущем проекте спросили, буду ли я брать на себя эту роль. Я сказал: «Почему бы нет, надо попробовать». Так и начал. Команда тогда была маленькая, несколько человек.
На старте было очень трудно: никакого обучения, никакой системы. Просто «ты теперь тимлид» — и вперёд, на проект. С тех пор мы сделали несколько продуктов. Сейчас я на четвёртом проекте, и в целом всё прошло удачно: набрал много опыта, особенно тимлидского.
Сейчас я на проекте, где прямо настоящая продуктовая большая команда. Часть людей — со стороны заказчика, часть — со стороны fuse8. От нас в команде человек восемь примерно. Я уже периодически пересчитываю: как с возрастом ребёнка после трёх лет — каждый раз вспоминать приходится.
Кроме разработчиков, в команде есть QA-специалисты, аналитики, PM, продуктолог, DevOps, БД-разработчики. В сумме постоянный состав — человек 15–20. Сейчас я управляю такой командой: стараюсь настраивать процессы, уменьшать свой бас-фактор на проекте, чтобы в идеале всё работало без меня, и активно занимаюсь онбордингом. За последние полгода–год к нам пришло много новых бэкендеров, их нужно погрузить в проект, научить нашим практикам и подходам.
А расскажи подробнее про сам продукт – для чего он, чем хорош?
Я работаю на Цессионарии – это ERP система для досудебного взыскания в сфере финтеха. Его мы начали разрабатывать давно — примерно в середине 2019 года. Активно делали его, а потом случился ковид, и проект приостановили: было неясно, что будет дальше.
Потом проект возобновили: сначала маленькой командой в несколько человек, потом вернули туда полноценную команду. И вот эта пауза — очень прикольный опыт. Ты приходишь на проект через несколько лет, в систему, которую сам же когда-то писал, и понимаешь, что теперь вообще ничего не понятно. Очень сильная рефлексия получается.
И как это ощущается? Получается распознать написанное? Вообще есть ли у твоего кода свой «почерк»?
Тут я бы разделил «почерк» на две части.
Первая — это именно стиль кода. Когда по формату, структуре, неймингу можно попытаться угадать, кто это писал. Вот здесь у нас всё, на мой взгляд, довольно хорошо: в идеале в команде ни у кого не должно быть своего ярко выраженного почерка. Тогда проще командно работать, поддерживать код, понимать друг друга.
Мы недавно были на вебинаре про простой код, и там как раз говорили, что все проверки и процессы должны быть устроены так, чтобы по виду кода было неясно, кто его написал. В целом у нас так и есть: часто смотришь на старый код — и вообще непонятно, чей он. Приходится идти в репозиторий и проверять. Иногда оказывается, что это ты сам, иногда — кто-то другой.
А вторая часть — это архитектурный почерк: как организованы файлы, как процессы взаимодействуют, как устроена БД, как применяются (или не применяются) DDD-практики и прочее.
Здесь это уже не только мой почерк — мы в 2019-м пытались командой сделать «идеальный» проект. Большинство решений действительно принимал я, но не все — мы что-то обсуждали вместе.
Есть архитектурные решения, которые сейчас я считаю неправильными. И исправлять их сложнее, чем править кодстайл. Приходится с этим жить: страдать, понимать, что на старте можно было сделать иначе, и потихоньку всё это рефакторить.
Где-то мы переписываем архитектуру «по пути», когда сильно дорабатываем функциональность и заодно чуть её улучшаем. Где-то приходит запрос от бизнеса, а мы честно говорим: «В текущей архитектуре это либо почти невозможно, либо очень долго. Давайте сначала отдельной задачей отрефакторим, а потом уже быстро сделаем нужную фичу». И рефакторим уже с пониманием того, какая функциональность сверху приедет.
Поэтому архитектурный почерк сейчас очень виден — и он создаёт нам немало проблем.
При этом есть интересный момент: проект по сути стартовал как внутренний стартап. Нужно было сделать быстро, непонятно было, «полетит» он или нет. И часть архитектурных решений, которые сейчас мы считаем неправильными, тогда были правильными — именно потому, что сильно ускоряли разработку MVP. Они помогли быстро выпустить первую версию.
А вот другая часть проблем — просто про недостаток хардскиллов и опыта в тот момент. Где-то сделали неоптимально, просто потому что ещё не знали, как лучше.
Можешь привести пример такого прям уродливого костыля, который в моменте сильно помог ускорить релиз и принёс пользу бизнесу?
Да, у нас есть такой классический пример — то, что мы делали ради ускорения MVP. И я думаю, это реально ускорило релиз процентов на 20. Мы использовали анти-паттерн generic repository для чтения данных из БД. Был один обобщенный репозиторий, не привязанный к конкретной таблице или сущности. Из него доставались данные для всего подряд.
Это работало в связке с автомаппером: мы настраивали маппинг таблиц БД и сущностей на кастомные DTO-модели. На базе этой модельки автоматически генерировался запрос в БД, а результат маппился обратно в DTO.
На старте это казалось магией. Пишешь модельку, настраиваешь маппинг — часто довольно простой — и буквально одним-двумя файлами у тебя уже есть и запрос, и ответ. Можно переиспользовать запросы. Для кучи задач на запуске проекта это было супер удобно и сильно ускоряло разработку.
Пока БД была маленькая, и нагрузки были небольшие, проблем не было. А вот потом, когда база разрослась и начался второй этап активных доработок, всё стало гораздо веселее.
Во-первых, проблемы с оптимизацией: автоматически собранные запросы часто получаются неоптимальными, их трудно тюнить по перформансу.
Во-вторых, проблемы с поддержкой. У тебя есть сущность, в БД у неё поле, например DealDate. Ты хочешь по коду найти, где «дата сделки» используется. И не находишь нигде. А по факту она используется в десяти местах — просто это поле продублировано в куче разных DTO-моделей. Это сильно мешает поддержке, повышает риск всё сломать при доработках.
Сейчас мы потихоньку избавляемся от этого generic-репозитория и переписываем всё на нормальные репозитории. Но как стартовый костыль для MVP он действительно помог.
А что для тебя пример красивого решения? Как ты понимаешь, что оно действительно оптимальное, достаточно хорошее, и его не придётся через полгода переписывать?
Для меня красота решения почти всегда в простоте. Чем проще и понятнее код, при этом хорошо работающий, тем лучше. Такой код легче читать, когда нужно разобраться, как всё устроено. Его проще дорабатывать. И он реже ломается.
Причём «понятный» не равно «примитивный». Понятность достигается за счёт нормального нейминга методов, переменных, за счет структуры. И ещё — за счёт комментариев.
У нас на старте проекта была большая проблема: мы почти не писали комментариев там, где боролись с какими-то особенностями библиотек или реализовывали странные, на первый взгляд, требования бизнеса. Когда мы вернулись к этому коду спустя несколько лет, стало понятно, что ничего не понятно. Почему сделано именно так: истории нет, объяснений нет.
Сейчас я считаю, что комментарии нужны как раз там, где есть неочевидное решение, хак или нелинейное требование. Не везде, а точечно. И это тоже часть «красоты»: ты смотришь на участок кода и сразу видишь контекст.
Поэтому, если коротко: красивый код — максимально простой и максимально понятный. А дальше уже идут другие характеристики: устойчивость к изменениям, тестируемость, архитектурная целостность. Это важные вещи, но это уже немного другая плоскость.
А где проходит граница между чистым, понятным кодом и оверинжинирингом? Как в команде заметить, что кто-то пошел слишком далеко в усложнение, и поймать этот момент до того, как станет поздно?
Начну с того, как у нас в принципе устроен процесс. На проекте почти все задачи проходят код-ревью — я бы сказал, процентов 99. Это последняя точка контроля. Не самая идеальная точка, но неизбежная: если проблема дошла до ревью, её там можно поймать.
Как это выглядит на практике. Ты читаешь задачу, идешь смотреть код — и вроде он что-то делает, но ничего не понятно. Появляется желание всё переписать. Или ты не можешь по коду понять, как это работает, и вынужден запускать проект, дебажить, чтобы разобраться. Это и есть маркеры: если ревьюер не может понять код без дебага, значит, человек переусложнил, либо есть проблемы с читабельностью и неймингом.
Теперь про оптимизацию. Часто оптимизация — противоположность простоте. Она усложняет код. И тут всё упирается в тип проекта. Если у тебя серьёзный публичный продукт с большими нагрузками — банки, маркетплейсы, крупные B2C-сервисы, — без оптимизаций никак. Там это обязательная часть работы.
Если это внутренняя система, как у нас, где пользователей ограниченное количество, то чаще всего можно обойтись без овероптимизаций. Дальше встаёт вопрос объёмов данных. Где-то их мало, где-то очень много. У нас данных достаточно много, и каждый месяц они растут, поэтому в отдельных местах нам оптимизации всё-таки нужны. Но всё равно — местами, не везде.
Есть ещё тема разделения потоков чтения и записи. Часто встречаются сервисы, в которых данные меняются не так часто, как читаются. Для таких случаев есть подходы, где чтение данных отделено от их редактирования. Тогда чтение можно сделать более сложным и оптимизированным, а код, который отвечает за добавление и обновление, оставить простым и понятным.
Если говорить про наш продукт: где-то нам нужна оптимизация обновления данных, где-то — только выборки, а чаще — ничего особо оптимизировать не нужно. Поэтому в основной массе кода хочется видеть простой, прозрачный код, и только в точечных местах — сложные мегазапросы с пятью join’ами и кучей интеграций.
И если ты заходишь на ревью и видишь вот такой «мегазапрос» там, где нет ни нагрузки, ни причин его делать — это и есть граница, которую человек перешёл.
Давай поговорим про код-ревью. Почему это не бюрократия, а необходимый инструмент для роста разработчиков и улучшения практик?
Важно сразу сказать: код-ревью — не только про развитие людей. Оно ещё и про здоровье кодовой базы, особенно если проект долгоживущий. Ревью помогает поддерживать нормальное состояние кода: простоту, читаемость, поддерживаемость, возможность вносить изменения без тотального переписывания.
Если говорить про разные типы проектов, то код-ревью, на мой взгляд, почти не нужно в ситуации, когда у тебя чистый стартап: нужно максимально быстро собрать MVP за минимальные деньги. Там ревью — это прямое замедление выхода фич и рост бюджета.
У нас на проекте ревью очень помогает в онбординге. У нас нет огромной документации по best practices в Confluence, нет отдельного тома по кодстайлу, паттернам и подходам — того самого «учебника по проекту», который можно выдать новичку со словами «Вот тебе неделя, сядь почитай, тут всё есть».
Раньше в этом не было особой необходимости: команда мало менялась, было много «старичков», которые и онбордили новичков, и держали в голове контекст. Поэтому мы использовали связку: несколько созвонов для ввода в проект, объяснение общих принципов и потом — погружение через задачи и код-ревью.
Человек делает задачи, а мы на ревью проверяем и кодстайл, и архитектурные принципы, и понятность. По ходу дела корректируем, подсказываем. За несколько месяцев — полгода человек через такой практический онбординг очень хорошо встраивается в проект и уже пишет код, который органично в него ложится.
Потом произошел переломный момент: новых разработчиков стало больше, чем старых. И тут начались сложности. Старички просто физически не успевают ревьюить всё подряд и параллельно вести свои задачи. Поэтому сейчас мы, начали-таки описывать best practices.
Ревью все равно остается полезным для шаринга новых знаний: ты заходишь в чужую задачку и вдруг видишь, что можно использовать какой-то новый оператор, по-другому написать запрос, применить новый подход или библиотеку.
Но тут тоже есть эффект масштаба: в большой команде из восьми человек один внедрил что-то классное, двое это увидели на ревью, а остальные так и не узнали.
Чтобы это исправить, мы ввели на проекте tech demo. Когда кто-то делает большую бизнес-фичу или важную техническую задачу, он потом созванивается с командой, рассказывает, какую проблему решал, показывает верхнеуровнево код, объясняет новые подходы или библиотеки. Если это что-то переиспользуемое, например обработка Excel, — показывает, как этим пользоваться.
В итоге у нас сейчас разделение:
- в команде до четырёх человек код-ревью действительно может быть единой точкой развития — и дополнительных процессов почти не нужно;
- если человек больше, ревью будет про поиск сложных багов, которые сложно поймать тестами, про то, чтобы не упустить важные нюансы из ТЗ и сохранить единый стиль и качество кодовой базы.
Ты говорил, что на ревью часто ловятся вещи, которые негативно влияют на продукт и могут не вылезти в тестах. Как вы обычно делитесь такими находками с командой?
Если мы на код-ревью видим проблему — будь то недочёт в ТЗ или ошибка в реализации — мы решаем её в рамках этого ревью. До продакшена такие вещи, как правило, не доходят. И тогда нет необходимости поднимать это на уровень всей команды.
Зато очень важно, как мы даём фидбэк. Просто написать «здесь какая-то фигня, перепиши» — плохой вариант. У меня у самого лет пять назад был период, когда я, видя неоптимальный код или баг, просто заходил в чужую ветку, переписывал за человека и оставлял сверху комментарий, что «так лучше». Это не работает.
Нужно писать развернутый комментарий: описать проблему, объяснить, почему это не будет работать или почему это ухудшает поддержку. Иногда — дать больше контекста по функциональности. Можно накидать кусочек примера кода, но не переписывать всё целиком за человека. Или пошагово описать, как можно улучшить решение.
Так человек сам вносит правки, при этом обучается и онбордится дальше. И это касается не только новичков, но и старичков тоже — все иногда узнают что-то новое через ревью.
Если это типовая проблема, которая повторяется у двух-трех людей, тогда её проще обсуждать уже как паттерн, а не как персональный промах. Мы выносим её в отдельные созвоны и в документацию по best practices. Там мы разбираем конкретные кейсы, но стараемся делать это без эмоциональной окраски: просто по фактам — что было сделано, к каким проблемам это может привести, как лучше делать. Обычно формулируем это так: «В последнее время в ревью часто видим вот такую ошибку». Без привязки к конкретным именам.
И это сильно снижает напряжение. Люди видят, что это не «я один всё делаю неправильно», а просто общая «болячка» команды, которую мы сейчас лечим.
Главное — тон, а не сам факт разбора.
Перейдём к более общим практикам. Какую привычку или практику ты бы вообще выпилил из отрасли? То, что, по твоему опыту не приносит, а мешает.
Сложный вопрос — у нас просто сейчас очень адекватный заказчик и довольно здоровые процессы. Но если говорить в целом по индустрии, я бы выделил такую практику: полная изоляция IT-команды от заказчика.
Когда вся коммуникация идёт только через PM’а или продуктолога. Есть один–два человека-«шлюза», и через них проходит всё общение. Разработчики никогда не общаются напрямую с бизнесом, не ходят на общие созвоны.
Эта практика, с одной стороны, «экранирует» разработчиков от давления, от эмоциональных качелей заказчика, улучшает атмосферу в команде. В этом действительно есть плюсы. Но с другой стороны, сильно снижается скорость и качество обратной связи. Не всегда понятно, как люди реально пользуются фичами, что им неудобно, какие у них ожидания на будущее.
В такой ситуации есть риск релиза фич, которыми потом невозможно пользоваться в реальной жизни. Не потому, что разработчики плохие, а потому что где-то по пути потерялся контекст, что-то не спросили, не учли, неправильно поняли. Плюс риск неправильной приоритизации, потому что о планах как следует никто не поговорил.
Мне кажется, здесь важно не уходить в крайности. Не нужно давать всей команде неограниченный доступ к заказчику. Но и полная изоляция — тоже вредна. Как минимум лиды направлений должны иметь возможность общаться с бизнесом напрямую и периодически участвовать в общих созвонах.
Иногда проблема не в том, что разработчики не общаются с заказчиком, а в том, что тот самый «шлюз» — аналитик или PM — что-то недоработал. Ты с этим согласен?
Отчасти да, но я бы всё равно смотрел на это как на вопрос процесса, а не конкретного человека. Если у тебя один аналитик ведёт одну задачу от начала до конца, то да — он может что-то не учесть, где-то ошибиться, о чём-то не спросить. Все люди ошибаются. И чем дольше всё держится на одном человеке, тем выше риск.
Если бы на каком-то этапе — не обязательно с самого начала, хотя бы ближе к концу — подключались другие люди и обсуждали всё вместе, в том числе с заказчиком, качество ТЗ и решений было бы выше.
Я иногда сравниваю это с публичными продуктами. Представь сайт, где нет ни одного канала обратной связи: ни формочки «оцените сервис», ни быстрого контакта, ни опросов. Тогда команда делает продукт по ощущениям: как кажется, так и будет. Но проверить, как совпадает это с реальностью, невозможно.
Здесь похожая история. Разработчики не могут почувствовать, как система живёт в руках пользователей, если они вообще не видят этих людей и не слышат их голос.
Если, например, хотя бы на этапе демо перед выкладкой на прод подключать тех, для кого делалась фича, можно заранее поймать несовпадения ожиданий и реальности. Прокликать сценарий, посмотреть, где неудобно, и заложить время на доработки ещё до релиза.
Раз уж мы ушли в тему коммуникации, должен ли разработчик уметь объяснять свои решения простыми словами и понимать «обывательский» язык запросов бизнеса?
Не везде есть идеально настроенные процессы передачи информации от бизнеса к аналитике, от аналитика к разработке и дальше. Часто приходится самим «переводить» туда-обратно. Мне кажется, это как раз тот навык, тот софт-скилл, который универсален не только для разработки, но и вообще для любой отрасли.
В нашей команде этот скилл мы развиваем на перфоманс-ревью. Берем примеры, когда что-то вовремя не спросили, когда что-то было не так описано. Важно все эти проблемы не замалчивать и не обсуждать абстрактно. Лучше всего учат конкретные примеры.
На сеньорном грейде навык перевода с разработчиковского на русский должен быть вообще у каждого, на мой взгляд. Потому что делать это не только с заказчиком надо будет, но и с командой. Нужно будет на техдемо, например, объяснять фичи всей команде. Ну а в идеале, конечно, уметь это делать уже будучи мидлом.
У нас на проекте есть еще чатик с вопросами на всю команду. Когда по задаче возникает какой-то вопрос, разработчик должен в этом чате максимально понятно все расписать: дать контекст, сформулировать проблему, поразмышлять, к чему может привести игнорирование этой проблемы, добавить все полезные ссылки. Так все вместе тренируемся друг друга понимать и не оставлять пробелов в знаниях по продукту.
Ну, и напоследок, что можешь посоветовать ребятам, которые читают это сейчас?
Для новичков лучший совет – не стесняться задавать глупые вопросы. Потому что это единственный путь к развитию. По себе помню, что первые два года на работе я сильно стеснялся спрашивать старших коллег о моментах, которые мне были непонятны. Я считал, что вопросы, которые у меня появляются, глупые, и молчал, чтобы не сойти за неумеху. Поэтому гуглил сам, понимал что-то урывками и частично. Сейчас могу точно сказать, что это плохой путь.
Сейчас задаю вопросы везде и всегда: бизнесу на проекте, если не понимаю что-то по их части; коллегам по разработке, если не вижу где-то полной картины или кто-то что-то новое впилил. И это всегда окей и круто, если в итоге удается все слепые зоны для себя закрыть.