Когда меня просят дать интервью, или когда приглашают выступить с рассказом о моей работе по модернизации устаревших систем, то, и так происходит всегда, все хотят говорить о мейнфреймах и о COBOL. Я так думаю, что собеседники ждут от меня хороших баек о тяготах возни со старыми системами. Эти байки интересно послушать программистам, которым о подобных вещах беспокоиться не приходится, так как их профессиональные навыки построены на современных технологиях.
Тут надо учитывать, что и меня, когда я начала работать со старыми системами, тоже привлекало в самых древних программах то, то можно назвать фактором «Хотите верьте, хотите нет» Роберта Рипли. Это — трепет, который охватывает исследователя, отыскивающего и препарирующего всё более старые системы, разбирающегося в забытых языках, о которых большинство программистов никогда не слышало, не говоря уже о работе с этими языками. Меня всегда привлекали низкоуровневые языки и системы, то волшебство, которое превращает изменения напряжения в математические и графические абстракции. Но позже я серьёзно заинтересовалась исследованием приближающегося «апокалипсиса устаревших систем» и изучением того, как замедлить рост технического долга в более новых технологиях. А этот долг с каждым годом растёт всё быстрее.
«Апокалипсис устаревших систем» — это не то, что случится после смерти последнего COBOL-программиста из поколения бебибумеров. Если честно, то этот кризис пришёл и ушёл. Когда люди говорят об опасности использования старых систем, они обычно любят обращать всеобщее внимание на статические сведения, указывающие на то, как велик средний возраст COBOL-программистов. Например, в 2006 году этот возраст составлял 55 лет. Звучало это устрашающе. Получается, что множество очень важных сотрудников близки к пенсии! Кто же будет присматривать за их системами после того, как они уйдут?
Статистика может вводить людей в заблуждение. В одном и том же исследовании сказано, что возраст 52% программистов находится в диапазоне 45-55 лет, а возраст 34% программистов — 35-45 лет. Но, если говорить более конкретно, то через восемь лет, когда предполагалось, что все эти 55-летние программисты уйдут на пенсию, исследование программистов и других специалистов, связанных с COBOL, проведённое Micro Focus, показало, что их средний возраст снова равен 55 годам. А их же исследование 2019 года выяснило, что средний возраст тех, кто связан с COBOL, составляет 50 лет.
На самом деле, средний возраст COBOL-программистов оставался стабильным в течение десятилетий. Когда мой отец работал над проблемами Y2K, ему было около 50 лет. Возраст его коллег был примерно таким же. Каждый раз, когда я вижу, как кто-то придаёт большое значение возрасту членов COBOL-сообщества, я вспоминаю о том, что американская гобоистка Блэр Тиндалл написала в своей книге «Моцарт в джунглях» о слушателях классической музыки:
Ужас по поводу старения зрителей был довольно нелеп: их средний возраст уже какое-то время колебался в районе сорока с лишним лет. Довольно логично повременить до зрелости с посещением симфонических концертов. Когда дети выросли, кредиты выплачены, а свободного времени стало больше, концерты – отличный вариант, соответствующий возможностям и вкусам бывших бебибумеров.
Похожее можно сказать и о COBOL. В отличие от программистов, которые были молоды в 60-е, 70-е и 80-е годы, у современных молодых программистов нет институтских мейнфреймов, с которыми они могли бы поэкспериментировать. Если же в их учебном заведении и есть подобная машина, то она представляет собой «рабочую лошадку» администрации. Такая машина слишком важна для того, чтобы на ней позволили бы делать студенческие проекты. Молодым программистам никто не предлагает изучать COBOL. И если даже они этот язык и выучат, то сотни, а кто-то скажет, что и тысячи COBOL-вакансий не рассчитаны на новичков.
По всей вероятности, причиной того, что средний возраст COBOL-программистов остаётся стабильным, является то, что эти программисты нарабатывают опыт и знания в других областях, а уже после этого, на поздних этапах своей карьеры, переходят на COBOL.
Беспокойство по поводу возраста COBOL-программистов можно объяснить страхом того, что когда исчезнет последний такой программист, некому будет поддерживать программы, написанные на COBOL. А эти программы в некоторых областях весьма важны. Это беспокойство вполне обосновано. Но многих удивит тот факт, что угроза неподдерживаемого устаревшего кода гораздо ближе, чем они думают. И мейнфреймы тут совершенно ни при чём.
64% Java-приложений застряли на Java 8
Если вы в курсе того, что происходит в мире Java, то знаете, что самой свежей версией Java является 14. Окончание поддержки Java 8 планировалось на 2019 год.
В Java 9 появились некоторые структурные изменения, направленные на то, чтобы повысить возможности языка по работе с модулями. Это должно было сделать так, чтобы язык лучше подходил бы для встроенных систем. Переход с Java 8 на Java 9 — это не рядовое обновление. Это — полномасштабная миграция. Кроме прочего, в Java 9 внутренние API JDK недоступны, из языка убрали некоторые инструменты и методы. Сдвиг в сторону модульной структуры требовал изменений в системе управления зависимостями. Другими словами, переход с Java 8 на Java 9, весьма вероятно, может потребовать переписывания больших объёмов кода.
В результате, по данным исследования Snyk, в 2020 более половины продакшн-приложений, написанных на Java, используют Java 8.
Python 2
Конечно, ярчайшим примером обновлений, которые, на самом деле, являются серьёзными миграциями, является переход с Python 2 на Python 3. Как и в случае с Java 8, Python 2 не спешит уходить из-за того, что миграция на Python 3 требует и переписывания собственного кода, и исключения Python 2 из всех зависимостей. Хотя инструменты, вроде six Бенджамина Петерсона, серьёзно упрощают эту задачу, понятие «зависимости» — это нечто большее, чем пакеты и библиотеки. Платформа, на которой выполняется код, это тоже зависимость, а платформы медленно реагируют на изменения. Хотя Python — чрезвычайно популярный инструмент для разработки скриптов, AWS Lambda не поддерживала Python 3 версии 3.6 до 2017 года. А это значит, что с момента выхода Python 3.6 до момента, когда эта версия языка получила поддержку AWS, прошёл год. В тот же год поддержка Python 3 появилась в Salt. Ansible представила поддержку Python 3 ещё через год, то есть — примерно через 10 лет после того, как вышел Python 3.
Сложно оценить объёмы Python 2-кода, которые продолжают использоваться в мире. По оценке JetBrains, объём такого кода в 2019 году составлял всего 10% от общего объёма Python-кода. Учитывая то, то в исследовании JetBrains приняли участие 24 тысячи респондентов из 150 стран, вышеозвученная цифра, вероятно, достаточно точна. Проблема Python 2 может заключаться не в количестве кода, который всё ещё используется. Проблема тут скорее в том — где именно используется такой код. В соответствии с данными JetBrains, области, в которых Python 2 всё ещё впереди Python 3 — это DevOps и автоматизация работы, это тестирование и сетевое программирование. Как оказалось, очень сложно полностью перевести на Python 3 всяческие разновидности Linux. И борьба Python 2 с Python 3 всё ещё не окончена. Каждый питонист, работающий на компьютерах от Apple, знает о том, что на эти компьютеры всё ещё, по умолчанию, устанавливается Python 2.7, что вызвано особенностями внутренних инструментов macOS.
Все ненавидят jQuery, но эта библиотека всё ещё применяется повсюду
C другой стороны ада зависимостей находится библиотека jQuery. Миграция с jQuery сложна не из-за зависимостей. Сложности при миграции вызывает то, что очень много всего зависит от jQuery.
Когда в 2019 году из зависимостей Twitter Bootstrap наконец убрали jQuery, суть происходящего была в том, что код jQuery просто перенесли в Bootstrap. Но даже при таком подходе на этот проект ушло два года.
jQuery — это жертва собственного успеха. Простой синтаксис библиотеки стал настолько популярным, что другие библиотеки и фреймворки, и даже сам JavaScript, начали внедрять его у себя. Кроме того, много устаревших технологий, совместимость с которыми даёт jQuery, наконец то были выведены из эксплуатации (я смотрю в твою сторону, Internet Explorer). Лично я полагаю, что вся эта шумиха вокруг jQuery несколько преувеличена, но я не JavaScript-программист. Похоже, что война с jQuery началась из-за конфликта между этой библиотекой и React — современной популярной JS-библиотекой для разработки интерфейсов.
Но, как и в случае со всеми остальными «священными войнами» в сфере технологий, здравые доводы по поводу выбора одной технологии, а не другой, становятся, чем чаще их повторяют, всё невнятнее. Полагаю, что история с jQuery чем-то похожа на историю с COBOL, когда в статьях об этом языке начинали разговор с того, что он присутствует повсюду, и имели в виду то, что, так как другие технологии могут решать те же задачи, эти технологии (более новые) должны быть лучше COBOL.
Проблема — в глубине деревьев зависимости, а не в возрасте технологий
Многое делает свой вклад в сложность поддержки устаревших систем. И возраст программистов, которые их поддерживают, это не особенно серьёзная проблема. Если говорить честно, то здесь важна потеря части коллективной памяти организации. Когда программист, который очень хорошо знает систему, уходит, часть коллективной памяти уходит вместе с ним. Но эта проблема не относится исключительно к достаточно старым технологиям. Организации теряют коллективную память из-за того, что их сотрудников переманивают другие компании, так же часто, как и из-за ухода сотрудников на пенсию (а возможно, даже чаще).
То, что в мире существует ограниченное количество программистов, разбирающихся в COBOL, это проблема, которую дешевле и проще всего решить, создав системы подготовки новых COBOL-кадров. Например, в этой сфере весьма активна компания IBM, представившая программу Master the Mainframe. То есть получается, что идея, в соответствии с которой COBOL-программисты — это исчерпаемый ресурс, который постепенно истощается, попросту не соответствует действительности.
Опыт подсказывает мне, что когда с COBOL-системой что-то идёт не так, проблемы практически никогда не связаны с самим COBOL. Я сталкивалась с отказами аппаратного обеспечения, видела ошибки систем, не выполняющих COBOL-код, но как-то поддерживающих COBOL-системы или как-то интегрированных с ними. Я видела задержки в добавлении в системы новых возможностей, вызванные тем, что код на COBOL был плохо документирован, что требовало от программистов разбираться с ним и искать пути его правильного изменения. Но мне не доводилось видеть множества происшествий, причиной которых был сам факт того, что в системе используется COBOL. Само по себе использование COBOL — это не проблема. Я не хочу сказать, что у тех, кто использует COBOL, нет причин перейти на что-то другое. Такие причины есть. Я просто не склонна соглашаться с тем, что миллионы строк COBOL не смогут поддерживать механизмы, обеспечивающие функционирование гражданского общества, ещё 60 лет. Эти механизмы, определённо, могут продолжать работать на COBOL.
С другой стороны, Java 8 и Python 2 — это гораздо более серьёзные угрозы. Когда системы не могут избавиться от технологий, которые больше не поддерживаются производителем, то в эти системы не устанавливаются обновления безопасности, в них не появляются новые возможности, не улучшается производительность механизмов, на которых они основаны. Чем дольше система держится за свой собственный технический долг, тем больше всего строится на базе устаревших технологий, и тем сильнее укрепляются позиции таких технологий.
Мы оказываем программистам медвежью услугу, когда ведём себя так, будто разговор о растущей угрозе, связанной со старым кодом, начинается с COBOL и им же заканчивается. Целое поколение программистов тратит своё рабочее время только на то, чтобы усугубить проблему. Они передают реализацию почти всего функционала своих приложений, кроме их совершенно уникальных механизмов, армиям библиотек, плагинов, модулей. За всем этим программистам практически невозможно уследить, не говоря уже о том, чтобы всё это нормально обновлять.
Настоящий всадник «апокалипсиса устаревших систем» — это глубина деревьев зависимостей. В современных стеках разработки ПО одни абстракции строятся на других абстракциях. Если инцидент с left-pad в 2016 году что-то и доказал, так это то, что даже опытные программисты бездумно добавляют в свои проекты зависимости. Ведь в их распоряжении имеется инфраструктура, до крайности упрощающая установку зависимостей. Современные среды разработки — это настоящие кондитерские, в которых лежат горы дешёвых и удобных зависимостей.
Эра фреймворков
Если ресурс Wikipedia можно считать авторитетным источником информации, то оказывается, что в 90-е годы наблюдался пик разработки совершенно новых языков программирования. Это случилось тогда, когда компьютеры стали доступны очень многим. Но тогда создавалось сравнительно мало неких инструментов, абстракций, основанных на чём-то другом. Интернет изменил и разработку языков, и разработку инструментов, сделав реальностью создание более сложных распределённых систем, но и расширив «радиус поражения» общества при инцидентах, связанных с компьютерной безопасностью. Необходимость в более высокой производительности и в более высоком уровне безопасности сделала разработку новых языков достаточно сложной задачей. Прошли те времена, когда продвинутый компьютерщик мог создать прототип языка «на коленке» и ждать того, что этот язык будет применён для решения реальных задач и принесёт реальную пользу. Сейчас существует огромное количество сложных задач, которые, как ожидается, языки программирования должны решать за программистов.
И даже хотя количество профессиональных программистов теперь значительно больше, чем в старые добрые 90-е годы, эти специалисты переключились с разработки новых языков на разработку новых фреймворков.
А фреймворк — это, в сущности, всего лишь поддерживаемая кем-то коллекция зависимостей с общим интерфейсом. Бесспорно то, что фреймворки позволяют быстрее разрабатывать программы, но они ещё и забирают у разработчиков возможность поддержки собственного кода. Развитие инструментов, которые увеличивают скорость разработки, неизбежно приводит к разрастанию деревьев зависимостей среднестатистических проектов.
Возьмём, например, платформу Node.js. Node.js — это интересный фреймворк, который позволяет запускать JavaScript-код в серверной среде. Но с его появлением появился (в виде зависимости) аккуратный и компактный менеджер пакетов NPM. Менеджеры пакетов существовали и до NPM, и NPM, определённо, не самый лучший из таких менеджеров. Но он дал программистам больше удобств, так как его разработчики учли недочёты тех менеджеров зависимостей, которые существовали до NPM. Он, по умолчанию, устанавливает пакеты локально, а не глобально. Его интерфейс командной строки изначально был создан в расчёте на работу с репозиторием пакетов. Его применение облегчает разработку и публикацию пакетов.
В результате средняя глубина дерева зависимостей в NPM составляет 4.39 пакета, в то время как такой же показатель у сравнимого менеджера пакетов (в данном случае — у PyPi) равняется 1.7. А средний размер дерева зависимостей в NPM — 86.55. В PyPi — это 7.33. Страшные цифры. Python-программисты не являются, по своей природе, более ответственными, чем JavaScript-программисты. То, что в JavaScript отсутствует хорошая стандартная библиотека, и то, что это был изначально «игрушечный» язык, созданный за неделю, делает JavaScript благодатной средой для разработки фреймворков, исправляющих его недостатки. Существует огромное количество npm-пакетов, которые реализуют мелкие элементарные действия. В других языках то же самое реализуется встроенными средствами. А NPM просто очень сильно облегчает процесс разработки и публикации пакетов.
Что произойдёт, если ECMA решит исправить некоторые недостатки JavaScript, пойдя тем же путём, которым пошли создатели Java 9 и Python 3 в попытке решить структурные проблемы соответствующих языков? Около 60% пакетов в NPM не обновлялось в течение года или более длительного периода времени. Но, несмотря на то, что эти пакеты плохо поддерживаются, их, всё равно, загружают миллиарды раз.
О том, что в JavaScript не планируется вводить изменения, нарушающие обратную совместимость, говорит и комитет ECMA в документе «One JavaScript»:
Но как избавиться от версионирования? Всегда поддерживать обратную совместимость. Это означает, что мы должны отказаться от некоторых из наших устремлений относительно «чистки» JavaScript. Мы не можем вводить в язык возможности, нарушающие обратную совместимость. Язык, поддерживающий обратную совместимость, это язык, из которого ничего не удаляют, и в котором ничего существующего не меняют. Суть этого принципа можно выразить так: «не ломайте веб».
Обсуждать достоинства вечной обратной совместимости можно без остановки. Главное тут то, что с ростом популярности JS-фреймворков колоссальная проблема зависимостей, которая всегда был присуща JavaScript, становится неизмеримо хуже. Оказывается, что те же инструменты, которые сглаживают множество структурных проблем в таком языке, как JavaScript, ведут к тому, что в новых версиях JavaScript эти проблемы невозможно исправить.
Если говорить о поддержании работоспособных и безопасных технических систем в долгосрочной перспективе, перед нами встают куда более масштабные проблемы, чем возраст COBOL-программистов. И всё же, когда мы говорим об устаревших технологиях, мы об этих проблемах не упоминаем.
Итоги: стратегическое планирование, а не скорость разработки
Зависимости — это неизбежное зло, но их использование не обязательно означает неизбежное попадание проекта в ад устаревших технологий. Нам нужно начинать включать в обсуждения, касающиеся выбора технологий, вопросы о возможностях долгосрочной поддержки проектов. Никто не станет спорить с тем, что JavaScript-фреймворки способствуют разрастанию деревьев зависимостей, но несмотря на то, что NPM был разработан для серверной среды, 80% его возможностей используется во фронтенд-разработке. Мы постоянно отказываемся от старых фронтенд-технологий и заменяем их технологиями более новыми. В сообществе дизайнеров есть одна «народная мудрость», в соответствии с которой сайты подвергаются редизайну примерно каждые 3 года. В результате React-фронтенд с большим деревом зависимостей выглядит менее опасным с точки зрения модернизации устаревшего кода, чем Node.js-приложение с таким же деревом, но находящееся на более глубоком уровне архитектуры проекта.
Другими словами, нам пора начать критически размышлять о том, как долго будет существовать та или иная технология. Нам пора начать задаваться вопросом о том, усложнит ли то, что мы выбираем, модернизацию наших проектов в будущем. Мы больше не можем позволить себе ожидание появления какой-нибудь более совершенной технологии. Нам необходимо принять как данность точку зрения, в соответствии с которой что-то более совершенное, так или иначе, появится.
И наконец, нам стоит перевести обсуждения в другую плоскость и перестать обвинять во всех грехах некие технологии только из-за того, что они устарели, и из-за того, что эти технологии созданы людьми, которые уже далеко не молоды. Большая часть COBOL-кода, существующего в мире, работает совершенно нормально. А проблемы, которые реально существуют, можно найти и в достаточно современных веб-приложениях. Тот факт, что COBOL — старый язык, к делу особо не относится. Этот факт просто отвлекает нас от проблемы растущей экосистемы кода, в которой используются технологии, жизненный цикл которых уже закончился.
Сталкивались ли вы с проблемами, связанными с использованием устаревших технологий?
Автор: ru_vds