О, таблица merchants2? Да, у нас закончились столбцы в merchants, поэтому мы сделали merchants2.Когда я начинал программировать в детстве, я не знал, что людям платят за программирование. Даже когда я закончил среднюю школу, я полагал, что мир «профессиональной разработки» выглядит совсем иначе, чем код, который я писал в свободное время. Когда мне посчастливилось устроиться на свою первую работу в сфере программного обеспечения, я быстро понял, насколько я ошибался и насколько был прав. Моя первая работа была испытанием огнем, и по сей день та кодовая база остается худшей и лучшей кодовой базой, в которой мне довелось работать. Хотя кодовая база навсегда останется запертой в проприетарных стенах той конкретной компании, я надеюсь, что смогу поделиться с вами некоторыми самыми забавными и страшными историями из нее.
База данных живет вечно
В большой legacy системе база данных — это не просто место для хранения данных, это создатель культуры. База данных задает ограничения для работы системы в целом. Это место, где встречается весь код. База данных — это «колодец». В нашем случае этот колодец был достаточно грязным.
Знаете ли вы, что в SQL Server есть ограничение на количество столбцов в таблице? Я тоже не знал. В то время это было 1024, а сегодня, похоже, 4096. Нет нужды говорить, что большинству людей это не нужно знать. А нам было нужно. Причина в том, что в Merchants (наша таблица для хранения информации о клиентах) давно закончились столбцы. Решением стала Merchants2. Таблица, в которой (если я правильно помню) было около 500+ столбцов.
Merchants (и ее лучший друг Merchants2) были жизненной силой системы. Все так или иначе возвращалось к Merchants. Но все было бы не так страшно, если бы Merchants была одной странной таблицей (ладно, двумя). Существовало множество правильно нормализованных таблиц, все они имели внешние ключи к Merchants. Но одна из них всегда будет занимать особое место в моем сердце — SequenceKey.
SequenceKey
SequenceKey |
1251238 |
Но вы можете спросить себя, какое применение может найти таблица с одним столбцом и строкой? Генерация идентификаторов. В то время я слышал историю о том, что когда-то SQL Server не поддерживал автоинкремент идентификаторов. Это был приемлемый, правильный ответ. Мои поиски, чтобы выяснить, правда ли это, не дали результатов. Но на практике он служил гораздо большим, чем это.
SequenceKey был клеем. В каждой хранимой процедуре, создающей новые сущности, вы сначала брали ключ из SequenceKey, увеличивали его. А затем вставляли его в качестве идентификатора для N различных таблиц. Теперь у вас было неявное соединение между всеми этими таблицами сущностей. Если вы видели ID в системе, то с большой вероятностью в смежных таблицах будет строка с точно таким же ID. Честно говоря, очень умно.
Календарь
База данных может жить вечно, но наша система входа в систему была ограничена календарем. Я не имею в виду настоящий календарь. Я имею в виду таблицу базы данных под названием calendar. Что она содержала? Календарь, заполняемый вручную. Когда я спросил нашего шамана (его звали Манч), он сообщил мне, что когда календарь заканчивается, мы не можем войти в систему. Это случилось несколько лет назад. Поэтому они попросили стажера заполнить еще 5 лет, чтобы убедиться, что этого не случится в ближайшее время. Какая система использовала этот календарь? Никто не знал.
Сотрудники
Каждое утро в 7:15 таблица employees дропалась. Все данные полностью исчезали. Затем в таблицу загружался csv из adp. В это время вы не могли войти в систему. Иногда этот процесс заканчивался неудачей. Но и в лучшем случае на этом процесс не заканчивался. Данные нужно было реплицировать в штаб-квартиру. Поэтому по электронной почте отправлялось письмо человеку, который каждый день нажимал кнопку, чтобы скопировать данные.
Замена базы данных
Вы, наверное, думаете: неужели никто не может навести порядок в этой базе данных? Сделать ее более удобной для работы? Что ж, компания опередила вас. Была создана копия базы данных. Данные в этой копии устаревали примерно на 10 минут. Синхронизация шла только в одну сторону. Но эта база данных была нормализована. Насколько нормализована? Чтобы перейти от продавца к номеру телефона, нужно было выполнить 7 соединений.
Цифры продаж
У каждого продавца была своя квота, которую он должен был выполнять каждый месяц и которая называлась «победа». Таблицы, в которых хранились эти данные (не финансовые, а специфические для продаж), были невероятно сложными. Каждый день нужно было выяснять, какие строки были добавлены и обновлены, и синхронизировать их с какой-то системой в штаб-квартире. Это не было проблемой, пока один продавец не догадался, что можно попросить изменить эти записи вручную.
Этот продавец уже получил свою победу и заключил еще одну крупную сделку в том месяце. Они хотели перенести ее на следующий месяц. Стажеру было поручено сделать это. Слухи распространились, и в течение следующих трех лет количество запросов росло в геометрической прогрессии. В какой-то момент у нас было 3 стажера, чьей постоянной работой было написание таких SQL-запросов. Написание приложения для этого было сочтено слишком сложным. Однако перед уходом я постарался помочь стажерам создать собственное приложение. Не знаю, удалось ли это сделать.
Кодовая база
Но что такое база данных без кодовой базы? И какая это была великолепная кодовая база. Когда я присоединился, все было в Team Foundation Server. Если вы не знакомы, это была централизованная система управления версиями, созданная Microsoft. Основная кодовая база, с которой я работал, была наполовину VB, наполовину C#. Она работала в IIS и использовала состояние сессии для всего. Что это означало на практике? Если вы перейдете на страницу по пути A или пути B, вы увидите на этой странице совершенно разные вещи.
Но описывать эту кодовую базу как наполовину VB, наполовину C# означало бы похвалить ее. Каждый существовавший на тот момент фреймворк javascript был загружен в этот репозиторий. Как правило, с некоторыми пользовательскими изменениями, которые, по мнению автора, были необходимы. В первую очередь, это knockout, backbone и marionette. Но, конечно, было немного jquery и плагинов для jquery.
Но эта кодовая база не стояла особняком. Рядом с ней было около дюжины soap-сервисов и несколько собственных приложений для Windows. Наиболее заметным был менеджер поставки. В “Сказании” говорится, что все приложение было создано за выходные одним разработчиком. Давайте назовем его Гилфойл. По общему мнению, Гилфойл был невероятно быстрым программистом. Я никогда не встречался с ним, но чувствовал, что знаю его не только по его коду в репозиториях, но и по всему коду, оставшемуся от него на жестких дисках.
Жесткие диски Гилфойла
Манч (да, его действительно так звали) хранил жесткий диск Гилфойла в RAID-конфигурации на своем десктопе спустя годы после того, как Гилфойл покинул компанию. Почему? Потому что Гилфойл был известен тем, что не вносил свой код в систему контроля версий. И не только этим, но и созданием случайного одноразового приложения для Windows для одного пользователя. Так что не было ничего необычного в том, что пользователь обращался к нам с сообщением об ошибке в приложении, которое существовало только на жестком диске Гилфойла.
Ошибка при доставке
Большая часть моей работы заключалась в выявлении ошибок, которым команды не хотели посвящать время. Одна особенно неприятная ошибка появлялась раз в несколько месяцев. После того, как мы отправляли товары, в очереди на доставку появлялись товары, которые одновременно были и отправлены, и не отправлены. Я использовал ряд обходных путей (SQL-скрипт, Windows-приложение и т.д.), чтобы попытаться вывести нас из состояния сбоя. Мне посоветовали не пытаться найти первопричину. Но я ничего не мог с собой поделать.
Попутно я узнал, как мыслит Гилфойл. Приложение для доставки извлекало всю базу данных, а затем отфильтровывало по дате, сохранив все заказы после даты запуска приложения. Приложение использовало SOAP-сервис, а не выполняло какие-либо сервисные функции. Нет, сервис был простой функцией. Именно клиент создавал все побочные эффекты. В этом клиенте я обнаружил огромную иерархию классов. 120 классов, каждый с различными методами, наследование шло на 10 уровней глубже. Единственная проблема? ВСЕ МЕТОДЫ БЫЛИ ПУСТЫМИ. Здесь я не преувеличиваю. Не то чтобы отчасти пустыми. Полностью пустыми.
Это на какое-то время поставило меня в тупик. В конце концов, я узнал, что это было сделано для создания структуры, над которой он мог бы потом поразмыслить. Это размышление позволило бы ему создать строку с разделителем (структура которой полностью зависела бы от базы данных, но была полностью статичной), которую он отправил бы через сокет. Оказывается, все это в конечном итоге было отправлено в Kewill, службу, которая взаимодействовала с перевозчиками. Почему произошла эта ошибка? Компания Kewill каждый месяц повторно использовала длинные 9-значные номера и кто-то отключил cron-задание, которое удаляло старые заказы.
Красивый беспорядок
Об этой кодовой базе можно рассказать еще много интересного. Например, о команде Super Senior разработчиков, которые в течение 5 лет переписывали все подряд, не поставив ни одного кода. Или Red Hat консультантах, создававшие единую базу данных, которая должна была править всеми. В этой кодовой базе было так много безумных уголков. Так много причин, по которым существовали целые команды, посвятившие себя тому, чтобы переписать с нуля хотя бы одну часть ее функциональности.
Но я думаю, что самая важная история, которую стоит рассказать, связана с улучшением Джастином страницы поиска продавцов. Страница поиска продавцов была точкой входа во все приложение. Каждый сотрудник службы поддержки клиентов, разговаривая по телефону с продавцом, вводил его идентификатор или имя, чтобы найти информацию о нем. В результате вы попадали на огромную страницу со всей информацией о продавце. Страница была информационно насыщенной в лучшем смысле этого слова, полна любой информацией, которая могла вам понадобиться, и любых ссылок, которые вы могли захотеть посетить. Но она была по-собачьи медленной.
Джастин был единственным старшим разработчиком в моей группе. Он был ярким, язвительным и не мог наплевать на бизнес. Он говорил все как есть, не тянул время и всегда мог решить проблемы в одиночку быстрее, чем команда вокруг него. Однажды Джастину надоело слушать о том, как медленно работает страница поиска продавца, и он пошел и исправил это. Каждое поле на этом экране стало собственной конечной точкой. При загрузке начинали получаться данные и по мере загрузки одного запроса выполнялись остальные. Время загрузки страницы уменьшилось с нескольких минут до нескольких секунд.
Два способа разделения
Почему Джастин смог это сделать? Потому что у этой кодовой базы не было генерального плана. Не было общего дизайна, в который должна была вписываться система. Не было ожидаемого формата для API. Не было документированной системы проектирования. Не было архитектурного совета, который бы следил за тем, чтобы все было согласовано. Приложение представляло собой полный и абсолютный беспорядок. Никто не мог его исправить, поэтому никто и не пытался. Что мы делали вместо этого? Мы вгрызались и делали свой собственный маленький мир здравомыслия.
Это монолитное приложение, в силу крайней необходимости, превратилось в микрокосм, состоящий из множества маленьких приложений по краям. Каждый, кому поручалось улучшить какую-то часть приложения, неизбежно бросал распутывать эту паутину и находил какой-нибудь уютный уголок для создания новых вещей. А затем медленно обновлял ссылки, чтобы они указывали на новые вещи, вытесняя старые.
Для вас это может показаться беспорядком. Но работать в нем было удивительно приятно. Исчезли проблемы с дублированием кода. Исчезли проблемы с согласованностью. Исчезли заботы о расширяемости. Код писался для того, чтобы служить своему назначению, затрагивать как можно меньше области вокруг себя и быть легко заменяемым. Наш код был разрозненным, потому что соединять его было бы намного сложнее.
После
За всю мою последующую карьеру мне никогда не доводилось работать в такой удивительно уродливой кодовой базе. Все уродливые кодовые базы, с которыми я сталкивался впоследствии, так и не преодолели свою потребность в согласованности. Возможно, это происходило потому, что кодовая база была покинута «серьезными» разработчиками задолго до этого. Остались лишь разношерстные стажеры и младшие разработчики. А может быть, дело в том, что между этими разработчиками и пользователями не было никакой прослойки: ни переводов, ни сбора требований, ни карт. Просто вы стоите за столом представителя службы поддержки и спрашиваете, как сделать их жизнь лучше.
Я скучаю по этой прямой связи. Быстрой обратной связи. Отсутствие необходимости строить грандиозные планы. По простой проблеме и коду. Возможно, это просто наивная ностальгия. Но в тот момент, когда я лежу на диване и мечтаю вернуться в худшие годы своего детства, когда я сталкиваюсь с очередным «enterprise шаблоном проектирования», в моей голове вспыхивают воспоминания о той прекрасной, ужасной кодовой базе.
Комментарии (11)
mynameco
05.08.2024 20:27+7У нас программист базы данных. Создал структуру. А все что в нее не вписывалось, он делал строкой, разделял разные уровни разными видами разделителей и записывал эту мега строку в специальное поле.
xFFFF
05.08.2024 20:27+1У нас было примерно так же, только разработчик решил во все поля складывать JSON. Через ORM все работало неплохо, но потом возникла необходимость писать SQL запросы к этой базе. Это был просто ад )
GBR-613
05.08.2024 20:27+9Куча ошибок перевода.
Water hole это не водопой (который обычно на реке), а колодец. Оно и естественно: чтобы река была грязной, надо было очень постараться, с колодцем это гораздо легче.
У шамана диск в RAID-конфигурации был, все-таки, не на столе, на в стационарном компьютере (desktop).
Код свой Гилфойл не "не проверял" (checked), а "не вносил в систему контроля версий (checked in).
Это я пока только до половины оригинал прочитал.
redfox0
05.08.2024 20:27Добавлю пару историй:
куча почти одинаковых таблиц (30+) в PostgreSQL. Одна триггерная функция для всех этих таблиц, которая выполняет буквально одну строчку:
:new.is_update := 1;
куча почти одинаковых таблиц (30+) но теперь в Oracle DB. Версия СУБД старая и не поддерживает автоинкремент. Стандартным решением является создание вручную последовательностей для каждой таблицы и триггеров для вставки. Так вот история почти один-в-один с SequenceKey из статьи: для всех таблиц используется одна общая последовательность для генерации уникальных значений первичных ключей.
redfox0
05.08.2024 20:27Ещё история из недавнего: огромная поисковая форма с десятками фильтров и сортировок (внутри ещё более монструозный sql-запрос). Каждый запрос к форме выполняется около 10 секунд. Внутренних клиентов это полностью устраивает.
Приходит жалоба, что форма перестала открываться и отваливается по таймауту (10 минут!). Проверяем, этот же запрос на СУБД вся так же стабильно отрабатывает за 10 секунд, но никак не висит более за 10 минут. Причину так и не нашли. Сделали временную таблицу и кнопку "Заполнить значения". После нажатия кнопки данные из sql-запроса копировались во временную таблицу и дальше работа с фильтрами и сортировками шла только с этой временной таблицей. Любые запросы стали выполняться мгновенно!
Dominux
05.08.2024 20:27Даже когда я закончил среднюю школу, я полагал, что мир «профессиональной разработки» выглядит совсем иначе, чем код, который я писал в свободное время
Значит вам удалось попасть лишь в недалекую галеру с такими же маслятами у руля.
Нормальные кодовые базы может быть не всегда соответствуют всем идеалам, вроде соблюдения всех форм нормализации данных в бд или какой-нибудь Чистой Архитектуры, но уж точно лучше того, что вы описали на порядки
hardtop
Это прекрааааааасно!