Быть программистом

Чему я научился за 5 лет продуктовой разработки и общения с коллегами

Сегодня поговорим про best practices разработки и командой работы. Многие из них я взял из опыта работы в Яндексе в отделе внутренних сервисов. Другие вывел сам за время работы в ДомКлик (дочка Сбера). Принципы разработки имеют уклон в Python backend, но не ограничены им. Принципы командной работы справедливы для любого стека и предметной области.

Предисловие

Зачем разработчику составлять манифест своих взглядов на рабочий процесс?

  1. Собрать перед глазами, чем для него отличается плохо пахнущий проект от проекта мечты.
  2. Снабдить свои принципы выверенными примерами и аргументацией. В будущем не тратить время на объяснение новому коллеге, почему ставить trailing comma — это хороший тон, а давать ссылку на свою статью.
  3. Отправить свой взгляд на работу будущем тимлиду в ответ на оффер. Если ваши подходы отличаются принципиально, то лучше выяснить это на берегу и сразу пойти в другую команду.

Часть-1. Принципы разработки

Итак, как по моему мнению должен выглядеть код Python-бекенда.

  • Fail early
    Программа должна валидировать все входные данные на формат и логическую корректность (данные из БД, из сторонних API, с фронтенда). Если в данных есть ошибка, падать нужно сразу. Иначе можно получить неожиданное поведение в продакшене, порчу данных и потратить часы на поиск причин.
    Подробнее: https://www.wikiwand.com/en/Fail-fast
  • Классы — это объекты доменной области
    Иначе появляются абстракции, понятные только автору кода. Реализация, зависящая от абстракции, может плохо поддаваться простым в продуктовом смысле расширениям.
    Развитие этой идеи до стиля организации кода: Domain-driven design
  • Логика инкапсулируется в классы
    Если логики слишком много, чтобы быть частью модели или клиента, создаются классы экшенов и воркфлоу для запуск этих экшенов. Воркфлоу может хранить стейт, и экшены могут декларировать множество стейтов, в которых и только в которых их можно запускать.
    Если создание класса чрезмерно, вынесите функции в отдельный модуль и осмысленно его назовите.
    Аргументация: никто не любит лапша-код.
  • Думать шире, чем один вызов API
    Считаете код корректным? А как он поведет себя при повторном запуске? Первый запуск мог привести данные в базе к такому виду, какого до этого релиза быть не могло. Не забыли ли вы поддержать на вход данные, которые ранее были невозможны?
  • Обрабатывать все коды ответов
    Bad practice: обращаться к телу ответа API и начинать с ним работать, не посмотрев код ответа. Некоторые API при ответе 400 все-равно кладут данные, но объект может содержать не все поля, не просто так же сервер ответил 400.
    Аргументация: это частный случай fail early.
  • Явное лучше неявного, простое лучше сложного
    Очевидные правила, но и про них можно забыть.
    Аргументация: Zen of Python
Программист, постигший дзен
  • Стой на плечах гигантов
    Была проблема. Ты написал для нее тривиальное решение. Но вот уже тривиальное решение начинает обрастать вспомогательными методами и обрабатывать различные сценарии. Задумайся. Если сейчас потребовалось докинуть логики, не потребуется ли вновь? Найди готовое решение с уже проработанными различными кейсами и перейди на него, пока переход не дорог, и вы не пришли к узко специфичной логике, которой не будет ни в каком готовом решении. Иначе придется развивать и поддерживать велосипед.
    Аргументация (сомнительная): порядок лучше хаоса, энтропию следует уменьшать.
  • Не закапывайся
    Не закапывайся в эти правила, когда сжег все SP, отведенные на задачу. Заведи тикет на доработку, поставь в код TODO с этим тикетом и закодь как получается. Product first. Один-два спринта безопасный, но не элегантный код может пожить.
    Вы можете возразить:
    “Миша, до такого тикета никогда не дойдут руки. Лучше я сейчас задержу фичу, иначе мы вечно будет жить с некрасивым кодом в этом месте”.
    Отвечу коротко:
    Такие проблемы нужно обсуждать на ретро, а в моменте нужно работать на завершение спринта.
    Отвечу длинно:
    Если до таких тикетов никогда не доходят руки, то либо у вас проблемы с планированием/отстаиванием тех квоты перед продакт менеджером (опять же обсуждается на ретро), либо ваш менеджер принял стратегическое решение, что реализация должна быть good enough, а на допиливание напильником у вас времени нет. Тогда допиливание напильником — это диверсия за спиной продакта, и так делать тоже нельзя.
  • По логам должно быть легко искать
    Пишите логи в виде json и снабжайте сквозным идентификатором запроса (для API) или номером запуска (для периодических задач). При входе в код по работе с объектом доменной области добавляйте его id в контекст.
    Пример вопроса, на который хочется найти ответ в логах:
    “почему на странице этого товара старое описание”.
    Что делаем?
    Ищем лог по id товара, берем из него номер последнего запуска периодической таски по обновлению данных, фильтруем логи по ее id и id товара. Видим лог про timeout запроса в микросервис с описаниями товаров. Проблема локализована.
    Больше идей про работу с логами: Доклад с PiterPy “Разработчики имеют такие логи, на какие у них хватает фантазии”

