Последнее изменение: 11 июня 2008г.

Экстремальное программирование – реальность и мифы

Написать эту статью меня побудило большое количество дискуссий вокруг XP как методологии разработки. Мнение о ней в основном негативное. По разным причинам. Проанализировав все, что я слышал, я пришел к выводу, что многие просто не представляют себе, что такое XP, и делают выводы либо в отсутствие опыта вообще, либо при его наличии, но в очень малом объеме.

В этой статье я хочу коснуться следующих вопросов – теории и практики XP, распространенных мифов об XP и предназначения XP. Я пройдусь по всем практикам, поясняя, что есть что и, самое главное, зачем это надо.

Итак, краткое оглавление.

Пожалуй, начнем.

Теория и практика XP

Теория XP в своей основе проста. Существует некий набор практик, применение которых способно дать лучший результат, нежели стандартные жесткие методики. Причем эти практики в XP применяются в превосходной, экстремальной форме, откуда, собственно, и название.

Происходит XP из простой посылки. Программное обеспечение – чрезвычайно непостоянная вещь. И эта непостоянность вызывает закономерные проблемы при стандартных способах разработки. Хуже всего то, что требования могут меняться непосредственно в то время, когда разрабатывается продукт. Т.е. к моменту выхода он уже несколько устаревает. Именно эту проблему – жесткости стандартных методик разработки – и призвана решить методология XP.

Здесь, однако, есть небольшой нюанс. Все книги, которые я читал, – они несколько популяризаторского толка. И именно в таком шоу-стиле они и написаны. "Йо, вау, аплодисменты, XP это круто" – такие ощущения просвечивают тут и там. И уже одно это зачастую вызывает неприятие. Кроме того, некоторые высказывания народ почему-то склонен понимать буквально, что приводит опять-таки к неприятию идей еще до того момента, как их успевают осознать.

Есть еще один момент, которого не учитывают многие. Дело в том, что между теорией и практикой – пропасть. Вы можете прекрасно знать теорию, но не быть способным применить ее на практике.

Приведу пример. Он, правда, не из области программирования, а из области вождения машины. Мне как-то эта область весьма близка. Допустим, некто (пусть это будет девушка) учится водить машину. Для определенности – трогаться с места. Она прекрасно знает, что надо плавно отпустить сцепление. И тем не менее – машина глохнет. Раз, другой, третий, десятый. Почему? Потому что это практика. Создатели педального узла не предусмотрели вождение на "шпильках". В нужный момент каблук теряет опору и проскальзывает, в результате сцепление отпускается рывком.

Ладно, обувь сменили. Машина продолжает глохнуть. Почему? Потому что отпускать сцепление плавно нужно не на всей длине хода, а только на рабочей – 2 см от силы. И если отпускать сцепление с той скоростью, которая нужна именно на рабочей части хода – до этой рабочей части нога будет подниматься минуту. Затечет, заболит, этот процесс в конце концов просто надоест. В результате – сцепление брошено, машина заглохла.

Что же нужно на самом деле? Практика и ничего более. БЫСТРО поднять ногу до рабочей зоны, плавно и существенно медленнее отпустить педаль в рабочей зоне, после схватывания отпустить вообще. Практика, практика и еще раз практика. И очень желательно – вместе с инструктором. Который точно знает, где начинается рабочая зона, и даст это почувствовать обучаемому.

Точно так же, как надо учиться вождению с инструктором – XP надо изучать рядом с тем, кто уже знает практические тонкости, сильно меняющие всю картину. А чаще всего бывает вот как: человек "XP изучал с пристрастием (хотелось понять, что же это за фигня такая на мою голову), так что понял всё правильно", потом "пережил два внедрения XP в разных проектах и дважды задавил эту ересь", и на этом основании делает далеко идущие выводы – "XP полностью ... исключает возможность получения качественного результата". Это все равно, что девушка из примера выше после трех попыток, закончившихся заглохшим двигателем, безапелляционно заявила бы, что на коробке с ручным переключением водить невозможно. Ересь это. Автогонщиков бы такое заявление весьма позабавило, я думаю.

У меня опыт работы по классической методологи XP больше года. Причем я начинал в команде, которая уже давно работала с использованием XP, так что у меня была возможность обойти те грабли, на которые наступают теоретики. На текущем же месте работы мы применяем некоторые из практик и постепенно движемся в сторону введения остальных. Постепенно – ввиду сильной сопротивляемости менеджмента. В следующей части я хочу дать описание практик XP прежде всего с точки зрения их реального применения, а не с позиций голой теории.

Еще один момент. XP – это не набор правил из серии "шаг вправо, шаг влево, прыжки на месте – расценивается как попытка к бегству и карается расстрелом". Это всего лишь рекомендации. Каждая из них имеет свой смысл и способна дать свой эффект. В какой степени применять каждую из этих рекомендаций – нужно решать по обстоятельствам. И это один из моментов, который понимаешь только при работе с методикой. Применение практик "в максимальной степени" на деле означает "в максимальной степени, возможной в каждом конкретном случае". Ибо, как и в любой работе, приходится иметь дело с людьми. Другой вопрос, что вместе практики дают эффект, зачастую сильно превосходящий сумму. Это можно сравнить с резонансом. Полк, проходящий по мосту, способен разрушить его, если не собьет шаг (существует специальная команда, и обусловлена она именно этим, были прецеденты!) И это при том, что то же самое, но вразбивку (фактически, сумма воздействий) он выдерживает "на ура".

Итак, перейдем к рассмотрению непосредственно практик XP.

Практики XP

По сути, тут нет ничего нового. Все эти вещи известны. Некоторые, правда, сильно пугают заказчика, не привыкшего к подобному подходу. Тут "в лоб" не стоит давить, не получится. Только испугается. Практика показывает, что когда заказчик начинает ощущать преимущество даже от частичного применения практик, его скептицизм изрядно ослабевает.

Ну, вот мы и добрались до самих практик. Пойдем по порядку.

Планирование

Это та стадия, на которой вы обсуждаете с заказчиком, чего он хочет. Как правило, заказчик знает это весьма смутно. Ваша задача – вытащить из него необходимую вам информацию.

