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

Здание абстракций и как выглядит его вертикальный разрез

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

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

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

Закон дырявых абстракций и его жесткое правило

Профессиональный реестр осведомлённости об этой цене пополняется уже давно. В 2002 году программист и писатель Джоэл Спольски сформулировал аксиому, получившую название закона дырявых абстракций: всякая нетривиальная абстракция в некоторой степени дырява. Обещание упрощения всегда отчасти нарушается, и детали реализации просачиваются сквозь интерфейс вверх, норовя застать пользующегося абстракцией человека врасплох.

Памятные примеры этого процесса известны каждому практику. Удобный протокол TCP строится над ненадёжным IP и прячет от вас потерю пакетов до тех пор, пока сеть не начинает хромать: тогда все механики доводки вскрываются и требуют вашего понимания. Абстракция "общий доступ к памяти" испаряется в многоядерной жизни, требуя ведомости про кэши и протоколы согласования. Даже благородная картина "объект живёт в базе данных" рассыпается, как только схема вырастает за пределы детского масштаба.

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

Что теряет программист, не знающий устройства диска

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

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

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

Осторожный опыт и лучшие привычки против провала

Хорошая новость: полного возврата к голому железу не требуется, а нужно разумное среднее. Из опыта профессионалов собирается короткий список житейских правил:

  1. Узнайте хотя бы один слой ниже вашего основного: уверенный жилец должен понимать соседей под полом;
  2. Изучите настоящую шкалу стоимостей и научитесь чувствовать разницу в порядках величин, это дешевле многих попыток ловить тормоза в продукте;
  3. Когда код тормозит, читайте не его, а механику под ним, спускаясь по абстракциям от слоя к слою, пока не найдёте комнату с настоящей бедой;
  4. Не бойтесь признать, что абстракция протекла: пребывание в иллюзии дороже, чем изучение реальности;
  5. Перед тем как сетовать на медлительность библиотеки, посмотрите на счётчики диска и сети, возможно, мешает не она.

Этими правилами живут настоящие ремесленники: их удивляет не то, что система сломалась, а то, что она в принципе могла работать с такой ветвистой начинкой.

Случай из быта больницы данных или как умирает доверие к плоским спискам

Чтобы ощутить практику на пальцах, рассмотрим обычный сюжет из жизни среднего проекта. Веб-приложение медленно грузит список заказов. Начинающий разработчик, обожающий свою уютную ORM, по часу потеет над индексом таблицы, а потом узнаёт от старейшины, что программа к каждой записи списка делает отдельный тихий запрос к базе. Перед вами классика полупрозрачной абстракции: код выглядит плоским и чистым, а внизу между строчкой и ответом идёт сетевой обмен стоимостью в микросекунды за штуку, деловито умножаемый на тысячи строк. Достаточно одного взгляда на нижние слои, и медитация над индексами сменяется простым вопросом про число обращений.

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

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

Иерархии памяти как компас хорошего вкуса

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

Спасительной чертой всей этой науки является её удивительная переносимость. Понимание стоимостей не привязано к конкретному году или языку: шкалы слегка двигаются с обновлением техники, но их порядок живуч, и человек, однажды проникшийся ими, с улыбкой разгадывает логику архитектур ещё тридцать лет спустя. Абстракции сменяются, реальность остаётся: она просто переезжает на новые этажи.

Почему всё-таки абстракции это благо

Раздосадованным читателем неправдой было бы сказать: давайте все спустимся обратно и перепишем всё руками. Смотрите скорее так. Абстракция это высокая спина кресла, позволяющая нам хранить в уме суть вместо мелочей, и без такого кресла никакая индустрия не построила бы сегодняшний объём программ. Дискуссия здесь витают всегда вокруг признания ограничений: знаем ли мы, чем расплачиваемся за удобство, и готовы ли спуститься в подвал, когда в трубах стучит.

Обнадёживает и другая черта: подъём абстракций шёл вместе с приращением культуры их использования. Современные программисты пользуются институциональными приёмами избежания ловушек: меры производительности на каждом этаже, независимое размышление "а что если этот слой откажет", развитые инструменты наблюдаемости. Высота этажей не означает потерю сознания, зрелость показывает себя в умении смотреть вниз через перила.

Итоговая нотация про подвал и чердак.

Статья про абстракции могла бы закончиться банальным "всё знать нельзя, но главное знать можно", однако правда много интереснее: у каждого слоя восьмидесятилетней истории техники своя цена, и разработчик, чувствующий эти цены, кодит иначе. Абстракции освобождают руки, но счёт за всё делает реальный мир с реальными задержками и механикой, и самая надёжная квалификация в программировании складывается там, где человек не только пользуется лифтом, но и знает, как подняться по лестнице, если лифт застрянет. Не бойтесь подвалов, господа программисты: именно там лежит ваш настоящий профессиональный тыл.

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

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