В качестве оправдания за свою статью в номере 16 (393)
АрхивСтанислав Осташевский прислал в редакцию свой вариант рецензии на книгу, которая начинается словами, взятыми мной в качества эпиграфа. Самое интересное, что у меня не возникло желания горячо спорить со Станиславом, поскольку, по большому счету, я согласен с рядом его суждений, ряду же других не достает обоснования, что лишает спор предмета. (Публикуется только в on-line)
К великому сожалению, статья г-на Козлова в основном касалась не собственно рецензируемой книги, а его личной позиции по технологии разработки программного обеспечения. Поэтому суть книги осталась за бортом.
Станислав Осташевский
Станислав Осташевский прислал в редакцию свой вариант рецензии на книгу, которая начинается словами, взятыми мной в качества эпиграфа. Самое интересное, что у меня не возникло желания горячо спорить со Станиславом, поскольку, по большому счету, я согласен с рядом его суждений, ряду же других не достает обоснования, что лишает спор предмета. Просто мы посмотрели на книгу с разных точек зрения. Но из-за выраженного им "великого сожаления", с которым я скорее не согласен, мне хотелось бы все-таки пояснить, почему мой взгляд оказался непараллельным его взгляду, и почему перпендикулярный взгляд показался мне стоящим того, чтобы его изложить.
Самое забавное, что моя статья действительно была рецензией на книгу, но лишь в своем первом абзаце. И там было вполне однозначно сказано, что книга очень полезная.
Но пересказывать книгу, особенно посвященную основополагающим концепциям, на паре страниц представляется мне занятием наивным - ведь всегда лучше обратиться к первоисточнику, прочитать его самому, составить свое мнение и извлечь что-то полезное для своей практической работы. Результат же от чтения пересказа очень напоминает получение впечатления о пении Карузо при прослушивании куплета из его песни в исполнении картавого безголосого соседа. Не хочется ни оказываться в положении такого несчастного соседа, ни ставить других в положение его несчастных слушателей. Пересказ не был целью!
И еще - количество информации. Представляется, что вместо пересказа достаточно дать ссылку и тем повысить плотность информации в тексте. Выражение несогласия (т.е. не ожидаемых суждений) несет больше информации.
У меня была прекрасная возможность остановиться на первом абзаце, сорвать аплодисменты и получить подтверждения того, какой я правильный, высококвалифицированный, профессионал и т.д. и т.п. (Что сродни потребности заполучить "эмблемы".) Если же все-таки предположить, что я до какой-то степени разумный человек (поскольку осознавал последствия написанного, о чем сказал в третьем абзаце), то из этого следует, что решение написать остальное (еще раз повторюсь, предвидя при этом весь последующий град критики) было вызвано другими причинами.
В этом отношении Станислав Осташевский действительно прав, остальная часть статьи - это изложение моего взгляда на некоторые моменты в книге, которые вызвали, назовем так, несогласие с определенной точки зрения. Но это несогласие не означает "плохость" содержимого книги вообще (еще раз повторюсь, книга в целом как раз очень полезная и интересная).
О "личной позиции". На самом деле позиция никогда не бывает личной полностью. Позиции формируются под воздействием воспитания, культуры, прочитанных книг, изучения мнений других людей, и, наконец, личного опыта. Они могут быть "конформистскими" (т.е. подтверждающими - "Да, я тоже так думаю. Я ваш, буржуинский", поэтому в какой-то мере консервативными) и "бунтарскими" (которые, в свою очередь, можно разделить на конструктивные - "Нет, потому что хочется это сделать лучше, и, возможно, этого можно достигнуть так" - и деструктивные - "Нет, просто потому что мне этого не хочется или мне это не нравится"). Мне ближе позиция конструктивная "бунтарская". Это не желание громко поорать "must die" (что деструктивно), а попытка осознать текущее состояние, найти его причины и поискать пути перехода к лучшему состоянию. При этом предполагается, что идейный застой, самоуспокоенность, самодовольство и т.д. - это остановка развития, это смерть (то самое die). Человеку свойственно любить жизнь.
Эти вещи пришлось пояснять. Но это присказка...
Познай главную тайну. Мы не знаем, какое сокровище мы ищем: то, которое было зарыто нашими предками, или то, которое будет зарыто нашими потомками.
П.Д. Успенский
Неявные предположения, или упущенная суть
Попытаюсь кратко сформулировать "общесистемные" предпосылки, ибо они определяют точки зрения (взгляды) и составляют суть:
-
Общепринятый "индустриальный" подход к программированию подразумевает концепцию "программной фабрики", т.е. формализованный и стандартизованный процесс с сильной специализацией (разделением труда) и регламентацией. При этом подразумевается аналогичность деятельности программиста труду рабочего на конвейере.
-
Декларируется, что программирование перестало быть искусством и стало ремеслом. И считается возможным описание этой деятельности в виде процедурных инструкций.
-
Считается, что в качестве программистов можно успешно использовать "кодирующих обезьян", которые будут трудиться под зорким оком надсмотрщиков. Это концепция "винтиков" и играния ролей.
-
Институт надсмотрщиков требует соответствующей административной деятельности, регламентации и "объективных критериев надсмотра". Для упрощения администрирования ("управляемости") разделение труда происходит на уровне элементарных операций (ролей) со строгим дифференцированием.
-
От исполнителей требуется лишь добросовестное исполнение ролей. А остальное получится автоматически.
Собственно, в своей статье я отметил проявление в книге этих "общесистемных" моментов и попытался обратить внимание на них, а также на их следствия. Причем эти предпосылки представляются первичными, поскольку положенные в основу любого подхода, будь то MSF или какого-нибудь еще, они приведут к примерно одинаковому результату. Поскольку они в книге подразумеваются, одобряются и признаются по умолчанию, то я посчитал нужным о них заговорить, а для неголословности подтвердить их проявление цитатами.
Соответственно, с моей стороны в качестве конструктивной позиции выдвигаются антитезисы к этим предпосылкам:
-
Механический перенос концепции конвейера на работу программной фирмы не адекватен сути деятельности программиста как главного действующего лица, решающим компонентом которой является мышление, а не механическая работа. При этом утверждается, что концепция разработки, основывающаяся на разделении труда по признаку роли (и аксиоматически постулирующая элементарными определенные операции программирования как деятельности), означает неприемлемые непроизводительные издержки и упускание понимания по мере продвижения "конвейера" по спирали разработки. Предполагается, что более продуктивна концепция, основывающаяся на атомах познания (некоторого взаимосвязанного клубка проблем, требующего решения). Расщепление атома познания проекта - это проектирование его архитектуры. И при этом специализация и разделения труда возможны не на уровне ролей, а на уровне проблемных областей, подсистем, слоев архитектуры (база данных, бизнес-логика, интерфейс пользователя, средства обработки сигналов, взаимодействие с "железом" и т.п.). При этом работник может, решая свою подзадачу, принимать участие в разработке нескольких подсистем или выполнять несколько "ролей" по необходимости.
-
Если сформулировать задачу программирования как получение понимания (расщепление атома познания), то это плохо формализуемый творческий подход, в котором используются ремесленные навыки. При этом более точно ее можно сформулировать как создание красивого, элегантного, компактного и адекватного задаче решения при условии дефицита времени, что есть искусство. Достижение решения подразумевает грамотное владение инструментом, что есть ремесло.
-
Творческий работник самомотивирован, не требует надсмотра и заинтересован в результате своего труда. Для успешной деятельности в области программирования необходимо определенное состояние ума (способность понимать реальность, строить карту реальности и действовать в соответствии с этим пониманием реальности), которое несколько отличается от состояния, предполагаемого в книге (нежелание или неспособность понимать, стремление формально следовать готовым правилам и инструкциям, избегание ответственности, незаинтересованность в результатах своего труда, игры вместо работы).
-
Ненужность надсмотра оборачивается необходимостью лишь координации. Что вполне реализуется при непосредственном взаимодействии заинтересованных сторон и наличии совместно используемой и постоянно совершенствуемой всеми участниками проекта мысленной модели проекта. Единство замысла регулируется менеджером, широковещательно декларирующим свои цели и получающим обратную связь непосредственно от самих работников, а не путем отслеживания их транзакций (важен ведь результат, а не путь к нему). Управляемая самоорганизация. Работники должны понимать стратегические цели (обеспечение единства замысла, концептуальной целостности), а также задачи и требования тех, кому передаются результаты их работы (заранее учитывая эти требования в своей работе, что устраняет лишние итерации).
-
Следует отказаться от играния ролей, поскольку играние ролей означает присутствие поощряемой нечестности, взаимо- и самообмана ("Я делаю вид, что не умею этого делать или не знаю как это делается, поэтому стараюсь достигнуть лишь своих целей, затрудняя при этом работу других, поскольку это не моя задача - облегчать им жизнь"), и наличие скрытых неявных целей участников, с высокой степенью достоверности не совпадающих с целью проекта. Нужна способность встать на позицию другой стороны, т.е. живое взаимодействие. И явная (честная) подчиненность каждого главной цели.
Эти моменты не реализуются, игнорируются, подавляются в концепции "программной фабрики".
Успешный программный проект
Мою точку зрения можно выразить так. Успешный программный проект - это результат правильного понимания проблемной области, адекватного отражения этого понимания в программном продукте, при условии эффективного использования имеющихся в распоряжении ресурсов. И еще нужно вспомнить о треугольнике "требования-время-ресурсы". Поскольку требования и время обычно определяются более жестко, то основным рычагом оказывается "человеческий ресурс" - важнейший ресурс проекта, проявляющий себя во всех трех сформулированных пунктах, поэтому я уделяю ему особое внимание.
Понимание проблемной области
Если задача проработана, хорошо формализуется, а ее решение сводится лишь к элементарной штамповке, доработке, подгонке, подрихтовке почти готовых решений, то действительно стоит по максимуму воспользоваться опытом и результатами труда прошедших этот путь, не изобретать велосипеды и т.д. Идти по пути грамотного применения уже отработанных решений. И какая-то доля (и вполне заметная) задач укладывается в эти рамки.
Но, на мой взгляд, интересными и благодатными представляются не те области, где для простых или типовых задач обычно уже есть готовые (или почти готовые) решения, но те, где программный проект затевается для решения действительно сложных и/или уникальных задач. Либо обнаруживается ниша, для которой еще нет адекватного типового решения. Это ведь более сильное стратегическое решение для программистской фирмы, чем конкуренция со многими похожими как близнецы типовыми решениями.
Сложные задачи плохо формулируются, присутствует множество параметров и критериев, многие проблемы сначала просто не видны или не осознаются. Они не решаются одним кавалерийским наскоком. Если исходить из "стандартных" решений, то бездумное их применение (т.е. принудительное втискивание решений в рамки задач) обычно влечет за собой громоздкость и дополнительную сложность, перемножающуюся со сложностью самой проблемной области. Громоздкое решение содержит больше трудноустранимых ошибок, хуже сопровождается и с большим трудом развивается.
В этом смысле понимание подразумевает получение "правильного" (соответствующего действительности) представления о проблемной области и согласование концепции проекта с этим представлением. При этом думается, что это ведет к элегантным, красивым, компактным, понятным решениям. Механическое применение подходов без понимания - путь против Природы, следствие чего - значительные усилия в попытках ее преодолеть. Понимание дается не сразу, а постепенно, и лишь если к этому стремиться. И, как следствие, первоначальное решение наверняка не будет адекватным, поэтому вполне вероятно, что в процессе работы как результат возросшего понимания может появиться стратегически более "правильное" решение, реализация которого может повлечь изменение концепции. В качестве иллюстрации можно сравнить первоначальное сложное и запутанное описание перемещения планет с помощью эпициклов (в предположении неподвижной Земли и перемещающихся вокруг нее планет и Солнца) и гораздо более простое представление при появлении современной гелиоцентрической модели Солнечной системы. Аналогичные находки наверняка существуют и находятся (если их искать!) в любой предметной области.
В этом смысле мне показалось "неправильным" не утверждение о как можно более быстром начале выпуска версий (поскольку это позволяет быстрее столкнуть проект с действительностью), а сочетание этого утверждения с подходом "от молотка" (а не от проблемной области), усугубляемое чрезмерной заботой о стабильности проекта, особенно на начальных стадиях разработки. Т.е. плох не ранний выпуск версий, плох ранний выпуск без предшествующего осмысления проблемной области, соединенный со стремлением закрепить первоначальные решения (особенно уже "отлитые в коде"). И отказ от умеренно революционных изменений в пользу слабо эволюционных.
Адекватность отражения понимания
Другой важный аспект - это "пересказ". Т.е. в условиях каждой роли присутствует не взаимодействие непосредственно с действительностью, а взаимодействие с несколькими ограниченными "пересказами" действительности. Заказчик излагает свое понимание, менеджер проекта его перерабатывает в меру своего понимания проблемной области и своего понимания возможностей разработчиков, переформулирует, специфицирует, менеджер программы этот пересказ разжевывает разработчикам, разработчики реализуют свое понимание этого пересказа, тестеры изучают продукт как "черный ящик" (не интересуясь тем, что же он делает на самом деле), в конце концов продукт попадает к пользователям, а их мнения опять через целую цепочку пересказов попадают к разработчикам. Это называется "испорченный телефон". Все усугубляется еще и тем, что под флагом повышения эффективности добавляются вспомогательные коммуникационные посредники-клерки. А менеджеры узнают о работе подчиненных лишь понаслышке (в виде искаженного желанием избежать ответственности пересказа).
Не удержусь от иллюстрации.
Еще один аспект - это добывание понимания "избранными". Разделение на "избранных понимателей" и "добросовестных исполнителей" означает, что мыслительные возможности коллектива не используются в полной мере. Т.е. понимание не поощряется, не считается необходимым (ему как бы "запрещается" присутствовать) на всех уровнях и этапах разработки. Альтернативой этому могло бы стать "распределенное" объединение творческий способностей, когда результат объединения способностей превышает их арифметическую сумму, поскольку точки зрения отличаются.
Взаимопонимание, совместное формирование понимания и совместное использование понимания дает "голографический" эффект, в отличие от "фотографического", при котором сбегание ценного сотрудника означает (часто фатальную) потерю необходимого для завершения проекта знания и умения. В "голографическом" случае отламывание кусочка делает картинку чуть более размытой, а не ведет к полному исчезновению фрагмента.
Представляется неправильным отделение разработчиков от реальности проблемной области. Обратная связь должна (при необходимости, но не по обязанности) идти не через посредников, а напрямую. Лучше один раз увидеть, чем сто раз услышать.
Эффективное использование ресурсов
С точки зрения эффективного использования "человеческого ресурса" очень важны, на мой взгляд, следующие аспекты.
Высокая мотивированность в достижении цели - успешного проекта. Что в немалой степени определяется степенью вовлеченности. Когда человек работает просто от и до, опасаясь лишь надсмотрщиков, а его творческие способности при этом задействованы по минимуму, он лишь исполнитель инструкций. И о вовлеченности тут говорить трудно. Если творчество востребовано на рабочем месте, то рабочие места займут творческие, думающие люди. Если нет - "кодирующие обезьяны".
Непосредственное взаимодействие заинтересованных сторон. Это подразумевает устранение ненужных посредников и живое взаимодействие. Например, на пресловутом стыке кодирование-тестирование, это означает разработку для тестирования и проведение тестирования тестером и разработчиком вместе, параллельно. Когда я упоминал такой инструмент как отладчик - то это не отладка, а именно тестирование отлаженного кода с использованием отладчика (хотя не настаиваю именно на таком методе). Программа тестируется на соответствие спецификации, ТЗ. Но не как "черный ящик". Гоняется программа, и одновременно, синхронно с ее работой изучается ее код, и мы убеждаемся, что этот код не только соответствует спецификации, но делает именно то, что нужно, в нем нет побочных эффектов (которые не проявляются в данном тесте, но окажутся разрушительными при других обстоятельствах, и хуже всего, если только после сдачи в эксплуатацию).
[Есть требования заказчика - есть ошибки проекта (неправильное разделение на подсистемы, не соответствующая задаче архитектура, невыполнение требований к системе и т.д.), есть проект системы - есть ошибки спецификации подсистемы, есть спецификация подсистемы (unit) - есть ошибки ее реализации (реализация не соответствует спецификации), наконец, есть ошибки кодирования. Все это ошибки. И тут всплывает тонкость отличия unit testing и usage testing. Ведь ошибки, пропущенные на стадии unit testing, становятся ошибками, отлавливаемыми на стадии usage testing. Плюс ошибки, проявляющиеся только на стадии usage testing.]
Обычно проявления последствий ошибки распространяются. Быстрое (на месте) исправление "простых" ошибок ведет к более продуктивному кодированию и тестированию (обратная связь). В какой-то степени это напоминает парное программирование в eXtreme Programming - более быстрое прокручивание спирали разработки без посредников. Другая вариация более быстрой прокрутки - тестирование после каждого шага разработки (после внесения согласованных изменений в код программы, например при реализации какой-то задачки, пунктика спецификации). Внесение сразу большого количества разнородных изменений приводит к лавинному проявлению ошибок, а их выявление при тестировании оборачивается фактически случайным поиском. Более быстрый цикл разработка-тестирование сокращает, и даже устраняет лавину, делает тестирование более осмысленным. При достаточной степени ответственности (и доверия) разработчик в какой-то части способен выполнять тестирование сам, хотя ему может понадобиться помощник-собеседник. И нет речи о бесполезности тестеров в "традиционном" понимании! Они необходимы как носители другой точки зрения.
Возможно, и представляется желательным, взаимодействие не на уровне административно зафиксированных ролей, а динамичное формирование групп по атомам познания из работников, административно (по ролям) подчиненных разным менеджерам. Их совместная работа на основе взаимодействия, взаимодополнения, а не противостояния или конкуренции, служит (через сотрудничество) достижению общей цели. В случае "конкуренции" мы на самом деле размазываем цель на множество эгоистичных подцелей, которые вовсе не обязательно дадут в результате требуемое движение к единой цели. Минус затраты сил на противодействие чужим эгоистичным подцелям. Роль менеджеров при этом - роль рефери, разрешающих конфликты, которые не удается устранить на "нижнем" уровне самостоятельно.
Все вышесказанное не отменяет того, что
-
Практически всегда можно найти готовые библиотеки или полуготовые решения. Просто к их применению нужно подходить разумно. Иногда "самодельное" решение может оказаться предпочтительнее - оно менее универсально, следовательно проще, и может оказаться лучше приспособленным к архитектуре конкретного проекта.
-
Компонентно-ориентированные системы - ускорение разработки за счет использования чужих компонентов. До той поры, пока в них нет ошибок! Отсутствие исходников обычно ведет к очень сильной зависимости от результатов и качества работы сторонних разработчиков, что в серьезных проектах не всегда допустимо, поскольку делает потребителей компонентов беспомощными. Поэтому самостоятельная разработка компонентов вовсе не прихоть, а обеспечение стратегической надежности решения. Можно потратить кучу времени на борьбу с "фичами", несоответствиями спецификациям, нарваться на ошибки, и потом прождать непонятно сколько без гарантии их исправления. Можно неожиданно столкнуться с несовместимостью в будущем. Садиться на иглу чужих компонентов не так безобидно и безвредно, как кажется. Тут нужна "золотая середина". Там где приемлемо - пользоваться, в остальных случаях - разрабатывать самостоятельно. Не нужно жестко это закреплять!
-
Часто поставлено жесткое ограничение на время исполнения проекта. Это ограничение необходимо учитывать. При продуманном проекте можно выделить критические блоки и за отведенное время максимум усилий приложить к именно к ним, а остальное выполнить как получается (методом "грубой силы"). Причем, скорее всего, это должны быть блоки "нижних" уровней. И они станут фундаментом развития проекта. Внешне благополучное решение на неблагополучной основе с большой вероятностью приведет к сложностям при его развитии. Ведь перекраивание впоследствии фундамента означает, что рюшечки и бантики делались напрасно. А ведь сегодняшняя легкость создания форм и тенденция начинать плясать именно от них провоцирует увлечение именно рюшечками и бантиками и создание иллюзии благополучия.
Изложенные моменты, как правило, недооцениваются и/или просто игнорируются в общепринятых "индустриальных" подходах, что губит сами подходы. Но программирование - это все-таки деятельность ума, а не тела. И человек, и его подходы должны совершенствоваться.