Прежде всего, человек, с которым вы общаетесь, должен быть профессионалом, знающим область, для которой вы пишете продукт, равно как и всю операционную деятельность, для которой этот продукт предназначен. Попытки обсуждать с генеральным директором программу для бухгалтерии обречены на провал. Разве что он и сам бухгалтер. Человек должен разбираться в проблемах, стоящих перед ними, и в том, как они хотели бы их решить с помощью обсуждаемого ПО.

Обсуждение идет по принципу "от крупного к мелкому". Сначала обсуждается задача в общих чертах, потом она разбивается на подзадачи. И так далее. До какого уровня? Я считаю, что можно остановиться, когда каждая задача займет где-то день. В этом случае ее можно обсудить досконально.

Пример

Допустим, разрабатывается система документооборота. Заказчик хочет в нее добавить выставляемые счета и платежи по ним. Что нужно сделать:

  1. Добавить в систему такие понятия как invoice и payment. Т.е. определить их в базе, сделать бизнес-объекты, сделать UI под каждый из них. Итого – по дню работы на каждый элемент.
  2. Добавить в систему возможность сопоставлять счета и платежи. Бизнес-логика, таблица сопоставления в базе, UI. В зависимости от сложности логики – это 1-3 дня.
  3. Добавить в систему возможность связывать счета с уже имеющейся в системе информацией по сделкам, издержкам и т.п. Это опять-таки бизнес-логика, изменения в базе, UI.

Для начала хватит. Что нужно выяснить у заказчика:

  1. Какая информация должна содержаться в объектах типа счет и платеж. Поля, названия их, которые должны фигурировать в UI, форматы вводимых данных, возможная их валидация. Должны ли вводимые данные соответствовать чему-то имеющемуся в системе, например, выставляемый счет может относиться к определенному контрагенту, соответственно, нужна возможность поиска контрагента вместо его ввода в UI, и т.д. и т.п. Вам нужна ВСЯ информация, которая потребуется вам для реализации этого требования заказчика.
  2. Может ли один платеж быть соотнесен с несколькими счетами? Может ли один счет быть оплачен несколькими платежами? Что должно быть видно пользователю, какие действия он должен предпринять, чтобы поставить в соответствие счет и платеж? Должен ли пользователь видеть полностью оплаченные счета? Полностью расписанные по счетам платежи?
  3. Должен ли пользователь иметь возможность создавать счет прямо из UI, относящегося, скажем, к сделке, или он всего лишь может выбрать уже существующий счет? Должен ли пользователь иметь возможность выставлять несколько счетов на сделку? Один счет на несколько сделок?

И т.д. и т.п. Вопросов может быть существенно больше.

Когда все требования таким образом сформированы – они фиксируются где-нибудь в письменном виде. Дабы не было разногласий. Затем заказчик определяет приоритеты. Допустим, ему очень нужна возможность выставлять счета, причем из UI сделок. А соотношение с платежами пока не очень актуально. Тогда именно в этом порядке и сортируются данные задачи. Причем обратите внимание – в примере заказчику нужна часть пункта 1, часть пункта 3. Т.е. эти пункты должны быть разбиты на подзадачи.

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

Первое – оценивать задачу может лишь тот, кто будет ею непосредственно заниматься. По той простой причине, что разработчики не идентичны. Один может сделать задачу быстрее, другой – медленнее. Следовательно, точная оценка при обсуждении задач с заказчиком сделана быть не может. Впрочем, она не может быть сделана по определению. :)

Второе. Разработчики работают в разных ситуациях. Они могут целый день не отрываться от задачи, а могут полдня потратить на консультации других разработчиков, исследования и т.п. Это непредсказуемо. Возможно лишь статистическое накопление информации. Так вот, как правило, на разработку из-за таких отвлекающих факторов тратится больше времени, нежели изначальная оценка. Потому в XP принят такой подход. Оценка работы при условии, что разработчик будет сконцентрирован исключительно на задаче, что его ничто не будет отвлекать, что он будет работать с максимальной продуктивностью – эта оценка называется идеальное время. Время, потраченное на разработку в действительности, называется реальным. Отношение этих времен, усредненное за определенный промежуток времени (например, за 8 циклов разработки, см. Частые выпуски версий), называется коэффициентом загрузки (load factor).

Таким образом, разработчики оценивают идеальное время выполнения задачи, после чего, умножив его на коэффициент загрузки, можно получить реальную оценку выполнения задачи. И именно эта оценка должны фигурировать в описании задачи.

Принято считать, что коэффициент загрузки в начале работы команды равен 3. После того, как команда уже втянулась в проект, скорость разработки начинает повышаться. Для профессиональной команды, хорошо сработавшейся друг с другом и с заказчиком, коэффициент загрузки по опыту находится где-то в районе 1.7-1.8.