Список чтения:

Часть 2. Принципы командной работы

Что я ценю в коллегах и не ленюсь поддерживать в себе.

  • Проактивность
    Вот пример, когда продукт кратно страдает от пассивной команды.
    Менеджер обнаружил баг в данных на некоторой странице вашего сайта и написал об этом в чат команды.
    Плохо: все молчат.
    Все еще плохо: разработчики пофилософствовали о теоретически возможных причинах и вернулись к своим делам.
    Хорошо: Команда и менеджер оценили срочность бага. Фронты и беки посмотрели недавние релизы на связанность с ним, локализовали проблему сервисом и местом в сервисе или данных. Завели тикет-баг. Команда возвращается к менеджеру с вопросом о критичности появившийся задачи.
    Идеально: то же самое, но из фронтов и беков быстро вызвалось по одному разработчику этим заниматься, а остальные продолжили работать по спринту.
Программист, игнорирующий сообщение о проблеме
  • Эмпатичность
    Эмпатия к коллегам и к пользователям продукта. Старайся сопереживать окружающим и помогать им в их трудностях. Ставь себя на место собеседника или потребителя.
  • Болеть за качество сервиса при программировании
    Продумывать свой код на всех путях исполнения, на всех возможных входных данных и на всех возможных ответах внешних API.
  • Болеть за качество сервиса в тестах
    Не писать тесты исключительно ради тестов. Покрывать реальные пути исполнения. Если нет ресурсов покрыть фичу юнитами, напиши один, пусть даже не самый элегантный, интеграционный тест, который точно проверит позитивный и основные негативные сценарии.
    Подробнее: Простые рецепты хороших юнит тестов
    Подробнее:
    1 test = 1 arrange + 1 act + 1 assertion block
  • Болеть за качество сервиса в runtime
    Если релиз привел к пятисоткам, ASAP откатываться и потом чинить. Не забывать про SRE, как минимум — создавать мониторинги и реагировать на алерты.
    Подробнее: SRE-guide от Google
  • Болеть за качество сервиса при ревью
    Быть ответственными к отправляемому на ревью коду. Ответственно ревьювить чужой код. Опасность: на это уходит много времени и приходится идти на компромиссы.
    Реальные советы: Рецепт полезного код-ревью от разработчика из Яндекса и еще один.
  • Продуктовое мышление
    Разрабатывать продукт, а не закрывать тикеты. Product first, технологии должны решать проблемы продукта, а не воплощать в реальность влажные сны разработчика.
  • Не держи в себе
    Если что-то не получается или непонятно, не жди стендапа и сразу обратись к коллегам за советом. Работа профессионала в команде остается командной работой. В long run для команды выгоднее, если ты быстро задашь вопрос и потратишь 2 x 10 человеко-минут на вопрос и пару своих часов на реализацию (то есть меньше 2.5 человеко-часов), чем целый день на закапывание.
    Дальше по теме: принять комплекс самозванца и научиться жить с ним
Программист не стесняется себя и счастлив
  • Критическое мышление
    Умение мыслить и аргументировать свои мысли помогает при проектировании фичей и при ревью, избавляет от лишних обид.
  • Готовность и желание учиться
    В идеале — еще и желание учить.
  • Soft skills
    На уровне, необходимом для успешной и быстрой коммуникации.

Уверен, есть множество senior-разработчиков, обходящихся без этих качеств, получающие неплохую зарплату и довольных собой. Но в моем видение Senior Developer — это не только опытный programmer, но и немного лидер команды (даже если не является тим лидом) и хороший помощник менеджера продукта.

Список чтения:

Напутствие

За пару недель до публикации статьи я дал друзьям-разработчикам почитать драфт. Обсуждение было интересным и даже повлияло на финальное изложение некоторых пунктов. А один из друзей назвал раздел про командную работу “рецептом как вырасти из мидла в сеньоры”. Надеюсь, рецепт вышел удачным, и специй в самый раз. Удачи вам, коллеги, и достойных проектов!

Software developer, IT enthusiast and life researcher https://agrml.github.io/me/

Software developer, IT enthusiast and life researcher https://agrml.github.io/me/