Быть программистом
Чему я научился за 5 лет продуктовой разработки и общения с коллегами
Сегодня поговорим про best practices разработки и командой работы. Многие из них я взял из опыта работы в Яндексе в отделе внутренних сервисов. Другие вывел сам за время работы в ДомКлик (дочка Сбера). Принципы разработки имеют уклон в Python backend, но не ограничены им. Принципы командной работы справедливы для любого стека и предметной области.
Предисловие
Зачем разработчику составлять манифест своих взглядов на рабочий процесс?
- Собрать перед глазами, чем для него отличается плохо пахнущий проект от проекта мечты.
- Снабдить свои принципы выверенными примерами и аргументацией. В будущем не тратить время на объяснение новому коллеге, почему ставить trailing comma — это хороший тон, а давать ссылку на свою статью.
- Отправить свой взгляд на работу будущем тимлиду в ответ на оффер. Если ваши подходы отличаются принципиально, то лучше выяснить это на берегу и сразу пойти в другую команду.
Часть-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 “Разработчики имеют такие логи, на какие у них хватает фантазии”
Список чтения:
- 12 факторное приложение
- Ликбез по бекенд-разработке на Python: Школа Бекенд Разработки Яндекса
Часть 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, но и немного лидер команды (даже если не является тим лидом) и хороший помощник менеджера продукта.
Список чтения:
Напутствие
За пару недель до публикации статьи я дал друзьям-разработчикам почитать драфт. Обсуждение было интересным и даже повлияло на финальное изложение некоторых пунктов. А один из друзей назвал раздел про командную работу “рецептом как вырасти из мидла в сеньоры”. Надеюсь, рецепт вышел удачным, и специй в самый раз. Удачи вам, коллеги, и достойных проектов!