Пойдем дальше. Описание задачи получено, временная оценка тоже, приоритеты расставлены. При работе с заказчиком удобно иметь систему общего доступа, в которой будут отражаться все детали, касающиеся разработки. Что-нибудь типа TWiki (http://www.twiki.org/). Это позволяет оперативно вносить изменения в работу. Итак, в TWiki создается т.н. история (user story), в которой описывается все, что относится к конкретной задаче. Все, что сообщил заказчик, временные оценки, приоритет. Возможно, комментарии в процессе реализации. После того, как история создана, заказчик еще раз просматривает ее и подтверждает, что все верно и он с такой постановкой задачи согласен. Всё, можно работать.

Такой подход может показаться весьма муторным. Однако он служит одной очень важной цели. Такое обсуждение заставляет заказчика продумать до мелочей его требования к системе. Следовательно, сильно уменьшается вероятность того, "через неделю заказчик меняет требования на противоположные", как упоминал кто-то из форума. Да и вообще, часто оказывается, что часть изначальных требований заказчику и не нужна, а нужно ему что-то совсем другое. Если бы он просто написал ТЗ, а потом бы его реализовали, – пришлось бы переделывать очень много. И заказчик был бы явно недоволен. В случае же XP-планирования – он с гораздо большей вероятностью получает именно то, что хочет.

Стоит еще упомянуть вот какой момент, связанный с планированием. Если какая-то функциональность вызвала затруднения в реализации и все, что запланировано, не укладывается в рамки очередного релиза – объем реализуемой функциональности необходимо снизить. Именно так – не отодвинуть выпуск продукта, а уменьшить функциональность. Почему – см. в разделе Частые выпуски версий. Т.е. убрать из запланированной функциональности то, что наименее критично. Решить это должен заказчик, и никто кроме него не может это сделать. Парадоксально, но факт, – часть функциональности таким образом вообще отсеивается. Что означает, что она изначально не была нужна.

В результате процесса планирования/перепланирования мы получаем четкий план работы на ближайшую версию-две. Таким образом, планирование нужно производить по меньшей мере раз в версию. А лучше – раз в неделю, для того, чтобы скорректировать оценки, основываясь на текущем положении вещей.

О планировании пока всё. Переходим к следующей практике XP –

Тестирование

А точнее – написание тестов ПЕРЕД написанием кода.

Тестирование вообще процесс, нелюбимый разработчиками. Написание тестов – тем более. А уж написание тестов ДО кода – вообще смертоубийство.

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

Далее. Зачем писать тесты ДО функциональности. Во-первых, неукоснительное соблюдение этого правила гарантирует, что любая часть функциональности системы покрывается тестами. Если оставить тесты на потом – в силу природной лени (нелюбви к тестам, нехватки ресурсов – нужное подчеркнуть) есть большая вероятность того, что они так и не будут написаны.

Во-вторых, есть гораздо более интересная тонкость. При написании тестов ДО функциональности мы расцениваем эту самую функциональность как черный ящик. У нас есть набор требований – на входе А, на выходе В. Тест состоит в том, чтобы подать на вход А и сравнить результат с В. И именно так мы его и будем писать. Конечно, тесты бывают существенно более сложными, с проверкой множества правил бизнес-логики.

Так вот, если оставить тест на потом, то мы уже будем знать, как работает этот черный ящик. И вольно или невольно, но мы можем заложиться на собственную реализацию. Т.е. для теста блок функциональности будет уже не черным ящиком, а механизмом, принцип работы которого известен. Таким образом, если этот блок изменится, но продолжит работать корректно – тест может не пройти. Это еще полбеды. Хуже, если в тесте мы будем проверять не конечный результат, а именно работу механизма. И тогда, с точки зрения теста механизм может работать верно, но на выходе даст не В, а С. Я видел и такое. Польза от подобного теста сомнительна. Чтобы избежать такого эффекта, и нужно писать тесты ДО функциональности.

Я знаю, что писать тесты не любит никто, но все же хочу призвать ими заниматься. Польза от этого может быть существенно больше, чем представляется.

Двигаемся дальше. На очереди практика, вызывающая очень много вопросов, критики и т.п., а именно –

Парное программирование

В прямом смысле этого слова. Парное – два разработчика за одним компьютером. Критики обычно утверждают, что в таком раскладе общая производительность пары разработчиков ниже, чем по отдельности. Хочу поделиться опытом.

Собственный опыт

Декабрь 2000 года. Мы с коллегой сидим в длительной коммандировке в Голландии. Днем работа, вечером делать нечего. Пиво пить мы не любим. В карты играть – вдвоем неинтересно. Пойти тоже особо некуда. Есть один компьютер на двоих.

В итоге родилась идея написать компоненту для Delphi, которая нам обоим была нужна. Надо сказать, что и он и я до этого предпринимали попытки ее написать, однако безрезультатные.

Работали мы с ним как и положено в XP – вдвоем за одним компьютером. Один вечер он сидит и пишет, я рядом. Другой вечер – наоборот.

Результат меня поражает до сих пор. Мы написали то, что надо, за 4 вечера. При том что до этого каждый из нас потратил на эту задачу как минимум пару недель, и с гораздо худшим результатом. Написаную компоненту мы активно использовали в дальнейшем и не выявили ни одной ошибки. Она работала как часы, при этом ее код был простым, оптимальным, а интерфейс – интуитивным.

Я хорошо помню процесс разработки. Ошибки вылавливались еще до того, как код компилировался впервые. Существенный плюс второй пары глаз. В процессе написания кода постоянное обсуждение, в результате чего код получался чистым, ибо оптимизировался на лету. Очень большое количество интересных идей – результат мышления в одном направлении, один был катализатором для другого.

Учитывая, сколько времени было потрачено ДО этого, и каков был результат – я этот опыт парного программирования расцениваю как сугубо положительный.

Итак, написание кода вдвоем. Что это дает? Прежде всего – мышление у людей не совпадает. В результате в каждой точке ветвления есть большая вероятность, что я и кто-то другой изберем разные направления движения. Т.е. родятся две идеи вместо одной. Соответственно, появляется возможность выбрать более оптимальную из них. Если такое происходит в каждый момент, как это бывает при парном программировании – оптимальность и ясность кода резко возрастает. Соответственно, падает количество ошибок, что немаловажно для стоимости дальнейшей поддержки кода. Критики этого, как правило, не учитывают.

Далее. Написание кода вдвоем означает, что код досконально знает не один человек, а два. Таким образом, знания о работе кода сложнее потерять. При традиционном подходе – каждый сам за себя – это зачастую очень острая проблема.

Третье. При работе в паре разработчики, фактически, подстегивают друг друга. В результате этого повышается скорость работы каждого из них. Это же, кстати, является и минусом парной работы. Работать вдвоем в таком режиме 8 часов в день – ОЧЕНЬ тяжело. Средний выход сильно выше, соответственно, выше и усталость. Но общий эффект, даже при отдыхе, перекурах, питье кофе, чая и т.п. все равно лучше, нежели при программировании порознь.

Довольно часто парное программирования настолько пугает заказчика (как вариант – руководство компании), что от XP отказываются только поэтому. В таком случае можно применять т.н. параллельное программирование. Отличается от парного оно исключительно тем, что у разработчиков не один компьютер, а два. И внешне ситуация гораздо приятнее для руководящего ока. В случае применения такой практики есть два "но". Первое – разработчики должны сидеть за соседними столами, так, чтобы быть максимально близко друг к другу. Это даст им возможность обсуждения задачи. Опять-таки можно подвинуться на полметра в сторону – и оказаться за одним компьютером. Правда, при этом несколько упадет эффективность написания кода, ибо уже не будет одновременного мышления в одном направлении при работе на двух компьютерах. Но даже это способно дать лучший эффект, чем программирование порознь.

Должен сказать, что опыт параллельного программирования у меня тоже есть. Собственно, он основной, именно так и работала команда, в которой я начинал изучение XP на практике. Как я уже говорил, эффективность у такой работы ниже, чем при парном программировании, однако свои плюсы там тоже несомненно были.

В этом месте очень многие критики заявляют – это уже не XP. Потому еще раз хочу подчеркнуть – XP не есть набор наручников и железных клеток. Это всего лишь рекомендации. И степень применения каждой из них надо выбирать в каждом случае. Да, модификации могут повлиять на общую производительность, но общий эффект все равно будет. Называйте то, что получилось как хотите. Я лично это называю XP.

В противном случае можно договориться до того, что из двух автомобилей один является автомобилем, а другой нет, на том основании, что какой-то гуру двадцать лет назад описал автомобиль как имеющий механическую коробку передач, а у второго из них она автоматическая. Не стоит относится к чьим-то словам фанатично, пусть даже они и стояли у истоков. В 1977-м году Кен Олсон, основатель Digital Equipment Corp. (DEC), сказал, что "нет никаких причин кому-либо желать иметь дома компьютер". Что вы ответите тому, кто будет отговаривать вас купить домой компьютер только на основании этого утверждения?

Подводя итог. Парное программирование имеет несомненные плюсы. Я это знаю на личном опыте. Если по каким-то причинам не получается ввести именно парное программирование – используйте хотя бы параллельное. С течением времени, когда доверие со стороны заказчика (руководства) к методике в общем возрастет (ибо они будут видеть результат!), можно будет перейти и на парное программирование.

Переходим к одной из наиболее обсуждаемых практик –

Рефакторинги

Как правило, само слово "рефакторинг" – словно красная тряпка для быка. Причем как для критиков XP, так и для руководства. Меня однажды уволили после произнесения этого слова. Руководство просто не смогло понять, что можно сделать в течение двух месяцев, если не разрабатывать новую функциональность.

Происходит это по большей части ввиду глубокого непонимания сути рефакторинга как явления. Потому я хочу дать объяснение, зачем это нужно.

Я уже говорил, но повторю еще раз. Программное обеспечение – вещь непостоянная, подверженная изменениям. Требуется новая функциональность. Расширяется старая. Вот тут и зарыта собака размером с доброго слона.

Дело в том, что, каким бы тщательным ни было проектирования приложения – невозможно предугадать, что понадобится через полгода-год. Более того, попытки предугадать приведут лишь к пустой трате времени. Ибо будет реализовано очень много того, что не понадобится с большой вероятностью.

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

Через некоторое время (иногда немалое!) появились новые требования к продукту. И теперь старое решение уже не очень хорошо. Единственным возможным вариантом является переработка старого решения так, чтобы оно отвечало новым требованиям, но при этом вся старая функциональность сохранилась.

Пример

Пусть у нас есть система, скажем, что-то типа webmail administration. Создание почтовых ящиков, управление квотами и т.д. и т.п. При некоторых действиях администратора происходят изменения не только в базе данных, но и в LDAP и на диске. И изменения эти должны быть выполняться целиком и в определенной последовательности, чтобы избежать несоответствий. Т.е. при изменениях, скажем, А и В, проводимых из двух потоков одновременно, состояние после завершения изменений должно быть либо (А1,В1), либо (А2,В2), но никак не (А1,В2) или (А2,В1). Т.е. – нужна синхронизация. Какое решение можно предложить? Использовать механизм синхронизации Java – synchronized- методы и блоки. Просто и красиво. Сделали. Работает.

Через год в систему введены фоновые процессы. Которые тоже надо синхронизировать, причем как между собой, так и со старым кодом. И если со старым кодом еще как-то можно использовать ту же модель синхронизации, то при синхронизации процессов между собой это уже проблематично. Дело в том, что процесс может длиться достаточно долго. И если ждать, пока завершится первый процесс, чтобы затем запустить второй (а именно так и будет при использовании synchronized) – запрос к системе будет висеть. Администратор может решить, что где-то сбой, нажать Stop в браузере, потом запустить процесс еще раз. В общем, ничего хорошего не получится. Нужно иметь возможность не ждать, если ресурс уже блокирован. В случае с synchronized-кодом это невозможно.

Решение – использовать, скажем, библиотеку синхронизации Дуга Ли. Причем не напрямую, а с созданием некого интерфейсного слоя. Дело в том, что эта библиотека введена в Java 1.5 (пакет java.util.concurrent), но интерфейс ее сильно при этом изменен. В свете планирующегося перехода на версию 1.5 лучше полностью повторить именно этот интерфейс, чтобы впоследствии миграция состояла бы только в замене имени пакета – с собственного на java.util.concurrent. Cделали. Работает. Замечательно!

Еще через год от бизнеса приходит требование – обеспечить устойчивость системы. Т.е. – поднять ее на не менее чем двух серверах. А это значит – обеспечить синхронизацию при наличии нескольких JVM. Все методы, используемые до этого времени, не годятся. Решение – используя уже существующий интерфейс, написать реализацию системы распределенной блокировки. Т.е. на поверхности – ровно те же интерфейсы, что и в Java 1.5, а под ним что-то принципиально другое. На основе, например, транзакционного кластерного кеша. JBossCache, Tangosol Coherence и иже с ними. Сделали. Работает. Причем основной код вообще не пришлось трогать! Подменили реализацию библиотеки синхронизации. С библиотеки Дуга Ли на собственную.

Разумеется, при всех переделках система внешне осталось той же. Не изменились ни поведение, ни бизнес-логика.

Приведенный пример является типичным описанием рефакторинга. По своему определению, рефакторинг – это переработка системы для приведения в соответствие с текущими требованиями. При этом внешне система остается той же.

Критики часто утверждают, что рефакторинг есть следствие некачественного кода, дизайна, и т.п. Дескать, если система написана хорошо, никакие рефакторинги ей не нужны. Это в корне неверно. Рефакторинг МОЖЕТ быть следствием неправильного дизайна. Но я за 3 года ни разу не встречался с подобными случаями, хотя рефакторингами занимаюсь постоянно.

В основном рефакторинг есть следствие эволюции системы, ее естественного развития. Сравните его с ремонтом квартиры. В комнате переклеивают обои. Почему? Обязательно ли потому, что старые были плохо наклеены? Совсем нет. Обои можно переклеивать потому, что хочется новых. Другого цвета. Другой фактуры. Изменились требования к комнате – и ее обновляют. Но это ни в коем случае не является следствием изначальных ошибок. У пятилетней девочки в комнате розовые обои с зайчиками и мишками – норма. У пятнадцатилетней – вряд ли. И это – изменившиеся требования – есть повод к ремонту.

В XP приняты т.н. постоянные рефакторинги. Зачем это делается? Сравним с тем же ремонтом. На кухне стала качаться плитка. Можно подклеить, а можно и оставить на потом. Подклейка займет час. Дует из окон, пыль проникает. Можно поменять рамы на современные, пластиковые, шумоизолирующие и т.п., а можно оставить на потом. Замена займет полдня. Ну и т.д. и т.п. А теперь представьте себе, что ВСЁ, что каким-то образом не устраивало в кухне, оставляют на потом. Что будет в итоге? В итоге из кухни вынесут мебель, плиту, мойку, собьют ВСЮ плитку, т.к. она уже наполовину осыпалась, снимут рамы, ..., ... В общем, разнесут кухню вдребезги напополам. Ремонт будет продолжаться неделю, в течение которой в квартире жить будет невозможно. А после ремонта еще и убрать надо будет.

Что в итоге? В итоге мы имеем капитальный ремонт, который во-первых, дольше, во-вторых, дороже текущего. И весь промежуток времени до капитального ремонта мы будем жить в состоянии, которое нас каким-то образом не устраивает. Жить, конечно, можно, но... При том, что в случае текущего ремонта – это было бы и быстрее, и дешевле, и комфортнее в конечном итоге.

Параллель с постоянным (текущим) рефакторингом проводить или уже не стоит?

Рефакторинги жизненно необходимы в любой эволюционирующей системе. И тем более – при использовании XP, которая изначально ориентирована именно на гибкую разработку. Единственный вопрос. "За чей счет этот банкет? Кто оплачивать будет?" – как говорил незабвенный Иван Васильевич.

Это решать вам. У нас было принято, что раз уж рефакторинги наша инициатива, то и делаются они за наш счет. Часть из них мы иногда предлагали оплачивать заказчику, если эта часть была связана с реализацией новой функциональности, которую он запрашивал. Но делали мы это не всегда.

Надеюсь, что теперь суть рефакторинга как процесса вы уловили. Перейдем к следующей, не менее обсуждаемой практике –

Простой дизайн

Я не знаю, откуда что берется, но у подавляющего большинства противников XP прочно укоренилось утверждение: простой == плохой (некачественный, безграмотный). Может, из-за того, что кто-то где-то сказал про минимально возможное количество классов. Может, еще из-за чего. Но их этого правила делается совершенно фантастический вывод – XP ориентирована на безграмотный дизайн и, следовательно, на безграмотный код.

Господа! Если вам кто-то сказал, что "просто" значит "безграмотно" – вас этот кто-то обманул. Можно писать просто, но при этом грамотно. А можна – как слышыца. И эта какрас и будит бизграматна. А можна писать са страшна сложными граматичискими канструкцыями упатрибляя диипричасные абароты и все равно эта будит бизграматна.

Грамотность – она не зависит от сложности кода. Это правило означает всего лишь одно – изобретать паровоз надо только тогда, когда лошадь уже не справляется. А пока она бодро топает, причем вас устраивает и скорость и грузоподъемность – паровоз вам не нужен. Если бы в примере, упомянутом выше, с самого начала разрабатывали распределенную синхронизацию – добром бы это не кончилось. Во-первых, с самого начала намаялись бы страшно, ибо библиотеки, пригодные для использования, появились существенно позднее. Во-вторых, систему все равно пришлось бы неоднократно переделывать. Потому как два года назад предугадать требования, выдвигаемые сегодня – НЕ-ВОЗ-МОЖ-НО! А следовательно – время, силы и средства потрачены впустую. Наконец, третье. Где гарантии, что то, на что вы хотите заложиться сегодня, ВООБЩЕ понадобится? На мой взгляд система вообще не требовала дублирования. Были проблемы с устойчивостью, но обусловлены они были исключительно системными проблемами. И решать их надо было на системном уровне – операционной системы и железа. Решение же этих проблем на уровне ПО было продиктовано исключительно политикой.

Итак, что же все-таки означает "простой дизайн"? Делайте то, что нужно именно сейчас. Никто не мешает делать это грамотно. Но не закладывайтесь на то, что может произойти далее чем через два месяца! Потому что два месяца – это уже достаточный срок для пересмотра направления движения.

Правило "простого дизайна" неразрывно связано с правилом "непрерывных рефакторингов". Это как раз тот случай, когда результат больше чем простая сумма. Если писать просто, но не корректировать систему под текущие требования – хорошо не будет. Если корректировать, но писать при этом очень сложно – тоже ничего хорошего. Ибо коррекция как раз будет направлена на то, чтобы разгрести изобретенную кучу незнам-чего. Если же писать просто, и постоянно при этом корректировать код – результат будет гораздо лучше.

Вернемся к аналогии с ремонтом. Вариант "сделать просто, но не поддерживать постоянно" мы уже рассматривали. Рассмотрим теперь другой вариант – "сделать сложно и поддерживать". Представьте, что вместо аккуратно крашеного потолка, обоев и кафеля мы сделаем на потолке штукатурные фрески, а пол покроем деревом. Как это было сделано, скажем, в 17 веке во дворцах саксонских королей. И все это – на кухне. К чему это приведет?

Во-первых, из-за влажности и тепла фрески будут разрушаться катастрофическими темпами. Мы ежедневно будем что-то подправлять. Во-вторых, дерево на полу не лучшим образом отнесется к воде, которая на него будет попадать. Плюс дерево – материал более капризный, нежели кафель, оно будет "гулять" под воздействием температуры. В результате мы будем на поддержку всего этого хозяйства в нормальном состоянии тратить куда больше времени, нежели могли бы.

Но самое печальное, что сделано все это было для того, чтобы блеснуть перед гостями из далеких краев. А гости не приехали.

Это – классический пример того, зачем нужен простой дизайн. Я сталкивался с таким много раз, еще до того, как познакомился с XP. Функциональность сначала создавалась сложная, запутанная, потом она использовалась дай Бог на 10%, потом ее упрощали, а половину вообще уничтожали на корню. И оставался вопрос – а зачем так было делать с самого начала?

Что в итоге? В итоге мы приходим к тому, что

ПРОСТО не тождественно ПЛОХО

И это надо осознать любому. Простой дизайн не означает, что можно писать как попало. В этом случае не спасет никакая методология. Очень надеюсь, что эту мысль мне удалось донести.

Идем дальше. Следующая практика –

Коллективное владение кодом

Что это означает? Означает это вот что: у кода нет автора, его может менять любой разработчик.

Что это НЕ означает? Это НЕ означает, что за код не отвечает никто. Существует четкое правило: сломал – чини. Если изменения, внесенные кем-то, сделали код неработоспособным – именно автор изменений ответственнен за приведение кода в рабочее состояние. Причем не когда-нибудь потом, а ПРЯМО СЕЙЧАС! Разработчик не имеет права заниматься чем-либо другим до того, как исправит внесенные им ошибки.

Потому, когда говорят, что если у кода нет автора, то это анархия – не верьте. Автор кода – вся команда. И всегда есть тот, кто за код отвечает. Это тот, кто менял его последним.

Больше по этому поводу сказать нечего. Следующая практика –

Постоянные (непрерывные) интеграции кода

Зачем это надо. Чтобы быть уверенным, что продукт не сломан последними изменениями. Это дает возможность быстро отреагировать на возможные проблемы. Кроме того, "интеграция" вовсе не означает "компиляция". Это еще и всевозможные проверки целостности кода, включая запуск тестов.

Собственный опыт

В текущем проекте у нас сборки происходят раз в полчаса, если за это время были изменения в коде, лежащем в репозитории. Сделано это на основе CruiseControl. Кроме unit-тестов (см. JUnit) у нас запускается проверка кода с помощью FindBugs, которая находит весьма нетривиальные ошибки, и проверка с помощью JCSC – на соответствие нашим стандартам кодирования. Все это вместе минимизирует возможность того, что в продукт попадет неработающий или даже просто не соответствующий стандартам код.

Таким образом, при постоянной интеграции кода есть уверенность в том, что в любое время можно взять код – и он будет работать. Или же есть информация о том, что код сломан. Причем поступает она в самом худшем случае через час после того, как это случилось.

Следующая практика –

Заказчик в команде

Идея проста. Чем длиннее цепочка, тем больше она напоминает испорченный телефон. Каким бы тщательным ни было планирование – некоторые вопросы все равно всплывут непосредственно в процессе реализации. И от того, насколько быстро и полно разработчик пообщается с заказчиком, зависит скорость реализации им задачи. Если отправить письмо, то придется ждать ответа. Часто имеющийся вопрос блокирует дальнейшую работу, потому разработчик просто будет простаивать. Пока заказчик получит письмо, пока он его прочитает, пока он ответит... Звонить – тоже не всегда удобно. Иногда проще нарисовать одну картинку, чем полчаса ее описывать. Да и где гарантия, что заказчик будет в этот момент на месте, что он будет в состоянии ответить на вопрос?

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

Идем дальше.

Частые выпуски версий

ОЧЕНЬ важная практика. Предпосылок к ней – масса.

Первое. Чем меньше функциональности в очередном релизе – тем легче его протестировать. Если вы поменяли половину системы, и тестирование выявило плавающую ошибку – вы поседеете раньше, чем ее найдете. Если в системе было одно единственное изменение – поиск практически тривиален. А потому – в этом отношении частые выпуски версий весьма выгодны.

Второе. Чем чаще выпускается версия – тем быстрее заказчик получает требуемую функциональность. Как вы помните, на каждую версию в результате планирования составляется список историй с приоритетами. После выпуска заказчик получает всю эту функциональность.

Давайте посмотрим по времени. За неделю до начала разработки очередной версии создается список историй. Далее, одна итерация на разработку. Неделю на тестирование. Итого – две недели плюс длина итерации (которая чем короче, тем лучше). Сравните это со стандартными методиками. Месяц на техническое задание. Два месяца на разработку. Месяц на тестирование. Итого – четыре месяца.

Это может показаться игрой цифрами. В самом деле, через месяц в первом случае заказчик получит меньше, чем через четыре во втором. Так и есть. В смысле, получит он действительно меньше. Однако представим, что длина итерации – две недели. Т.е. два месяца разработки – это четыре итерации. Считаем еще раз по первому варианту. Неделя до начала, потом четыре итерации по две недели, потом неделя на тестирование после последней итерации – итого два с половиной месяца. При том, что в обоих случаях на тестирование потрачено по четыре недели, на ТЗ – по четыре недели. Однако в случае XP тестирование оконченной версии и проработка ТЗ для следующей происходит одновременно с разработкой текущей версии.

И что более важно – заказчику не приходится ждать четыре месяца, чтобы получить необходимую функциональность. Благодаря планированию самую необходимую ему часть он получает уже через месяц, а каждую следующую – через две недели после предыдущей. И это есть самый большой плюс частых выпусков версий.

В XP принято считать, что итерация должна занимать от недели до трех. Оптимально – две. Именно потому я и использовал это значение в расчетах выше. Практика показывает, что за две недели можно успеть реализовать достаточно большие фрагменты функциональности.

Важный момент. Я об этом упоминал в разделе планирование. Если получилось так, что разработчики не успевают закончить в итерации все, что запланировано – необходимо сократить объем функциональности, но НЕ ПЕРЕНОСИТЬ выпуск очередной версии. Почему? Потому что, во-первых, это расхолаживает. Раз перенесли, два перенесли, потом заказчик захотел добавить очень нужную фенечку прямо сейчас – и не успеете оглянуться, как потеряете основное преимущество. Каждый релиз будет затягиваться, раздуваться, его будет сложнее тестировать. Это не нужно никому. Кроме того, как я уже упоминал, были случаи, когда функциональность несколько раз переносилась из релиза в релиз, и в конечном итоге вообще убиралась заказчиком из списка задач. Это во-вторых.

Единственный случай, когда релиз может быть перенесен – когда этого требуют тестировщики. Выпустить недотестированный продукт или продукт, содержащий ошибки, – это намного хуже, чем отложить выпуск на неделю.

Следующая практика.

40-часовая рабочая неделя

Эту практику разработчики встречают с энтузиазмом. :) Еще бы! И это правильно. Авралы, переработки и прочая – они не приносят ничего хорошего. Производительность разработчиков сильно падает, если они вынуждены работать подолгу. Причем падает настолько, что за десять часов они начинают делать столько, сколько раньше делали за шесть. Вообще я убежден, что программист не может работать в день больше шести часов. Дальше – резко снижается как скорость, так и качество работы, что намного хуже.

