Как заставить машину написать тесты из кода за тебя
Мы живем в неидеальном мире. Здесь код пишут люди, а люди по своей природе склонны совершать ошибки. Все бы ничего, ошибки можно отловить на этапе тестирования и не дать им никому навредить. Можно, если писать тесты. Чего люди делать почему-то не любят. Но возможно, есть надежда — автогенерация тестов из написанного кода.
Юлия Волкова хочет проверить идею в реальности и пробует переложить на машину создание тестов на основе кода, причем без использования дополнительных инструкций или контрактов. О том, какие открытия приносит путешествие в мир метапрограммирования, AST, синтаксического анализа и токенизации, и чего это все позволило добиться в автогенерации тестов, Юлия расскажет на Moscow Python Conf++. А пока я расспросил, откуда появилась сама идея — автоматизировать тестирование, что лежит в основе прототипа и с чем еще предстоит справиться.
Юлия Волкова (xnuinside) Senior Python Developer в GridDynamics. В свободное время пишет pet-проекты, которые иногда находят применение в реальной жизни. Так, раз за разом обкладывая тестами legacy-код, Юлия заметила, что многие штуки можно сделать автоматически. Конечно, понять код и написать к нему «правильные» тесты иногда и для живого опытного разработчика слишком сложная задача. Но автоматика вполне может сделать много простых тестов и подготовить код-основу, которую разработчик сможет доработать по своему усмотрению.
— Начнем с самого больного, почему, как ты считаешь, люди не пишут тесты? Умные люди говорят писать тесты, а их все равно не пишут. Почему возникает такая проблема?
— Я думаю, есть несколько причин. Во-первых, большинство из нас ленивы по своей природе. Мало кто прямо любит писать тесты — просыпается с утра и говорит: «Надо начать день с 15 тестов, иначе все будет плохо, а заодно и моя жизнь не удастся». Чаще проявляется природная лень, особенно когда видишь, что метод не очень интересный, в нем понятный, примитивный код, но тебе надо все равно покрыть его тестами.
Мало, кто пишет TDD, потому мало того, что надо написать тест, надо еще потратить время на код.
Проблема связана с тем, что на разработку не отводится бесконечное количество времени. Всегда есть ограниченные по времени хотелки продактов. В продуктовых командах вообще, как правило, все надо было вчера, потому что время — деньги. Менеджерам кажется, чем больше мы зеделиверим фич, тем дороже и лучше будет наш продукт. И не всегда очевидно, что покрытие тестами, качество кода напрямую влияет и на последующую скорость добавления фич, поддержки кода, обновление и т.д.
Мы часто сваливаем все на менеджеров и говорим, что нам не дают достаточно времени, а так бы сидели и писали тесты. На самом деле не всегда это так. И не всегда опытные здравые разработчики говорят писать тесты, а более молодые коллеги не хотят.
Я давно в IT, но непосредственно разработкой занимаюсь 3–4 года. До этого я работала больше на менеджерских позициях и повидала разных разработчиков. Есть много людей, которых вроде как неопытными не назовешь, потому что они уже 10 лет пишут код, но при этом считают, что тесты как таковые не нужны. Допустим, не надо покрывать код unit-тестами, потому что есть QA-инженер, который должен выловить баги. А о том, что такой инженер может покрыть не все кейсы end-to-end тестами, они не думают.
— Если не впадать в такие крайности, то как ты считаешь, кто должен писать тесты? Это должен быть сам программист, джуниор или, наоборот, самый классный разработчик в команде?
— Если мы говорим про unit-тесты, это точно не должен быть QA. Это точно должны быть те тесты, которые проверяются, проходятся и пишутся до коммитов, Они должны быть направлены на pull request, то ни в коем случае их не должен потом писать другой человек. Я бы, например, как ленивый не-джуниор разработчик, как раз сажала бы джуниоров писать тесты на примитивный код. Есть вещи, для понимания которых достаточно просто читать код на среднем уровне и писать asserts, такая работа вполне подойдет для джуниоров и будет полезна для их развития.
Это unit-тесты, которые просто покрывают state кода, как он есть. Эти тесты не проверяют, насколько функция валидна по отношению к требованию задачи в таске, а просто убеждаются, что код делает то, что делает, и делает это корректно…
А вот проверять валидность кода к бизнес-требованиям, к бизнес-логике все-таки должен человек, который эти требования имплементирует. Он должен понимать, что и как он покрывает тестами. Но непонятно, чем поможет, если человек изначально не понял задачу, написал метод, который некорректно её решает, но к этому некорректному методу сделал корректный тест.
— Можно сказать, что проблема в том, что люди плохо себе представляют, как происходит процесс разработки программного обеспечения?
— Это очень субъективно. Ты представляешь себя как единицу разработчиков, которые понимают, что тесты нужны, почему они нужны, и ты считаешь, что это верно и хорошо. Но есть довольно большой пласт разработчиков, которые считают, что это избыточно. И, в каком-то смысле, наверняка и менеджеры по-своему правы, когда говорят, что тестами не нужно покрывать весь код, достаточно ручного тестирования на стейдже.
Не всегда корректно говорить, что человек, который не любит тесты, — неквалифицированный разработчик.
У него есть некоторое свое видение, и не мне судить. Я до сих пор часто встречаю разработчиков, которые пишут код 10 лет и говорят, что избыточно все покрывать unit-тестами, достаточно smoke-тестирования и работы QA.
Я, в свою очередь, некомфортно себя чувствую на проекте, в котором нет unit-тестов на функции. Мне важно, чтобы были хотя бы тесты, гарантирующие защиту от человеческого фактора, способные отловить случайно поставленную запятую или измененное имя ключа в dict. Но мне не нравится тратить на это время, всегда ведь хочется заниматься более «умными» задачами. Поэтому я и задумываюсь об инструментах для автоматизации процесса написания тестов.
— Как ты считаешь, играет ли свою роль то, что Python — динамически типизированный, и ничего не проверяется на этапе компиляции? Может быть, в других языках проще с этим?
— Думаю, играет, и сильную. Это вечная история про типы, но с появлением type annotations стало проще работать.
Например, в Python могут быть цепочки вложенных функций, где ожидаемый в конце list почему-то превращается в dictionary. Исполнение может никогда и не дойти до конечной функции, но в каком-то if, в каком-то исключительном случае все-таки дойдет, и тогда проявится ошибка.
Конечно, с типизированным языком такого не может произойти в принципе, потому что ошибка возникнет уже на этапе компиляции. В этом плане, конечно, Python дает дополнительные способы выстрелить себе в ногу (в голову и куда-нибудь еще). Особенно, если работаешь с крупными проектами с разветвленной логикой, где данные могут переливаться в разные вариации, в разные агрегации.
— Как тогда быть с типизацией? Как ты считаешь, типизация должна быть по максимуму или по минимуму? Какой должен быть баланс типизации в динамическом коде?
— Это опять-таки довольно субъективно. Многие люди пришли в Python именно потому, что здесь нет типизации и потому что он весь такой гибкий и удобный. Об этом надо не забывать и не отсеивать огромный пласт разработчиков, в том числе data scientist«ов и аналитиков, которые тоже пишут код. Допустим, мне, как бэкенд-разработчику, конечно, удобней, когда типизация есть вообще везде. В идеале, чтобы еще и mypy работал.
Но в большинстве проектов, в которых я участвовала, это невозможно. Потому что в проекте есть еще и data-аналитики, которые говорят, что потому и пишут на Python, что не хотят возиться с типами, им так удобно.
Большое количество людей считают, что плюс Python в отсутствии типов и типизации.
Нужно дорасти до некоторого уровня, чтобы понимать, когда и почему это становится минусом. В каких-то маленьких Python-скриптах или в маленьких проектах я тоже не использую типы, потому что знаю, что в скрипте на 2 функции типы не особенно нужны. Но это что-то такое, что я, грубо говоря, на коленке быстро сделала, чтобы что-то вынуть из базы. А в более крупных проектах я стараюсь везде по максимуму добавлять типы, если нет сопротивления со стороны других разработчиков.
— Я с тобой полностью в этом согласен. Осталось только понять, как использовать типы, потому что это отдельная непонятная тема.
Закончим разговор про типизацию шуткой: «Говорят, Haskell требует от разработчика намного меньше скиллов, потому что там все довольно просто: есть типы, ты их везде расставил и ничего не держишь в голове. А для разработки на Python нужны более классные разработчики, потому что тут нужно все типы держать в голове, не на кого положиться».
— Давай теперь обсудим подходы. Например, на прошлой конференции Кирилл Борисов рассказал, что legacy-проект можно покрыть smoke-тестами. А ты предлагаешь генерировать тесты из исходного кода. Чем такой подход лучше?
— Не скажу, что мой подход лучше, он просто другой. Покрыть код smoke-тестами хорошо тогда, когда это можно сделать. Мой предыдущий проект был квинтэссенцией боли, связанной с тестами. Это была data science платформа из 8 микросервисов и 20 тысяч строк кода. Проблема в том, что платформа получает на вход большое количество данных и характеристик к транспортным средствам, станциям и городам, различным парковкам и видам поставок, агрегирует и создает огромный набор потенциальных расписаний для этих транспортных средств по всему миру. Расписание учитывает огромное количество условий из разряда, где можно перезаправить транспортное средство, где сделать промежуточную остановку.
В системе множество разных методов, которые могут использоваться в 1–2 ситуациях, о которых, может, даже никто из клиентов никогда не вспомнит. Тогда написание smoke-тестов по факту превращается в написание тестов для всей системы с учетом всех функций и их комбинаций.
Smoke-тест должен проверить, что все работает на выходе и не сломалось минимально. Совсем примитивный smoke-тест о том, что система запустилась и как-то работает, в нашем случае не приносит никакой пользы. Допустим, мы проверили, что подключение к базе есть, что-то запускается, UI получает какой-то API. А потом шаг влево, шаг вправо — и ничего не работает. То есть smoke-тест как бы есть, но с продакшена все равно летят ошибки.
В этой системе как раз хорошо сработали unit-тесты: когда четко отслеживается, что функции не поменялись, не сломались после каких-то изменений кода. Код — он тоже разный. Разным проектам, разным задачам нужны и разные подходы к тестированию.
Идея, которую я сейчас прорабатываю, можно назвать автогенерацией тестов только условно. Это, скорее, инструмент разработчика. Я хочу получить инструмент, который напишет за меня тесты и прогонит весь код, который может прогнать и без меня.
Приведу пример. Есть маленькая функция, которая берет dictionary, из него какое-то значение и ключ. Этот ключ очень важен для бизнеса, но с точки зрения кода это довольно примитивная операция: взять из dictionary, пускай даже несколько раз вложенный, ключ; проверить, что он там есть, что он не нулевой; поменять его или, может быть, просто вернуть значение. Это довольно примитивный код именно с точки зрения AST. Я не хочу на него тратить свое время и писать тесты. Я хочу, чтобы машина сделала это за меня.
Речь идет именно о метапрограмме с кодом на входе и кодом на выходе. Скажем, py-модуле, который говорит: «Вот у меня есть assert, я «проассертил» тебе, что в таком условии есть raise-ошибки, в таком-то вернулись валидные value, при таком-то аргументе еще что-то произошло». То есть фактически делает ту работу, где я бы сама смотрела, что подается на вход функции, и писала бы это в тесте.
Я хочу, чтобы программа сгенерировала тот минимум, который она сама может за меня запускать. Но это должен быть файл с тестами, в котором потом при желании можно что-то изменить или расширить. Который можно закоммитить в Git, подвергнуть тесту тестов т.д.
— Насколько можно положиться на такие автосгенерированные тесты? Что я имею в виду — насколько сильно они завязаны на конкретную имплементацию, и как они будут себя вести при нормальном изменении бизнес-логики или рефакторинге?
— Идея в том, чтобы брать код в том виде, в котором он есть сейчас, и на его основе генерировать валидные тесты на текущий момент.
Можно, конечно, каждый раз перегенерировать тесты, но это будет неверно, потому что тогда не будет отслеживания состояния изменения кода. Соответственно, к этому всему есть еще test diff, то есть тесты генерируются только к тому, что не было покрыто тестами до этого. А уже созданные тесты нужно поддерживать самому.
Возможно, это немного паранойя, но пока я сомневаюсь, что при автогенерации можно гарантировать, что перегенерировав тесты, ты не покроешь валидными тестами не валидный код. Одно дело, когда в феврале 2019 года нагенерировал тесты, и если меняешь логику, то сам меняешь и тесты, потому что знаешь, какие внес изменения. Знаешь, почему посыпались тесты, и можешь соответствующе поправить тесты. И совсем другое дело, когда каждый раз их перегенерируешь. Тесты будут валидны, но уже только к тому измененному состоянию кода.
Я хочу получить инструмент для разработчика, а не штуку для повышения code coverage.
— Какие могут быть метрики успешности? Как понять, что мы хорошо сгенерировали тесты?
Назову то, на что я обращаю внимание, без чего мне кажется, что тесты не имеют смысла. Обязательно, чтобы в тестах обрабатывались все кейсы поведения кода, которые описал разработчик. Например, если есть if, который ничего не возвращает, а пишет лог, в тесте это лог должен отработать. Не просто так люди пишут warning и print. Соответственно, если где-то есть обработка raise-ошибки, надо отработать это в тесте. Если вдруг raise пропадет, то есть будет изменение логики кода, то это тоже надо отрабатывать.
Аналогично, если есть if-statements, то должна быть обработка в assert каждого условия. Тогда тест будет более-менее близок к правде. И не забываем, что это все должно запускаться, а не просто выдавать «success» в PyTest с пустыми телами тестов.
— Расскажи, насколько это сложно технически сделать. Звучит, как довольно тяжелая задача.
Да, это очень тяжелая задача, и, наверное, именно этот факт и несколько других обстоятельств как раз и привели к тому, что я об этом буду говорить в докладе на Moscow Python Conf++. Я хочу поднять эту тему, заинтересовать ей других людей, обсудить с ними варианты решения.
У меня ощущение, что никто просто не пытался этого делать, потому что задача сложная. Иначе бы в сети нашлись какие-нибудь артефакты типа кода, описаний, статей или хотя бы упоминаний, что была такая штука, но её забросили.
Чтобы понять, насколько это сложно, вспомним, как работает интерпретатор. В коде есть операции, statements, интерпретатор выполняет их — хорошо, не хорошо, завалились, не завалились — и выдает результат. Дальше разработчик вручную добавляет новые аргументы, опять запускает интерпретатор, смотрит, чтобы теперь все прошло успешно. Но когда пытаешься сгенерировать тесты для кода, сначала надо пройти по AST-дереву и понять, какие шаги нужно сделать, чтобы получить результат.
В функции может быть много групп аргументов, стратегий для аргументов, и много результатов под эти стратегии. Говоря стратегии, я имею в виду, что, допустим, есть if arg_1==1: raise error
. Это значит, что есть некоторая группа с arg_1=1
, для которой функция всегда возвращает ошибку. Но при аргументе arg_1>2
будет другой результат работы функции, и создается уже вторая группа, вторая стратегия.
Соответственно, нам нужно найти и выделить все такие группы аргументов (если они, конечно, есть), при которых функция меняет свое поведение. А потом проследить цепочку действий: что будет происходить внутри функции с этими аргументами для получения конечного результата.
Более того, не забываем о том, что кроме того, что есть некоторый аргумент, есть еще действия внутри функции, например, assign переменных, вызов других функций. То есть получается еще и граф зависимостей методов от методов, когда для проверки некоторого кода надо сначала получить результат работы другого кода.
Соответственно, для генерации тестов надо сначала получить всю нужную информацию из AST-дерева, а потом уже генерировать для каждой стратегии аргументы, параметры, данные. С ними пройти всю цепочку действий, получить результат, и только тогда у нас будет валидный тест с разными asserts. Это сложная задача.
Я не думаю, что когда-нибудь удастся на 100% покрывать всевозможные кейсы автоматически, например, для огромных полотен исходников Django. Это трудоемко, но интересно. Пока мне просто любопытно, до куда хватит терпения и сил дойти.
— Есть ли примеры из других языков и сфер, где что-то подобное работает?
— Известных аналогичных нет. Я думаю, потому что проще написать тест, чем пилить специальный инструмент.
Но у меня есть ощущение, что мы все равно рано или поздно автоматизируем то, что уже делаем хорошо.
Есть большой пласт разработчиков, которые хорошо пишут unit-тесты. У нас есть достаточно компетенций в разработке на Python, чтобы захотеть написать инструмент или библиотеку, которая сделает это за нас. А мы будем писать более сложные вещи, более сложные тесты.
Генерация тестов в каком-то виде есть и в Java, и в C, и в .Net. Но там тоже все, скорее, больше property-based или основывающиеся на контрактах. В C есть посимвольная генерация тестов, вроде как она как раз смотрит на код и на основе этого делает какие-то тесты. Но это настолько другой уровень абстракции в самом языке, что я не уверена, что это аналогичная история.
Если бы было что-то совсем похожее, то, конечно, можно было бы чего-нибудь перенять, подсмотреть.
— Как думаешь, фреймворки или, может быть, техники написания кода на Python упрощают или усложняют задачу по генерации тестов из AST-дерева?
— Трудно сказать, сильно ли в этом смысле отличается просто импорт какой-то библиотеки или использование прямо специфического фреймворка. Совершенно точно, может сильно усложнять работу то, что меняет поведение интерпретации процесса кода, например, C-расширения. Как с этим быть, я пока не знаю, но использование любимых третьих пакетов пока что в этой задаче упирается в то, что нужно резолвить импорты. Все просто со встроенными пакетами, но с импортами все становится сложнее. Некоторые идеи и реализации есть в mypy, но я пока историю с импортами сторонних пакетов не трогаю.
— Может быть, именно какие-то техники — много динамики, использование getattr — что-нибудь в этой духе? Или это нормально работает?
— Это как раз абсолютно нормально работает. Потому что getattr либо манипуляции с метаклассами видны в AST. Да, их нужно резолвить, и это добавляет определенную сложность. Но это в любом случае отслеживается.
— Мы уже говорили о том, что автосгенерированные тесты прежде всего предназначены для людей. Насколько они будут читаемы для людей? Внутри каждого теста будет много логики, assert? Как будет выглядеть разделение между кодом и данными, как ты это видишь?
— Сейчас я стараюсь изначально добавить в тесты всевозможные банальные вещи. Допустим, если это какой-то raise ошибки, то не просто with raise, а комментарий хотя бы оставить, что за ошибка, почему она выскакивает, чтобы человек, прочитав тест, понял, что произошло на самом деле, какой аргумент к какой ошибке приводит.
Asserts пока объединяются в один метод. То есть, если есть функция и к ней 5 состояний, которые мы хотим проверить, то пока 5 asserts пойдут внутрь функции.
Была мысль ввести name conventions, например: ошибкам в конце ставить error, логам тестирования тоже что-то свое. Но я пока это отложила, потому что вопрос, как создать конечный вид тестов в коде, непосредственно текстовый блок с тестами — это самая малозатратная операция. Если вдруг появится идея, что все надо переформатировать, то сделать это будет легко — есть уже готовые собранные asserts, надо просто выбрать другой внешний вид тестов.
— А ты поддерживаешь unittest или pytest?
— Pytest. И как раз из-за того, что мне не хочется сейчас тратить много сил на выходной результат. Pytest хорош тем, что для него есть много плагинов, декораторов, разных модификаторов, которые просто использовать.
Красивости, может быть, важны и для конечного пользователя, и для разработчика. Но на развитие идеи это вообще не влияет. Если понадобится поддерживать unittest, это можно будет легко добавить.
— Насколько этот подход связан с property-based-тестами?
— Сейчас для генерации аргументов используются просто моки типа: нужен int, дай random int. Но такие стратегии потом будет легко переписать, например, начать использовать hypothesis. Пока я не трачу много времени и сил на это, потому что понимаю, что потом смогу использовать сторонние генераторы для value. Сейчас, мне кажется, это не таким важным, как работа с AST.
— Планируешь поддерживать контрактное программирование или как-то особым образом выделять? Потому что оно сильно помогает в работе с unit-тестированием, property-based-тестирование, и тестами в принципе для понимания бизнес-логики.
— Если под контрактным программированием имеется в виду контракты в коде, то я как раз от этого отхожу максимально. Потому что, когда ты можешь использовать контрактное программирование, в принципе можно обвешать контрактами код и на их основе генерировать unit-тесты. И тогда мой инструмент не так нужен.
Я сейчас стараюсь вообще не думать ни о чем, что модифицирует код. Потому что, например, в проектах на аутсорсе, в которых я сталкивалась с проблемой отсутствия тестов —, а это были практически все проекты, как это ни грустно, в текущей компании — почти невозможно было трогать код. То есть было нельзя вносить изменения, пока не сможешь гарантировать, что этот декоратор или контракт не изменит всю функциональную составляющую кода.
Если есть возможность редактировать код, то контрактные тесты — это хорошо.
Но пока я исхожу из того, что такой возможности нет. А так, действительно, на базе контрактов можно генерировать unit-тесты и по сути реализовать дублирование функциональности.
— Расскажи о следующем важном моменте: как тестировать полученные тесты и насколько можно гарантировать, что эти тесты реально что-то проверяют?
— Мутационное тестирование никто не отменял, и в идеальной картине мира его безусловно нужно использовать в этой истории. Идея в целом такая же, как если бы тест был написан разработчиком вручную. То есть все, что есть для тестирования тестов, можно вполне применять.
— Давай теперь немного обсудим конференцию Moscow Python Conf++. У нас будет выступать один из разработчиков hypothesis, которую мы уже несколько раз упоминали. Что бы тебе было интересно у него спросить?
— Мне было бы интересно расспросить Зака о том, куда они хотят вместе с мейнтейнерами развивать проект: что добавлять, в какую сторону развиваться. Я знаю точно, что у Зака сейчас открыт PR на генерацию тестов. Они это делают регулярками. Точнее, добавляют к существующим unit-тестам декораторы.
Хотелось бы обсудить идеи автоматической генерации тестов с точки зрения того, как hypothesis на это смотрит, как смотрят на это контрибьюторы. Наверняка у людей, которые занимаются тестами на таком уровне есть какие-то задумки или, может быть, кто-то что-то уже пробовал.
— На это мы и рассчитываем, когда готовим программу конференции: чтобы доклады задавали темы для обсуждений, в процессе которых каждый нашел бы для себя новые идеи и направления для развития. На какие доклады ты сама пойдешь?
— Мне бы хотелось растроиться и пойти на все доклады в 12 часов. В это время как раз будет Zac Hatfield-Dodds, Андрей Светлов с докладом об асинхронном программировании и Владимир Протасов с автоматизацией рефакторинга. Пойду на какой-то из двух последних, а к Заку потом прибегу в конце доклада (прим. ред.: берите лайфхак на вооружение — почти полностью послушать новую тему, а к спикеру, с которым хочется пообщаться, прийти на конец доклада и вопросы).
Есть должен быть очень интересным доклад о data validation, мне это интересно непосредственно. И есть еще два доклада, на которые я бы тоже сходила, но они все буду идти параллельно с моим: это доклад Виталия Брагилевского о типизации и Christian Heimes про профайлинг. К сожалению, на них я никак не смогу попасть.
— Расскажи еще немного про тему своего доклада, зачем ты делаешь, то что делаешь, зачем выступаешь и чего ждешь от выступления?
— Мне хочется, чтобы было больше инструментов для автоматизации процессов разработки и чтобы возникало больше связанных с этим коллабораций. Такая активность есть, но на фоне постоянного написания одного и того же кода, мне кажется, что её должно быть больше.
Как я уже сказала, нет открытого опыта по автогенерации тестов в Python. Непонятно, занимался ли кто-нибудь этим, если да, то почему не взлетело, не пошло. Не знаю, насколько генерация тестов на базе AST будет актуальной для сообщества, насколько она может далеко зайти. Сейчас я занимаюсь этим, потому что мне интересен сам процесс, мне интересно копаться с AST-деревьями, более детально узнавать, как работает Python-код, встречаться с большим количеством разных нюансов, которые неочевидны, когда работаешь с кодом верхнеуровнево. Работа с AST деревьями приносит массу внезапных открытий.
Хочу, чтобы после доклада у людей возникли идеи, например, как автоматизировать что-то, что они используют в работе. Чтобы некоторые из них перестали писать куски кода, которые они и так пишут каждый день, и начали генерировать или уменьшать количество времени на их написание. Надеюсь кто-то выйдет с новым пониманием, как решить эту задачу.
— Где ты берешь время на то, чтобы выступать на конференциях, писать свои собственные библиотеки? Этот вопрос на самом деле всплывает постоянно, многие люди жалуются, что у них нет на времени вообще ни на что.
— Во-первых, про время. Я не очень удобный сотрудник для многих компаний в том плане, что я не занимаюсь вещами, которые мне кажутся неэффективными. Я стараюсь заниматься вещами, которые либо мне действительно интересны, либо которые я могу сделать эффективными и правильными. Если, допустим, менеджер хочет, чтобы я прямо сейчас поправила какой-то баг, который на самом деле не баг, а свежая хотелка заказчика, я не буду садиться и исправлять все обратно, потому что знаю, что заказчик вернется и скажет, зачем вы это сделали.
Я стараюсь на работе не делать лишнюю работу, не делать то, что повлечет потерю моего времени впоследствии.
Допустим, если меня просят задеплоить в пятницу, я говорю: «Ребята, я вас всех очень люблю, вы все большие молодцы, но если вам нужно сейчас что-то деплоить — пожалуйста, деплойте сами, а я пойду домой. Могу это задеплоить в понедельник, можем поговорить, почему произошла такая ситуация, что вы хотите деплоить сейчас в пятницу». Может быть болезненным в первые разы говорить заказчику или менеджерам такое, но в последующем люди привыкают, учатся и не просят тебя делать в пятницу вечером что-то очень экстренное. Они понимают, что, во-первых, никто не умер в прошлую пятницу, когда никто ничего не задеплоил, и даже никто деньги не потерял. Я стараюсь не делать то, что мне будет вредить.
Та же история про баги — если много багов, которые приходится постоянно фиксить, вопрос: почему эти баги появляются. Надо не фиксить их, а подумать, почему их так много, откуда они приходят и бороться в первую очередь с корневой проблемой. Это тоже всегда болезненные вопросы, когда менеджер или заказчик говорит, что нужно срочно фичу в продакшен запилить. Но нужно уметь говорить, что, если я сейчас трону этот код, то, возможно, у вас не то, что этой фичи, у вас продакшена не будет, так как код не покрыт тестами, в него нельзя добавить еще один if, потому что мы уже не помним, что делают остальные шесть.
Иногда надо перебороть себя и начать разговаривать. Это не всегда возможно, надо дорасти до некоторого уровня осознания того, что за то, какое количество времени ты тратишь на какую работу, ответственность несешь ты сам.
Поэтому, наверное, у меня есть время. Потому что я стараюсь оптимизировать мое рабочее время, сделать так, чтобы на задачу уходило определенное количество часов. При этом я понимаю, что в хорошей структуре должно оставаться 1–2 часа на техдолг и какие-то улучшения.
Не скажу, что я работаю 8 часов, не вставая. Я бы посмотрела на разработчика, который сидит и пишет код 8 часов рабочего времени. Если брать мой обычный рабочий день, то 2 часа — это как раз всякие тесты, код-ревью, техдолг, «тудушки» по коду. Часа 3 — это решение текущих задач, час на общение с менеджерами. А оставшиеся 2 часа размазываются непонятно на что, на обсуждение с командами и внештатные штуки.
Есть вещи, которыми тебе интересно заниматься — ты занимаешься, и когда у тебя нет сил, они тебе дают силы. У меня много разной активности — это называется, наверное, полезная прокрастинация — когда я делаю то, что мне интересно в данный момент, а не то, что мне нужно делать. Если научиться варьировать между тем, что интересно, и тем, что все-таки нужно, получается больше всего успевать. Ты просто не тратишь время впустую, чтобы заставить себя делать то, что не хочешь.
Никакого секрета нет, просто надо делать то, что тебе нравится, но при этом без вреда для окружающих людей и работы проекта.
За подробностями реализации генерации тестов из Python-кода, а также решением многих других задач Python-разработчика приходите на Moscow Python Conf++ 27 марта в Инфопространство (Москва). Сможете не только найти новые идеи для своей работы в докладах, но и обсудить проблемы с крутыми профессионалами.