Читать книгу «Дефрагментация мозга. Софтостроение изнутри» онлайн полностью📖 — Сергея Тарасова — MyBook.
image

Технологии

 
Никогда не догоняйте
устремившихся вперёд.
Через пять минут, ругаясь,
Побегут они обратно,
И тогда, толпу возглавив,
Вы помчитесь впереди.
 
Г. Остер

Термин «гуглизация» (googlization) не случайно созвучен с другим, с глобализацией. И если глобализация систематично уничтожает закрытые экономики, то «гуглизация» нивелирует энциклопедические знания. Пространство практического применения эрудита сузилось до рамок игры «Что? Где? Когда?». Доступность информации снизила ее значимость, ценность стали представлять не сами сведения из статей энциклопедии, а владение технологиями.

Часть пролетариата умственного труда, способная хранить и воспроизводить технологии, превратилась в «когнитариат». Появился термин «индустриальная археология», касающийся реинжиниринга[16] систем в промышленной эксплуатации, принципы и технологии работы которых неизвестны никому из обслуживающего их персонала.

Технологии в аппаратном обеспечении, «железе», подчинены законам физики, что делает их развитие предсказуемым с достаточной долей достоверности. Зная, какие работы ведутся в лабораториях, можно предугадывать потолок их развития и предполагать сроки готовности к практическому использованию. Например, сейчас в активной фазе находятся прикладные исследования по созданию масштабируемой технологии проектирования и производства устройств, способных заменить нынешние полупроводниковые схемы. Вывод: через десятилетие мир вычислительных устройств изменится.

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

Тем не менее в софтостроении, даже кустарном и далёком от индустриализации, технологии составляют основу. О них мы и поговорим.

Можно ли конструировать программы как аппаратуру?

Для развития аппаратной части определяющими являются физические законы, а основой индустриализации в производстве «железа» стали проектирование и сборка устройств из стандартизованных компонентов.

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

Возможность собирать изделия из «кубиков» стала предметом зависти софтостроителей, вылившейся в итоге в компонентный подход к разработке. Панацеи, разумеется, не получилось, несмотря на серьёзный вклад технологии в повторное использование «кубиков», оказавшихся скорее серыми ящиками с малопонятной начинкой. Но появился целый рынок, где писатели компонентов предлагают свои изделия «компонентокидателям» – это жаргонное слово возникло в среде наиболее массового применения компонентов, где их выбирают на палитре мышкой и, протаскивая, кидают[17] на разрабатываемую экранную форму.

Для аппаратуры используется модель конечного автомата. Во-первых, она обеспечивает полноту тестирования. Во-вторых, компонент работает с заданной тактовой частотой, то есть обеспечивает на выходе сигнал за определённый интервал времени. В-третьих, внешних характеристик (состояний) у микросхемы примерно два в степени количества «ножек», что на порядки меньше, чем у программных «кубиков». В-четвёртых, высокая степень стандартизации даёт возможность заменить компоненты одного производителя на другие, избежав сколько-нибудь значительных модификаций проекта.

В софтостроении использовать конечно-автоматную модель для программного компонента можно при двух основных условиях:

• Программисту не забыли объяснить эту теорию ещё в вузе (см. выше про «Круговорот»).

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

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

Возьмём относительно простой пример: компонент, конвертирующий сумму из одной валюты в другую.

Из элементов стандартизации точно присутствуют коды валют по ISO 4217[18] и, частично, список служб, к которым компонент может обращаться (см., например, каталог служб Financial API). Интерфейс самого компонента не стандартизован, для возможной его замены в будущем без последующей структурной перекройки вашего приложения потребуется обернуть компонент в адаптер (привет, шаблоны!). Это поможет избежать реструктуризации при замене, но не гарантирует работоспособность на том же входном наборе.

Теперь оценим количество состояний, которые необходимо охватить для полноты модульного тестирования, раз уж мы следуем логике разработки «железа». ISO 4217 даёт список из 164 валют. Предположим, что наши входные данные:

• имеют только два знака после запятой;

• значения положительные;

• максимальная величина – 1 миллион;

• дата конвертации всегда текущая;

• мы используем только 10 валют из 164.

Несложный комбинаторный подсчёт показывает, что даже такой сильно урезанный входной набор характеризуется количеством размещений из 10 по 2, помноженным на 100 миллионов входных значений (1 миллион с шагом 0,01):

102 × 100 000 000 = 10 000 000 000.

То есть для обеспечения полноты тестирования нашего входного набора потребуется 10 миллиардов проверок! Сравните, например, с микросхемой дешифратора, преобразующего входное 4-разрядное двоичное значение в сигнал на одном из 16 выходов. Входных наборов будет всего 16, а таблица истинности состоит из 162 = 256 значений.

На практике программист применит допустимую эвристику и будет тестировать, например, только несколько значений (один миллион, ноль, случайная величина из диапазона) для нескольких типовых конвертаций из 100 возможных, дополнительно проверяя допустимую точность значений на входе. При этом формальный показатель покрытия модульными тестами по-прежнему будет 100 %…

Но это ещё не всё. Микросхема работает с заданной тактовой частотой. Если, например, частота равна 1 МГц, то подав на вход набор значений, вы гарантированно через одну микросекунду получите результат на выходе.

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

Может быть, зависнет навечно, если не предусмотрен тайм-аут. А если несколько параллельных запросов?

Поэтому вдобавок к модульному тесту необходимо программировать тест производительности (нагрузочный), который тем не менее не гарантирует время отклика, а только позволяет определить его ожидаемое значение при некоторых условиях.

Таким образом, собрав из кучи микросхем устройство, мы уверены, что оно будет работать:

• согласно таблицам истинности;

• с заданной тактовой частотой.

Собрав же из компонентов программу, мы можем только:

• приблизительно и с некоторой вероятностью оценивать время отклика на выходе;

• в большинстве случаев ограничиться выборочным тестированием, забыв о полноте.

Если вам говорят: «Пришло время собранных из кубиков программ», будьте в курсе ограничений технологии. Очень уж далеки программные компоненты от электронных кубиков.

Безысходное программирование

Любая программа, даже созданная визуально, имеет в своей основе исходный код на каком-либо языке программирования.

Безысходное программирование – это программирование без «исходников». То есть мы пишем свой код, не имея исходных текстов используемой подпрограммы, класса, компонента и т. п.

Когда необходимо обеспечить гарантированную работу приложения, включающего в себя сторонние библиотеки или компоненты, то, не имея доступа к их исходному коду, вы остаётесь один на один с «чёрным ящиком». Даже покрыв их тестами, близкими к параноидальным, вы не сможете понять всю внутреннюю логику работы и предусмотреть адекватную реакцию системы на нестандартные ситуации. Поэтому программирование без исходников в таком сценарии превращается в настоящую безысходность и безнадёгу.

Пока цена ошибки в приложении – потеря нескольких строк введённой пользователем информации, дело может ограничиться долгоживущей записью в базе данных ошибок, закрываемой не её исправлением, а описанием обхода «граблей»[19]. Но ситуация кардинально поменяется, если цена будет исчисляться многими нулями потерь от упущенной сделки в торговой системе, сотнями исков клиентов, получивших неверные счета, или того хуже – аварией на производстве. Ответственность с разработчиков никто не снимал.

В рамках аудита нередко приходилось наблюдать, как правят программный код триггеров и хранимых процедур прямо в базе данных. Ассоциация с этим непотребством у меня тесно связана с утилитой debug, которая в MS DOS позволяла писать машинные команды прямо в память. Или с командой type > program.com для набора машинного кода с консоли в исполняемый файл. Понятное дело, что занимаются такими вещами при разработке программного обеспечения только от безысходности.

Частным, но частым случаем безысходного программирования является софтостроение без использования системы управления исходным кодом (revision control system), позволяющей архивировать и отслеживать все его изменения.

Эволюция аппаратуры и скорость разработки

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

В итоге, спустя 20 с лишним лет, все мы – и разработчики, и пользователи – продолжаем сидеть на «числогрызах» 4-го поколения. Производительность «железа» возросла на порядки, почти упёршись в физические ограничения миниатюризации полупроводников и скорость света. Стоимость тоже на порядки, но снизилась. Увеличилась надёжность, развилась инфраструктура, особенно сетевая. Параллелизация вычислений пошла в массы на плечах многоядерных процессоров.

Прежними остались лишь принципы, заложенные ещё в 1930-х годах и названные, согласно месту, Гарвардской и Принстонской архитектурами ЭВМ. Вчерашний студент теперь пишет не на ассемблере и C, а на Java, будучи уверенным в принципиальной новизне ситуации, не всегда осознавая, что изменилось только количество герц тактовой частоты и байтов запоминающих устройств.

Возросла ли при этом скорость разработки? Вопрос достаточно сложный, даже если сузить периметр до программирования согласно постановке задачи. Тем не менее я рискнул бы утверждать, что не только не возросла, но, наоборот, снизилась.

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

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

Бывшие разработчики PowerBuilder или FoxPro неоднократно выражали мне своё недоумение по поводу того, что для простейших операций, вроде настраиваемого показа табличных наборов данных на клиенте, теперь приходится тратить уйму времени и писать десятки строк кода, а каждая корректировка структур данных должна быть отражена во всех слоях системы. Опуская технические ограничения классических клиент-серверных приложений, нетрудно убедиться, что найти на рынке труда специалиста по PowerBuilder на порядок сложнее, чем VB.NET-программиста. К тому же поставщик среды PowerBuilder после многих перекупок за последние годы в итоге выглядит не слишком жизнеспособным.

С другой стороны, количество работающих в софтостроении женщин росло до начала 1990-х годов, после чего резко пошло на убыль. Рисунок 2 представляет ситуацию в США, но и в СССР и позднее в РФ она вряд ли отличалась.

Рис. 2. Процент женщин, занятых в компьютерной отрасли в 1985–2009 годах, согласно данным американского бюро статистики труда


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

В начале своей трудовой деятельности я наблюдал изображённый на графике пик в среде женщин-программистов. В отделах конструкторско-технологического центра профессия прикладного программиста прекрасно сочеталась с равноправием: мужчин и женщин среди них было примерно поровну. В разгаре был переход с больших ЭВМ на «персоналки» и локальные сети NetWare. Автоматизированные информационные системы переносили на новую платформу, используя специализированные среды разработки приложений баз данных типа FoxPro, Clipper, dBase. И женская половина коллектива успешно справлялась с поставленными перед ними задачами.