На одном из моих прошлых мест работы, там, где XP была внедрена полностью, нам НЕ РЕКОМЕНДОВАЛИ работать больше восьми часов. Кстати, в эти восемь часов входит и обед. Т.е. рабочий день начинался в 10:00 и заканчивался в 18:00. Работа после 18:00 не поощрялась.

Далее идут...

Стандарты кодирования

О стандартах кодирования я говорил в статье Качественный код – слагаемые, потому повторяться не буду. Из собственного опыта. Я уже писал выше о том, как мы с коллегой писали компоненту для Delphi в паре. Единственный неудобный момент, который был для меня – мы использовали разные отступы. Я делал отступ в два символа, он – в четыре. Было дико неудобно читать.

В текущем проекте у нас существуют единые стандарты. Поскольку вся компания использует IntelliJ IDEA, для нее написана конфигурация стиля, которую используют все разработчики. Комбинация из нескольких клавиш – и код отформатирован.

И последний акт марлезонского балета –

Метафора системы

Метафора системы – это аналогия, позволяющая точнее понять и прочувствовать систему. Я метафоры применяю часто. Пример с ремонтом квартиры в разделе о рефакторингах – метафора чистой воды. Путем проведения параллелей между чем-то незнакомым (в данном случае – рефакторинг) и чем-то интуитивно понятным (ремонт) можно дать хорошее представление о незнакомом предмете. Главное, правильно подобрать аналогию.

