К основному содержимому
к выпуску
Конспект выпуска2023Fellow

A Philosophy of Software Design — выпуск 4

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

Code of Architecture · книжный клуб7 минут

Конспект сокращён относительно полного выпуска; примеры и оценки передают опыт и позиции участников.

Основная линия материала
01

Консистентность: интерфейсы и инварианты

Алексей рассказывает о себе: он выстраивает архитектурные процессы в инвестиционном бизнесе и следит за участием команд в общих решениях. Его вердикт книге: она хорошо описывает типичные проблемы и даёт быстрые применимые решения, советы в разделах разноуровневые, но все актуальные, а раздел про agile и качество кода явно написан из наболевшего — такую книгу он готов советовать всем своим разработчикам. Участники в который раз жалеют, что не прочитали её лет пятнадцать назад. Автор, по их словам, сначала собрал материал в курс для студентов, а потом издал книжкой; он один из разработчиков языка Tcl и приводит в пример рефакторинг компилятора Tcl на байт-код, а ещё проект RAMCloud — видимо, распределённое хранилище в оперативной памяти. Двадцать первой главы в первом издании нет, а двадцать одну непрерывную главу клуб разложил по блокам сам — интро про сложность, борьба с ней, документация, теперь обобщения; автор книгу на части не поделил.

Главу про консистентность открывает простое определение: одинаковые задачи должны решаться одинаково, разные — по-разному. Примеры знакомые: именование без синонимов и код-стайл, о котором надо договориться и записать, — благо IDE приносит конфиги форматирования, а где-то форматтер идёт из коробки. Дальше интерфейсы: за одним простым интерфейсом прячется глубокий модуль, и автор приводит файловый и сетевой ввод-вывод, где сложность уходит внутрь реализации. Участники вспоминают принцип подстановки Лисков: если интерфейс хорошо описан, ментальная модель переносится с одной реализации на вторую и третью. Оговорка про распределённые системы: контракт есть, а поведение бывает очень неожиданным, поэтому к интерфейсу хочется описания поведения в разных ситуациях — сам автор делит интерфейс на формальную и неформальную часть, где одних сигнатур мало. На слове «инварианты» один из участников вспоминает кирпич на 1200 страниц по объектно-ориентированному проектированию, который бросал раза три-четыре на двухсотой странице: его автор придумал контрактное программирование.

02

Конвенции, ревью и очевидность кода

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

Глава про очевидность строится вокруг того, что очевидность — свойство читателя, а не писателя. Рабочий метод: если на ревью спрашивают, что здесь написано, ответ «тут всё понятно» — это только ваша точка зрения, и вопрос уже повод добавить комментарий или переписать. Один из участников считает, что автор вывалил в главу мысли разного уровня — от комментариев до событийно-ориентированного стиля, который уже про архитектуру. Сам автор к прежним рецептам, именованию и консистентности, добавляет два: комментарии и выравнивание пробелами; ведущий тут же вспоминает markdown-файлы рядом с кодом, где нет ни одной пустой строки между заголовками и текстом. Александр связывает очевидность с высоко- и низкоконтекстными культурами: если оба знают паттерны, достаточно назвать имя, а без общего контекста в комментариях, структуре кода и документации нужно больше слов. Отсюда идея страницы онбординга: сначала прочитайте такие-то книги и паттерны — это нормально и экономит время.

03

Тренды, производительность и важное

Тренды разбирают по большой схеме, которую один из участников нарисовал сам. Объектно-ориентированному программированию тридцать-сорок лет: наследование интерфейсов уменьшает сложность знания о контрактах, а наследование реализации автор откровенно критикует — оно повторяет логику в разных реализациях и протекает информацией, например работой с форматом файла; вместо него советуют композицию и helper-классы. Функционального программирования в трендах нет, хотя участники считают его большим трендом: асинхронные цепочки событий на нём проще, но по карточкам кандидатов видно, как цепочка из пяти-шести вызовов с глубокой вложенностью долго разворачивается в голове читателя. Agile вырос из затянутых проектов начала девяностых, где к запуску продукт был уже не нужен; автор требует, чтобы инкременты были инкрементами абстракций, и упрекает agile в тактическом программировании, а один из ведущих сравнивает это с градиентным спуском: без большой картинки находишь локальный экстремум и в итоге упираешься не в рефакторинг, а в версию 2.0. Зритель Дмитрий Ульянов просит примеры инкрементов в абстракциях: своих у автора не вспомнили и предложили контракт, который не меняется, пока усложняется реализация.

Паттерны упрощают проектирование и чтение кода, но провоцируют запихивать всё в готовый паттерн — «есть молоток, вокруг одни гвозди». Юнит-тесты автор хвалит: после рефакторинга компилятора Tcl в альфе нашли один баг. TDD он ругает за подгонку решения под тест-кейсы, но и здесь находит плюс: при починке бага тест пишут первым. Про производительность: измерять, а не оптимизировать всё подряд, дизайнить вокруг критического пути, а заросший проверками код переписывать простейшей функцией без corner-кейсов. Финальная глава про важное сводится к тому, что важным не может быть всё: остальное прячется за глубокие модули и дефолты параметров — вспоминают Kotlin и частично применённые функции в Scala. Один из ведущих признаётся, что глава оставила ощущение common sense, хотя такая ментальная модель лет пятнадцать назад ускорила бы его; в пару к ней звучит история про систему, которую делали четыре года под обещанный бизнесом миллион одновременных пользователей, которого на запуске не было. Итог: маленькая книга из эссе, которую стоило прочитать раньше, а список красных флагов спрятан за предметным указателем; дальше — перерыв недели на две и голосование за следующую книгу.

Выводы

Что стоит унести с собой

  1. 01Консистентность держится на трёх механизмах: зафиксированная документация, инфорсинг через линтеры и фитнес-функции на билде без линтерного ада и правило «в Риме поступай как римляне».
  2. 02Очевидность — свойство читателя: вопрос на ревью уже повод переписать код или добавить комментарий, а общий словарь паттернов и страница онбординга поднимают контекст команды.
  3. 03Менять чужую конвенцию стоит только после того, как понял, почему её сделали такой, и продумал план отказа от старой; продавать такие изменения людям — отдельный навык архитектора.
  4. 04Любимые концепты выпуска — глубокие модули, связывающие почти все практики книги, и ползучая сложность; в красных флагах — Java API, требующий лишних объектов ради буферизованного ввода.

Источники