Дисциплина или тесты. Выберите одно?
Luxoft Training подготовил для вас перевод статьи известного канадского консультанта по разработке ПО Джо Райнсбергера «Discipline or Tests. Pick one?».
Джо Райнсбергер — автор множества работ по информационным технологиям. За вклад в развитие гибких методологий был удостоен высшей награды от Agile-сообщества – Gordon Pask Award в 2005 г. (в первый год создания награды). Он является основателем XPDay (Северная Америка). Книга Джо Райнсбергера «JUnit Receipes: Practical Methods for Programmer Testing» стала всемирно известной. Джо практикует гибкие методологии, начиная с 2000 г., и за это время его статьи об Agile-разработке были опубликованы в ведущих журналах для разработчиков, в том числе IBM DeveloperWorks и IEEE Software. В журнале IEEE Software Джо является редактором колонки «Not Just Coding».
Дисциплина или тесты. Выберите одно?
Что бы вы предпочли: иметь дисциплинированного программиста или сильный набор тестов?
Недавно один из клиентов задал мне этот вопрос, и я не уверен, что дал достаточно хороший ответ. Я попробую сделать это здесь.
Я вижу преимущества в обоих случаях, поэтому, к сожалению, я не смогу кратко ответить на этот вопрос. С одной стороны, дисциплинированный программист будет принимать разумные решения, даже если не будет делать то же самое, что сделал бы я. Если бы я доверял общей дисциплине программиста, я бы чувствовал себя комфортно, доверяя ему (ей) производить качественную работу, под которой я понимаю постоянный поток ценного работающего кода. С другой стороны, если я доверюсь дисциплинированности конкретного программиста или программистов, то они будут производить артефакты, которые я не смогу легко потреблять, и когда они исчезнут, мой постоянный поток ценного работающего кода тоже исчезнет. Это превратится в проблему Числа Грузовика (Truck number, Bus factor).
Так как я использую хороший набор тестов, чтобы уменьшить непредсказуемость предельной стоимости функций (Когда мы говорим, что заботимся о проектировании «по экономическим причинам», я считаю, что мы имеем в виду именно это. Обратите внимание, что «предельная стоимость» просто означает «стоимость следующей функции».), то может имеет смысл требовать от программистов, поставляющих мне эти функции, чтобы они поставляли и тесты для сохранения возможности поддержки базы кода экономически эффективным способом и без них.
Так что бы я предпочел? Я собираюсь это выяснить в процессе написания данной статьи. Это действительно зависит от многого. Позвольте мне изучить вопрос, и возможно, при достаточном количестве сопутствующих факторов, я смогу сделать выбор.
Что значит «дисциплинированный»?
Предположим, что у программистов, о которых мы говорим, высокая дисциплинированность, но они не пишут тесты. Что мне от них нужно ( предполагая, что я не могу требовать тесты), что позволило бы мне комфортно перенять их базу кода? У меня есть несколько идей.
- Мне нужно знать, где в коде мне искать то, что мне нужно.
- После того, как я найду то, что мне нужно, мне нужно знать либо (1) что я нашел все, что мне нужно, либо (2) что я могу понять, где мне искать дальше.
- Я должен быть уверен, что если изменю что-нибудь, то это не повлечет за собой внезапных изменений в других местах.
Это становится очень похожим на следование Четырем элементам простого проектирования. И это ведет меня к другому вопросу.
Если программисты пишут простой код (такой, как в Четырех элементах) без тестов, что еще мне нужно, что позволило бы мне комфортно перенять их работу?
Забавно, что этот вопрос никогда не возникал. Если система разработана хорошо, в соответствии с Четырьмя элементами, тогда, мне кажется, относительно несложно добавить тесты после разработки. Это следует из базового понятия, что Четыре элемента побуждают нас писать проверяемый код, и согласно определению «проверяемости», у меня не должно возникнуть особых проблем при тестировании проверяемого кода.
Я предпочел бы не тратить дополнительное время на добавление этих тестов, но я бы стерпел. Это становится решением, основывающимся на коммерческих соображениях: если я заплачу 100 000$ за то, чтобы добавить тесты к вашему коду, то вам лучше бы сэкономить для меня больше, чем эти 100 000$, по сравнению с лучшим возможным вариантом, включающим написание тестов. До тех пор, пока у вас это получается, какое у меня право жаловаться?
Я сейчас не беру в расчет, что сильно сомневаюсь, что смог бы когда-либо нанять программистов, способных писать простой код без тестов, разве что по совершенной случайности. Я остаюсь открытым для этой возможности.
Время на самом деле не равно деньгам
Что ж… Я вижу одну потенциальную проблему. Стоимость перенятия такой базы простого кода включает в себя не только деньги, но и время, а если деньги я могу возместить, то время — никогда. Мне нужно учитывать стоимость задержки для добавления тестов к этой гипотетической базе кода. К счастью, я могу амортизировать эти затраты в течение всего срока реализации проектов, которые поддерживают эту базу кода, что делает его, вероятно, самым дешевым унаследованным кодом в истории.
Думаю, это я бы тоже пережил.
Итак, предположим, что у меня есть простой код, который сэкономил мне больше средств в процессе создания, чем я потрачу на добавление тестов после его создания. Упускаю ли я еще что-либо важное, не получая тесты вместе с кодом?
О базе кода
Мне, безусловно, нужен будет одностраничный документ, описывающий общий дизайн системы – пять самых важных вещей – а также то, какие функции, по мнению программистов, уже созданы и какие будут созданы следующими.
Предположительно как клиент этого гипотетического продукта я буду достаточно хорошо понимать продукт, предметную область и функции, которые по моему мнению уже были созданы. (Не мешало бы сравнить мое восприятие того, «что уже сделано», с их восприятием.)
Это подводит меня к первой версии окончательного списка вещей, которые мне понадобятся, чтобы довериться базе кода, написанного «дисциплинированными программистами», не пишущим тесты.
- Короткий документ, описывающий общий дизайн системы, функции, которые, по мнению программистов, уже созданы, и функции, которые программисты собираются создать следующими.
- База кода, неопровержимо демонстрирующая, что программисты сделали данный код простым согласно Четырем элементам простого проектирования.
- Постоянная скорость создания функций, которая покрывает затраты времени (относительно небольшие), необходимого мне для добавления тестов, так как я сам поддерживаю базу кода.
Думаю, имея все эти вещи, я могу согласиться перенять базу кода от группы программистов, которые не пишут тестов.
Конечно, это не значит, что я советую вам предать огню все ваши тесты, чтобы посмотреть, что из этого выйдет.
А что же с противоположной ситуацией? Допустим, у меня есть сильный набор тестов, но программисты у меня не слишком дисциплинированные. Какие возникнут риски, и как я могу их уменьшить или компенсировать?
Каков этот «сильный набор тестов»?
Что я ожидаю от «сильного набора тестов»? Я бы хотел иметь в основном микротесты, написанные в стиле совместных и контрактных тестов (У меня очень объемное мнение на тему «Интегрированные тесты – это ерунда», но я еще не нашел в себе сил записать его. Сейчас я очень медленно это делаю. Когда-нибудь я, может быть, напишу эту книгу. А пока что вот мое последнее выступление на эту тему), со щепоткой функциональных тестов, написанных в стиле самых заботливых практиков behavior-driven development. Эти функциональные тесты помогут мне понять функции, а микротесты дадут мне уверенность, нужную для внесения изменений в код.
В этом и заключается проблема: стоимость понимания и сопровождения этих тестов скорее всего соответствует стоимости понимания и сопровождения окончательного кода. Это имеет смысл, так как, скорее всего, одни и те же люди писали и то, и другое. (Я понимаю, что не могу обоснованно предполагать это, но, тем не менее, предположу.)
Поскольку обсуждаемые программисты по предположению не дисциплинированы настолько, насколько бы мне хотелось, можно ожидать, что в окончательном коде будут проблемы. В целом, там, где я найду проблемы в окончательном коде, я могу ожидать, что найду и тесную связь между тестами и деталями реализации этого окончательного кода. Я могу ожидать большое количество косвенности без абстракции. Короче говоря, я могу ожидать хорошо проверенный, хорошо покрытый унаследованный код. Теоретически я могу спасти этот унаследованный код, но мне придется столкнуться с обычными проблемами непостоянства стоимости спасения кода.
Вероятно, я смогу найти низшую общую стоимость спасения кода при таком же непостоянстве, как при унаследованном коде без тестов. Это кажется интуитивно очевидным, учитывая, что в некоторых случаях тесты окажутся настолько тесно связанными с деталями реализации, что мне придется выбросить тесты и начать заново (Я предположил, что программисты не писали окончательный код, зависящий от тестов. Я понимаю, что это предположение рискованно, но нахожу альтернативу слишком ужасной, чтобы думать о ней). По мере того, как тесты раскрывают реализацию во время тестирования, они скрывают ярко выраженные детали основных аспектов поведения, и в итоге я буду заниматься археологией, чтобы самому открыть основные аспекты поведения, с тестами или без.
Я не хочу изображать ситуацию совершенно ужасной. Конечно, наличие тестов увеличивает вероятность того, что я смогу изменить часть (возможно, большую) кода безопасно, хотя и не уверенно, по крайней мере, в начале. Вероятно, проектирование будет для меня неприятным, но изменение проекта будет, возможно, более простым, чем при типичной базе унаследованного кода. С другой стороны, необходимость написания тестов для унаследованного кода дает мне такую возможность разобраться в назначении кода, какую простое чтение чрезмерно детализированных тестов никогда не сможет дать. Я должен подготовить себя к возможности, что необходимость написания тестов с нуля для унаследованного кода является частью процесса, а не его дефектом.
Ух ты. Этого я не ожидал. (Серьезно)
Думаю, я бы предпочел дисциплинированных программистов
Кажется, что я бы предпочел иметь дисциплинированных программистов, которые не создают тесты, менее дисциплинированным программистам, предоставляющим сильный набор тестов. Нет, это не трюк. («Простой проект проходит все тесты, но если тестов нет, то проект заведомо простой…» Нет.) Да, тесты я бы тоже хотел иметь, но если кто-то может достичь хороших результатов без тестов, то у меня нет права навязывать им свои методы. Должен признаться, что это меня удивляет, потому что в прошлом я, вероятно, писал, что предпочел бы тесты. Тогда это казалось хорошей идеей. Я не могу объяснить сдвиг в моем мышлении.
Несмотря на это, пожалуйста, пишите тесты, независимо от того, насколько дисциплинированными вы себя считаете. Я считаю, что написание тестов и сопутствующий рефакторинг очень помогли мне развить дисциплинированность, и я все еще настаиваю на том, что буду писать тесты. Так что если бы я вас нанял, то требовал бы от вас того же.
Эпилог
Мой друг Michael Bolton сказал мне, что эта статья напомнила ему о проблеме неявного знания. В частности, тесты (на самом деле «проверки») могут отображать только чьи-либо идеи о том, что должна делать система, и, таким образом, обязательно упустят какую-то часть информации, которая по закону Мерфи станет для нас важной в самый неподходящий момент. И в этот момент мы точно захотим, чтобы у нас был доступ к программисту, а не только к проверкам/тестам.
Действительно, в проверках, похоже, закодирован в лучшем случае относительно недавний снимок того, что, вероятно, выбранная случайно версия нас думала о функционировании продукта, будучи ограниченной нашей способностью формулировать эти мысли (учитывая наши навыки и энергию в тот момент). Выражаясь таким образом, кажется очевидным, что я бы скорее выбрал дисциплинированных людей, а не сами проверки!
7 и 8 июля Джо Райнсбергер проводит онлайн мастер-класс на тему «Ценностно-ориентированный подход в разработке программного обеспечения»
Автор: Evgenia_s5