Возникает вопрос – а кто будет подбирать аналогии? Для этого нужен опыт и некоторый талант. Обычно этим занимается тот, кто прорабатывает архитектуру. Только он может представить систему целиком и грамотно создать ее метафору. Это весьма непростое занятие, требующее навыков как в архитектуре, так и в обучении.

* * *

Уф... Можете перевести дух. С практиками покончено. На очереди...

Мифы об XP

В основном, мифы об XP основаны на непонимании практик "рефакторинг" и "простой дизайн". Из "непрерывного рефакторинга" делается вывод – "XP рассчитана на "чудесное появление" некоей архитектуры в результате непрерывного рефакторинга". Из "простого дизайна" делается вывод о некачественном коде. Обо всем этом я уже писал. Однако одно высказывание хочу упомянуть особо.

XP ориентирован не на результат, а на процесс. На мой взгляд, это высказывание близко к абсурду. И тем более странно, что его приписывают одному из идеологов XP. Не уточняя, правда, какому.

Ни одна методика не может быть НЕ ориентирована на результат. Этот нонсенс. Если результат не достигнут – заказчик не удовлетворен. Кстати, еще одно интересное высказывание на эту тему – "в результате такого планирования и реализации в канонах XP в итоге получается лишь временно удовлетворенный заказчик". ОЧЕНЬ интересно. "Временно удовлетворенный". Т.е. в конечном итоге он удовлетворен не будет. Рассмотрим это заявление поближе.

