понедельник, февраля 11, 2008

«Пилите пули, Шура, они серебряные…»

или "В цепких лапах библиотек"


Поиски серебряной пули в разработке ПО продолжаются.
Сегодня читаю в «Блоге об IT бизнесе» Виктора Ронина статью «Серебряная пуля в разрезе». Если в прошлых постах автор искал источники повышения производительности разработки софта в мотивации программистов, то теперь он ищет серебряную пулю в open source software. Вот несколько цитат:

«Однако, я напоследок оставил то, что действительно является «серебряной пулей».
Это – библиотеки. Точнее не только библиотеки, но и разнообразные платформы. За последние двадцать лет появилось гигантское количество открытого доступного кода или хотя бы платформ с открытыми интерфейсами.
...
2008 год: RFID ворота на складе, подключенные к машинам на которых установлен Ubuntu с программой, которая выгребает данных с RFID считыватели и по сети закатывает в БД. На сервере крутится программа, которая выгребает данные из БД и через SOAP загружает их в Sales Force Automation...

Дай бог 0.01% [второй] системы написано программистом, а остальные (сайт sales force, библиотеки по работе с RFID, база данных, tcp/ip, xml, soap, графический интерфейс, броузер и т.п.) досталось ему фактически забесплатно. Если бы он пытался написать все сам (даже «срезая углы») у него это заняло бы всю жизнь.

...мы и не заметили серебряной пули, хотя она находится у нас под носом."


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

С одной стороны, прогресс вроде бы есть. Ранее интерфейс типичного бизнес приложения мерцал зелеными буквами и цифрами на мониторе PDP терминала, и представлял собой последовательность меню в стиле «Нажмите 1 для просмотра баланса; Нажмите 2 для пополнения счета; Нажмите Esc…». Сегодня, мы можем мышкой перетаскивать активы из Нью-Йоркского филиала в Нижегородский, на интерфейсе, представляющем собой географическую карту, и смотреть отчеты в своем мобильном телефоне. Т.е. тысячи новых возможностей, интерактивность, мобильность, красочность и т.д.
С другой стороны, прогресса никакого и нет. Как требовалось раньше 10 человеко-лет на разработку среднего бизнес приложения, так и сегодня требуется 10 человеко-лет. Как строился квартальный отчет 45 минут, так он и строится 45 минут.

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

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

  • несоответствие дизайна системы и подключаемой библиотеки. Наверное, все писали обертки, враперы, дополнительные слои и т.д. Яркий пример COM и .Net

  • наличие ошибок в библиотеках и компонентах. И обязательно эта ошибка проявляется именно у вас.

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

  • неэффективное использование библиотек из-за недостаточного знания ее внутреннего устройства. Например, мы можем, по незнанию использовать один экземпляр на все приложение интерфейса «Соединение» из внешней библиотеки, и получим кучу проблем с блокировками, просто потому, что мы не знали о том, что наилучшей стратегией использования этой библиотеки является создание отдельного экземпляра «Соединения» для каждого вызова.


Я думаю, все сталкивались с подобными проблемами. Многие склонны списывать эти проблемы на «кривые ручки» тех, кто создает библиотеки и компоненты, либо на собственные «кривые ручки». Некоторые уверены, что по настоящему «классные» компоненты лишены всех этих недостатков. Есть также мнение, что библиотеки и компоненты с открытым кодом способны решить все эти проблемы. Это очень распространенное мнение, и я с ним не согласен. Сейчас попытаюсь объяснить почему.

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

  • компоненты с открытым кодом имеют меньше ошибок. Даже если это и не так, у нас есть исходный код, и мы можем быстро исправить ошибку.

  • компоненты с открытым кодом открыты (извините за тавтологию), мы можем добавить нужный нам функционал, выкинуть не нужный.


Все это так. Но все это несущественно для решения тех проблем, которые порождаются использованием компонентов. Дело в том, что библиотеки и компоненты не в силах побороть трудности создания программных систем, обусловленные основными свойствами этих систем: сложностью, согласованностью, изменяемостью и незримостью [Брукс, Мифический человеко-месяц, глава 16]. Как бы мы не делили код на библиотеки, он все равно будет скомпилирован, слинкован и выполнен в едином адресном пространстве. Мы ничего не можем сделать с изменчивостью, - меняется внутреннее устройство библиотек, меняется интерфейс – это объективно. Мы не можем отменить согласованности, совместно работать могут только согласованные интерфейсы. Если мы используем не ту версию компонента, мы получаем runtime error. И все мы знаем, что такое “dll hell”. В этом плане, не имеет никакого значения, доступны исходные коды компонент в процессе создания программы, или не доступны.

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

Многим из вас, вероятно, приходилось сталкиваться с системами, как бы застывшими в своем развитии. Вот стоит система, созданная на основе «Турбо-Москаль» версии 5.1. «А чего вы не переведете ее на новую версию, ведь давно вышел «Гипер-Москаль» версии 9.0?». «Да ребятам там в пятом «Турбо-Москале» много чего пришлось переделать руками. Когда попробовали перейти на восьмую версию - даже не скомпилировалось. А ребята то уже уволились. Стали смотреть, что они там наменяли, но ты же знаешь, в пятом «Москале» двадцать мегабайт исходников. Разве там разберешься!». Знакомая картина?

Поэтому при использовании библиотек с открытым кодом используют специальные практики. Свои изменения необходимо, абсолютно необходимо ввести в общий код библиотеки (компонента). А для этого вы должны пойти на сайт сообщества, которое разрабатывает нужный вам компонент, и зарегистрировать в багтрекере (bugtracker) или в бэклоге (backlog) проекта баг (bug) или фичу (feature) или ишью (issue) - по обстоятельствам. Затем вам надо подключиться к репозиторию исходников проекта и взять рабочую копию исходного кода. Потом внести свои изменения в код, протестировать, интегрировать, затем создать патч (patch) и отправить его координатору проекта. Если координатор сочтет, что ваш патч удачен (а если не сочтет?), он включит его в код проекта и он войдет в следующий unstable релиз. Только так вы избавите себя от проблем с неподдерживаемыми изменениями в исходном коде сторонних компонент. Но вы почувствовали, как запахло жареным? Нет? Вы чувствуете, что вы начали заниматься разработкой компонента с открытым кодом, вместо того, чтобы заниматься разработкой своей системы. Оно вам надо!

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

Ну а что же может повысить эффективность создания программного обеспечения? Я думаю то, что помогает преодолевать основные факторы сдерживающие эффективность: сложность, изменчивость, связанность и неосязаемость. Заменить подключаемые библиотеки на связываемые автономные сервисы (слышали про SOA), для борьбы со сложностью. Вместо монолитных компилируемых приложений использовать распределенные, конфигурируемые в runtime для борьбы со связанностью и изменчивостью. Либо более радикальные решения в этом же направлении, связанные с применением динамических языков, позволяющих изменять интерфейсы взаимодействия непосредственно во время исполнения. С неосязаемостью программного обеспечения и связанными с этим проблемами помогают бороться проектирование на основе моделей (Model Driven Design) и разрабатываемые методики статического и динамического анализа кода. Конечно, решение одних проблем породит новые. Но мы научимся за стандартные 10 человеко-лет, создавать такие программы, о которых сейчас даже не мечтаем.
А серебряной пути нет.

Комментариев нет: