26 марта 2013 в 14:17

Лень-driven development

Человек — ужасно ленивая зараза. Нет, ну я не о вас, конечно! Ну что вы! Я так, о себе. О 99% человечества. Но не о вас, нет. Вы сами за себя решайте. Но вот те 99%, так уж вышло — ужасно ленивы. Кто-то это отрицает, кто-то мирится, кто-то борется. А лично мне кажется, что это такая же неотъемлемая черта нашего вида, как, например, две руки и две ноги. Можно убиваться, что у нас нет крыльев или жабр — а можно научиться хорошо пользоваться тем, что есть. Так же и с ленью. Зачем её отрицать? Надо её использовать по-полной. И вот тут, поскольку мы с вами имеем кое-какое отношение к ИТ, давайте посмотрим, как с этим обстоит дело в нашей профессии.


Поначалу делу обстояло не важно. Если посмотреть на те технологии, которые были в ходу пару десятков лет назад, можно заметить, что все они были сильно требовательны к человеку. В компьютерах 30-летней давности меня больше всего удивляет не их размер или малая производительность, а то, насколько умным, точным, ответственным и грамотным должен был быть программист тех лет. Лишняя дырочка на перфокарте — и сутки работы на свалку. Читая рассказы Фейнмана о том, как они делали расчёты для Манхэттенского проекта, я поражался, что эти расчеты вообще были сделаны. Мне кажется нынче людей с такой силой воли и ответственностью найти было бы крайне сложно. Лени здесь было не место. Однако, поскольку компьютеров было мало, а инженеров — относительно много, среди них находились достойные.

Время шло. В профессию начало приходить всё больше и больше людей. А откуда? Из тех самых 99%, о которых я писал в начале. Из нас, из ленивых. В некоторых случаях лень сразу работала на результат — приводила к упрощению интерфейсов, унификации протоколов, выбрасывание ненужного. В других случаях лень приводила к провалам проектов. Надо было что-то решать. И тут начали появляться методологии разработки программного обеспечения.

Из подслушанного разговора в курилке
-Любая методология разработки лучше просто «обычного программирования»!
-Нет, потому что если я назову методологией «писать код только ночью голым при луне в лесу на пеньке», то это не лучше просто «обычного программирования»


Сколько всего разного придумано в этой сфере! Вот, к примеру, идея формальной верификации написанного кода. Ну круто же! Взять, и доказать математически, что созданный тобой код идеальный. Мечта. Песня. Но где это нынче используется? В марсоходах? В софте кардиостимуляторов? Всё? Где-то в одном проекте из миллиона. Ну а почему? А вы возьмите и представьте себе программиста, который после написания своего кода, после его успешной компиляции, запуска, smoke-теста и проверки тестировщиком — берёт и садится выводить формальное доказательство корректности кода. Много вы таких не ленивых знаете? Я вот — одного человека на несколько сотен моих знакомых программистов. И «один знакомый на пару сотен» — это еще результат серьёзно выше среднего.

Ну или вот идея водопада. Хорошая же идея. Но тоже уже отживает своё. Теоретики говорят: «Не выдерживает текущей высокой динамики и быстро меняющихся требований». Тьфу, чушь. Всё дело в том, что под «водопадом» лентяи могли месяцами, годами прятаться и ничего не делать. Ну вот хорошо было там прятаться, а на выходе — провал проекта. Современные «гибкие» методологии рекламируют как «удобные для программистов» и «отвечающие сегодняшним реалиям». Снова — тьфу. Просто скрам очень быстро достаёт лентяев из нор, за уши просто вытягивает, ставит в центр комнаты и направляет на них все прожектора. Потому и популярен.

А вот самая касаемо отношения к лени интересная штука — это TDD. Она, на самом деле, исходит из того, с чего я начал эту статью: все люди — лентяи. Однако, у TDD получилось поставить эту лень на службу человечеству в промышленных масштабах. Вот смотрите.

Допустим, вы программируете без TDD. Вы сразу пишете код. Например, какой-то функции. При этом вы ленитесь. Вы не особо думаете над названием функции, именами её аргументов, вы можете забыть проверить входные значения или не поймать какое-то исключение. Можете опечататься или вообще допустить принципиальную ошибку. И это всё можно понять — вы пытаетесь минимизировать количество своей работы, вот прямо сейчас, в эту самую секунду. Да, завтра у вас здесь выскочит ошибка. Или не выскочит. Или не у вас. В любом случае — это будет когда-то потом, потом и будем бороться. Такие мысли расхолаживают, позволяют писать откровенную лажу. Ну а что поделаешь? Минимизируем усилия.

Теперь допустим, что вы программируете с TDD. Вы начинаете с написания тестов. При этом вы, конечно же, тоже ленитесь (а ничего же не изменилось!). Но, внимание: ленитесь вы уже не при написании production-кода, а при написании теста. При этом вот в чём будет выражаться лень:

  1. Вы напишете тест максимально похожим на то, как будет работать ваш реальный код вызова тестируемого функционала. Ну, чтобы потом скопипастить можно было. Ну или заглянуть, как в документацию. И это отлично — вы протестируете именно то, что нужно, время сэкономите и документацию получите.
  2. Вы подумаете, а как бы вам хотелось, чтобы назывался тестируемый класс, метод, его аргументы. Нет, вы и при написании этого метода, возможно, об этом подумаете. Но мы ведь помним — там вы будете лениться хорошенько подумать над этим, напишете как-нибудь. А вот при написании тестов объект вашей лени — тест. Тест вы будете пытаться сделать простым и понятным, а значит и название вызываемых методов, и передаваемые им аргументы будут максимально простыми, понятными, ничего лишнего и всё по делу. И реальный код эти свойства тоже автоматом унаследует.
  3. Лениться при написании тестов очень легко. Тест обычно не очень большой, очень легко его скопировать\вставить и, заменив пару символов, превратить в похожий, но чуть-чуть другой. И потом в еще один. А вот уже написав все эти тесты — вам деваться будет некуда, так или иначе придётся написать код, который через них проходит.


Мораль


Лень всегда была, есть и будет. Мы не можем вот так взять и навсегда от неё избавиться. Но мы можем управлять направлением этой лени, заставить эту лень не вредить, а приносить пользу. Не ленитесь правильно лениться!
Автор: @tangro
Инфопульс Украина
рейтинг 158,80
Creating Value, Delivering Excellence
Похожие публикации

