Як програмне забезпечення роздувається: від телефонії до біткойнів

Кожен програміст там знайомий з роздуттям. Це скрізь: корпоративне програмне забезпечення, яке вимагає від підприємства змінити свої процеси (інакше "чому курси в Cornell мають 4-значні номери?"), Фінансове програмне забезпечення (будь-якого виду, крім HFT), фреймворки javascript (незважаючи на спроби повторного використання лівої клавіатури ), веб-сервер (привіт там джанго-проміжне програмне забезпечення), СУБД, ОС, драйвери USB, браузери, плагіни браузера, програми перегляду PDF, які насправді є системами публікації документів, телефонні програми.

Але команди розробників не мають "дошки завдань scrum" з прикріпленими до них повідомленнями, на яких написано "ДОДАТИ БЛУТ". Іранські сплячі осередки не подають запити на витяг проти проектів з відкритим кодом (коли спецслужби додають бекдори, як це було зроблено з Juniper, вони, здається, роблять це дуже елегантно, змінюючи попередні бэкдори - ніякого роздуття!). Отже, як програмне забезпечення роздувається? Хто за цим стоїть? Який процес винен?

Хто не за спиною

біткойн

Знайшли винуватця.

Наївна точка зору полягає в тому, що роздуття походить від невідомих розробників, які не зовсім знають, що роблять. Ми всі писали згорнутий код, особливо коли ми недостатньо добре розуміли базові примітиви.

Але здуття великого, добре фінансуваного, значного програмного проекту, як правило, не є результатом безглуздості. Я думаю, що це випливає з простого зауваження, що продуктивність програмного забезпечення слідує за розподілом Zipf: велику частину коду, як правило, вносять лише декілька компетентних програмістів, тому безглузді не мають такої кількості можливостей для хаосу.

Тоді Хто є?

З мого досвіду, програмне забезпечення роздувається майже завжди походить від розумних, часто найрозумніших розробників, які технічно найбільш компетентні. Поєднуючи їх здібності з кількома вузько інтерпретованими обмеженнями, доброзичливими зусиллями врятувати день (зокрема, зберегти сьогодні за рахунок завтрашнього дня) та вуаля, ми маємо таку історію.

Казка про розширені структури

Мабуть, найкращий приклад роздуття, який мені передали, коли я працював у великій телекомунікації, включає флагманський телефонний комутатор. Це був монстр-перемикач, здатний керувати великим метрополітеном з мільйонами передплатників. У його основі якраз працював Unix, але ОС була трохи побічною, порівняно з жахливою реалізацією протоколу сигналізації, за чутками, якщо я добре пам’ятаю, приблизно 15 мільйонів рядків коду.

Припустимо, ви працюєте на базі коду такої великої, і ви хочете додати поле до структури. Скажімо, ви хочете додати поле до структури запису даних дзвінків (CDR), щоб вказати, чи викликана сторона входить до короткого списку друзів та сім'ї. Розумним завданням було б перейти до визначення структури та вставити "uint is_friend_fam: 1;" Це додало б додатковий біт до структури, і тоді ви можете робити все, що завгодно, у всьому коді.

Але коли код настільки великий, і коли ваш граничний час простою становить 2 години за 40 років, і якийсь польовий технік, який замінював резервний блок живлення ще в 1987 році, натиснув неправильний вимикач і пропустив половину вашого бюджету простою в районі Чикаго його товстими пальцями ви не можете просто змінити розмір структури. Оскільки це може змінитись там, де розподіляються структури CDR, скільки зайвого місця є навколо об’єкта CDR, і що відбувається з кодом, який помилково пише поза структурою. Це мало б невимовні, непередбачувані наслідки, жоден з них не мав б хороших наслідків.

Тож розробники комутаторів придумали абсолютно геніальну ідею. Я дам вам хвилинку, щоб подумати про те, що ви зробили б у подібній ситуації. Можна припустити, що код відповідає багатошаровій архітектурі, типовій для мережевого коду.

Добре, подивіться, чи відповідає ваше рішення наступному блискучому рішенню:

Шлях до пекла вимощений добрими намірами та розумними фокусами.

Отже, ви переходите до визначення структури CDR. Знайдіть поле, яке здається найменш важливим і найменш використовуваним в цілому, і переконайтесь, що воно взагалі не використовується нижче вашого шару в стеку викликів. Припустимо, CDR містить щось під назвою "uint inap_ain23", яке використовується виключно над вашим шаром. Ви не повинні уявляти, що таке inap_ain23 чи що робить. Потрібно зберегти значення, збережене в inap_ain23, коли потік управління проходить через ваш шар. Отже, під вашим шаром "inap_ain23" більше немає. Ви просто "переробили" це. Зараз це "is_friend_fam". Ви можете псевдонім так: "#define is_friend_fam inap_ain23", щоб полегшити вам роботу. І плюс до того, у вас є додаткові шматочки! Бонус!