Для начала вопрос. Что нужно заказчику от нас? Ему нужно программное обеспечение. Если он получает его – он удовлетворен. Если он не получает его – он не удовлетворен. "По-моему, так!" – как говорил Винни-Пух.

Рассмотрим классическую методику. Заказчик составляет техническое задание. Как я уже говорил, он весьма смутно представляет, что ему надо. И более того, из всего, что он написал в ТЗ, надо ему, во-первых, не все, а во-вторых, по большей части не совсем то. Поверьте человеку, наобщавшемуся с заказчиками. Далее, через четыре-шесть месяцев ему выкатывают продукт. В точности соответствующий ТЗ, т.е. – в большой степени не то, что заказчику надо. Кроме того, внести изменения в продукт по ходу разработки он не может.

Рассмотрим XP. Заказчику помогают понять, что ему нужно на самом деле. Он сам определяет, что из этого для него наиболее критично. Первую версию продукта, с наиболее критичной для него функциональностью, он получает уже через месяц-полтора, в дальнейшем раз в две недели он получает новую версию, в каждой из которых функциональность, наиболее критичная на данный момент. Заказчик может в любой момент внести изменения в продукт и через месяц получить их реализованными.

А теперь, господа, положите руку на сердце и чистосердечно признайтесь – в каком из двух сценариев у заказчика больше вероятность быть удовлетворенным в процессе разработки? И в каком – в конечном итоге. На мой взгляд, ответ очевиден. В обоих случаях – при применении XP. Ибо я не вижу ни единой причины для заказчика стать неудовлетворенным в конце, если в процессе он был удовлетворен.

