Software essays that shaped me
Автор делится программными эссе, которые повлияли на его мышление за 20 лет карьеры. Среди них — «Тест Джоэла» Джоэла Спольски, который предлагает 12 вопросов для оценки качества работы команды, подчеркивая уважение к разработчикам и их времени. Эссе Алексис Кинг «Парси, а не валидируй» показывает, как использование типов данных повышает безопасность, превращая сырые строки в проверенные структуры. Фред Брукс в «No Silver Bullet» утверждает, что не существует волшебного решения сложности ПО, поскольку её корни — в сущностных, а не случайных проблемах.
Практические выводы включают выбор работодателей, ценящих разработчиков, применение строгих типов для данных и принятие неизбежной сложности инженерии. Эти идеи формируют подход к надёжности, безопасности и человеческому фактору в разработке.
Комментарии (31)
- Обсуждаются влиятельные эссе и статьи о разработке ПО, включая "Parse, don't validate", "Choose Boring Technology" и анализ инцидентов с Therac-25.
- Поднимаются вопросы о качестве тестового кода: спор о допустимости логики в тестах и важности их простоты для избежания ложных срабатываний.
- Обсуждается влияние ИИ на классическую теорию Brooks'а об отсутствии "серебряной пули" и его способность снижать essential complexity.
- Упоминаются ключевые работы, повлиявшие на мышление разработчиков, такие как "Grug Brained Developer", "Code Complete" и "Don't Call Yourself A Programmer".
- Затрагивается проблема цифровой идентификации и доступа к аккаунтам в сравнении с аналоговым миром, где проще доказать свою личность.
Users only care about 20% of your application 🔥 Горячее 💬 Длинная дискуссия
Большинство пользователей применяют лишь около 20% функций приложения, но у каждого — свой уникальный набор. Например, один копирует таблицы из Excel в Word, другой строит сводные таблицы, а третий вообще использует его для учёта расходов. Когда софт обрастает новыми возможностями, они часто мешают тем, кому важна конкретная узкая функциональность, — это создаёт пространство для нишевых продуктов.
Такие сервисы, как Kagi или Figma, успешно занимают эти лакуны, фокусируясь на идеальном решении задач определённой аудитории вместо погони за универсальностью. Стратегия Win — не пытаться угодить всем, а позволить пользователям через расширения и кастомизацию самим формировать свой идеальный 20%, как это делают VS Code или Slack. Ключ в гибкости, а не в нагромождении функций.
Комментарии (173)
- Пользователи обычно используют лишь небольшую часть функционала приложения (около 20%), но у разных пользователей это разные 20%, что требует поддержки всего функционала.
- В корпоративном сегменте отсутствие даже редко используемых, но критичных для конкретного клиента функций (гигиенических фич) может привести к срыву сделки.
- Сложность удаления неиспользуемых функций обусловлена тем, что каждая функция может быть критически важна для узкой группы пользователей, и их удаление приведет к потере этих пользователей.
- Для борьбы с раздуванием функционала предлагаются различные стратегии: фокусировка на конкретной аудитории, модульность, соблюдение философии Unix (один инструмент — одна задача), сбор метрик использования.
- Разработчикам сложно предсказать, как именно будет использоваться продукт, поэтому важно сохранять гибкость и возможность кастомизации, особенно для технически подкованных пользователей.
Contracts for C
Контракты для C
C++ почти утвердила контракты (P2900); я попробовал адаптировать идеи для C.
Пока это черновик, без реализации в компиляторах.
Главное:
- `pre`/`post` — проверяемые условия на входе/выходе функции.
- Не ломают ABI.
- Компонуются и позволяют оптимизатору выкинуть лишние проверки.
- Если условие — константа, оно как `static_assert`: ошибка компиляции.
Базовые примитивы
```c
contract_assert(условие, "текст"); // всегда проверяется, при лжи — abort
contract_assume(условие, "текст"); // компилятор верит, иначе UB
Пример функции
// заголовок
void *my_malloc(size_t s) pre(s) post(r: r);
// реализация
inline void *my_malloc(size_t s) {
contract_assert(s, "size != 0");
defer { contract_assert(defer_return_value, "ok"); };
return malloc(s);
}
defer выполняет post-проверку при любом выходе.
Инлайн даёт компилятору видеть контракт; внешняя декларация сохраняет привычное разделение .h/.c.
Польза: читаемость, статический анализ, оптимизация.
Комментарии (82)
- Участники спорят, что считать «контрактом»: от assert до полноценных спецификаций, проверяемых компилятором.
- Проблема С/С++: нет единого стандарта, каждый реализует по-своему (Digital Mars — с 90-х, Frama-C, C23 unreachable()).
- Часть сообщества считает контракты костылем для слабой типизации: «если типы не выражают инварианты, добавь ещё один слой».
- Критика С23: макрос contract_assume вызывает UB через unreachable(), что делает поведение непредсказуемым и оптимизацию агрессивной.
- Альтернативы: переходить на Ada/SPARK, Rust, OCaml — там контракты либо встроены, либо доказываются статически.
Cognitive load is what matters 🔥 Горячее 💬 Длинная дискуссия
Когнитивная нагрузка — ключевой фактор качества кода.
Репозиторий собрал практические советы, как её уменьшать:
- Следи за «весом» кода: одна функция = одна идея, короткие имена, избегай вложенностей.
- Удаляй дубли: повторы усложняют чтение и тестирование.
- Используй типы и имена как документацию: ясные сигнатуры снижают необходимость комментариев.
- Ограничь контекст: меньше глобальных переменных, чёткие границы модулей.
- Автоматизируй рутину: линтеры, форматтеры и тесты экономят мозговые ресурсы.
Правила применимы к любому языку и масштабу проекта.
Комментарии (488)
- Участники сходятся во мнении, что «простота» кода измеряется не строками, а когнитивной нагрузкой при его изменении (Ousterhout: complexity = cognitive load × frequency of change).
- Часто «сложный» код — результат привычки или неопытности; опытные разработчики умеют сжимать идеи до минимально необходимого набора понятий.
- Помогают: ранние возвраты, выразительные имена переменных, тесты, чёткие границы компонентов и повторяющиеся стандарты проекта.
- Противоречие: «куча if-ов» кажётся простой, но скрывает дублирование; избыточные абстракции тоже усложняют отладку.
- Ключевой совет — писать код как текст для людей, а не для машины, и сознательно тратить время на упрощение, даже если это не приносит немедленной карьерной выгоды.
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 выразительнее.
- Для параметров функций булевы флаги считаются плохо читаемыми; спасают именованные аргументы, отдельные функции или бит-маски.
- Встраиваемые/индустриальные системы часто считают булевы типы оптимальными по памяти и не применяют совет к себе.