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

 

хорошие программы всегда оставляют наследство!

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

Даже ведущим производителям программного обеспечения трудно найти специалистов для работы с огромным кодом на C++, который считался передовым всего десять лет назад. Более того, частые изменения в языках и в платформах Java/C# очень быстро приводят к появлению унаследованных решений, поскольку то, что когда-то считалось новаторским, превращается в неподдерживаемый, устаревший код.

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

Нельзя переписать все программные системы и отказаться от унаследованного кода!

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

Вот почему опытные ИТ-директора и директора по технологиям стремятся использовать новую технологию только в тех областях, где она дает преимущество в бизнесе и позволяет, где возможно, развивать унаследованный код. Трудно возразить, когда производители и консультанты расписывают преимущества, которые сулит смена технологии. Даже те приложения, которые были созданы на основе новой технологии и оправдали все возложенные на них надежды, рано или поздно тоже становятся унаследованными. К примеру, пытались ли вы в последнее время пригласить на работу опытных программистов для работы с унаследованным кодом на C++?

Эволюция программного обеспечения обещает многое!

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

К сожалению, возникает ощущение, что современные студенты изучают только технологии и практические методы создания программного обеспечения, которые существуют всего несколько лет. В силу этого предприятия сталкиваются с невероятными проблемами в тех ситуациях, когда необходимо связать изолированные технокультуры, например COBOL/PLI с Cи; 4GL/VB/Smalltalk/C++ с Java/C#; Java/C# с LAMP и т.д. Каждая технокультура использует свои собственные методы, языки и инструменты.

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

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

Переосмысление профессиональных навыков

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

В своем выступлении на Dahl-Nygaard Prize ECOOP 2006 Ральф Джонсон заявил о необходимости менять приоритеты в образовании, исследовании и профессиональной практике и переносить упор с создания нового программного обеспечения на работу с уже существующим. Навыки, требуемые для успешного совершенствования унаследованных систем, кардинально отличаются от тех, что необходимы при разработке новых. Особое внимание следует уделять преобразованию [1], а не классическому проектированию и разработке. При грамотном программировании особое внимание следует уделять чтению кода, а не его написанию, вопреки сложившейся практике, которую долгое время считали наилучшим способом обучения разработчиков.

Поиск

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

Программист, работающий с крупными унаследованными программными системами, всегда рискует столкнуться с неизвестным. В силу этого крайне важно, чтобы разработка начиналась с глубинного поиска, позволяющего лучше разобраться в кодовой базе. Такой поиск дает возможность объединить информацию, полученную от опытных разработчиков и пользователей, со знаниями, приобретенными в результате анализа кода, а также с соответствующей документацией и тестами. При обратном инжиниринге используют языковые технологии, такие как статический (www.klocwork.com/products/klocworkk7.asp) и динамический контроль и анализ потоков данных для поиска в коде информации, необходимой для выявления скрытых зависимостей, определяющих возможность совершенствования кода.

Преобразование

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

Уже созданы и хороший инструментарий, и методики анализа и рефакторинга (www.refactory.com/tools.html), но мы только начинаем понимать, как программным образом анализировать программы и преобразовывать их. К сожалению, большинство существующих инструментов могут работать только в современных интегрированных средах разработки, в силу чего унаследованные языки остаются без поддержки инструментальных средств. Создано множество средств обратного инжиниринга, но большинство из них предназначены для быстрого и не очень точного переноса программ на другие языки, а не путем надежной эволюции.

Работа с унаследованным кодом

Недавно несколько организаций объявили о готовности использовать «скорые» (agile) методы с целью поддержки. Они быстро обнаружили множество аналогий между agile-концепциями и своими собственными процедурами поддержки (см. таблицу). Процесс agile-разработки включает в себя инструментарий и методы, требуемые для эффективного решения общих для многих организаций задач, связанных с поддержкой программного обеспечения.

Аналогии между agile-концепциями и традиционными операциями

Опыт ветеранов

Каким образом методы agile-разработки может помочь в решении задач, возникающих в процессе анализа сложного программного обеспечения? Практически для любой системы подготовлено множество случаев применения, масса требований и объемная проектная документация. К сожалению, эти документы часто не в состоянии передать суть работы системы специалисту, отвечающему за ее эволюцию. Вторая проблема заключается в том, что существующая документация неактуальна и зачастую имеет пробелы в важных областях. Один из лучших способов добиться понимания сути работы системы — сделать это с помощью людей, которые готовы поделиться знаниями, накопленными во время работы с ней. С недавнего времени этот подход снова начал широко применяться как средство сохранения корпоративных знаний [3].

