Коллеги. Вашему вниманию предлагается вторая часть книжки «Не спеша, эффективно и правильно: путь разработки», повествующая о теоретических аспектах. Также автор хотел бы выразить своим читателям две большие благодарности.
Общая благодарность за проявленный интерес, выставленные положительные оценки, лестные комментарии и конструктивное обсуждение. К сожалению, принять в этом обсуждении активное участие у меня сейчас возможности нет, но я постараюсь подготовить небольшой текст на поднятую в обсуждении актуальную тему «Все это хорошо в теории, но 95% реальной практики таковы что 😞». Можно считать небольшим анонсом и уж поверьте, показатель 95% слегка завышен.
Третья, практическая часть будет опубликована через неделю, редактура как раз завершается.
Disclaimer
Коллеги, вашему вниманию предлагается почти финальный вариант моей книжки. Была и остается идея издать это в бумажном варианте, но идея довольно зыбкая. К сожалению, имеются некоторые проблемы со здоровьем. Но очень хочется, чтобы труд не пропал, поэтому книжка отдается в открытый доступ. Можно публиковать где угодно, цитировать и так далее. Единственное условие – ничего в тексте не менять и указывать авторство. Автор текста – Никита Викторович Зайцев (также известный как WildHare).
Книжка повествует об эффективной разработке программного обеспечения. Можно сказать, что это дистиллят моей личной практики, наработанного опыта, знаний и умений.
Предыстория появления книжки в формате интервью порталу Инфостарт:
//infostart.ru/journal/news/mir-1s/nikita-zaytsev-ya-universalnyy-soldat-v-mire-1s_1250753/
Это уже не полуфабрикат, а почти готовый продукт, правда без нескольких не особо важных деталей. Если работу над книжкой получится продолжить, то эти недостающие детали будут добавляться именно сюда, в эти файлы.
Приятного чтения ;-)
Автор в естественной среде обитания – II
Сила светлой стороны
Как и почти в любом деле, сначала нужно изучить азбуку. Будущий специалист по космической связи начинает с изучения азбуки Морзе. Будущий капитан дальнего плавания начинает с парусной морской азбуки. Будущий такелажник начинает с такелажной азбуки, правда, она довольно короткая. Ну — и так далее.
Level zero
Будущему разработчику программного обеспечения так же стоит начинать с изучения азбуки. Не азбуки программирования, разумеется. Программирование — это искусство писать программы. Собственно, хрестоматийная работа Дональда Кнута так и называется. Попробуем сформулировать, в чем разница.
Разработка программного обеспечения — это искусство писать программу так, чтобы издержки на ее написание, на работу с ней, а также на ее сопровождение и развитие были минимальными.
Программист оперирует понятиями изящности, оптимальности, оригинальности. Разработчик программного обеспечения оперирует принципом экономии сил, экономической эффективностью и коэффициентом полезного действия. Это вовсе не значит, что можно писать кривые и неряшливые программы. Разумеется, программа должна быть красивой и оптимальной. Но важно помнить, что сама по себе красота может украсить только музей или подиум. А у нас, извините, но так уж вышло, промышленное производство.
Азбука никогда не бывает длинной, иначе это уже букварь. В нашем случае под азбукой понимается несколько крайне простых, но безусловных принципов. Базовые принципы разработки очень похожи на уставы караульной и конвойной службы. Эти принципы написаны кровью убитых систем, проваленных под землю проектов и утопленных в болоте талантов.
Принцип DRY
По-английски это значит Don’t Repeat Yourself. По-русски — Не Повторяй Себя. Есть и альтернативная аббревиатура, DIE. Duplication Is Evil. Копирование-вставка Есть Зло.
Смысл принципа: одинаковый или почти одинаковый программный код не должен располагаться в двух или более местах. Если ты скопировал фрагмент кода и хочешь вставить в другое место, а потом слегка поправить — бей себя по рукам линейкой. Ну, как раньше делали школьные учителя (и очень жаль, что такую практику запретили, любуйтесь теперь на плакаты и вывески, не говоря о письмах и документах).
Казалось бы, это все-таки принцип программирования, причем здесь разработка? А вот причем. Разработчик пишет не просто программу, он пишет код, устойчивый к изменениям. И допускает, что изменения будут вносить совсем другие люди. У этих людей не будет технической документации, потому что времени составить спецификацию кода, как всегда, не хватило. Эти люди добросовестно перекопают все модули, найдут два места из трех, и доработают логику. А вот до третьего места они не доберутся, слишком уж глубоко закопано. Что будет после обновления? Правильно, именно маленький и пушной. На экране все выверено и правильно, ну а что в налоговую уехали немного другие суммы, потребитель программы узнает чуть позже. Из повестки, например.
Этот же принцип касается не только кода, но и заголовочных комментариев. Просто возьмите любую конфигурацию и посмотрите на однотипные функции. Довольно поучительное зрелище.
Копирование — зло. Не копируйте, пишите. Руки не отвалятся.
Принцип GIGO
По-английски это значит Garbage In Garbage Out. Русский аналог — Мусор на Входе Мусор на Выходе.
Смысл принципа: если на вход алгоритму поданы некорректные или невалидные данные, на выходе получится полная ересь, пусть даже сам алгоритм реализован правильно. Казалось бы, это естественный закон природы. Залив утренние мюсли вместо йогурта жидким мылом странно рассчитывать на полезный и питательный завтрак.
Пример из личной практики автора. Некоему программисту, назовем его Петр, поручили реализацию довольно сложной отчетной формы. Там нужно было собрать хитрые агрегаты из огромного массива сырых данных, а потом еще и вывернуть наизнанку. На выходе одна страничка, что-то вроде черты под балансом. Петр написал изумительно красивую и оптимальную расчетную механику. Но когда стали проверять на продуктиве, вышло примерно так:
— Петр, позвольте, но здесь же отрицательная прибыль. Это как такое может быть? Вы же сами тестировали, вас не смутило?
— Ну и что из того? Отрицательное число такое же число, как и прочие. Алгоритм отработал правильно, что в базе, то и на выходе.
Притом, что бумажка должна была ехать в налоговую, за подписью генерального директора. Лучшего сигнала для проверки не придумать.
Принцип GIGO действительно является законом природы. Разница в практическом следствии. Программист заботится только о корректности работы своих алгоритмов. Разработчик программного обеспечения проверяет и валидирует входные данные, не допуская мусор дальше порога.
— Как вы могли выставить мне счет на два триллиона рублей?
— Ну вы же сами в форме заказа указали “по 31.12.2919”.
— <пять этажей 18+>
Пользователи ошибаются при вводе. Коллеги ошибаются при вызове ваших интерфейсов. Доверять нельзя никому. Проверяйте.
Принцип KISS
Согласно некоторым источникам, этот принцип был сформулирован проектировщиками US Navy в лохматых шестидесятых годах прошлого века. Но к разработке программного обеспечения он подходит идеально.
По-английски это значит Keep It Simple Stupid. Вольный перевод на русский звучит как Упрощай Где Можно Дурашка.
Суть принципа: простые системы всегда работают лучше и надежнее, чем сложные и навороченные. Если задачу можно решить простым способом, используя простые инструменты, именно так и нужно сделать.
Чтобы повесить картину на деревянную стену, достаточно вбить молотком обычный гвоздь. Пневматический молоток и лазерный дальномер в едином комплексе с бортовым микроконтроллером для развешивания картин слегка лишние. Хочешь, чтобы твое чадо не навернулось с продвинутого детского кресла (подогрев, пять режимов качания, встроенный дисплей с возможностью загрузки мультиков через WiFi), сажай на низкую трехногую еловую табуретку. Самая надежная конструкция. Ну, и так далее.
Собственно, KISS прямо запрещает использовать более сложные инструменты, алгоритмы, структуры данных и так далее, чем это необходимо для решения задачи. Не рекомендует, а именно запрещает, категорически.
Ну вот элементарный пример, известный всякому, кто имел дело с одной известной библиотекой. Нужно просто взять контрагента, и записать в базу контактный телефон. Казалось бы, один метод, да? Но это было бы слишком просто. Нужно применить магию типов и видов, вызвать несколько методов с последовательной передачей параметров, не напутать, что куда и откуда переставить, и вот тогда — если повезет, телефон в базу запишется. А если очень повезет, то потом даже и прочитается.
Автор ни в коем случае не хочет отнести крайнюю справа букву аббревиатуры KISS уважаемым коллегам, с которыми провел столько увлекательных совещаний. Но, парни, как так-то? Нужно просто вбить гвоздь, а плотнику вместо молотка выдают строительный станок с ЧПУ, который, если хорошо попросить, сумеет сыграть на перфораторе “Пещеру горного короля”.
Нет ничего проще, чем сделать сложное. Но довольно сложно сделать простое. Будьте проще, и к вам потянутся не только прочность и стабильность, но и добрые слова.
Принцип YAGNI
Нет, индийская мифология здесь ни при чем. Про индийское кодирование мы поговорим в следующей главе. Все гораздо прозаичнее: You Aren't Gonna Need It. Иными словами, Вам Это Не Нужно.
Суть принципа: реализации подлежат только те требования, которые нацелены непосредственно на решение задач, имеющих четкий практический смысл. Все остальное просто не нужно.
Собственно, незнание этого принципа представляет собой первую ловушку для начинающего специалиста по работе с требованиями. Ну а в нашей отрасли таким специалистом является сам разработчик. И вместо того, чтобы поставить перед капризным заказчиком гранитную стену принципа YAGNI
— Какую задачу мы решаем этой свистелкой? Нет ответа? Вам это не нужно.
этот специалист зачем-то превращается в официанта
— Чего изволите? Восьмибитный свист? Хорошо. И чтобы дырочки были восьмиугольными? Запросто. И чтобы если нарисовать в воздухе скрипичный ключ, оно само свистело “Коробейников” из тетриса? Не вопрос, я все записал.
Это какой-то бич отрасли. Заказчику, пользователю, потребителю не нужны программные продукты, как таковые. Ему даром не нужны функции, формы, команды и прочее. Потребителю программного продукта нужно просто выполнить свою задачу с максимальным КПД. И только. Но сам он этого не понимает, и вместо того, чтобы описать задачу, сразу описывает способ решения. Но это ведь то же самое, если бы пассажир на калькуляторе показывал конструктору, какая форма фюзеляжа обеспечивает лучшую аэродинамику.
YAGNI действует и в обратную сторону. Когда разработчики слушают воображаемого заказчика. Собираются и думают, а какую бы еще свистелку приделать, чтобы порадовать наших пользователей? Реальный пользователь получает обновление — что это, зачем оно высвистывает “Коробейников” при расчете? Мне это не нужно!
Собственно, принцип YAGNI в первом приближении сформулировал еще Оккам. Не плодите сущности сверх необходимого.
Принцип недотроги
Описанные выше принципы можно назвать каноническими. Но ими азбука разработки не исчерпывается. Попробуем сформулировать еще один принцип, который относится не просто к разработке, но к самому трудному ее виду — сопровождению. Автор называет это “принципом недотроги”, но наверняка есть десяток других названий.
Прежде всего, что такое сопровождение и в чем отличие от поддержки? Поддержка подразумевает эксплуатацию системы без возможности внести какие-либо изменения. Специалист поддержки просто рассказывает пользователю, как правильно работать с системой, то есть выполняет роль проводника через болото. А вот сопровождение не просто разрешает изменения, но и требует их — мир меняется, бизнес меняется, правила меняются, и система тоже должна меняться.
Суть принципа: не уверен, не трогай. Даже когда в системе что-то кажется странным, ошибочным, избыточным, но нет четкого понимания, зачем оно такое, и что может случиться, если это что-то исправить — убери руки за спину, не трогай.
Этот принцип лучше всего показать на примерах, так что самое время запустить кофе-машину. Примеры, само собой, из реальной практики.
Кофе-пауза #4
Пример первый. На некоем предприятии эксплуатируется довольно серьезна система. Ведущий разработчик системы, назовем его Петр, условно первый (a.k.a. Петр-строитель), построил систему с нуля, запустил и наладил. Но поскольку первому поколению Петров гораздо интереснее строить, чем сопровождать, он заскучал и отчислился с предприятия, покорять новые вершины профессии.
Сопровождать систему был призван другой специалист. Назовем его Петр, условно второй (a.k.a. Петр-эксплуатант). Это был прирожденный специалист по сопровождению, строить он не любил, выходило как-то кривовато, а вот дорабатывать и развивать уже готовое — совсем другое дело. Петр-эксплуатант был очень аккуратным, терпеть не мог неряшливости, у него все и всегда было разложено по полочкам.
Первым делом Петр-эксплуатант обследовал конфигурацию. Фу, сказал он. Ну вот кто не сортирует метаданные по алфавиту? И отсортировал. Все, не только верхний уровень. Разумеется, Петр-эксплуатант знал, что нельзя сортировать только измерения регистров, все же он был настоящий специалист.
После обновления... После обновления система не просто упала. Если бы упала, то ничего. Но все расчетные функции стали выдавать на выходе какую-то немыслимую ересь. Петр-эксплуатант был в полном непонимании, он же ничего не делал, просто сортировка по имени?
Расследование показало, что в конфигурации есть незаметное перечисление, МесяцГода. Просто названия месяцев. И есть еще более незаметная функция в расчетном ядре системы, которая по ссылке на это перечисление формирует дату начала месяца. И какой месяц пришел на вход, эта функция определяет — правильно, базируясь на порядковый номер значения. То есть на тот самый лохматый порядок, когда Январь, Февраль, и так далее. Одним движением Петр-эксплуатант превратил Январь в Август, поменял местами Июнь и Май, ну и так далее, прямо как в сказке.
Уж как там икалось Петру-строителю, история умалчивает.
Пример второй. Тоже серьезное предприятие и серьезная система, и тоже написана с нуля. Система довольно сложная, спарка из двух информационных баз с крайне затейливой интеграцией. “Крайне” в данном случае следует понимать буквально.
Разработал и запустил систему коллектив неких добрых людей, но затем добрые люди не поладили с руководством предприятия по вопросу о стоимости сопровождения. К доводам “с этой системой справимся только мы” руководство осталось глухо, что же с шантажистами разговаривать? Добрые люди отправились искать новое поприще, и на царство, как водится, был призван Петр-эксплуатант. Возможно, даже тот самый, кто знает?
Петр-эксплуатант, разумеется, начал с ревизии своих владений. Ну а войдя во вкус, занялся любимым делом — привести в порядок, разложить по полочкам, убрать лишний мусор. В конфигурациях А и Б, назовем их так, обнаружились какие-то уж совсем диковинные вкрапления мусора, комментарии с какими-то кракозябрами прямо внутри кода. Ни малейшего смысла в кракозябрах, на первый взгляд, не было. Петр-эксплуатант был чистюлей, лишний мусор не терпел. Поэтому взял глобальный поиск, посидел вечерок-другой, и все лишнее из кода вычистил.
После обновления... После обновления система не упала, нет. Если бы упала. Система пошла в полный разнос и раздрай. Все горело и рушилось. Фильм “2012” в отдельно взятой операционной системе. Петр-эксплуатант был в ужасе, он же ничего не делал, просто вытер какие-то лишние комментарии?
А дело было вот в чем. Спарка действительно была крайне хитрая. База А была управляющим контуром, ее конфигурацию можно было обновить в любой момент. А база Б была рабочим, нагруженным контуром, для нее технологическое окно для обновления нужно было каждый раз согласовывать, и ответ на запрос мог иметь вид “завтра в 23:50”. Кому же из добрых людей хочется ждать до завтрашней ночи, чтобы чуть-чуть подправить логику в коде? Тем более, что пользователи шипят уже по-злому. Динамическому обновлению тогда никто не доверял, а расширения еще не придумали.
Было изобретено инженерное решение. Часть кода была написана, скажем так, в стиле “мехом наружу, дважды, с морским узлом”. В подробности вдаваться не будем, важна суть. Чтобы не останавливать рабочий контур, в конфигурацию вписывались кракозябры (был разработан свой псевдоязык), конфигурацию базы А обновляли, давали команду базе Б, та забирала конфигурацию А в файлы, читала их, разбирала кракозябры и соответствующим образом подстраивала свое поведение.
Автор понимает, что это звучит как сон разума. Собственно, это и был сон разума, об этом подробно будет в следующей главе. Какой там KISS, это hardcore SM в чистом виде. Почему было сделано именно так? Ирония в том, что добрым людям это показалось простым и надежным решением. Конфигурации А и Б полностью тождественны по метаданным и формам, а вот ключевые участки кода Б (в первую очередь, разнообразная расчетная механика) могли замещаться кодом из А, через сложносочиненный стек из “Выполнить/Вычислить”. Эрзац-расширение из ветхого полотна, компоста и фрагментов древесных стволов. Плюс все кракозябры в хранилище, никаких внешних файлов, просто пересобрать, обновить и нажать одну кнопку. Ну и заодно представьте себе, сколько заплатило предприятие добрым людям за разработку и отладку этого монстра. Лишний раз прокатиться к теплому морю никому не вредит, работа-то нервная, на износ.
Вопрос, а должен ли был Петр-эксплуатант знать о том, что кракозябры в коде не просто так? В теории — разумеется, должен был знать. Добрые люди оставили новому поколению пухлую пачку технической документации, где вся механика была описана в деталях, и даже с UML-диаграммами. Но кто читает документацию?
Тут автор должен сделать небольшое лирическое отступление и отдать дань уважения человеку, без которого эта книга никогда не была бы написана. Будем надеяться, что до этого места он дочитает и узнает себя. Уважаемый коллега и дорогой товарищ. Да, документацию действительно почти никто не читает. Горек хлеб технического писателя, и стезя его усыпана крупным гравием. Но Север — помнит.
Сила темной стороны
Как и у всякого природного явления, у любой силы есть две стороны, темная и светлая. Искусство разработки программного обеспечения исключением не является, у него есть своя темная сторона. В предыдущем разделе мы попробовали сформулировать светлые паттерны разработки, ну а сейчас самое время заглянуть за Стену.
На всякий случай автор подует на воду: все приемы и паттерны разработки, описанные в этом разделе, являются типичными и ярчайшими примерами того, как делать ни в коем случае не нужно, никогда и ни при каких обстоятельствах. Текст будет довольно злым, если прошлая глава была мотиватором, то здесь в ход придется пустить stimulus.
Чтобы избежать голословности, описание некоторых темных паттернов снабжено примерами, причем не синтетическими, а из реальных разработок.
И еще — эрудированный читатель наверняка заметит некоторые фразы с Lurkmore, в авторской, понятно, редакции. Но это не плагиат, а цитирование. Пушкина, например, тоже цитируют без указания авторства на каждой строке.
Простейшие
К группе простейших антипаттернов (именно так это называется) относится абсолютно бездумная деятельность по написанию кода. Если рассматривать такое творчество беспристрастно, возникает ощущение, что головной мозг при работе рук не был задействован от слова “совсем”. Каким-то странным образом у этого вида руками управляет даже не мозжечок, а сразу спинной мозг, и как бы даже не костный. Сном разума это назвать нельзя, для сна нужен хоть какой-то разум.
Китайский стиль
Этот стиль разработки (мы ведь говорим не только про написание кода, но про разработку на платформе в целом) обычно называют китайским. Простота, брутальность, и неспособность открыть для себя даже такой примитивный феномен, как цикл с локальной переменной.
Отличительный видовой признак китайского стиля — эталонное нарушение принципа DRY. Если что-то можно скопировать и вставить, это будет скопировано и вставлено. Если нельзя, то все равно будет скопировано и вставлено, упорство и труд перетирают в пыль любые законы природы. Когда рассматриваешь примеры, возникает впечатление, что им платят построчно, как поэтам начала прошлого века.
Рассмотрим пример. Задача: требуется преобразовать целое число к строке фиксированной длины с ведущими нулями.
Китайское решение:
Человеческое решение:
Подсчитать количество недостающих нулей и добавить их к исходному номеру в цикле.
Экспертное решение:
Воспользоваться встроенной функцией платформы.
Индийский стиль
Этот стиль обычно определяют как “написанный наиболее неочевидным и противоественным способом программный код, который каким-то образом все же работает (хотя и не факт, что правильно)”. Англоязычные товарищи дали индийскому стилю прозвище Write Only, поскольку чтению и анализу такой код не поддается в принципе. Это примерно то же самое, как пытаться понять, о чем же на самом деле повествует “Бхагават-гита”, имея в руках только исходник на санскрите.
Есть довольно известная шутка, что аутентичный индийский код настоящие мастера не пишут, а поют и танцуют. Затем переводят на хинди/суахили, а уже затем транслируют в необходимый язык программирования. Автор может поручиться, что это не совсем шутка, поскольку лично был знаком с настоящим мастером индийского стиля. Правда, вместо пения был художественный свист, а танцевать, сидя в кресле, не так и просто, но у него получалось. Процедура в три тысячи строк, на вход которой подается двадцать два параметра, причем поведение управляется не только значением параметра, но и его типом — это далеко не вершина мастерства, так, предгорья.
Рассмотрим пример. Фрагмент кода выполняет элементарную задачу: проверить, является ли переданное значение числом.
Специалист, которому будет поручено сопровождение этого кода, зависнет над этим фрагментом надолго. Зачем здесь Попытка? Зачем возвращать Число(0)? Почему именно при -1111 наша система будет считать валидные входные данные мусором?
Крестьянский стиль
Этот стиль можно выразить как “крестьянин трудолюбив, но не доверяет этим городским штучкам”. Видовые признаки крестьянского стиля — сознательный отказ не только от “ненадежных” (по мнению крестьянина) библиотечных функций, но даже и “ненадежных” методов платформы, и даже в самых простых ситуациях. Если в случае прикладных библиотек такой подход иногда, к сожалению, бывает уместен (об этом мы поговорим в третьей части, которая целиком посвящена практическим аспектам разработки), то в случае платформы принцип “зачем трактор, если в руках мотыга” выглядит, мягко говоря, неэффективным.
Нужно сделать небольшую оговорку. Антипаттерны темной стороны вовсе не обязательно ведут к ошибкам и проблемам. Код, написанный посредством таких антипаттернов, может быть вполне работоспособен. Но мы говорим не просто о разработке, а об эффективной разработке. Если паттерн снижает эффективность, значит, это плохой, негодный паттерн.
В качестве эталонного примера перелистаем чуть назад и повторим фрагмент из китайского примера. Задача, напомним, проще не бывает: привести число к строке заданной длины и дополнить слева ведущими нулями.
Вместо того, чтобы решить задачу однократным вызовом оператора Формат, крестьянин будет трудолюбиво вспахивать поле мотыгой и делать всю работу руками. Ну а поскольку принцип DRY крестьянину неизвестен, этот фрагмент кода при необходимости будет размножаться копированием на все участки конфигурации. Смекалистый крестьянин даже внесет его в файл шаблонов, и вместо простой мотыги у него будет механизированная.
Тяжелый ручной труд — дело хорошее и правильное, с этим не поспоришь... Но с эффективной разработкой совмещается плохо.
Беспозвоночные
Группа беспозвоночных антипаттернов стоит на гораздо более высокой ступени развития, нежели простейшие. Здесь уже почти полный порядок с владением самыми базовыми методами (никого из беспозвоночных не удивишь, например, циклом), в деятельности уже задействован мозг, но, к сожалению, только одним полушарием. Причем какое именно это будет полушарие, правое или левое, диктуется не только личными качествами носителя антипаттерна, но и конкретной задачей, и даже расположением звезд.
Общие видовые признаки — тотальное пренебрежение логикой (даже не формальной, а обычной, бытовой), элементарными принципами разработки, наличие в коде огромного количества детских ошибок, использование методик “копировать-вставить-подправить”, “завалим-запинаем”, “исключения придумали трусы”, и тому подобных везде, где только возможно.
Носители этого антипаттерна могли бы взять девизом слова известного литературного персонажа по имени майор Прыщ:
Новых идей не понимаю. Не понимаю даже того, зачем их следует понимать-с.
На практике беспозвоночный вид антипаттернов часто идет рука об руку с методикой Google-driven development. Поискать что-то похожее на решение своей задачи (как вариант, поклянчить на сетевых площадках у более опытных товарищей), стянуть к себе и попробовать из чужого кода выпилить лобзиком свой. Характерным является и отношение к языку программирования — он считается чем-то вроде набора заклинаний. Нужно просто подобрать правильные слова, вычертить мышкой определенную фигуру, а дальше волшебная сила все сделает сама. Эталонный представитель такой школы — небезызвестный Григорий Чайников (a.k.a. Harry James Potter).
— Filus chitatus exelus! Documentus convertatus!! Obmenus oshibkus sginus!!!
Ну или как-то так. Беда в том, что написанный таким образом программный код пытается работать (именно пытается, полностью он не работает никогда) на живых предприятиях и у живых потребителей. Как бульдозером по влажному тропическому лесу.
Школокод
Обычно такой стиль разработки называют более грубым словом, но мы постараемся от грубостей воздерживаться. Англоязычные товарищи называют носителей этого антипаттерна Code Monkey, что гораздо точнее раскрывает суть явления. В качестве видовых признаков можно перечислить абсолютное непонимание основных стандартов и особенностей используемого языка, незнание не только лучших, но и базовых практик, а главное, отсутствие желания со всем этим познакомиться хотя бы шапочно.
Как следствие, носитель антипаттерна принимает неочевидные, неверные, а часто и просто абсурдные решения — результат, как гласит поговорка, слегка предсказуем. Написанный посредством этого антипаттерна код находится даже не в световых годах от здравого смысла, а где-то в злой параллельной вселенной.
Вторичные видовые признаки включают в себя общую языковую безграмотность (не только в программировании, но и в русском), неумение и нежелание выразить свою мысль связно, серьезные проблемы не только с культурой разработки, но и вообще с абстрактным мышлением. Ну и лень, разумеется — не благородная лень настоящего мастера, который изобретает инженерное решение, позволяющее получить лучший результат с меньшими затратами, а обычная, подростковая, когда даже прибраться в своей комнате это уже проблема.
Представьте себе паренька в кепке, турецкой кожаной куртке, заправленной в тренировочные штаны, зажатой в зубах сигаретой, сидящего на корточках с пакетом семечек — это почти точное ощущение, которое возникает при просмотре такого программного кода. Любые правила, любые ограничения, любая строгость языка — все это воспринимается как личное оскорбление.
Зачем столько лирики? Тема действительно больная, поскольку носителей антипаттерна, к сожалению, в сообществе очень много, именно из-за них термин “1С-программист” в профессиональной среде обрел негативную и комическую коннотацию.
Разбирать конкретные примеры автор смысла не видит — будет очень много кода и очень много букв, а у нас все-таки теоретический блок.
Шизокод
Как известно из медицинских справочников, характерными симптомами шизофренического расстройства психики являются зрительные и слуховые галлюцинации, а также дезорганизованность речи и мышления. К сожалению, даже вполне вменяемые разработчики порой порождают код, вызывающий у читателя легкий ступор и потерю ощущения сцепления с реальностью.
Подчеркнем еще раз: в разборе этого антипаттерна термин “шизофрения” относится ни в коем случае не к разработчику, а только к написанному им коду. Также отблеск антипаттерна во всей красе можно увидеть в многочисленных сообщениях пользователям, особенно в сообщениях об ошибках.
— Обработка завершена. Внимание! При выполнении обработки возникли ошибки!!! Процедура успешно выполнена.
— WTF? O_O
Стоит привести пару простых примеров, из реальной практики. Просто представьте, что этот код пришел к вам на аудит.
Пример #1
Фрагмент кода выполняет крайне простую задачу: уведомляет пользователя о завершении обработки данных.
Здесь:
- Взаимоисключающие параграфы. Обработка одновременно и выполнена успешно, и выполнена с ошибками.
- Дезориентация пользователя. Кроме сообщения о том, что возникли ошибки (в двух разных вариантах), система не даёт больше никакой информации. Пользователь должен догадаться сам, куда смотреть и что делать.
Пример #2
Фрагмент кода выполняет крайне простую задачу: копирует значения реквизитов элемента справочника в реквизиты обработки.
Здесь:
- Имя реквизита БанковскийСчетВыгрузки подразумевает (как это принято среди разработчиков на платформе 1С) тип значения СправочникСсылка.БанковскиеСчета. Но в реальности реквизит имеет тип СправочникСсылка.ПравилаОбменаКлиентаБанка – тот специалист, который будет заниматься сопровождением кода, слегка поцарапает себе мозг об это несоответствие.
- Обращение к реквизитам объекта ссылочного типа «через точку». Грубейшее нарушение стандартов разработки.
- «Однотипные» реквизиты почему-то имеют разные имена в справочнике и в обработке. Это вызывает к жизни портянку кода вместо оператора ЗаполнитьЗначенияСвойств, и отнюдь не упрощает сопровождение кода.
- Хардкод имени файла в модуле обработки. В подобных случаях константы, если параметрическая настройка для них является избыточной, должны располагаться в соответствующих функциях общего модуля ФункцииПовторногоИспользования.
Пример #3
Фрагмент кода выполняет крайне простую задачу: в зависимости от выбранного пользователем вида операции либо выводит табличный документ для просмотра, либо выводит этот же документ на печать.
Здесь:
- Числовая константа в качестве кода «вида операции». Это абсолютно неестественное сопоставление: «0» ассоциируется с «пустым местом», «первым элементом», «началом списка», но никак не с «печать с предварительным просмотром».
Возможные правильные решения:
- Использовать строковые константы: РежимПросмотр, РежимПечать.
- Так как вариантов всего два, и расширение их количества не предполагается, достаточно использовать булевский флаг ЭтоПечатьНаПринтер.
TurDuсkEn
Этот стиль назван именем блюда североамериканской кухни. Технически блюдо представляет собой индейку (turkey), фаршированную уткой (duck), в свою очередь фаршированной курицей (chicken). Блюдо невероятной калорийности и вредности, кулинарный гротеск, ночной кошмар диетолога — но любителей хватает.
В кулинарной аналогии этот паттерн кодирования выглядит примерно так.
Применительно к разработке программного кода словом TurDuсkEn называют манеру не просто приготовить “лапшу”, перемешав на одном участке принципиально разные сущности, но еще и сделать это на различных языках программирования одновременно.
Чаще всего антипаттерн применяется в разработке веб-приложений. Разработчик-кулинар Петр закатывает рукава и кладет первый слой, PHP-логику. PHP-логика генерирует HTML-разметку и JS-логику. JS-логика собирает фрагменты SQL-запросов. SQL-запрос при желании, и, если позволяет СУБД, тоже можно фаршировать чем-нибудь скриптовым. Которое будет формировать набор данных для другого, внешнего скрипта на каком-нибудь Python, который… ну, идею вы поняли. На выходе получается чистейший turducken, дополненный морской свинкой и парочкой анчоусов в придачу. Блюдо такой убийственной силы, что попытка вручить это для сопровождения сколько-нибудь опытному разработчику запросто может завершиться заявлением об уходе, а то и жалобой в общество защиты людей от животных.
Платформа “1С” обладает не такими богатыми возможностями для смешения языков, как веб-платформы, но и на нашей кухне антипаттерн встречается гораздо чаще, чем хотелось бы. Прежде всего это касается техники “собрать текст запроса сложным кодом с множеством ветвлений”. Казалось бы, уже довольно давно у объекта Запрос появился вполне сносный программный интерфейс, так зачем же собирать текст конкатенацией и заменами в двадцатиэтажной конструкции “Если - Иначе”, врагу на страх, себе на горе? Вопрос, к сожалению, риторический, “пишем, как привыкли”. В эту же категорию попадает ручная сборка shell-скриптов (bat, vbs, ps, etc.), запросов к внешним источникам данных (ADO, и иже с ним), HTML-разметки для внешних и внутренних страниц, ну — и так далее.
Почему turducken является безусловным злом? Применение этой техники на ровном месте усложняет код на порядок, а возможно, и больше. Отладка и сопровождение обычного кода сродни попытке уследить за разбегающимися в разные стороны сотнями тараканов, а в случае turducken-кода тараканы разбегаются сразу в пяти измерениях. Логика генерирует другую логику, ошибки в разных слоях накладываются друг на друга, вызывая самые неожиданные эффекты. Код при turducken-подходе стремительно теряет ключевые качества — понятность, прозрачность и, как следствие, управляемость. Взамен же разработчик получает не “гибкость”, а кратное увеличение трудозатрат на отладку.
Каков правильный способ, если нужно все-таки генерировать какую-то управляющую логику на другом языке? Лучше шаблона с подстановкой параметров и заменой ключевых сигнатур еще ничего пока не придумано. Разных птиц следует подавать на разных тарелках.
Позвоночные
Как и в живой природе, более развитые существа являются и наиболее опасными. Антипаттернов разработки это касается в полной мере. Если носитель антипаттерна не умеет даже склепать простейший цикл, большой беды от его деятельности не будет. Например, на каком-то конкретном небольшом предприятии “система” будет стартовать десять минут, выполняя некую инициализацию прямым перебором всех объектов данных (пример из личной кунсткамеры автора). Или расчет заработной платы будет выполняться два часа, потому что из-за конструктивной особенности кода при расчете для каждого сотрудника проверка принадлежности к подразделению выполняется в среднем четыреста тысяч раз (другой пример из личной кунсткамеры автора). Это неприятно, конечно, но и только.
Гораздо хуже, когда носитель антипаттерна имеет довольно высокий уровень умения работать с платформой и получает возможность применить свой талант на больших проектах или, что гораздо опаснее, в тиражируемых продуктах. Ведь почти весь код почти всех тиражных продуктов открыт, и начинающие специалисты пытаются по этому открытому коду учиться. И когда опытный тимлид, проводя аудит, пытается поднять с пола упавшую туда челюсть, диалог с джуниором получается примерно таким:
— Сынок, <18+>, это кто тебя научил такое <18+> отливать в коде?
— Так вот же, дяденька, в <не будем показывать пальцем> точно так и сделано. Написал по образцу.
К сожалению, далеко не каждому джуниору везет попасть к опытному тимлиду. Как следствие, наиболее опасные антипаттерны разработки разносятся по сообществу, как вирусы, массово поражая молодые растущие организмы. Рассмотрим под микроскопом пару образчиков этой нечистой силы.
Спагетти
Рассказу о феномене спагетти-кода следует предпослать замечательное высказывание одного из отцов-основателей нашей отрасли по имени Эдгер ван Дейкстра:
— Компетентный программист полностью осознает строго ограниченные возможности своего мозга, поэтому подходит к задачам программирования со всей возможной скромностью.
Что же такое разработка в стиле спагетти (можно также назвать лапшой, солянкой, и даже ботвиньей)? Этот феномен можно определить, как локально переусложнённый и локально же нечитаемый программный код, в котором описывается множество перемешанных до полного непонимания разнородных сущностей, при грамотном подходе требующих строжайшего разграничения.
Можно и одним словом: месиво.
Нельзя сказать, что спагетти-код обязательно содержит ошибки или работает медленно. Такой код может быть почти идеально отлажен, может отвечать всем поставленным внешним требованиям, может выполнять поставленные задачи с высокой производительностью, и так далее. Тогда что же с ”лапшой” не так?
А вот что. Во-первых, такой код практически не поддается ни сопровождению, ни даже простому чтению. Во-вторых, обладает чудовищной трудоемкостью отладки и особенно локализации ошибок. И в-третьих, любое, даже совсем незначительное изменение или расширение требований почти в ста процентах случаев приводит к ситуации “проще это все переписать полностью, чем что-то здесь изменить”. Расширяемость “лапши” близка к нулевой.
Никого не напоминает этот трудящийся?
Памятка для самопроверки. Попробуйте поискать в своем коде следующие видовые признаки:
- Применение оператора GOTO (понятно, что этим оператором пугают детей, но все же).
- Процедуры и функции огромного размера, на несколько экранов.
- Многострочные блоки case (Если.. ИначеЕсли..), особенно с несколькими уровнями вложенности
- Обработка частных случаев, ломающих парадигму окружающего кода (например, добавление дополнительных условий вместо дополнительных значений переменной «состояние» конечного автомата).
- Общая неряшливость кода. Пренебрежение общепринятыми элементарными правилами форматирования и оформления.
- Укороченные и/или не имеющие смысла имена переменных.
- Отсутствие в коде внятных комментариев, начиная с заголовков процедур и функций.
Следует понимать, что каждый из приведенных признаков сам по себе не является абсолютным злом и при определенных обстоятельствах имеет право на существование (даже оператор GOTO, представьте себе). Но сколько-нибудь опытный разработчик всегда с первого же взгляда отличит, где вынужденное нарушение правил в человеческом коде, а где глубокая тарелка с ботвиньей.
Разберем простейший пример. Задача: в зависимости от неких внешних условий (состояние определенных объектов данных) выполнять определенные действия. Реализация в спагетти-стиле будет выглядеть примерно так:
- Единственная огромная функция СделатьВсеКакНадо(), двенадцать экранов длиной.
- Проверка внешних условий расположена внутри функции.
- Выбор действия реализован в формате многостраничного блока Если.. ИначеЕсли с тремя уровнями вложенности. Внутри каждой пары операторных скобок нижнего уровня расположен код, выполняющий действие. Причем действия принципиально разные, от “бросить исключение” до “прочитать и разобрать внешний файл данных”.
- Используется огромное количество переменных-флагов с ничего не говорящими (в лучшем случае) именами. В худшем случае это переменные вида ФлагОбработки23. В терминальном случае назначение переменной противоречит ее названию.
- Использование в case-блоке семантических конструкций вида Если Не НеДелать Тогда...
- Использование многоэтажных (от пяти и больше) условий И/ИЛИ в каждой ветке case-блока.
- Часть действий выполняется через Попытку, часть выполняется в транзакции. Операторные скобки попыток и транзакций перемешаны самым причудливым образом.
- Заворачивание некоторых (видимо, особо опасных) фрагментов кода в попытки без обработки исключений (“проглатывание исключения”).
- Функция решает несколько принципиально разных задач. По сути, это несколько функций, перемешанных в одну и имеющих некие общие фрагменты.
- Разумеется, никаких комментариев.
- Заголовок функции просто пересказывает ее название (“Функция делает все, как надо”). Описанные в заголовке параметры не соответствуют реальными ни по именам, ни по количеству.
В наиболее брутальном варианте разные фрагменты чудо-функции окажутся еще и написаны в разных стилях: часть по-китайски, часть по-индийски, ну и так далее.
Ничего не напоминает? Вот и автору тоже кое-что напомнило.
В чем же причина такого подхода к разработке со стороны многих и многих, казалось бы, квалифицированных (сертификаты на стене точно есть) разработчиков? Причины обычно самые банальные. У кого-то сложности с абстрактным мышлением, способностью проектировать сложные сущности и особенно их взаимодействие. Кому-то просто лень. А кто-то просто не знает, что можно разрабатывать программный код как-то по-другому.
Проблема спагетти-кода не в том, что код ужасен. Написание и особенно сопровождение такого кода требует очень существенных избыточных затрат, причем потери несет не только потребитель, но и разработчик. Это как подниматься на велосипеде в горку, для бодрости навесив себе на шею пару-тройку кирпичей.
Как избежать соблазна свалить все в одну неряшливую кучу и побыстрее покончить с задачей? Есть простой, но действенный принцип аутотренинга.
Программный код нужно писать так, как если бы дальнейшим сопровождением занимался агрессивный социопат, который точно знает, где вы живете.
Если соединить этот принцип с японской ментальной техникой “сегодня меня ждет...” и представить живую картину — сырой подвал, ржавая батарея, стальные наручники, клюшка для гольфа, вот это все. И вопрос
— Форма обработки, процедура ОбработатьНаСервере, строка 4546
Если Не НеТрогать ИЛИ Тр2.Количество() > 1 И ФлагУпр = Ложь Тогда
Что — тогда? В глаза смотреть! Отвечать!
Достаточно разок-другой проснуться в холодном поту, и настоятельное желание изучить более эффективные приемы разработки появится из ниоткуда, как новогодняя посылка от давно и прочно забытого двоюродного дядюшки-эмигранта. Не пренебрегайте этой посылкой.
Оверинжиниринг
Название этого печального явления происходит от английского термина Overengineenering (на русский можно перевести как сверхтехнологичность или переусложненность). Суть явления прекрасно раскрыта в старой русской поговорке про “из пушки по воробьям”. Разработчик пытается найти как можно более сложный, технологичный и затейливый способ решения элементарной задачи, исключительно с целью продемонстрировать миру свое (зачастую, сильно преувеличенное) владение различными приемами, техниками и возможностями языка/платформы. Фактически, это сознательное и последовательное нарушение принципа KISS, причем ничем не спровоцированное, а просто для удовольствия.
Нечто подобное массово наблюдалось в двадцатых годах прошлого века (см., например, великолепный фильм Чаплина “Новые времена”).
Сейчас, в почти уже двадцатые годы нынешнего века, явление снова расцветает пышным цветом, и не только в электронике, но и в нашей с вами отрасли разработки программного обеспечения. Причем сектор open source подвержен этому раку ничуть не меньше коммерческого, просто в коммерческом разработчик пытается впечатлить заказчика (и попутно слегка раздуть стоимость проекта, разумеется), а в открытом ПО впечатляют менее квалифицированных коллег по цеху. О вкусах не спорят, кому-то по нраву лишний рубль, а кому-то — когда молодые почтительно смотрят снизу вверх, в любом случае следствие важнее причины.
А следствие довольно грустное. Искусственно усложненный код, во-первых, крайне трудоемок в сопровождении, во-вторых, почти всегда содержит неочевидные ошибки, которые непросто даже не исправить, а хотя бы локализовать. Ну и в-третьих, разработка в стиле Overengineering стоит неоправданно дорого. Как в той старой шутке про заключение контракта на убийство на Диком Западе.
— Его зовут Джон Доу, он живет в шахтерском городке Маунтин-Крик, его дом..
— О, не стоит вдаваться в подробности. За такую цену достаточно просто назвать город.
Как и в случае стиля спагетти, главная проблема сверхтехнологичности заключается не в ошибках или некачественном коде, здесь-то как раз код бывает эталонного качества. Проблема в неприемлемо низкой эффективности разработки. Если гиперзвуковой аэроплан использовать для перевозки из Петербурга в Москву корзинки с печеньем, его КПД, при всех встроенных чудесах теплотехники и аэродинамики, будет ничуть не выше, чем у парового котла имени братьев Черепановых. Зато стоимость эксплуатации будет сопоставима со стоимостью разработки, и это не преувеличение.
Рассмотрим простой пример, в качестве учебного пособия возьмем <не будем показывать пальцем, какой> довольно распространенный программный продукт. Конфигурация написана профессорами, а сопровождает — ну, кто сопровождает? Обычные линейные специалисты. Потребитель зовет такого специалиста и просит: вот этой печатной форме расшифровка подписи выводится вот в таком виде, а нам нужно, чтобы в другом. Казалось бы, десять минут работы? Линейный специалист, перекрестившись, лезет в Конфигуратор.
Через два часа изысканий становится понятно, что форма не является чем-то обособленным. Все печатные формы порождаются циклопической схемой компоновки, данные для которой собираются посредством добавления и преобразования временных таблиц, последовательно на нескольких уровнях стека. Триумф технологичности. Но как подпись-то поправить? Еще через час и семь этажей <18+> искомое место найдено и туда внесена крохотная, косметическая правка.
Проверяем — порядок, наша форма выглядит, как и заказано. Но и во всех других формах, где подпись должна была остаться прежней, наша косметическая правка также сработала. Механика-то общая, и схема компоновки тоже общая, включая макеты. Отделить логику извлечения и подготовки данных от логики вывода не представляется возможным, для этого потребуется переписать половину механизма. Ну а счетчик тем временем тикает, и заказчик задает вполне резонный вопрос
— Юноша, вы сидите тут уже три часа. И не можете изменить в печатной форме “а” на “бэ”. Стоимость часа вашей работы, как вы знаете, N рублей. Не говоря о том, что вы еще и отняли три часа рабочего времени у нашего сотрудника. Вам эта ситуация не кажется немного странной?
В такие моменты начинающий, пока еще линейный специалист мечтает не о повышении своей квалификации, а только чтобы провалиться сквозь землю, немедленно, прямо здесь и сейчас. Интересно, почему разработчиков тиражных продуктов не посылают на стажировку в поля? Два-три месяца сервисным инженером, мальчиком на все руки, на обновлениях и мелких доработках — вернется другим человеком. Level-up на десять уровней с гарантией.
Оверинжиниринг на корню уничтожает ключевое преимущество платформы “1С”, нашу главную киллер-фичу — открытость кода и дружественность к быстрым доработкам под частные задачи потребителей силами специалистов средней квалификации. Никто не спорит с тем, что технологичность разработки влияет на экономическую эффективность строго позитивно. Но, как говаривал любимый философ автора, добродетель, переходящая границы разумного, всегда оборачивается пороком. В том числе и в экономическом аспекте.
Сверхвариативность
Может показаться, что антипаттерн сверхвариативности (кустарный термин автора, наверняка есть более изящный) является частным случаем оверинжиниринга. Но это не так. Если множество порочных техник и методик разработки представить как граненый стакан, до краев полный темной, пузырящейся, и, разумеется, очень токсичной жидкостью, оверинжиниринг и сверхвариативность будут сверкать разными гранями.
В пяти словах — сверхвариативность это когда очень много непонятных взаимосвязанных настроек. Расширения из реального мира — часть которых уже/еще не работает, а некоторые нельзя/опасно использовать.
Сама по себе вариативность, то есть возможность для потребителя подстраивать поведение продукта под различные частные случаи, ни в коем случае не является злом, только при соблюдении нескольких простых принципов, как то: все настройки снабжены разумными “заводскими” умолчаниями; противоречия и опасные комбинации настроек проверяются и блокируются автоматически; при детализации настроек используется принцип малозаметности; каждая настройка снабжена интуитивно-понятным названием и краткой аннотацией “что будет, если установить этот флажок”. Последним по перечислению, но далеко не последним по важности, выступает соответствующий раздел технической документации к продукту, где каждая настройка описана во всех подробностях и со всеми нюансами “что будет, если настройку А установить в ноль при ненулевом значении настройки Б”
(В скобках необходимо раскрыть термин “малозаметность настроек”, этот принцип можно определить как “чем более опасной и экзотичной является настройка, тем слабее заметность этой настройки для пользователя и тем сложнее до нее добраться”).
К сожалению, вряд ли получится дать четкий измеримый критерий для определения ситуаций, когда к здоровой вариативности добавляется приставка “сверх-”. Это примерно то же самое, как пытаться высчитать точную объемную долю масла, при которой каша переходит в категорию “испорчено”. Попробуем показать антипаттерн нездоровой сверхвариативности на простом и наглядном примере.
Проектируем конфигурацию, которая нацелена на решение задач проектного управления на предприятиях класса “проектный институт”, “системный интегратор”, “инжиниринговая компания”, “разработчик ПО” и так далее. Одним из ключевых объектов управления здесь является ресурс — либо трудовой, либо материальный. С материальными ресурсами сложностей не возникает, экскаватор, станок или сервер в нашей системе, к счастью, акторами быть не могут. Иное дело трудовые ресурсы, то есть квалифицированные специалисты. Эти люди, при определенных условиях, могут взаимодействовать с нашей системой, и довольно активно. Что еще хуже, взаимодействие может выполняться не напрямую, а через пользователя-посредника (вариант “инженер находится в поле и шлет по телеграфу инструкции, какие показатели ввести по его задачам”). Важный момент: под “настройками” здесь понимается не только какой-то набор констант, но и любая возможность влиять на поведение системы.
Продумав множество возможных вариантов, разработчик системы (не будем показывать пальцем на автора книги, ему, разумеется, очень стыдно, но все сроки давности уже вышли) спроектировал прекрасную структуру метаданных, которая полностью покрывала все относительно разумные варианты. Аплодисменты и конный памятник в масштабе “один к тридцати двум”? Рановато.
Получилось так, что при наиболее частом сценарии “трудовой ресурс является сотрудником предприятия и пользователем системы” необходимо было завести несколько сущностей:
- “Петр Петрович Гарин” — элемент справочника “Трудовые ресурсы”, инженер-проектировщик первой категории.
- “Петр Петрович Гарин” — элемент справочника “Сотрудники”, ведущий инженер отдела перспективных оптических энергосистем.
- “Петр Петрович Гарин” — элемент справочника “Пользователи”, пользователь системы.
- “Петр Петрович Гарин” — элемент справочника “Диспетчеры”, промежуточное звено между пользователем и сотрудником.
- “Петр Петрович Гарин” — элемент справочника “Физические лица”, обладатель возраста, паспорта РФ, трудового стажа и прочих свойств, потребных для расчета заработной платы.
И только после того, как все пятеро Петров Петровичей окажутся внесены в соответствующие справочники, и элементы справочников окажутся связаны соответствующими документами (“Назначение диспетчера сотруднику”, “Назначение владельца трудовому ресурсу”, и прочее, и прочее), только после этого взвод товарищей Гариных сможет наконец-то приступить к проектированию своих гиперболоидов.
Разумеется, когда дело дошло до реальных внедрений, сразу появились запросы на упрощение процедуры. В конфигурации стали появляться разнородные “помощники ввода”, что добавило бодрости тестировщикам. Простой, как табуретка, тестовый сценарий стал ветвится на “часть данных ввели помощником, часть вручную, а часть уже была в системе”, ну — и так далее.
Можно ли говорить об эффективной разработке, когда нужно изобретать и реализовывать сложные инструменты для решения элементарных задач? Можно ли говорить об эффективном использовании системы, если простейшая процедура “добавить трудовой ресурс” требует дюжины приемов заряжания, как фитильный мушкет времен Варфоломеевской ночи? Если только в насмешку.
Если бы автор пытался стилизовать текст под когда-то модный lurkmore-style, то закончил бы описание антипаттерна сверхвариативности чем-то вроде следующего:
Петя проектирует систему, исходя из своего понимания полного множества вариантов использования. Петя начертил уже два погонных километра Use Case diagrams, но ему кажется, что картина пока еще не цельная. Петя собирается учесть все эти варианты в своей архитектурной модели.
Митя проектирует систему, исходя из своего точного знания степени неопределенности задач, для решения которых будет привлекаться система. Митя пытается совместить пути наименьшего сопротивления и следования массовой потребности, но не забывает о феномене “fog of war” и предусматривает в своей архитектурной модели возможности расширения и проектной кастомизации.
Товарищи проектировщики. Будьте умными, будьте как Митя.
Вот бы кто-нибудь рассказал автору нечто подобное в далеком уже 2006-м. Не то, чтобы автор прямо сразу поверил на слово (этому слегка мешал нимб), но повод задуматься был бы неплохой. Учиться на ошибках дело здравое и полезное, но все-таки лучше, когда это чужие ошибки.
Кофе-пауза #5
Подводя итоги исследованию темной и светлой сторон искусства разработки программных продуктов, можно сделать два простых и важных вывода. Во-первых, любой темный паттерн в своей глубинной сути есть ни что иное, как отрицание тех или иных светлых паттернов, и если просто держаться светлой стороны, не придется беспокоиться о проблемах темной. Чисто и светло там, где не темнят и не мусорят.
Ну и во-вторых, описанные антипаттерны вовсе не являются чем-то специфическим именно для ИТ, их можно повстречать в любой отрасли инженерного дела. И чтобы не чувствовать себя изгоями из старого баяна про строительство программистами многоквартирного дома, мы рассмотрим несколько аналогичных примеров из самой брутально-серьезной локации реального сектора. Трехминутка истории военной техники от нашей доброй феи Аналогии из мира прекрасного. Три минуты — три примера инженерного кретинизма, проявленного там, где цена ошибки измеряется далеко не условными единицами.
Пример первый. Линейный корабль “Ваза”.
Первая половина XVII века. Швеция, как один из претендентов на гегемонию в континентальной Европе, ведет постоянную войну, в том числе и морскую. Но дела на море складываются неважно, требуется усиление флота. Шведский король принимает эпическое решение построить для флота не просто новый флагман, а что-то вроде Death Star, в масштабах тогдашней Балтики, понятно. Самый крупный и сильный корабль на театре: 1200 тонн, 64 орудия, из которых 48 крупных, морская пехота в количестве трех рот. Также были запланированы высокая скорость и прекрасная мореходность. С такой штукой во главе флота действительно можно было попробовать превратить Балтику во внутреннее шведское озеро.
Строительство велось ударными темпами и к лету 1628-го корабль был готов к первому пробному походу. При огромном стечении столичной публики “Ваза” торжественно вышел из гавани на открытый рейд. Налетел порыв посвежевшего ветра, корабль разок-другой качнулся... Накренился, принял в открытые пушечные порты воду, лег на борт, и затонул за считанные минуты, прямо под парусами.
Следствие вскрыло удивительные подробности. Корабль оказался спроектирован настолько удачно, что с такой боевой нагрузкой, при таких размерах и таком расположении центра тяжести его мореходность была примерно нулевой. Главный конструктор пытался возражать, но заказчик в лице Густава II Адольфа продавил все свои поправки к проекту. Когда дело дошло до назначения виновных, привлечь главного конструктора не удалось, по вполне уважительной причине, бесконечные споры с августейшим заказчиком стоили конструктору сердечного приступа, и бедняга сгорел на работе еще до спуска корабля на воду. Чертежи, размеры и прочее подписаны лично королем, что же, на допрос вызывать его величество? Пришлось списать катастрофу на буйство природной стихии (см. “слегка посвежевший ветер”).
Ничего из нашей с вами практики не напоминает? Кто сказал “не самый удачный кейс глубокой модификации ERP, не поддержанный экспертной оценкой и нагрузочными тестами”? Отставить разговорчики в задних рядах.
Пример второй. Артиллерийская система “Дора”.
Тридцатые годы прошлого века, Германия готовит второй акт Мировой войны. Разработка нового оружия ведется во всех мыслимых направлениях, одним из которых являлась сверхтяжелая артиллерийская система. Нужно же будет чем-то сломать линию Мажино? Нужно. Кому именно из германских функционеров попался в руки роман Жюля Верна “Пятьсот миллионов бегумы”, история умалчивает (хотя был там у них один любитель такого чтения), но разработка орудия велась строго по заветам великого французского мастера ненаучной фантастики.
Сумрачный германский гений в лице заводов Круппа произвел не просто большую пушку. Это было непревзойденное чудо инженерной мысли. Достаточно просто перечислить основные характеристики: калибр 807 мм; вес 1’350 тонн; орудийный расчет 250 человек; дополнительный персонал 2’500 человек, заявленная прицельная дальность стрельбы до 40 км. Чтобы передвинуть “Дору” (примечательно, что главный конструктор назвал детище именем своей жены) с места на место, требовалось сдвоенное полотно и два усиленных железнодорожных состава.
Казалось бы, вот это уже точно Death Star. Но два технических параметра оказались, как бы это сказать вежливо, слегка маломерными. Скорострельность “Доры” составила один выстрел в примерно сорок пять минут, но это бы еще ничего. А вот боевая нагрузка в 700 кг на снаряд (это на фугасный, бетонобойный нес всего лишь 250 кг) выглядела какой-то дурацкой шуткой. Для сравнения — максимальная нагрузка пикирующего бомбардировщика Ju-87 модификации D составляла 1400 кг, при максимальной скорости ~400 км/ч, то есть одна “Штука” за десять минут сработала бы не хуже, чем “Дора” за полтора часа.
Что же получилось у концерна Крупп на выходе? Несколько поездов неделю везут почти полторы тысячи тонн стали и три тысячи человек, чтобы за сутки выпустить в белый свет (про прицеливание на дистанции 40 км средствами того времени конструктор пускай своей фрау Доре заливает в уши) всего-то полтора десятка снарядов общей массой жалкий десяток тонн. То есть выполнить работу, с которой за один вылет легко справится десяток пикировщиков. Германский генеральный штаб устами генерала Гальдера оценил сокрушительное оружие по достоинству:
— Настоящее произведение искусства. Однако, абсолютно бесполезное.
Генерал Гальдер был вежливый человек и не стал приводить оценку, сколько полезных артиллерийских систем обычных классов можно было произвести, используя угробленные на “Дору” ресурсы. Опять же, ничего не напоминает из нашей отрасли?
Пример третий. Сигнальная система шевалье де Павильона.
Вторая половина XVIII века. Великие европейские державы ведут почти постоянную морскую войну, формально в различных сочетаниях и союзах, а фактически мировой океан представляет собой арену для игры в общий deathmatch. С кораблестроением и кораблевождением уже почти полный порядок, так что одним из наиболее тормозящих факторов остается связь. На суше связь организовать довольно просто, в любой момент на любой участок поля битвы можно послать гонца с приказом (лучше, конечно, сразу трех, для надежности). В морском сражении такой возможности нет. До изобретения и запуска в промышленную эксплуатацию радио единственным действенным средством передачи приказов и донесений на море оставались флажные сигналы.
Передача приказа выглядела следующим образом: адмирал диктовал приказ, флагманский корабль поднимал соответствующую гирлянду цветных вымпелов, соседние корабли, которые находятся в прямой видимости, повторяли адмиральский сигнал, и так по всей эскадре. Понятно, что при помощи нескольких цветных тряпочек передавать связную человеческую речь затруднительно, и требуется какая-то другая сигнальная система. Удивительно, но в течение очень долгого времени военные моряки не могли додуматься даже до элементарного алфавита, и вся сигнальная механика базировалась на примитивной кодификации. Флагами можно было указать только пункт инструкции по кораблевождению или заранее составленного боевого приказа. Неудивительно, что морские сражения того времени не отличались осмысленностью, каждый командир обычно действовал сам по себе.
И вот перед европейскими морскими ведомствами в полный рост встала задача модернизации систем связи. Каждая страна решала задачу по-своему (разумеется, не брезгуя подглядывать друг за другом), и результаты у всех получились разные. Английский флот, например, все же додумался до зачатков алфавита и разработали сигнальный свод, посредством которого можно было передавать хотя бы простые, но все же приказы. К началу наполеоновских войн Royal Navy успел внедрить и обкатать новую систему связи на практике.
Чем ответил французский флот? Два морских офицера, правда, не королевских, а на службе Ост-Индской Компании, изобрели простую, но эффективную числовую систему, которая позволяла комбинациями из всего лишь десяти вымпелов передавать тысячи различных и легко читаемых сигналов. Никакой военной тайны эта система не представляла, подробное описание было даже опубликовано. Но для французских адмиралов идея просто комбинировать цифры показалась слишком простой. Детский сад какой-то, циферки складывать, вот еще. Так что модернизация систем связи была поручена специальному отделу под началом королевского офицера с дивной фамилией де Павильон.
Шевалье де Павильон оказался настоящим воплощением острого галльского смысла. Под его руководством была проделана титаническая работа, проанализированы все инструкции и уставы, на основе анализа были синтезированы фрагменты приказов на все мыслимые ситуации. Каждый фрагмент получил свою комбинацию сигнальных флагов, комбинации были сведены в таблицы, а таблицы в книги. Система получилась буквально всеобъемлющей, но чудовищно медленной и неудобной в реальной боевой работе.
Представьте, что вы командуете, например, фрегатом. Соседний вымпел репетует сигналы флагмана, ваш сигнальщик эти сигналы разбирает и докладывает. Но это только начало дела. Каждый сигнал является как бы иероглифом, который нужно найти в кодовой таблице. Затем сложить иероглифы в нечто, обладающее смыслом, также через специальную таблицу сопоставления. И только тогда вы узнаете, что, оказывается, нужно было срочно идти на помощь товарищу, которого вражеские корабли зажали вон за тем островком. А пока вы занимались расшифровкой и готовили маневр, пришел уже другой приказ, отмена предыдущего, поскольку помогать там уже и некому.
Шевалье де Павильону, очевидно, пришлось перепробовать все современные ему средства от икоты. Но вряд ли какое-то из них помогло.
Приведенные примеры отнюдь не являются чем-то выпуклым и выходящим вон из общего ряда шедевров военно-инженерного кретинизма. Стройный ряд можно продолжать, насколько хватит бумаги. Вот просто навскидку — супертанк Porsche Maus; ракетный перехватчик Me-163 “Комета”; подводный броненосец “Сюркуф”; летающий теплоход Hughes H-4 (a.k.a. “Еловый Гусь”); авианосец-дредноут “Синано”; динамитный пневмо-крейсер “Везувий”; бронированные бублики имени адмирала Попова (круглые, Карл! эти корабли были круглые!); “белые слоны” имени адмирала Фишера; “свиные корыта” имени лорда Черчилля; и так далее, и тому подобное, тысячи их. И все были не просто спроектированы, но и построены, а многие так даже и приняты на вооружение флотов и армий.
Давайте, уважаемые коллеги, не забудем возносить благодарность дорогому мирозданию за то, что мы не проектируем военную технику. Иначе идея привинтить к реактивному истребителю в качестве оружия дальнего боя наземный комплекс ПТУР, с управлением ракетой по проводам (не шутка, отлито в металле), показалась бы легкой разминкой перед настоящим парадом инженерной смекалки.
Слагаемые эффективной разработки
Знание и понимание базовых принципов “как нужно” и “как ни в коем случае нельзя” является необходимым, но далеко не достаточным для выстраивания процессов разработки в стиле “Не спеша, эффективно и правильно”. Если мы научились различать зеленый и красный сигналы светофора, это еще не значит, что мы сможем не заблудиться по дороге хотя бы к ближайшей булочной, не говоря уже о деловом центре мегаполиса.
Тавтология, конечно, далеко не самый изящный способ донести мысль, но здесь без нее не обойтись. Эффективная разработка подразумевает, что сама по себе разработка (то есть специфицирование метаданных, написание программного кода и конструирование визуального интерфейса) занимает далеко не первое место ни по важности, ни по длительности. Собственно, ни про какую стадию создания программного продукта нельзя сказать, что она самая важная или длительная, все стадии примерно равнозначны, и мы рассмотрим их одну за другой.
Планирование
Любая работа начинается с плана. В случае работы над созданием или модернизацией программного продукта существуют два принципиально разных подходах к планированию. Эти подходы называются ”академический” и “системный” (автор допускает, что в университетах учат иначе, но тем хуже для университетов).
Любое планирование в нашей отрасли означает прежде всего декомпозицию крупной задачи на более мелкие, вопрос только в том, в каком направлении движется мысль проектировщика при декомпозиции. Первый подход, называемый академическим, предполагает движение “от общего к частному”. Большая задача разбивается на фрагменты поменьше, каждый из фрагментов нарезается на кусочки, каждый кусочек расщепляется на ломтики, и так далее, до самого нижнего уровня. Результат визуализируется в формате диаграммы Гантта, сетевой диаграмме, ну или чего-то в этом роде.
Нельзя сказать, что академический подход плох. Но у него есть одна особенность, которая может обернуться фатальным дефектом: академический подход требует полной определенности. Еще до начала работ должны быть определены точные результаты, полностью оценены и описаны требования, заданы ограничения, выдвинуты и проверены допущения, ну и так далее. И только получив всю необходимую исходную информацию, можно приступить к проектированию.
Но в реальной практике академические задачи встречаются не так, чтобы часто. Наши задачи обычно характеризуются высокой неопределенностью во всем, кроме сроков. Типичный пример такой задачи:
Наш заказчик очень хочет интегрировать свою учетную систему (наша разработка) с неким сторонним сервисом, позволяющим организовывать промо-компании посредством рассылок по электронной почте. Будут автоматически рассылать письма с предложениями, описанием новых продуктов, акций и так далее. Принципиальное решение уже принято, но никаких деталей пока нет. Что за сторонний сервис, неизвестно, еще не выбрали. Но к первому октября это должно заработать, у них какая-то круглая дата, они своим директорам уже обещали, а мы уже подписались это реализовать.
Академический подход при такой постановке мало чем поможет. “Реализовать интеграцию примерно известно чего с абсолютно неизвестно чем”, а где вторая-то половина задачи, и где точные требования по первой половине? Пока мы ждем уточнений и ограничений, время идет, дело стоит, а первое число приближается. Когда постановка обретает более-менее прочную форму, времени остается уже совсем немного, и нужно либо срывать сроки с потерей прибыли, либо пытаться сработать ударным темпом (см.”быстро и криво”).
Системный подход так же заключается в декомпозиции крупной задачи на мелкие, но мысль проектировщика двигается в строго противоположном направлении, “от частного к общему”. Из любой большой и непонятной задачи всегда можно сразу выделить какие-то фрагменты, которые потребуются в любом случае, причем эти фрагменты можно расположить в порядке приоритета. Затем, по мере убывания неопределенности, можно будет выделить другие фрагменты, связать с уже существующими, и так далее, до победного финала. Если декомпозиция была выполнена правильно, в финале все составные части будут собраны в единое целое, которое и будет решением задачи.
Университетские преподаватели обожают иллюстрировать системный подход ситуацией “человек проснулся в чужой стране/на необитаемом острове”, но мы возьмем тот же самый кейс, интеграцию примерно понятно чего с абсолютно неизвестно чем. Кое-что мы уже знаем, следовательно, можем прикинуть, а что же нам потребуется в обязательном порядке? Садимся, в формате “brainstorming” набрасываем список, анализируем, оставляем действительно разумные варианты. Например:
- Для писем потребуется какая-то механика шаблонизации, эта механика должна будет оперировать реквизитами клиента и должна быть очень простой в управлении.
- Рассылки явно будут группироваться в маркетинговые кампании (для простой отправки одиночных писем нет никакой необходимости в стороннем сервисе).
- Нужны будут инструменты для проверки валидности адресов электронной почты, для проверки дублирования адресов у разных клиентов, и так далее.
- Потребуется хранить историю, когда, кому и что было выслано.
- На каком бы техническом транспорте не была реализована интеграция, потребуется полное логирование взаимодействия со сторонней системой.
- Будет нужен черный список, чтобы иметь возможность быстро выключить всякое почтовое взаимодействие с определенным клиентом.
- Наверняка потребуется сопоставлять статистику проведенных маркетинговых кампаний с продажами, и возможно, здесь можно применить какой-то универсальный механизм.
- Если писем будет много (а мы можем оценить размер клиентской базы), наверняка нужно будет уметь рассылать в несколько потоков.
- Что мы еще забыли?
По каждому из пунктов такого списка первичных потребностей можно, как минимум, проектировать метаданные и специфицировать программные интерфейсы. Не дожидаясь даже информации о том, что же это за сторонний сервис и как с ним взаимодействовать. Причем по некоторым пунктам списка можно либо переходить к разработке, либо подбирать и адаптировать готовые наработки. Никакой диаграммы Гантта у нас, конечно, при таком подходе не будет, а будет обычный список задач. Будет ли этого достаточно? В условиях высокой неопределенности и сжатых сроков — вполне.
Какой подход применять — для каждой задачи решается индивидуально, точных рецептов нет. Впрочем, эту фразу можно повторять в заключение почти каждой главы этой книги. “Изучаем чужой опыт, но думаем своей головой”, золотые слова, в буквальном смысле.
Проектирование
Несмотря на важность предмета, повествующая о нем глава будет довольно короткой. Критичность стадии проектирования была довольно ярко раскрыта в первой части, а практические приемы проектирования намечены к раскрытию, будем надеяться, не менее яркому, в третьей части. Ну а здесь мы попробуем акцентировать внимание на разнице между проектированием для разработчика и проектированием для заказчика.
Различие является ключевым. Проектирование для заказчика имеет целью создать и передать набор артефактов, оговоренных контрактом. Как правило, это какой-то объем (от пары листов до пары кубометров) относительно хорошей, пропущенной через принтер бумаги, которую заказчик получает в качестве результата работ по этапу такому-то. Никакой иронии, это действительно важная и нужная часть проекта, работа специалистов по бизнес-анализу, консультантов и технических писателей заслуживает только уважения. Но следует понимать, что к собственно разработке эти артефакты имеют, скажем там, слегка опосредованное отношение.
Для себя разработчик программного продукта готовит совсем другой артефакт, внутренний технический проект. Форма, структура и нотация могут быть абсолютно любыми, вся ценность заключается в содержании. Этот документ составляется для того, чтобы потомки разработчика могли быстро получить ответы на практические вопросы. Например, “для чего все это было сделано”, “какие проектные решения были приняты”, “по какому принципу это работает”, “где искать точки входа для такой-то функции”, ну — и так далее. Что характерно, в роли потомка может выступать и сам разработчик, как показывает практика, период полураспада идей по старым разработкам в голове даже очень хорошего разработчика не превышает примерно шести месяцев.
Понятно, что грамотная реализация прекрасно описывает себя и сама, так зачем какие-то дополнительные артефакты? Попробуем привести аналогию. Когда-то давным-давно автору нужно было написать сочинение по великому роману-эпохе “Война и мир”. В те времена у автора отношения с творчеством графа Толстого были довольно напряженными, и перспектива разбираться в эдакой махине методом последовательного чтения исходного текста отнюдь не радовала. К счастью, в библиотеке нашелся технический проект (созданный методом reverse engineering, но не все ли равно), в котором кратко и сжато были описаны все основные персонажи, сюжетные линии, а также даны адресные привязки к тексту (что же за сочинение без цитат). Было ли это жульничеством? Учись автор на гуманитарном факультете, возможно. Но факультет был инженерный, и плох тот инженер, который не умеет читать документацию и применять принцип экономии ресурсов.
Прототипирование
Сразу оговоримся: прототипирование не является обязательной стадией, равно как и не всякое письмо требует черновика. Когда задача разработки является, скажем так, серийной — уже неоднократно делали нечто подобное, никаких подводных камней, детских болезней и внезапных капризов заказчика не ожидается — необходимость проверять какие-то фрагменты решения на прототипах не возникает. Прототип нужен, когда нет четкого понимания, как же это должно выглядеть, управляться и действовать?
Что действительно важно при работе с прототипами, и что (по наблюдениям автора) часто упускается при разработке — отчетливое понимание того факта, что прототип, скорее всего, делается на выброс. Даже если обкатанная на прототипе идея окажется удачной, перетаскивать черновой код в продукт “как есть” гарантированно не будет хорошим начинанием. Дело в том, что прототип, если мы говорим об эффективной разработке, не требует обязательного соблюдения правил и стандартов. Например, если мы делаем прототип UI, можно не обращать внимания на факторы нагрузки, можно вместо запроса выполнить вызов через три точки, можно ставить в коде любые подпорки. А если мы делаем прототип серверной обработки данных, интерфейса может не быть вовсе, не потребуется никаких проверок заполнения, валидности, связности вводимых данных.
Понятно, что при переходе от прототипа к собственно продукту, возникнет (и прежде всего у самого разработчика) соблазн оставить все, как есть, просто слегка доработать напильником. Жалко написанного кода, жалко потраченного времени. Что-то вроде жалости к просроченному провианту, пусть молоко скисло, так хоть блинчики из него забодяжим. Чем такая практика порочна — тоже понятно. В чистовую версию продукта обязательно попадет что-то недоделанное, где-то что-то забудут, где-то не уберут отладочный код.
Вполне реальный пример. Потребовалась оптимизация некоей серверной обработки данных. На входе вроде бы немного, не более миллиона строк, но с какого-то момента работает крайне медленно. Результат оптимизации: удалена одна-единственная строка. Дело в том, что разработчик отлаживал алгоритм на прототипе, входные данные передавались через форму, обработка выполнялась в режиме “обычное приложение, толстый клиент”. Для визуальной проверки правильности расчетов вызывался выбор строки таблицы значений, чтобы можно было посмотреть глазами. И оказалось, что если такой код перенести в фоновое задание, исключения не будет, но лаг на операторе выбора строки будет чудовищным. А всего-то, “скопировать и вставить”.
Важное дополнение. “На выброс” не означает, что после окончания испытаний все файлы прототипа необходимо пропустить через программу-шреддер. Какие-то фрагменты вполне можно портировать в чистовую версию, а сам прототип в дальнейшем использовать в качестве подобия “летающей лаборатории”.
Ну а чтобы победить неуместную жалость к уже написанному коду, можно использовать простейшую ментальную уловку. Вместо “прототип” говорить и записывать в проектные документы слово “черновик”. Помогает, проверено.
Отладка
Критериев, по которым эффективность разработки можно измерить численно и почти точно, не так и много. Одним из таких критериев является доля трудозатрат на отладку. Если мы вынесем за скобки стадии планирования, проектирования и проведения экспериментов на прототипах, а также все прочие работы, не связанные напрямую с программным кодом, внутри скобок останется три стадии. А именно: кодирование, отладка, исправление ошибок. Взаимное отношение этих величин даст нам профиль нагрузки, который и будет той самой почти точной численной оценкой.
Профиль нагрузки здорового человека должен выглядеть примерно так: 80/10/10. Почти все рабочее время уходит на собственно написание кода, затем отладка выявляет явные опечатки, допущенные при кодировании, на этапе проверки вскрываются какие-то мелкие ошибки (где-то что-то не подключили, что-то где-то ведет себя не очень прилично, etc).
Профиль курильщика, назовем его так, по форме похож на юного крокодила: хвост, туловище и пасть имеют примерно одинаковый размер. 33/34/33, примерно так. Такое распределение означает, что кодирование было, мягко говоря, неряшливым, раз времени на запуск даже основного потока событий пришлось потратить столько же, сколько и на его программирование. Ошибки в этом случае уже не только мелкие, попадаются и серьезные.
Наконец, профиль, который можно назвать терминальной стадией легочной чумы. 20/80/0. Код пишется очень быстро и практически бездумно, затем следует длительная и мучительная стадия отладки. Во время отладки существенная часть кода переписывается полностью, на ходу меняются ключевые проектные решения, и все это в ритме бешеной гонки за отходящим поездом. Как образовалась нулевая доля трудозатрат на исправление ошибок? Очень просто. Специалисты, предпочитающие работать по такому профилю, обычно не утруждают себя ни альфа-, ни даже дымовым тестированием. В переданном тестировщику продукте с двух кликов нашлась критичная ошибка? Не ошибается тот, кто ничего не делает, регистрируйте (хорошо бы рассказать о том, какое зло таит в себе KPI на количество найденных/исправленных ошибок, но это тема совсем другой книжки).
Еще раз повторим базовое правило:
Разработку нельзя считать эффективной, если в общих трудозатратах на работу с программным кодом первичное написание кода, за вычетом экспериментов с прототипами, занимает меньше ~80%.
Как добиться такого качества, код пишется сразу и почти без ошибок? В этом нет никакого секрета, а три главных принципа вынесены в название этой книги. Программный код нужно писать: а) Не спеша; б) Эффективно; в) Правильно. Если придерживаться этих принципов, отладка из полноценной стадии технологического процесса превращается в довольно скучную, хотя и недолгую, формальную процедуру.
Тестирование
При рассмотрении любого аспекта разработки можно сформулировать три позиции: крайний минимализм (неправильная позиция), крайняя претенциозность (еще хуже), и разумная эффективность (единственно верный путь). Все три позиции прекрасно иллюстрируются подходами к тестированию.
Минималисты придерживаются принципа, внедренного в производственную практику еще во времена Генри Форда: каждый на своем рабочем месте делает строго свое дело. Программисты просто пишут код, тестировщики просто проверяют, а работает ли этот код. У такого подхода есть и свое экономическое обоснование, якобы дешевле содержать раздутый штат сотрудников низкой квалификации для механической проверки, чем отвлекать небожителей от программирования. Разумеется, потери “на трение” (то есть довольно существенные коммуникативные издержки) такая модель не учитывает, и уже по одной этой причине детального рассмотрения не заслуживает.
У перфекционистов процесс перекошен в противоположную сторону. Только хардкор, только test-driven development. Не покрытый тестами код считается недоделанным, а тестировщик в понимании адептов этой религии — это просто такой выделенный программист, который вместо “просто кода” пишет программный код тестов, сценариев, алгоритмов анализа/верификации кода, и так далее. В общем случае этот подход можно только приветствовать. Почему же в нашей вселенной разработки на платформе “1С” полное покрытие кода тестами не является и не может являться эффективным? Все дело в стоимости. Такая разработка является очень, ну очень, ну вот просто-таки неприлично дорогой. Есть отрасли, где “дорого” имеет исчезающе низкий приоритет в сравнении с “надежно” — аэрокосмическая, энергетическая, да та же военная. Но на тех рынках, где торгуются решения на “1С: Предприятии”, ставки все-таки слегка пониже. Поэтому в реальной жизни “набор тестов, который покроет весь мир” остается либо светлой мечтой, либо плодом неудачных экспериментов.
Эффективное решение задачи тестирования старается держаться подальше от крайностей, как, впрочем, и любое другое эффективное решение. У минималистов мы позаимствуем принцип разделения труда. Программист создает продукт, тестировщик (руками, или при помощи специальных инструментов) пытается этот продукт сломать. Разные специальности и даже разные базовые психотипы у специалистов. А у перфекционистов переймем очень простую идею: за минимально допустимое качество программного кода отвечает разработчик и только разработчик. Трудолюбивый программист будет все и всегда проверять руками, ленивый напишет набор тестов для наиболее сложных и хрупких участков, важно, чтобы оба четко понимали зону и меру своей ответственности за качество кода.
Памятка руководителю. Просто подсчитывать обнаруженные ошибки, даже с относительно внятным распределением по критичности — хорошо, но недостаточно. Во всех типичных системах багтрекинга не хватает двух очень простых характеристик. Во-первых, “ошибка является очевидной”. Что это значит? Это такая ошибка, которую невозможно не выявить, просто выполнив штатные действия пользователя из основного потока событий программы. Не какой-то затейливый случай, а то, что лежит на самой поверхности. Такие ошибки следует считать отдельно. И во-вторых, “количество итераций исправления”. Сколько раз промежуточный результат передавался от разработчика тестировщику и возвращался обратно. Эти характеристики не обязательно демонстрировать публично, при оценке профессиональной отдачи специалистов лишними они точно не будут.
Комментирование
И в этом аспекте разработки не избежать драматического противостояния минималистов с перфекционистами. Первые убеждены, что “код написан по-русски, так что комментирует себя сам”. Вторые не просто ставят нормативы вида “объемная доля пояснений в коде должна составлять не менее 35% от общего”, но еще и организуют контроль техническими средствами (например, на базе дополнительных правил АПК).
Оба крайних похода, разумеется, имеют право на существование, но во вселенной, где самым главным критерием является эффективность, место этим подходам в дальнем углу мусорного бака. Минималисту не приходит в голову, что если добавить в код одну-единственную строчку даже не русском, скажем, что-то вроде этого:
читабельность сложного кириллического кода окажется повышена примерно на порядок. Ну а перфекционисты своими правилами добиваются лишь того, что в коде появляются паразитные комментарии вида:
Здесь из пяти строчек три явно лишние — оказывается (внезапно для перфекционистов), что далеко не все участки кода требуют каких-то дополнительных пояснений.
Эффективная же разработка во всех аспектах оперирует принципом разумной достаточности. Что это значит в плане комментирования кода?
- Простой код необходимо писать и структурировать так, чтобы никаких дополнительных пояснений к нему не требовалось.
- Комментарии, которые не поясняют, а пересказывают код, недопустимы.
- Сложный код необходимо сопровождать сжатыми и четкими пояснениями.
- Длинные линейные фрагменты кода при помощи комментариев необходимо разбивать на фрагменты.
- Заголовки процедур и функций (за исключением предопределенных) являются строго обязательными.
На последнем пункте следует остановиться особо. Нет абсолютно никакого смысла в том, чтобы выписывать заголовки для каждой ПередЗаписью() или указывать тип значения для каждого параметра каждого обработчика каждого элемента формы. Но прикладная, произвольно добавленная разработчиком процедура или функция без внятного и понятного заголовка с перечислением всех параметров это причина бить таких разработчиков по рукам линейкой, как делал учитель в старой доброй сельской школе.
Если же заголовок на месте, но написан в стиле “с особым цинизмом”, например, так
то линейку хорошо бы взять металлическую. Эффективная разработка требует, чтобы заголовок был написан прежде, чем собственно процедура (см. “Проектирование”). В заголовке должна быть описана задача, решению которой служит процедура. Все важные нюансы (например, использование исключений и режима транзакции) также должны быть описаны в заголовке. Количество и наименования описанных в заголовке параметров должны точно соответствовать фактическим. Проще говоря,
Заголовок процедуры или функции должен быть написан так, чтобы абсолютно постороннему разработчику, который вызывает эту процедуру или функцию, не пришлось читать ее дальше заголовка.
И только так. Причем правило действует не только для методов программного интерфейса, но и для любых, сколь угодно внутренних и служебных процедур и функций. Дело в том, что “абсолютно посторонний разработчик” и “я, любимый, три проекта спустя” в общем случае указывают на одного и того же человека.
Документирование
Можно не верить, но факт: именно эта часть плана книги была придумана и записана как раз на том самом перегоне между станциями “Серп и Молот” и “Карачарово”, в той самой электричке.
— И немедленно перечитал главу “Проектирование”...
Никакой другой документации от разработчика при эффективной разработке просто не требуется.
Сопровождение
Как уже было указано выше, сопровождение представляет собой наиболее сложный случай разработки. И самая большая сложность, для квалифицированного, по крайней мере, разработчика, заключается в необходимости преодолеть огромное желание “слить это в компост и переписать по-человечески”. Как выразился в свое время один коллега по команде:
Знаете, чтобы здесь все правильно посчитать, нужно вот это все снести, физически. До нулевой отметки. А потом построить заново, но только по-правильному, квадратно-гнездовым способом. Чтобы продольно-поперечно, и никаких “через два дымохода налево”.
Причем под “вот это все” понималась не какая-то программная система, а вся жилая застройка город Санкт-Петербург. Действительно, вот как правильно посчитать стоимость отопления, если дом старый, квартира длинная, как удав, и в двух концах две магистральные тепловые трубы, каждая от своей котельной, и в договорах разные тарифы? И как ни считай, в любом случае выйдет нарушение закона.
Речь не идет о синдроме плохого водопроводчика (“Кто же это вам такое <18+> смонтировал? Руки отрывать таким мастерам”), но плох тот разработчик, который почти сразу после завершения работы над проектом не начинает понимать, как нужно было на самом деле, а свой же собственный код двухлетней давности не встречает улыбкой легкого снисхождения. Философы учат нас, что предела совершенству нет, и приближаться к нему мы можем только асимптотически, но вряд ли стоит доверять сопровождение сложного программного кода выходцу с философского факультета (ничего личного к парням из Сорбонны). Для разработчика программного обеспечения, занятого сопровождением, первыми строчками катехизиса должны стать вот эти:
Не пытайся приближаться к совершенству. Даже не думай об этом. Работает? Вот и не трогай там ничего.
Можно считать эту максиму сознательным расширением описанного выше принципа недотроги: не трогай не только, когда не уверен, но и вообще не трогай, если без этого можно обойтись. Особенную осторожность нужно соблюдать непосредственно перед передачей в сборку или в эксплуатацию уже доработанного и проверенного кода. Как показывает печальная практика, именно в этот момент риск поддаться соблазну “чуть-чуть подправить” является наиболее высоким. Всего-то заменили неуклюжий, топорный, нечитабельный case на чуть более изящную конструкцию, чисто техническая правка, ну что может случиться?
— WTF? O_O
— Oops…
Прекрасная пантомима для разыгрывания в лицах, но как финал обновления — так себе. Даже когда имеешь дело всего-то с учетной системой, а не с атомным, например, реактором.
Оптимизация
Этот сегмент общего пула задач разработки можно сравнить с медицинской или правоохранительной деятельностью. Во всех трех случаях речь идет о системе, будь то программное обеспечение, организм или социум, в которой что-то либо пошло не так, либо изначально было устроено как-то кривенько. По аналогии с деятельностью медиков и правоохранителей по оптимизации программного кода можно выделить три принципиально разных направления деятельности, а именно:
- Врожденная оптимизация.
Такая методика предполагает, что практически любой программный код изначально пишется наиболее оптимальным образом. Это очень дорогая методика, и дело здесь не только в очень строгих требованиях к уровню квалификации программистов, а еще и в том, что оптимальность кода требует безусловного подтверждения нагрузочными тестами. В качестве ориентира можно принять эмпирическое правило: «Оптимальный код составляет ~50% кодовой базы = себестоимость разработки умножить примерно на троечку». Оценка очень грубая, но в целом примерно так.
- Опережающая эксплуатационная оптимизация.
Здесь главный принцип заключается в том, что программная система пишется в обычном режиме, без дополнительных затрат на обеспечение непременной оптимальности кода, а в процессе эксплуатации ведется тщательный мониторинг показателей производительности всех важных участков системы. Регулярный анализ динамики этих показателей позволяет выявить те участки, про которые в народе была сложена поговорка: «Где тонко, там и рвется». Искусство опережающей оптимизации заключается в том, чтобы успеть выявить тонкое место еще до того, как оно окажется порванным, и успеть пришить на код оптимизирующие заплатки. Такая методика хорошо подходит для индивидуальных программных решений (к этому классу относится также и «глубокая» модификация типовых/отраслевых конфигураций), но также является недешевой и требует высокой квалификации инженеров- эксплуатантов (термин «Эксплуататор» давайте все же оставим для теории о классовой борьбе). Наиболее типичный пример в реальной практике – облачные сервисы.
- Пожарная оптимизация.
Наиболее дешевый и наиболее часто встречаемый в живой природе подвид оптимизации. На практике он выглядит следующим образом: систему запустили, система работает, и через какое-то время на каких-то участках производительность и/или стабильность ухудшаются до неприлично низких величин, вплоть до полного ступора и перманентного отказа в обслуживании. Тогда на помощь призывается аварийная команда, обычно внешняя, и оптимизация проводится в лихорадочном темпе на продуктивной системе, в которой едва-едва теплится жизнь. Автору неоднократно приходилось принимать участие в такого рода спасательных операциях, и надо признать, что каждый раз веселье било через край. Некоторое представление об этом процессе можно получить, если задать на YouTube поисковый запрос: «Пожарный самолет Ил 76 в работе».
Какой же из этих способов оптимизации, уж простят читатели за тавтологию, является оптимальным? Как и почти в любом техническом вопросе, правильный ответ должен содержать какое-либо производное от слова «гибрид».
Правильный рецепт этого блюда по мнению автора таков. Для тех (немногих, на самом-то деле) участков конфигурации, где однозначно предполагается высокая нагрузка и работа с большими объемами данных, программный код должен изначально быть если не полностью оптимальным, то очень близким к тому. Для неоднозначных участков, где нагрузка и объемы напрямую зависят от паттерна эксплуатации, достаточно спроектировать решение таким образом, чтобы можно было без труда организовать мониторинг. Ну а внезапные пожары следует оперативно тушить по мере возгорания.
Понятно, что достичь оптимального, еще раз sorry за тавтологию, баланса подвидов оптимизации в конкретном проекте совсем непросто, но
– Так вы всё знали заранее, полковник?
– На то и существует разведка.
Умение распознать где оптимизацию нужно проводить прямо по ходу разработки, а где можно поместить ее в резерв, приходит с опытом и только с опытом.
Кофе-пауза #6
Этот запуск кофе-машины будет больше похож на экспрессивный перекур в антракте между двумя действиями не самой скучной пьесы. И поскольку пауза у нас играет роль терминатора между теоретической и практической частями, невозможно еще раз не вспомнить старую добрую инженерную шутку про успешное соединение теории с практикой. Ну и по традиции – все примеры родом строго из текущей реальности. Отсебятина в нашей курительной под тотальным запретом, примерно как если бы к перекуру попытался присоединиться человек в противогазе замкнутого цикла.
Когда автор в своем профессиональном развитии был еще маленьким-маленьким, на его тогда ещё неокрепшую психику оказали довольно сильное влияние дичайшие истории из сериала «Крах дотком-трестов начала нулевых». Иллюстрированные рассказы о том, как стоимость IT-инфраструктуры для конторы по продаже авиабилетов через интернет оказывалась сопоставимой с аналогичной статьей затрат авиакомпании-перевозчика, не только звучали как логичное продолжение характерного телешоу «Байки из склепа», но и вполне стыковались с этими байками по хронологии. Но по ходу профессиональной карьеры автору и самому пришлось повстречать превосходные примеры соединения теории с практикой.
Итак, IT-инфраструктура. Самый нижний уровень – железо. В качестве теории выступают расчеты потребности в аппаратных ресурсах, а в качестве практики – совокупность счетов на оплату от железных вендоров и отчетов эксплуатационной службы, повествующих об утилизации закупленного оборудования. И на всякий случай, в используемой терминологии нет ни капли агитации за нездоровый образ жизни, эти термины должны быть известны любому инженеру со времен студенчества.
Акт первый, «Недопил».
Место действия – бэк-офис довольно крупного культурно-медийного предприятия. Мелкий проект по модернизации и небольшой оптимизации типового зарплатного решения на платформе «1С: Предприятие». Довольно скромные железки, но нагрузки вполне соответствуют. По результатам обследования – оптимизация если и требуется, то по вершкам.
И тут нас за каким-то чертом заносит в серверную. Помимо обычной стойки в уголке скромно стоят два звездолета класса «Энтерпрайз», иначе не скажешь. Огроменные угольно-черные агрегаты производства IBM из тех, что при включении перекрывают по децибельной шкале взлетающий «Боинг 737» (автор как-то умудрился попасть под прогреваемые движки такого аэроплана, так что уши можно считать откалиброванными). Но в серверной эти два монстра даже не подключены к электропитанию.
На молчаливый вопрос – О_О !!! – местный гид пояснил. Поскольку капитал предприятия совместный, русско-германский, расчеты потребной аппаратной мощности проводил IT-дивизион головного предприятия в Германии. Железо подогнали соответствующее, оттуда же. Но поскольку «Зарплата и управление персоналом» легко обсчитывает те несколько сотен сотрудников, которые числятся в местной части холдинга, прибывшие из Германии звездолеты не пробовали даже включать. Тем более, что никто не знает как с ними работать, админ в комплект поставки не входил.
Правда есть в этой ситуации и плюс – нулевая утилизация означает нулевую амортизацию, ну а в бухгалтерских документах можно провести как высокотехнологичные предметы интерьера, типа офисной столешницы красного гранита или корзины для бумаг, отлитой из авиационного титана.
Акт второй, «Перепил».
Проект по линии ЦКТП, из тех, что завершаются вроде бы успешно, но без публикации отзыва. Изначальная постановка задачи звучала как: «Наше железо уже не справляется с нагрузкой, требуется небольшая оптимизация». Как водится, серия наводящих и уточняющих вопросов, ответы на которые породили у команды оптимизаторов заочный когнитивный диссонанс.
В самом деле – «слегка доработанная» УТ 10.3, некий управленческий отчет, формируется регулярно. Судя по счетчикам производительности, в расстоянии примерно полутора метров от сервера можно приготовить яйца пашот прямо в воздухе. А параметры сервера таковы, что в переложении на любимую нами вселенную имени товарища Лукаса это класс если не «Death Star», то уж как минимум «Star Destroyer», причем тюнингованный. Достаточно будет назвать только количество процессорных ядер – их шестьдесят четыре и ни единым ядром меньше. И вот как такое может быть?
Некоторую ясность внесло изучение образцов «управленческого отчета». Если это распечатать на бумаге, то в ширину получится примерно два с половиной погонных метра, количество колонок исчисляется сотнями. Ну и в длину метров десять-двенадцать. От мозгового штурма на тему «каким может быть техпроцесс работы с этим отчетом» мы решительно воздержались, психика все-таки не казенная. Ну а после изучения программного кода, применяемого для формирования этого воистину восьмого чуда аналитической мысли, вопросов по температуре воздуха вокруг сервера уже не осталось.
Проект оптимизации растянулся на полгода и завершился двумя практическими результатами. Первый: радиус приготовления яйца пашот возле сервера снижен до примерно двадцати пяти сантиметров. И второй: единственной разумной альтернативой является переписывание всей системы с нуля. Тот редкий случай, когда никакие пожарные самолеты даже не рассматриваются, а пылающий лес можно погасить только встречным термоядерным ударом. В портфолио команды и лично автора проект, понятное дело, не вошел. Что характерно, капитал предприятия и здесь был совместный, русско-датский, но у датчан свой стиль, они предпочитают приставку пере-.
И наконец, акт третий, «Перенедопил».
Как пояснит любой студент технического ВУЗа, имевший через знакомых лаборантов доступ к C2H5OH в категории технический ректификат – это когда «больше чем смог, но меньше чем хотелось». Самый коварный паттерн нагрузки массовых сервисов, ориентированных на регулярный расчетный цикл. Количество пользователей сервиса измеряется многими-многими тысячами, но обычная активность держится на уровне «разок другой в час из-под кувшинки лениво квакнет лягушка». За исключением тех двух-трех волшебных дней, когда абсолютно все пользователи сервиса с утра и до ночи ведут лихорадочную деятельность по массовому вводу накопленных за месяц данных, многократному запуску тяжелых расчетов и пакетному формированию отчетов, правок, квитанций и прочих выходных форм. Во вселенной жилищно-коммунального хозяйства таким нагрузочным паттерном никого не удивить.
Как результат – двадцать семь дней месяца железо класса «космическое» загружено примерно на два-три процента, а в расчетный период обычной картиной в серверном здании (это не шутка в качестве серверной используется отдельно стоящее здание о нескольких этажах) является небольшая группа админов в полной полярной экипировке (тоже не шутка, автор разок побывал в этом дивном месте, без термобелья там слегка неуютно), лихорадочно подключающих к общему кластеру последние резервные мощности. И вот что прикажете с этим делать?
Казалось бы – не парься и просто займи железо под майнинг биткоинов. К сожалению, объяснить прогрессивную криптовалютную модель прокуратуре будет не так-то просто. Понятно, что инженерное решение существует, но требует полной перестройки всего серверного парка под единую схему виртуализации, и затраты на одну только оркестрацию всей этой музыки будут сопоставимы с пожаром или наводнением. Но зато в этом случае теория и практика соединились просто идеально, как болт и гайка одного калибра – все прекрасно работает, и все знают как именно оно работает. Вот только сделать ничего с этим не могут.
На этой радостной ноте интермедию можно считать законченной, и добро пожаловать в суровый мир сугубо практических приемов и принципов эффективной разработки.