На мой взгляд, этот миф не относится к словам "одного из идеологов", а в гораздо большей степени появился вследствие вот чего. Читайте продолжение цитаты.

Когда я говорю, что XP не ориентирована на результат, я во-первых, цитирую одного из авторов-идеологов XP, а во-вторых говорю то, что есть (пережил два внедрения XP в разных проектах и дважды задавил эту ересь). XP полностью исключает предварительную проработку архитектуры продукта, а такой подход полностью исключает возможность получения качественного результата. XP не предполагает в проекте таких ролей, как архитектор или проектировщик БД. Если вы захотите возразить, значит вы не знаете, что такое XP.

Конец цитаты.

Да, я хочу возразить. И именно потому, что знаю, что такое XP.

Пункт номер один. Какой результат можно получить, если изначально считать методику ересью и активно ее давить?

Пункт номер два. XP исключает проработку архитектуры, вообще архитектора как такового, разработчика БД. Обратимся к практикам. В частности, к Метафоре системы. Вопрос на засыпку. Кто может построить метафору, кроме архитектора? По моему скромному мнению – никто. А раз метафора необходима – необходим и архитектор. Который для начала проработает архитектуру, а потом уже построит метафору. И по ходу разработки будет за архитектурой следить и подправлять ее, ибо для него это – рефакторинг. То же самое относится к проектировщику БД.

