Очередной урок, «кейс», экспериментаторам с единственно правильными архитектурами, любителям городить новые слои, чтобы спрятать за ними свою некомпетентность в области СУБД. Не исключаю, конечно, что для некоторых менеджеров, получивших выгоду от поглощения, этот прецедент мог быть и позитивным.
Один из «Технических Дней Microsoft» (TechDays) в 2011 году был целиком посвящён специализации DBA (DataBase Administrator). А выступающий на сцене ведущий эксперт не постеснялся напрямую высказать призыв: «Последние годы я вижу тотальное падение компетенции в области баз данных. DBA, проснитесь!»
Code revision, или Коза кричала
Ревизия программного кода всякий раз напоминает мне эпизод из фильма Г. Данелии «Осенний марафон». Главный герой, преподаватель университета Андрей Бузыкин сидит у своей бывшей сокурсницы Варвары, помогая ей с переводом художественного произведения. Время перевалило за полночь, происходит примерно такой диалог.
– Скажи, Бузыкин, может, я бездарная?
– Не-е-е. .
– Но ты же всё повычеркивал!
– Не всё. . Но вот это, например, я не мог оставить: «Коза кричала нечеловеческим голосом».
Мой коллега, обладатель диплома историка, переквалифицировавшийся в консультанты по BI, как-то посетовал, что он плохой программист. Будучи несколько удивлённым, я успокоил его тем, что в BI программирования как такового немного и критичные куски кода всегда могут помочь написать коллеги соответствующей специализации, стоит обратиться к ним по внутренней рассылке. Хуже, когда вполне программистский коллектив умудряется годами работать без системы контроля версий исходников, и тогда в коде половину объёма составляют закомментированные куски многолетней давности. Выбросить их жалко, вдруг пригодятся. Но и контроль версий с архивацией не спасает от цифровой пыли десятилетий. В подобных залежах порой можно обнаружить настоящие образцы софтостроительных антипрактик.
Например, одна ERP-система много лет назад переносилась из файл-серверной архитектуры в среду клиент-серверной СУБД. Вполне ожидаемо в базе данных обнаруживается таблица типа «мегасправочник», хранящая все ссылки вида «ключ-значение». Структура состоит из трех колонок: код справочника, код значения и само значение. В прежней архитектуре ссылочная целостность поддерживалась приложением, теперь же стандартным образом приспособить для этой цели транзакционную СУБД невозможно, потребуется написать достаточно длинный линейный триггер.
Такой универсализм стал причиной использования мегасправочника одновременно для хранения внутренних счётчиков нумерации записей: текущая величина хранилась в строковом поле колонки «Значение» в формате «префикс; текущий номер». Приложение считывает текущее значение счётчика, анализирует строку, выделяя префикс и величину, переводит величину из строки в целое, увеличивает его на 1, формирует новое значение строки и снова записывает всё это обратно в базу данных.
Кроме перечисленных манипуляций со строкой, вначале делается попытка заблокировать запись через соответствующую опцию SQL-запроса. Мысль правильная, но, к сожалению, блокировка делается вне контекста транзакции, то есть снимается сразу после окончания выполнения запроса. На вопрос: «У вас конфликтов нарушения первичного ключа не было?» был дан самый оригинальный ответ за всю мою практику: «Они нам мешали делать каскадные обновления в связанных таблицах, и мы их удалили, оставив просто индексы».
В другом случае на форме Delphi-приложения имелась группа из двух опций (радиокнопок) для взаимоисключающего выбора. Кнопки были подписаны как «Объём ограничен» и «Объём неограничен». Вроде бы ничего особенного. Но открываем форму и обнаруживаем, что кнопка с надписью «Объём ограничен» поименована программистом как «КнопкаОбНеограничен». И, разумеется, наоборот. Ошибся человек, бывает…
К счастью, в коде формы есть только одно место, где значения кнопок используются. Видимо, во избежание путаницы процедура оформлена следующим образом:
var ОбъёмТакиОграничен: boolean;
…
if КнопкаОбНеограничен. Выбрана then
ОбъёмТакиОграничен:= true
else
ОбъёмТакиОграничен:= false;
…
ВызовКакойТоФункции(ОбъёмТакиОграничен);
Дальше ревизия коснулась SQL-кода. Программист пытался выбрать следующий элемент списка, обрабатывая только первую запись из пришедшего по запросу набора. При этом сортировку он делал совсем по другой колонке, нежели порядковый номер в списке. В итоге выбиралось что угодно, но не следующий элемент.
Не буду утомлять вас другими примерами, надеюсь, вы просто поверите в их многочисленность и оригинальность. Мне хотелось лишь донести простую мысль, что ревизия кода, несомненно, весьма полезная процедура, но как минимум при двух условиях:
• эта процедура регулярная и запускается с момента написания самых первых тысяч строк;
• процедуру проводят специалисты, имеющие представление о системе в целом. Потому что отловить бесполезную цепочку условных переходов может и компилятор, а вот как отсутствие контекста транзакции в обработке повлияет на результат, определит только опытный программист.
Дж. Фокс [2] выводит из своего опыта проектной работы в IBM важную мысль, что большой ошибкой является привлечение к процессу внутреннего тестирования и обеспечения качества посредственных программистов. По его мнению, компетентность специалиста в этом процессе должна быть не ниже архитектора соответствующей подсистемы. Действительно, ведь оба работают примерно на одном уровне, просто один занят анализом, а другой – синтезом.
Качество кода во многом зависит от степени повторного использования, поэтому приведу простой и доступный способ проверки того, не занимается ли ваша команда программистов копированием готовых кусков вместо их факторизации. Для этого регулярно делайте сжатый архив исходников, например zip с обычным коэффициентом компрессии, и оценивайте динамику роста его размера относительно количества строк. Если размер архива растёт медленнее, чем количество строк, это означает рост размера кода за счёт его копирования.
Наживулька или гибкость?
Приходишь в отечественную компанию, смотришь, как у нее устроено IT, и видишь, что люди просто упали с дуба.
Не все гигагерцы и гигабайты расходуются впустую. Кризис в софтостроении, о котором говорят уже более 30 лет, продолжается. В ответ на усложняющиеся требования к программным системам и неадекватные им методологии (технологии), особенно в части моделирования и проектирования, индустрия выставила свое решение. Оно состоит в достижении максимальной гибкости средств программирования и минимизации ошибок кодирования. Проще говоря, если мы не можем или не успеваем (что в итоге приводит к одному и тому же результату) достаточно хорошо спроектировать систему, значит, надо дать возможность быстро и с минимальными затратами её изменять на этапе кодирования. Но принцип для заказчика остался прежним: «Быстро, качественно, дёшево – выбери два критерия из трёх».
Поднимать тему так называемой гибкой (agile) разработки программ достаточно рискованно. С одной стороны, вокруг сюжета много шума, эмоций и мало объективной фактической информации. С другой – большинство встречавшихся мне собеседников опирались больше на веру, чем на рациональные аргументы, что делало дискуссию бессмысленной. Но мы тем не менее попробуем.
Причина возникновения экстремальных методик, сосредоточенных на фазе кодирования, не случайна. Американский специалист по методологиям софтостроения Кэпер Джонс в своей книге[23] приводит весьма удручающие статистические данные, например:
• среди проектов с объёмом кода от 1 до 10 миллиона строк только 13 % завершаются в срок, а около 60 % свёртываются без результата;
• в проектах от 100 тысяч до 1 миллиона строк эти показатели выглядят лучше (примерно 25 % и 45 %), но признать их удовлетворительными никак нельзя;
• в проектах примерно от 100 тысяч строк на кодирование уходит около 20 % всего времени, и эта доля снижается с ростом сложности, тогда как обнаружение и исправление ошибок требует от 35 % времени с тенденцией к увеличению.
В любом софтостроительном процессе, будь то заказной проект или продукт для рынка, всегда можно выделить 4 основные стадии:
• анализ, чтобы понять «что делать»;
• проектирование, чтобы определить и запланировать «как делать»;
• разработка, чтобы собственно сделать;
• стабилизация, чтобы зафиксировать результат предыдущих этапов.
Если стадии, органично совпадающие с концептуальным, логическим и физическим дизайном системы, расположить иерархически без обратных связей, то получим классическую схему «водопад», исторически считающуюся первой методологией в софтостроении.
Рис. 13. Методология «водопад» в идеальном случае
«Водопад» также соответствует разработке «сверху-вниз» в структурном программировании: выделяем самые общие функции системы, проводим их декомпозицию на подфункции, а те, в свою очередь, на подподфункции и так далее, пока не упрёмся в элементарные операции.
Что же не так в схеме? С увеличением сложности реализуемой системы анализ и следующее за ним проектирование начинают занимать всё больше времени. Постепенно обнаруживаются новые детали и подробности, изменяются требования к системе, возникают новые сопутствующие задачи, расширяющие периметр. Приходится, не отдавая проектную документацию в разработку, возвращаться к анализу. Возникает риск зацикливания процесса без конечного выхода какого-либо программного обеспечения вообще.
Рис. 14. На практике «водопад» зацикливается с увеличением сложности проекта
Из сказанного вовсе не следует, что методология плоха. Просто она имеет свои границы применения, широта которых напрямую зависит не только от опыта аналитиков и проектировщиков, но и от новизны моделируемой предметной области. В достаточно консервативных банковских или промышленных приложениях «водопад» может подойти и для комплексной системы. Если же, например, возникает принципиально новый рынок, то найти опытных подрядчиков проблематично, а бизнес-направление заказчика находится в постоянной реорганизации, поэтому стадия анализа, занимающая больше 3–4 недель, рискует быть малопродуктивной. И не только стадия анализа, но и весь проект в целом. Тут впору подумать над временной автоматизацией в рамках офисного пакета и скриптовых сред вместо комплексного решения.
Очень важно отделить редкую ситуацию «бизнес меняется еженедельно» от гораздо более распространённой «представления команды разработчиков о бизнесе меняются еженедельно». Если вам говорят о якобы часто изменяющихся требованиях, всегда уточняйте, о чём, собственно, идёт речь.
Но если проблема зацикливания на требованиях может быть успешно решена выбором подрядчиков, уже имевших опыт в построении систем данного типа, то другая, гораздо более значимая проблема несовпадения взглядов заказчика и подрядчика на казалось бы одни и те же вещи стабильно проявляется с ростом проекта. Даже если принять во внимание, что только 20 % требований специфичны для заказчика, тогда как 80 % исходят непосредственно от предметной области инвариантно среде и контексту, то эти 20 % способны угробить весь проект.
Для снижения рисков такого рода в конце 1980-х годов была предложена спиральная модель софтостроения.
Необходимо отличать спиральную модель от итеративной. Спиральная модель сходится в точку «система готова», итеративная модель в общем случае не сходится, но обеспечивает реализацию всё новых и новых требований.
Рис. 15. Спиральная модель с двумя витками
Ключевой особенностью спиральной технологии является прототипирование. В конце каждого витка после этапа стабилизации заказчик получает в своё распоряжение ограниченно работающий прототип целой системы, а не отдельных функций. Основная цель прототипа состоит в максимально возможном сближении взглядов заказчика и подрядчика на систему в целом и выявлении противоречивых требований.
Спиральная модель не навязывает присутствие всех стадий на каждом витке. Вполне может статься, что первый же прототип будет удачным, а функциональная и техническая архитектуры соответствуют требованиям. Тогда финальные витки будут фактически состоять только из разработки и стабилизации.
Слабым звеном в спиральной методологии является определение длительности очередного витка, его стадий и соответствующее выработанному плану управление ресурсами. Пока анализ не выдал концептуальные модели, проектировщики и ведущие программисты ограничены техническими требованиями, тогда как рядовые программисты просто ожидают спецификации. Но неудачно сократив фазу анализа или проектирования на первом витке, тем самым можно увеличить их общее количество, рискуя выйти за рамки первоначальной оценки сроков и бюджета.
Тотальный анализ и проектирование вырождают спираль в водопад, тогда как формальный подход, «для галочки», выдающий бесполезные для программистов спецификации, превращает спираль в бесконечный цикл, прерываемый управленческим решением. Поиск компромисса в такой ситуации – трудная задача многокритериального выбора, в большой степени зависящая от опыта и здравого смысла руководителей рабочих групп и проекта в целом.
Таким образом, из научной плоскости мы переходим в область экспертных оценок. Поэтому слово «методология» справедливо вызывает у многих негативное отношение. Правильнее было бы говорить о технологии ведения со-фтостроительных проектов, но исторически появившийся термин так просто из обращения не выкинуть. Да и надо ли?
Создатели итерационных методологий, также называющихся гибкими (agile) или экстремальными, выкинули не термин, а фазы анализа и проектирования.
Ключевой особенностью гибкой методики является наличие мифологического титана – владельца продукта (product owner), который лучше всех знает, что должно получиться в итоге. На самом деле это просто иная формулировка старого правила «кто платит, тот и заказывает музыку». Именно владелец, за рамками собственно гибкого процесса, гением своего разума проводит анализ и функциональное проектирование, подавая команде на вход уже готовые пачки требований. Размер пачки должен укладываться в интеллектуальные и технологические возможности разработчиков, которым предстоит осуществить её реализацию за одну итерацию.
В итоге мы получаем знакомую софтостроительную схему «снизу-вверх», появившуюся на свет гораздо раньше «водопада», с его упорядочивающей моделью «сверху-вниз». То есть мы не знаем точно, что хотим получить в целом, но знаем отдельные функции, реализовав которые, мы, возможно, придём к решению.
Рис. 16. «Гибкая» методология соответствует классической схеме «снизу-вверх»
С точки зрения проектирования такой подход даже хуже обсуждавшихся прецедентов в UML. Прецеденты использования, «кейсы», хоть как-то формализуются, накапливаются и обобщаются на стадии, формально отсутствующей в гибкой разработке. Поэтому новое требование-прецедент, поступившее на вход, «обобщается» с прежними уже на стадии разработки, приводя к необходимости серьёзной реструктуризации (рефакторинга) кода программ и подпорок из тестов.
Не буду заниматься критикой, её можно без труда найти даже в энциклопедических статьях. Поговорим лучше о некоторых позитивных моментах, которые вносит гибкая методология.
Для программистов позитив, к сожалению, изрядно разбавлен издержками производственного процесса. Методология, по сути, направлена на увеличение времени работы с клавиатурой и не располагает к размышлениям. Пиши код! Поэтому для стимуляции персонала процесс окружен религиозной атрибутикой, манипуляциями, иносказаниями и метафорами. С другой стороны, требования к уровню программиста ограничиваются знанием конкретных технологий кодирования, стандартных фреймворков, «умением разбираться в чужом коде» и «умением работать в команде», уже упоминавшимся в словаре для начинающего соискателя. Способность решать олимпиадные задачки здесь от вас не требуется. Скорее, наоборот, будет помехой.
Позитив для заказчика в том, что, осознавая свою несхожесть с мифологическим титаном мысли, он может достаточно быстро увидеть сформулированные требования и сценарии в реализации, отлитыми, разумеется, не в бетоне, а в гипсе, и на практике понять их противоречивость и неполноту. После чего он может переформулировать существующие и добавлять новые требования с учётом уже набитых шишек. Тем не менее с ростом сложности системы возрастает и риск увеличения стоимости внесения изменений. И если проект выходит за рамки бюджета, то «козлом отпущения» становится именно владелец продукта.
Другой позитив для компании-заказчика состоит в непосредственной близости выполняемой подрядчиком работы. Зачастую «гибкие» команды работают на площадке компании и доступны в любой рабочий момент. Если заинтересованному лицу не хватает информации, он может просто подойти и посмотреть на месте, поговорить с исполнителем и тем самым восстановить расстроившееся было душевное равновесие.
Ничего не напоминает? Всего 15–20 лет назад эти же подрядчики сидели на тех же площадках, но назывались «отделами АСУ» и входили в штат фирмы. Да, теперь сторонние программисты обходятся вдвое-втрое дороже прежних сотрудников, зато в любой момент можно устроить имитацию конкурсного отбора исполнителя, а при необходимости быстро свернуть непрофильную деятельность, не испытывая законодательных и профсоюзных затруднений с сокращением персонала.
Позитив для подрядчика состоит в том, что «гибкая» разработка позволяет вовлечь в проект как можно больше разработчиков с менее высокими требованиями к квалификации. Это позволяет содержать больше сотрудников в штате, включая оффшорные команды. Будучи поставленным перед выбором между небольшой программистской фирмой с квалифицированным персоналом и софтверхаузом-«тысячником», крупный заказчик в общем случае склонится ко второму варианту.
Как вы заметили, я начал с «водопада» и завершил «гибкой разработкой». Хотя адепты обоих подходов испытывают друг к другу сложные чувства, не бросилось ли вам в глаза их разительное сходство? Обе методологии зацикливаются с увеличением сложности проекта. Только «водопад» замыкается на тотальном анализе и проектировании, а гибкая методика «уходит в себя» на разработке и стабилизации, что, в общем, даже не скрывается, а рисуется на слайдах презентаций. Из этого следует вывод, что успешно выполненный в «водопадной» схеме проект может быть также в большинстве случаев выполнен в «гибкой» разработке. И наоборот, если не касаться вопроса людских ресурсов. Дело в масштабе.
Труднее формально определить пороговую сложность системы, за которой начинаются проблемы. Я обозначил бы её как систему, которую даже один опытный аналитик способен охватить формальными моделями за относительно короткий срок, исчисляемый несколькими неделями. Например, заказной, то есть нетиражный, пакет для расчёта зарплаты, подсистема формирования и массовой рассылки счетов клиентам или система складского учёта в компаниях среднего и крупного масштаба.
Как происходит зацикливание даже в простых случаях? Программисты классифицируют коров и столы по признаку наличия четырёх ножек, после чего всю энергию тратят на то, чтобы написать интерсепторы, аспекты, применяют мощные инструменты рефакторинга кода для того, чтобы ad hoc[119] разрешить некоторые возникающие противоречия в созданной модели.
Конечно, это просто шутка, в которой немало правды. В реальности же наиболее распространённым явлением становится частичное дублирование структур и функциональности отдельных подсистем, реализуемых разными командами и владельцами. Поскольку общего взгляда на систему нет.
Борьба с зацикливанием с обеих сторон нередко принимает причудливые формы.
Совсем недавно мне выдалось консультировать по сугубо техническим вопросам одну скрам[120]-команду. Проект был заказан крупным автопроизводителем и касался разработки бортовой системы мониторинга и управления периферийным оборудованием. Функциональные спецификации составили вместе толстую пачку листов формата А4, напечатанных с двух сторон, думаю, в общей сложности не менее 2 тысяч страниц. Понятно, что никто из программистов в здравом уме не стал читать документацию целиком, а взяли несколько функций, под которые и начали строить реализацию. После четырёх месяцев работы выяснилось, что архитектура эволюционным путём не выстраивается, хотя заказчик регулярно видел разные красивые экраны с заглушками и симуляцией приходящих от устройств прерываний. Разумеется, и я не стал читать все эти тысячи страниц, ограничившись весьма интересным документом, содержащим иерархию функций, то есть фактически глоссарий функциональной декомпозиции системы. Из документа следовало, что архитектура, состоящая из набора служб, доступных на общей шине (здравствуй, CORBA), охватывала несколько верхних уровней иерархии. Однако такая перестановка означала переделку большей части системы, тогда как регламент не разрешал увеличить время очередной итерации до минимально необходимых 2–3 месяцев, а бюджет и ресурсы не позволяли начать параллельную стройку. В итоге команда осталась в прежней архитектуре, осознавая на собственной шкуре, что затраты на добавление новых функций растут.
Совсем свежий пример: настоящее время, крупная корпорация – строится внутренняя система управления предприятием. Официально написаны 3,5 тысячи страниц функциональных спецификаций, полтора десятка программистов в том же «скраме» уже приступили к реализации отдельных частей. Через год-полтора будет ясно, получилось ли что-нибудь в итоге.
Эти два примера вполне соответствуют тенденциям взаимного перекладывания ответственности на сложных проектах. Заказчик осознаёт, что реализовать спецификации своими силами невозможно, прежде всего потому, что при таком объёме они тем не менее неполные и неизбежно содержат противоречия. Подрядчику же в принципе наплевать на спецификации, он будет крутить итерации, честно реализуя заявленный функционал и отрабатывая бюджет. Получился коровник на подпорках с покосившимися заборами и дырявой крышей вместо современного агрокомплекса? Извините, всё по спецификации, каждые две-три недели вы видели расцвеченные фотографии разных участков возводимого сооружения.
Синтез «водопада» сложной системы, итоги проектирования которого подаются на вход «гибкой» производственной машины кодирования и стабилизации – что может быть бессмысленнее и беспощаднее?
Кроме частных примеров относительно крупных заказов, современная тенденция – огромное число некритичных проектов, программ и систем-пристроек к основной КИС. Масштаб проектов небольшой (сотни тысяч строк кода), заказчик точно не знает, что хочет получить в итоге, а подрядчик не имеет опыта в данной предметной области, если вообще имеет хоть в какой-то, и поэтому не может ему объяснить, что кактусы за полярным кругом не растут. Для такой ситуации привлечение команд с имеющими требуемый опыт квалифицированными специалистами и технологиями предметно-ориентированных языков или разработки по моделям маловероятна, поэтому пусть уж лучше итеративная методология «наживульки», чем никакая, как оно зачастую бывало в эпоху штатных отделов АСУ.
Тесты и практика продуктового софтостроения
Привожу комментарий Максима Крамаренко, руководителя компании Trackstudio, выпускающей одноимённый продукт для управления задачами в софтостроении.
У нас тотальные модульные тесты, что называется, «не пошли». Сложилось впечатление, что их хорошо использовать для продуктов, которые реализуют какой-то стандарт или спецификацию (СУБД, веб-сервер), но для тиражируемого веб-приложения это смертельно. Причины:
1. При изменении спецификаций затраты времени на приведение в актуальное состояния тестов могут быть куда больше, чем на собственно код. Скажем, если пользователи захотят поменять синтаксис SQL в СУБД и писать SEARCH вместо SELECT, то это одно изменение продукта в одном месте приведёт к переписыванию почти всех тестов. Если для СУБД такие пожелания пользователей – редкость, то для менее стандартных программ – обычное дело.
2. Сбои, которые могут выловить тесты (повторяемый сбой в модуле, ранее уже исправлявшийся), – довольно редкое дело. Гораздо чаще сбои возникают в интеграции разных технологий, которые сложно автоматически протестировать. Например, при работе под таким-то браузером при таких-то настройках вот этот JavaScript работает неправильно.
3. Наличие модульных тестов сильно затрудняет масштабный рефакторинг. Если у нашего приложения есть какой-то внешний API, то все, что ниже, мы можем менять быстро и без особых проблем. Но если для этого низкоуровневого кода есть тесты, то их придётся основательно переделывать. (Прим. автора: при этом функциональные тесты, работающие с API, переделывать не требуется.)
4. Если не напрягаться с выпуском раз в 2 недели, то возможность быстро что-то протестировать не так уж и важна. Мы себя совершенно нормально чувствуем с испытанием бета-версии в течение 2–3 месяцев, зачем чаще?
5. Одни из наших конкурентов широко используют agile-методы и TDD[121], но что-то оно им не очень помогает писать безошибочный код. Мы сравнивали количество найденных проблем в течение месяца-двух после major release, у нас показатели лучше в разы, если не на порядок. Частый выпуск версий просто не позволяет им довести код до ума и провоцирует исправление старых и серьёзных проблем методом написания «залепени». (Прим. автора: исправление ошибок, добавляющее новые проблемы.)
6. Я совсем не уверен, что пользователи хотят получать новую версию раз в 2 недели. TrackStudio 3.5 вышла примерно через полгода, после TrackStudio 3.2, и мы получили много негативных откликов, что такие частые релизы заставляют их обновлять локальную документацию и задерживают их собственные процессы на несколько месяцев. Многие пользователи совершенно спокойно живут без обновлений несколько лет. Если они купили продукт – значит, он делает то, что они хотят. Если чего-то не делает – они все равно уже нашли workaround (обходное решение), им всё равно.
7. Для корпоративного софта пользователи хотят длительного периода поддержки, пара лет, минимум. Если мы выпускаем продукт раз в 2 недели и находится серьёзная ошибка в версии годичной давности, то нам её нужно будет исправить примерно в 40 ветках. Конечно, править 40 веток кода никто не будет, пользователям сообщат, что ошибка будет исправлена в следующей версии, многие пользователи перейти на неё не смогут (см. п.6) к большой радости конкурентов с предложениями типа competitive upgrade offer.
Максим затронул важную тему модульного тестирования, являющуюся краеугольным камнем всех гибких методик: если изначально неизвестно, что выстроится в итоге, дом или коровник, то подпорки у его стен должны быть в любом случае.
Излишне религиозная атмосфера превратила вполне здравую и работающую с 1970-х годов технологию модульных тестов в настоящий карго-культ. Подобно жителям островов Меланезии, старательно строящих соломенные аэропланы для привлечения сбрасывающих груз транспортных самолётов, адепты 100 % покрытия кода модульными тестами считают, что это обеспечит успех проекту.
Разработка модульных тестов – это тоже разработка. Для 100 % покрытия потребуется примерно столько же времени, сколько и на основную работу. А может, и больше, смотря как подойти к делу. По моим наблюдениям, соотношение объёмов рабочего и тестирующего кода примерно 1 к 2.
В отличие от тестов функциональных, завязанных на интерфейсы подсистем, модульные тесты требуют переработки одновременно с рефакторингом рабочего кода. Это увеличивает время на внесение изменений и ограничивает их масштаб, приучая разработчика минимизировать реструктуризацию, заменяя её надстройкой, быстро трансформирующей архитектуру в Ад Паттернов.
Модульные тесты тоже бывают сложными, а значит, с высокой вероятностью могут содержать ошибки. Тогда возникает дилемма: оставить всё как есть или перейти к мета-тестированию, то есть создавать тест для теста.
Наконец, модульный тест – это самый нижний уровень проверок. Прохождение серии модульных тестов вовсе не гарантирует, что пройдут функциональные тесты.
Практические выводы. Соизмеряйте затраты на создание и поддержу автоматизированных модульных тестов с бюджетом проекта и располагаемым временем. Тем более плохо фанатично навязывать разработку «от тестов», умалчивая о названных особенностях и не учитывая другие возможности. Например, код, генерируемый по моделям, вообще не требует модульных тестов.
Говорящие изменения в MSF и выключатель
Мне с давних пор импонирует подход, описанный в MSF[122]. Прежде всего масштабируемостью, относительной логичностью, разумными ограничениями на совмещение ролей в проекте. Но целью написания этих строк вовсе не является продвижение технологий известной всем корпорации, думаю, и без меня для этих целей найдутся хорошо оплачиваемые консультанты.
Интерес представляет следующий факт: в своей текущей редакции MSF 4.0 была разделена на два направления: MSF for Agile Software Development и MSF for CMMI Process Improvement.
CMMI, или Capability Maturity Model Integration, – модель зрелости процессов организации вообще и софтостроения в частности. Ключевое слово здесь именно «зрелость», описываемая в CMMI несколькими уровнями организации: от хаоса до системы качества.
Соответственно, одно из нынешних направлений MSF предназначено для достижения зрелости процессов софтостроения или дальнейшего улучшения процессов уже более-менее зрелых организаций. А второе… для гибкой разработки.
В завершение темы было бы непростительно не вспомнить о производственной системе Toyota, которую почему-то считают основой того же скрама. Ключевой особенностью системы в Тойоте является принцип «дзидока» (jidoka), означающий самостоятельность людей в управлении автоматизированной производственной линией. Если рабочий видит нарушение качества продукции или хода процесса, он имеет право, повернув соответствующий рубильник, остановить всю линию до установления причин дефектов и их устранения.
Внедрение таких методик вполне возможно и вне рамок японского общества. New United Motor Manufacturing Inc (NUMMI) – знаменитое совместное предприятие Toyota и General Motors, ещё в 1970-х годах вошло в «кейсы» бизнес-школ как пример повышения эффективности и качества через смену культуры работы.
Качество программного продукта – многозначное и сложное понятие. Производственная культура – ещё более сложное. В одном можно быть уверенным: ни о какой культуре софтостроения не может идти и речи, если любой программист из коллектива не способен остановить бессмысленный циклический процесс для выяснения, какого же рожна по историям заказчика потребовалось обобщать четырёхногих коров и обеденные столы.