В течение многих лет молодые специалисты, приходившие в такие компании, как Nortel или IBM, сначала проходили серьезное обучение, во время которого они часто постигали огромные объемы исходного кода; кроме того, своими знаниями с ними делились ведущие архитекторы и разработчики, которые рассказывали новым специалистам о сути систем. Сведения, которыми они делились, были неточными и порой неверными, однако они часто формировали важный контекст, который позволял новым сотрудникам работать над продуктами компании. Благодаря этому неофиты быстро узнавали, где находятся «опасные области», почему система работает не так, как они предполагали, или даже каков способ, используемый для ее документирования, а также многое другое.

И до сих пор лучше всего любознательные ученики могут постичь суть того, как работает система, изучая горы сложного кода, параллельно получая информацию от знающих специалистов. «Главной историей» в экстремальном программировании называют метафору (metaphor), а «небольшими историями» — описания случаев применения. Эти практические описания представляют своего рода «срезы» системы, позволяющие понять ее сущность. Понимание сути работы системы — важная часть процесса поддержки программ, и сделать это можно с помощью agile-методик. Обсуждения всегда лучше документации.

Запросы на изменения

Одна из самых серьезных задач в процессе разработки новой системы — поиск нужного пользователя и создание нужных решений. Это критический фактор успеха и ключ к agile-методам. Однако решить эту задачу весьма непросто, особенно при создании продукта, предназначенного для массового рынка, где требования к продукту часто формулируются на основе отзывов так называемых «суррогатных» пользователей, фокус-групп и участников бета-тестирования.

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

Парная разработка: общий риск и обратная связь

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

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

К примеру, менять критически важные приложения часто приходится непосредственно в офисе заказчика. Опытные специалисты по поддержке предпочитают вносить такие критические изменения вдвоем, поскольку это помогает снизить число ошибок. Точно также анализ архитектуры и кода — это хорошо известные способы избавиться от ошибок за счет «взаимного контроля», который позволяет получить обратную связь, увеличить точность оценок, а также снизить риск и повысить качество.

Обратное тестирование и непрерывная интеграция

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

Постепенный реинжиниринг и разработка

«Самым поразительным было то, что за три года моего участия в проекте месяц, когда я занимался поддержкой, дал мне больше, чем месяц программирования при создании системы. Я думаю о практических решениях как о своего рода сети безопасности, которая позволяет использовать процесс поддержки как возможность учиться, не беспокоясь о возможных неприятностях. Самые ценные моменты в продукте не были обнаружены и даже не могли быть обнаружены до тех пор, пока мы не использовали все многообразие возможностей среды поддержки agile-разработки», — отмечает Вард Каннингэм [4].

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

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

Рефакторинг

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

***

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

Литература

  1. Ralph Johnson, Software Development Is Program Transformation. www.cincomsmalltalk.com/userblogs/ralph/blogView?showComments=true&entry=3319915651
  2. Adele Goldberg, Story Telling: Collaborative Software Engineering, Journal of Object Technology. www.jot.fm/issues/issue_2002_05/column1
  3. Michael Feathers, Working Effectively with Legacy Code. Prentice Hall, 2004.
  4. Ward Cunningham Personal Communication, fit.c2.com/wiki.cgi?WhatsWhat

Дейв Томас (dave@bedarra.com) — основатель и руководитель Bedarra Research Labs и Open Augment Consortium, а также директор Agile Alliance (www.agilealliance.com). В прошлом генеральный директор Object Technology International (www.oti.com), автор платформы Eclipse IDE Platform, IBM VisualAge для Smalltalk, для Java и MicroEdition для встроенных систем.


Translated from «Agile Evolution -Towards The Continuous Improvement of Legacy Software», by Dave Thomas in Journal of Object Technology (JOT), vol. 5, no. 7, September-October 2006, pp. 19-26. Translated into Russian for Otkrytye Systemy under special permission of the original publisher. Copyright JOT, 2006. Original article see at www.jot.fm/issues/issue_2006_09/column2