На мой взгляд это заявление выросло опять-таки из стандартного мнения по поводу практики простой дизайн. Человек не понял до конца, что это значит, в том виде, в котором понял – не принял. Справедливо рассудил, что безграмотный код – это плохо. Сделал вывод, что XP это плохо и в дальнейшем искал лишь подтверждения своему выводу. При том, что основан он был на изначально неверное посылке.

Как человек, работавший по методологии XP в чистом виде, я могу сказать, что и архитектор, и проектировщик БД, и вообще все, кто нужен для грамотной разработки программного обеспечения – все они вписываются в XP. И архитектуру можно проработать, это ничему не противоречит. И базу можно проработать. И вообще сделать все, что надо. XP этого не запрещает. А что не запрещено – то разрешено.

XP как методика слишком непривычна, чтобы легко восприниматься. Особенно в случае, когда человек привык к стандартным методикам разработки и планирования. Кстати, чем больше опыт, чем больше недостатков стандартных методик видит разработчик – тем лучше он воспринимает XP. Опять-таки, все упирается в опыт.

И последнее, чего я хотел бы коснуться –

Предназначение XP

Рассмотрим ключевые направления, на которые нацелена XP:

Таким образом, можно сформулировать предназначение XP.

XP как методология предназначена для уменьшения затрат на разработку и поддержку ПО. При этом она обеспечивает гибкость процесса разработки, качество продукта и его соответствие реальным потребностям заказчика.

Я лично считаю, что в свете такого предназначения XP можно применять в любом проекте. Если даже заказчику не нужна дальнейшая поддержка (во что мне ОЧЕНЬ трудно поверить) – гибкость разработки и возможность включение в продукт дополнительных пожеланий заказчика совершенно необходимы. Я еще не видел ни одного случая за восемь лет моего опыта коммерческих разработок, чтобы заказчик точно и до конца знал, что ему нужно, смог бы это сформулировать и не вмешиваться в процесс разработки до получения готового продукта.

* * *

Проведение грани – тут XP, тут не XP.

Эта часть добавлена существенно позже основной. Она написана по результатам многих писем, обсуждений и т.п. Тема, которую я хочу поднять – это проведение границы. Как это было, кажется, у Ширвиндта, "здесь играем, здесь не играем, здесь селедку заворачиваем". Этой же темы я касался вскользь, когда говорил о парном и параллельном программировании. Итак, вопрос – где та грань, за которой заканчивается XP? Действительно ли шаг в сторону – уже не XP?

Я догадываюсь, что для многих являюсь куда меньшим авторитетом, чем отцы-основатели XP. Потому своего мнения на эту тему в очередной раз высказывать не буду. Вместо этого я дам ссылку статью одного из основателей XP, а именно – Мартина Фаулера (Martin Fowler). Статья посвящена гибким технологиям разработки вообще и XP в том числе. Ссылка на русский перевод: http://www.maxkir.com/sd/newmethRUS.html.

Общий смысл статьи таков – если при разработке используется гибкая методика, она должна адаптироваться под конкретный проект, конкретного заказчика и конкретную команду. Две же цитаты хочу привести дословно.

Цитата первая (акценты расставил я), из части "Адаптация адаптивного процесса":

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

Цитата вторая, из той же части:

Что касается ХР, то на первый взгляд может показаться, что строгие правила этой методологии не позволяют вносить в процесс изменения. На самом деле это не так. Однако главное отличие по сравнению с другими гибкими методологиями состоит в данном случае в том, что пропагандисты ХР предлагают следовать "официальной версии" этой методологии в течение нескольких итераций, а уже потом менять ее. Кроме того, источники по ХР вообще не упоминают пересмотры процесса и не считают их частью методологии (хотя существует мнение, что они должны быть сделаны одной из практик ХР).

Таким образом, согласно мнению одного из отцов-основателей XP, процесс не то что может – должен пересматриваться. И я с ним в этом согласен.

А потому, разговоры о том, что "это XP, а это уже нет", лишены смысла. Суть XP – в гибкости. И если какая-то из практик в исходном виде не удовлетворяет команду – ее стоит поменять так, чтобы это принесло пользу. Процесс от этого не потеряет, а только приобретет. И если он сохранит основные ориентиры XP – его можно называть XP. "По-моему, так!"

* * *

Наверное, это все. В смысле, все о теории XP. Но не обольщайтесь. Теперь вы более-менее знаете теорию, не более. А от нее до практики, как я уже говорил... При внедрении XP в производственный процесс есть много разных тонкостей, однако при этом существует некий фундамент, на котором держится практика XP и без которого она заведомо обречена на провал. Об этом фундаменте пойдет речь во второй статье об экстремальном программировании – Экстремальное программирование – практика, психология и ошибки. Читайте.