Protobuffers Are Wrong (2018) 💬 Длинная дискуссия
Почему Protobuf плохи
Protobuf — это любительская, непродуманная технология, созданная для задачи, которую в действительности имеет только Google. Их главная беда — убогая типовая система: нет композиции, куча произвольных запретов (oneof
нельзя повторять, map
нельзя параметризовать, ключ map
не может быть bytes
или enum
и т.д.). Всё это — следствие донавешивания фич «как получится» вместо проектирования.
Достаточно трёх простых конструкций: обязательные поля (произведение типов), oneof
как отдельная копроизводная и параметрические типы. На них можно выразить optional
, repeated
, map
без всяких хаков.
Ещё protobuf разделяет «скаляры» и «сообщения». Скалярные поля всегда «есть»: даже если ты их не заполнял, они инициализируются нулём/пустой строкой. Отличить «поле не прислали» от «прислали 0» невозможно — источник багов и лишних костылей.
Комментарии (231)
- Критика protobuf сводится к «плохо, но альтернатив ещё хуже»: ни одна другая схема не даёт таких же гарантий обратной совместимости + встроенный линтер.
- Главные боли: нулевые значения неотличимы от «не установлено», нет композиции/алгебраических типов, oneof и repeated ограничены, инструментарий (protoc) громоздок.
- Часть проблем — культурное наследие Google: «не давать пользователю обобщений, зато добавить 100 специальных случаев».
- Реальный совет: использовать protobuf только как быстрый бинарный wire-формат, а внутри приложения держать свою доменную модель и писать явные конвертеры.
- Живые альтернативы обсуждаются (Avro, FlatBuffers, Cap’n Proto, JSON+схема), но у каждой свои компромиссы; серебряной пули пока нет.
That boolean should probably be something else
Булево значение почти всегда маскирует более точный тип.
Проверь, не дата-время ли это: is_confirmed
лучше заменить на nullable confirmed_at
. Вы получите момент подтверждения и сможете анализировать баги по времени.
Если поле описывает роль или статус (is_admin
, failed
), превращайте в enum.
enum UserRole { User, Admin, Guest, SuperAdmin }
enum JobStatus { Queued, Started, Failed, Done }
Enum упрощает добавление новых состояний и защищает от забытых веток.
Проверка прав тоже не должна возвращать bool
.
enum PermissionCheck { Allowed, NotPermitted(reason: String) }
Так код читабельнее и можно вернуть причину отказа.
Когда же использовать bool
? Только как временную переменную-флаг для сложного условия, чтобы не вычислять его дважды или дать имя.
Комментарии (103)
- Основной спор: стоит ли хранить «события» как булевы флаги или как nullable-даты/enum’ы, чтобы не терять данные (время события).
- Противники: это нарушает KISS, раздувает схему и вводит двусмысленность (null = не случилось или ошибка?).
- Сторонники: булевы поля не «помнят» контекст, легко образуют недопустимые комбинации флагов, а дата или enum выразительнее.
- Для параметров функций булевы флаги считаются плохо читаемыми; спасают именованные аргументы, отдельные функции или бит-маски.
- Встраиваемые/индустриальные системы часто считают булевы типы оптимальными по памяти и не применяют совет к себе.