Єдине, у чому потрібно переконатися, це те, що ви розміщуєте якийсь код для перехоплення кожного шляху коду від шарів нижче до вашого. Оскільки на цих шляхах вам потрібно залишити все значення, яке ви знайшли в "inap_ain23" до того, як вас викликали. Якщо ви цього не зробите, перемикач обов’язково зламається.

Погано гірше

Отже, це було погано, але стає гірше. Практично неможливо вловити кожен контрольний шлях назад. Хтось проскакує і залишить біт друзів та сім’ї там, де повинна бути контрольна інформація для бази даних, що може спричинити масовий збій. Отже, у інженерів був процес, завданням якого було сканування структур даних у купі реальної системи та перевірка на наявність інваріантів, таких як "inap_ain23 повинен містити номер порту, якщо верхній біт не дорівнює 1" тощо. І коли він виявляв інваріантне порушення, цей процес мавпочки виправляв структури даних якнайкраще, намагаючись уникнути простоїв. Повторюся: вони здогадувались, що поля повинні містити, і просто латали їх.

Від гіршого до поганого

Отже, «переназначення» поля здається досить потворним. Вам потрібно зберегти поле у ​​допоміжній області, можливо, у стеці, можливо десь ще в купі, далеко від дорогоцінної та непорушної структури CDR, і пам’ятати про її відновлення на кожному окремому шляху резервного копіювання. Ви будете штрафувати за продуктивність за два записи за кожен дзвінок, а також за кожен зворотний виклик. Хтось повинен виконати динамічну перевірку через купу, щоб зловити випадки, коли ви проскакуєте.

Але це навряд чи викликає занепокоєння частина історії. Це наступне.

Уявіть, що трапляється, коли цей блискучий інженер балакає в обід про свій крутий трюк із переробкою полів. Уявіть, що відбувається в масштабі.

Це не зайняло багато часу, зробило це?

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

І як ви думаєте, на яких полях наші чудові інженери зупиняться на цій грі? Чому, звичайно, той inap_ani23 впевнено не виглядає таким важливим. Оскільки блискучі люди в кінцевому підсумку перепрофілювали ті самі, "не дуже часто використовувані" поля, виявилося, що найнебезпечніші, найменш важливі поля в критичних структурах даних комутатора насправді були найважливішими, з них доступ до них був найбільш важким.

Зараз цього не відбувається, правда?

Мені нагадали цю телекомунікаційну історію, коли розробники біткойнів розмірковували над тим, чи слід їм ефективно збільшувати розмір біткойн-блоку за допомогою механізму "soft-fork". Я не хочу тут переробляти дебати щодо біткойна про м'які та жорсткі форки, але деякі передумови в порядку.

По суті, деякі розробники біткойнів розглядають фокус, коли вони переробляють транзакції "кожен може витратити" на підтримку чогось, що називається відокремленими свідками. Для старих версій програмного забезпечення для біткойнів, розгорнутих у дикій природі, схоже, хтось кидає готівку буквально у повітря таким чином, щоб кожен міг схопити його та зробити своїм. За винятком новіших версій програмного забезпечення, переконайтеся, що лише люди, яких передбачається, вловлюють його, якщо вони мають правильний тип підпису, відокремлений належним чином від транзакції, щоб її можна було передавати, перевіряти та зберігати або відкидати самостійно. Дивно, але старе застаріле програмне забезпечення, яке важко змінити, бачить, що гроші кидаються в повітря і підбираються кимось, тоді як нове програмне забезпечення весь час знало, що його могло забрати лише призначений одержувач. Це, за кожним показником, дуже розумна ідея, і я надзвичайно поважаю людей, які її придумали. Більшість мого мозку відчуває, що це геніальний фокус, за винятком того, що мої нейро-дежа-вю кричать "це точно такий самий переробляючий фокус, як у телефонному комутаторі". Це просто в різних версіях програмного забезпечення в розподіленій системі, на відміну від різних рівнів в одній ОС [1].

Вартість складності

Тримати розумні трюки поза програмним забезпеченням неможливо і, мабуть, небажано. Але дуже важливо розуміти витрати, тому можна ефективно їх порівняти з вигодами.

Коли системи роздуваються, зусилля, необхідні для їх розуміння та зміни, зростають в геометричній прогресії. Важко зацікавити нових розробників програмним проектом, якщо ми змусимо їх не просто дізнатися, як це працює, а й те, як він там потрапив, тому що процес його еволюції настільки важливий для остаточної форми, в якій він опинився. Вони стали інженерами саме тому, що вони нічого не знали в історії.

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

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

Моя особиста позиція щодо біткойн-фронту полягає в тому, що я в порядку з segwit, але це використовує дотепні розумні хаки Біткойн протягом усього життя - будь-яка додаткова складність призведе до того, що біткойн потрапить в ту ж категорію, що і код комутатора телекомунікацій.

На щастя, складність і роздуття часто є їх власним лікуванням: після певного порогу люди просто відкидають роздуту систему і переходять на чистіші, елегантніші платформи.