Комментарии (53)

  • 0
    лень — двигатель прогресса. давно доказано
  • +42
    При этом вот в чём будет выражаться лень:

    Вы забыли варианты «лень написать тест еще на вот такой случай» и «лень думать, какие случаи бывают».
    • +1
      Наверное, последний пункт — самый проблематичный в TDD.
      • 0
        От него спасает «попросить тестировщиков написать тест-кейсы» и «просмотреть code coverage». Но все равно не полностью.

        Поэтому no silver bullet here too.
        • 0
          no silver bullet here either.
    • +2
      Почти классика.

      Там вообще многое прекрасно.
  • +25
    Небольшой лирический отступ: Программист всегда может логически обосновать свою лень! :)
    • +56
      Но ему лень.
    • +1
      Лаконичный пример:
      Иногда лучше остаться спать дома в понедельник, чем провести всю неделю отлаживая написанный в понедельник код.
      — Christopher Thompson
  • +2
    Короче говоря, перекуем баги на фичи!
  • +4
    «Самую сложную задачу я поручу решать самому ленивому программисту» (с) Билл Гейтс.
    • +24
      «Это многое объясняет».
    • 0
      И он потом меня сможет уговорить, что на самом деле задачу можно и не решать/задача не актуальна
  • +29
    Лень читать, про что там?
    • +24
      Лень рассказывать…
      • +40
        Плюсанул бы, но лень
        • +3
          47 человек поленились оставить тут комментарий.
    • 0
      Про лень.
  • +6
    Поставил бы плюс уже за один только заголовок статьи :-)
    • +9
      Но лень? ))
      • +7
        Но лень было продолжать, почему всё же не поставил...)
  • +4
    Тесты, вообще, вселяют спокойствие и уменьшают тревожность при написании следующего участка кода, когда более-менее уверен, что прошлый участок работает, а если перестанет работать, то это всплывёт.
  • +12
    Странно, что еще никто не привел цитату Билла Гейтса… Наверное всем лень.

    Ах нет же, мне просто было лень читать комментарии…
    • 0
      Цитаты из Билли Гейтса приводят, когда лень разыскивать более подходящие к случаю цитаты.
  • +3
    >Тест обычно не очень большой, очень легко его скопировать\вставить и, заменив пару символов, превратить в похожий, но чуть-чуть другой.

    И получить тонны неподдерживаемого кода…
    • +6
      И получить тонны кривых тестов (копипаста она такая)…
  • +8
    Почему то все думают что тесты решают все проблемы. Но в реальности плохо написанный тест создаст еще больше проблем чем если бы его не было вовсе. Поэтому тесты требу.т не меньшего внимания и серйозности подхода как напиание самого продакшен кода. В итоге нормальный тест занимает раз в 5 больше времени чем написание самого функционала. А это говорит о том что такие пряники досутпны далеко не каждому проекту.
    • +1
      Да не решают они всех проблем, конечно. Почему бы не начать с написания простых тестов? А 80% от всех тестов действительно написать легко. Да, оставшиеся 20 часто требуют мощных фреймворков, написания моков и т.д. Но даже если написать только простые тесты — это уже в 100 раз лучше, чем никаких. Лично я тесты, которые надо писать в 5 раз дольше самого функционала не пишу вообще.
      • +2
        Из личного опыта — сложный тест пишется один раз. Зато потом он может несколько раз сэкономить кучу времени при отлове трудообнаружимого при обычных условиях (всмысле, без этого теста) бага.
      • +1
        Лично я тесты, которые надо писать в 5 раз дольше самого функционала не пишу вообще.

        ибо лень?
        • 0
          Не окупается потому что. Если тест тестирует какую-то ну очень хитро-через-пень-колоду-сделанную хрень, то высока вероятность, что она вообще пропадёт\переделается нормально\разделится на несколько.
          • 0
            Ну так для такой хитрой хрени и придумали интеграционное тестирование. Которое кстати выловит косяки рефакторинга хитрой хрени. А переделывать/добавлять нужно будет разве что юнит-тесты.
      • 0
        А что можно тестировать в большом проекте без моков и фреймов? И вообще какой смысл тестировать то что вероятнее всего и не поламается.
        Ну тоесть тестировать сеттер и геттер смысла нет, а вот парсер запроса или менеджер памяти есть.

        Тесты не ловят ошибки, они только дают какуюто вероятность того что вы не создали новых. Ошибки же ловятся дебагерами и логгерами на стадии нагрузочного и системного тестирования. Особые гурманы еще делают фаззи-тесты.
        • 0
          А то, что архитектура позволяет без них :) Например, событийно-ориентированная архитектура моков не требует для тестов, только лишь того, что одно событие с одними параметрами вызвало другое с другими. Упрощая, что событие «мышка кликнула в таких-то координатах» вызвало событие «записать файл с таким-то содержимым по такому-то пути».

          Вы, похоже, судите о по ситуации покрытия существующего кода тестами. В случае реального TDD тесты практически моментально «ловят» ошибки несоответствия функциональности кода грамотным формальным требованиям ТЗ (требования типа «программа должна считать сумму целых чисел неограниченной величины» пускай и формальные, но не грамотные на данном уровне развития науки и техники). Ловят их просто по определению TDD (если в самих тестах нет ошибки) — сначала формализуем требования к юниту, потом убеждаемся, что отсутствие кода тест не проходит, потом что только написанный код этим требованиям удовлетворяет, а значит ошибок в нем нет. Есть исключительные случаи, когда такая методика не работает (например, если результаты, хотя бы частные случаи, неизвестны), но в целом TDD гарантирует отсутствие ошибок, если сформулировано что это означает в терминах ВТ.
    • –3
      Вообще не очень понимаю смысла тестов. Если тест предназначен для того, чтобы отлавливать ошибки, которые программист предусмотрел, то зачем это нужно? Обычно все же код работает верно на тех данных, для которых он писался, а сыпется там, где что-то не предусмотрели. Если код сыпется на данных, под которые он делался без редких каких либо странных юз-кейсов, то это печально, тут уже мало что поможет. А как написать тест, который будет отлавливать ошибки на непредусмотренных кейсах, если кейс непредусмотрен? :)
      Надеюсь, выразился понятно
      • +5
        Тест пишется потому, что код может начать сыпаться по непонятным причинам. Где-то обновили версию низкоуровневой библиотеки, где-то сделали рефакторинг и т.д. Тест проверяет уже известный набор входных данных и говорит — всё ок, с этой библиотекой\рефакторингом всё работает так же хорошо. Кроме того, написать тест очень полезно при нахождении какого-то бага: нашел баг — воспроизвел — написал тест — тест падает — пофиксил баг — тест проходит — комитнул код и тест — всё, больше этот баг не вылезет, а если и вылезет, то будет тут же замечен тестом.
      • 0
        в первуюочередь «простые тесты» отлавливают траблы неявно родившиеся во время добавления фич и т.д.
      • +1
        Тест фиксирует поведение кода. Когда будет «непредусмотренная» ошибка, то тест поможет не сломать в ходе её исправления то, что уже работало. Вернее сразу заметить поломку, а не после сдачи заказчику. Тест служит документацией того, что предусмотрели, а что нет. По сути тесты — это требования к функциональности выраженные в формальном форме. А тестирование — формальная проверка соответствия кода требованиям.
        • 0
          Давно занимает вопрос — как юнит-тестирование соотносится с программированием по контракту? Почему-то кажется, что то, что вы сейчас описали, можно отнести и туда, и туда. Помогите выбрать с чего начать причесывание большого не совсем красивого проекта.
          • 0
            Юнит-тестирование можно считать одним из методов проверки выполнения модулем контракта, выполнения им постусловий и инвариантов, а также соблюдения предусловий тех модулей, которые он вызывает как клиент. Но нужно понимать, что юнит-тесты не гарантируют соблюдение контракта в любой ситуации, а лишь показывают, что в некоторых он выполняется. С другой стороны, область применения юнит-тестов шире — они проверяют не только формальное соблюдение контракта методом, но и алгоритм его работы.

            С чего начинать во многом зависит от специфики приложения и степени его некрасивости. В особо тяжелых случаях — в эмулировании боевого окружения для создания приемочных тестов. В частности для веб-приложения может потребоваться создание инфраструктуры записывающей реальные запросы пользователей (целиком, со всеми заголовками и телом), дамп базы и части ФС после запроса (или, что реалистичней, после большой пачки запросов), а потом выдающей их серверу, да с эмуляцией временных промежутков (проще говоря, переводящей на сервере часы) и последующего сравнения базы на эмуляторе и эталоном.

            Если все не такк плохо, то начинать надо, пожалуй, с раздергивания приложения на более-менее изолированые части — современные IDE позволяют это делать относительно безопасно без покрытия тестами, — а затем покрывать эти изолированные части, хотя бы перед внесением в них изменений. В общем без соответствующего теста функциональность продукта меняться не должна, включая исправление ошибок. Иначе очень легко попасть в ситуацию, когда новой или измененная функциональность внедреятся быстрее, чем старая покрывается тестами. Тесты должны опережать изменения функциональности. Чтобы работа по покрытию тестами (юнит) не казалась неподъемной, нужно четко себя ограничивать что нужно тестировать, а что нет. Например, нет нужды тестировать как СУБД выполняет SQL запросы, достаточно проверить, что запрос соответствует ожидаемому и/или подсунуть ожидаемые методом данные.
  • +2
    Именно лень толкает программистов писать код, который будет потом генерировать рутинный код за них :)
    • 0
      Метапрограммирование придумали лентяи. И еще не родились такие лентяи, которые смогут построить автоматический конвертер галлюцинаций заказчика в жесткую формальную систему.
  • +2
    Самое главное, чтобы потом было не лень переписывать вашу груду тестов, когда поменяются условия и придется вносить измнения дважды: в сам код, да еще тесты править.
  • +1
    Получается, самые главные лентяи — авторы современных продвинутых IDE.
  • +1
    Хороший пост, особенно понра
  • +1
    Не указаны главные, имхо, свойства TDD в контексте лени:
    — Слабосвязанная и сильносвязная архитектура — лень иниициировать кучу объектов, параметров, стабов и моков, а потому число внешних зависимостей уменьшается, а внутренних увеличивается.
    — Соблюдение DRY — лень писать повторяющиеся тесты для повторяющейся функциональности, проще один раз её выделить и один раз протестировать
    — Соблюдение KISS и YAGNI — лень придумывать что-то «на всякий случай», ведь это ещё и тест придется придумывать и писать на этот случай
    — Декомпозиция задач (а-ля «unix-way») — лень приступать к большой и сложной задаче, не получая немедленный результат. А вот если её формально разбить на подзадачи, то мелкие подзадачи делать уже не так лень, ведь есть немедленный и, что немаловажно, формально верифицируемый результат.
  • 0
    Комментарии напомнили сказку «Лень да Отеть».
  • 0
    Для меня лень была и остается основным определителем соотношения трудозатрат к конечной цели. Это чувство никогда не подводило — если лень, значит есть необходимость пересматривать пути решения, возвращаясь к задаче, возможно изменились условия или появились лучшие варианты….

    Если смотреть на методологию как на один способов снизить трудозатраты, то обнаружим различия в этом соотношении отдельного программиста и группы, непременно нужно искать баланс, а это не учитывается даже в современных методологиях и должно ложится на плечи ПМ, а ему всегда лень ))).
  • 0
    Ну, иногда формальный вывод алгоритма (или, в частном случае, цикла) из предусловия и инварианта — самый ленивый способ. При некотором опыте он позволяет вообще не думать как раз в сложных случаях и писать код, в принципе не требующий никаких тестов.
    • 0
      Можно пример? Можно на Эйфеле :)
      • +1
        Так лень же, да и на эйфеле я не пишу :) Но классический пример — двоичный поиск — отлично разобран у Бентли в его «Жемчужинах» (глава «как писать правильные программы»). Ну и вся книжка Гриса («Наука программирования») об этом. Это только сначала кажется, что оно трудно и непривычно, а потом начинает получаться и потихоньку становится тривиальным навыком.
  • 0
    rm
  • 0
    Кстати, в лесу на пеньке нет интернета, начальства, соседей и других отвлекающих факторов.
    Так что код, написанный там, вполне может быть лучше

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка