
Простота — это сложно: уроки Дейкстры о том, как писать код, который не стыдно показать
Эдсгер Дейкстра — один из тех людей, чьи мысли о программировании звучат так, будто он видел будущее и не особо им восхищался. Он умер в 2002-м, но если бы увидел современный фронтенд со стеком из двадцати фреймворков, пятидесяти зависимостей и конфигом на триста строк — наверное, просто тихо закрыл бы ноутбук и ушёл пить джин.
Дейкстра был фанатиком простоты. Не в смысле «упрощённости» или «примитивности», а в смысле элегантности. Он считал, что программист должен стремиться к тому, чтобы решение было настолько прозрачным и очевидным, что в нём просто не осталось места для ошибки. «Простота — это предпосылка надёжности», — писал он. И добавлял с характерной для него язвительностью: «Если вы не можете объяснить свою программу за пять минут, возможно, вы сами не понимаете, что она делает».
Он ненавидел goto. Написал знаменитое письмо «Go To Statement Considered Harmful», которое разозлило половину программистов того времени. Не потому что goto — это «плохая команда сама по себе», а потому что она делает код непрозрачным. Ты читаешь программу сверху вниз, и вдруг — прыжок. Куда? Зачем? При каких условиях? Приходится держать в голове весь граф переходов. Это сложно. А сложность — источник багов.
Дейкстра говорил: программирование — это борьба со сложностью. Не создание сложности (хотя многие этим занимаются), а укрощение её. Задачи сами по себе сложные. Мир сложный. Но решение должно быть настолько простым, насколько возможно. Не проще (это уже упрощение, потеря функциональности), а именно настолько простым, насколько задача позволяет.
Он любил повторять фразу, которую приписывал Эйнштейну (хотя авторство спорно): «Всё должно быть сделано настолько просто, насколько возможно, но не проще». Вот в этом «но не проще» — вся соль. Простота — это не отказ от функциональности, это кристаллизация сути. Выпаривание всего лишнего до тех пор, пока не остаётся чистая логика, которая решает задачу и ничего больше.
Я наткнулся на его тексты (EWD — Edsger W. Dijkstra manuscripts, коллекция его заметок, которые он печатал на машинке и рассылал коллегам) несколько лет назад и залип. Потому что он писал о том, что я смутно чувствовал, но не мог сформулировать. Это ощущение, когда смотришь на код и понимаешь: вот тут что-то не так. Работает, тесты проходят, но внутри скребёт. Дейкстра называл это «интеллектуальным дискомфортом». Когда решение не элегантно, ты это чувствуешь. Даже если не можешь сразу объяснить почему.
И вот что интересно: он не просто критиковал плохой код. Он пытался понять, почему мы пишем сложно. Почему программисты так любят городить конструкции, добавлять слои абстракций, плодить зависимости. И пришёл к выводу: потому что простота требует усилий. Гораздо проще накидать решение «в лоб», чем найти элегантное. Простота — это результат работы, думанья, рефакторинга. Сложность получается сама, простоту нужно выстрадать.
Он говорил: «Вопрос не в том, можете ли вы написать программу, которая работает. Вопрос в том, можете ли вы доказать, что она работает правильно». И для этого программа должна быть понятной. Не только компьютеру (ему всё равно, хоть на ассемблере), но и человеку. Потому что только понятную программу можно проверить, отладить, изменить, поддерживать.
У Дейкстры была привычка: он писал программы на бумаге. Не за компьютером — за столом, ручкой. Сначала думал, потом записывал. Потом доказывал корректность — тоже на бумаге, математически. И только после этого, когда был уверен, что программа верна, набирал её в компилятор. Он говорил: «Если ты не можешь доказать корректность программы на бумаге, компьютер тебе не поможет. Он только быстрее выполнит твои ошибки».
Это звучит безумно по современным меркам, когда мы пишем код, запускаем, смотрим что сломалось, правим, снова запускаем. Итеративно, методом тыка. Но в его подходе была железная логика: думать дешевле, чем отлаживать. Час думанья может сэкономить неделю отладки. А если код простой — его легко понять с первого раза.
Дейкстра жил в эпоху, когда программы были маленькими по сегодняшним меркам. Но проблемы, о которых он писал, стали только острее. Сейчас проекты огромные, команды распределённые, кодбейзы — миллионы строк. И если не держать простоту как главный принцип, всё превращается в неуправляемое болото. Легаси, в котором никто не может разобраться, которое все боятся трогать, которое «работает, не трогай».
Он был резким. Его тексты полны сарказма и прямых высказываний. «BASIC калечит мозг программиста», «Использование COBOL парализует ум», «Объектно-ориентированное программирование — это исключительно плохая идея, которая могла родиться только в Калифорнии». Его не любили за язык, но уважали за мысль. Потому что за едкостью стояла глубина и последовательность.
«Простота — это предпосылка надёжности» — Эдсгер Дейкстра
Так вот. Дейкстра был прав. Простота — это не роскошь, не «когда будет время причесать». Это основа. Код должен быть простым не потому, что это красиво (хотя и это тоже), а потому что только простой код можно понять, а значит — контролировать.
И это касается не только кода. Текстов тоже. Решений тоже. Архитектуры тоже. Везде, где есть сложность, нужно бороться за простоту.
Давайте разберёмся, кто такой был этот человек, что он имел в виду, когда говорил о простоте, и почему языки программирования, которые помогают быть простыми, — это благословение.
Кто такой Дейкстра: коротко о главном
Чтобы понимать, почему к его словам стоит прислушиваться, нужно знать, кто это был.
Эдсгер Вибе Дейкстра (1930–2002) — нидерландский учёный, один из основателей computer science как научной дисциплины. Не просто программист, а теоретик, который закладывал фундамент того, как мы думаем о программировании.
Алгоритм Дейкстры (1956). Поиск кратчайшего пути в графе. Если вы хоть раз пользовались навигатором — скажите спасибо Дейкстре за его элегантное и простое решение сложной задачи. Именно таким он видел идеальный код.
Структурное программирование (1960-е). Боролся с goto, продвигал идею, что программа должна строиться из простых блоков: последовательность, ветвление, цикл. Сейчас это кажется очевидным, но тогда было революцией. Он доказал, что этих трёх конструкций достаточно для любой программы, и они делают код понятным.
Семафоры и мьютексы (1965). Решал проблему синхронизации процессов. Придумал концепцию семафоров — элегантный механизм, который до сих пор используется в многопоточном программировании.
THE multiprogramming system (1968). Операционная система, которую он спроектировал как доказательство концепции: можно создать сложную систему, которая будет доказуемо корректной. Использовал иерархическую структуру, где каждый уровень можно понять и проверить независимо. Это была демонстрация того, что простота и строгость работают даже в больших проектах.
Тьюринговская премия (1972). «Нобелевская премия» в computer science. Получил за вклад в развитие языков программирования и операционных систем. В речи при вручении говорил — конечно же — о простоте и дисциплине мышления.
EWD manuscripts (1959–2002). Больше тысячи заметок, которые он писал всю жизнь. Печатал на машинке, нумеровал (EWD1, EWD2… EWD1318), рассылал коллегам. Там всё: технические идеи, философские размышления, критика индустрии. Многие до сих пор актуальны. Читаются как эссе умного и язвительного человека, который видит проблему глубже остальных.
Преподавание. Работал в университетах Нидерландов и США. Считал, что учить программированию — это учить думать. Не синтаксису, не фреймворкам, а дисциплине мышления. Говорил студентам: «Не спрашивайте, работает ли программа. Спрашивайте, почему она должна работать».
Язвительность и принципиальность. Он не боялся говорить неудобные вещи. Критиковал языки программирования (BASIC, COBOL, позже Java), критиковал индустрию за гонку за фичами вместо надёжности, критиковал коллег за неряшливость мышления. Его не всегда любили, но всегда уважали. Потому что он был последователен и глубок.
Главная идея всей его жизни: программирование — это интеллектуальная деятельность, требующая дисциплины и строгости. Код — это не «что-то, что работает», а формальное доказательство решения задачи. И чтобы доказательство было возможным, код должен быть простым.
Вот такой человек. Когда он говорил о простоте — это не были пустые слова. Это был выстраданный опыт создания сложных систем и понимание, что единственный способ справиться со сложностью — это радикальная простота на каждом уровне.
Что Дейкстра понимал под простотой
Слово «простота» превратилось в штап. Его используют все для обозначения буквально всего. Но у Дейкстры была чёткая концепция, что это такое. Разберём её на составляющие.
Простота ≠ примитивность
Это первое, что нужно понять. Простое решение может быть глубоким, мощным, универсальным. Но оно прозрачно. Ты смотришь — и понимаешь, как оно работает. Не нужно держать в голове десяток контекстов.
Пример: алгоритм Дейкстры поиска кратчайшего пути. Идея проста и элегантна — от стартовой точки расходимся волной, выбирая каждый раз ближайшую непосещённую вершину. Но решает сложную задачу, которая используется везде: от GPS до маршрутизации пакетов в интернете.
Простота — это не отказ от сложных задач. Это способ их решать так, чтобы решение было понятным.
Простота = отсутствие лишнего
Каждая строка кода должна быть там по причине. Если можешь убрать — убери. Если можешь заменить три функции одной — замени. Не потому что «меньше строк = лучше», а потому что меньше сущностей = меньше когнитивной нагрузки.
Закон Оккама в программировании: не умножай сущности без необходимости.
Дейкстра писал: «Совершенство достигается не тогда, когда нечего добавить, а когда нечего убрать» (на самом деле это Антуан де Сент-Экзюпери, но Дейкстра часто цитировал и считал эту мысль фундаментальной).
«Совершенство — когда нечего убрать»
Простота = локальность
Чтобы понять кусок кода, не нужно держать в голове всю программу. Функция делает одно, зависит от минимума внешнего контекста. Можно вырвать, прочитать, понять изолированно.
Дейкстра писал: «Наш интеллект ограничен. Мы не можем держать в голове всю программу сразу. Значит, программа должна быть организована так, чтобы можно было понимать её по кускам».
Это означает: каждая функция, каждый модуль должны быть самодостаточными в понимании. Да, они взаимодействуют с другими частями системы, но это взаимодействие должно быть явным и минимальным. Не нужно изучать половину кодбейза, чтобы понять, что делает одна функция.
Простота = предсказуемость
Код делает то, что ожидаешь. Нет «магии», неявных побочных эффектов, скрытых зависимостей. Читаешь — и предсказываешь поведение. Запускаешь — и оно совпадает с ожиданием.
Функция calculateTotalPrice(items) должна считать цену. Не отправлять метрики в аналитику. Не логировать в базу. Не модифицировать глобальное состояние. Просто считать цену и возвращать результат. Делать то, что написано на этикетке.
Когда код предсказуем, его легко использовать, легко комбинировать, легко тестировать. Когда непредсказуем — каждый вызов функции — рулетка.
Простота = доказуемость
Это был конёк Дейкстры. Он считал: программа должна быть настолько ясной, что ты можешь доказать её корректность. Не тестами (тесты показывают наличие багов, но не их отсутствие — его знаменитая фраза), а логически.
Конечно, формально доказывать каждую программу — нереально в коммерческой разработке. Но идея в том: если ты не можешь объяснить (себе или коллеге), почему твой код корректен, — возможно, он некорректен.
Можешь ли ты сказать: «Вот эта функция работает правильно, потому что…» и дальше привести аргументы? Если да — хорошо. Если «ну, я запустил и оно сработало» — плохо. Это не понимание, это удача.
Дейкстра учил думать о программе как о математическом доказательстве. Каждый шаг должен быть обоснован. Каждое условие — проверено. Каждый цикл должен иметь инвариант (утверждение, которое истинно на каждой итерации) и условие завершения.
Звучит академически? Возможно. Но это делает код надёжным. Когда понимаешь, почему программа работает, она продолжает работать при изменениях. Когда не понимаешь — любое изменение может всё сломать.
Почему сложность — враг
Дейкстра не просто любил простоту. Он ненавидел сложность. Считал её главным врагом программиста. Разберём, почему.
Сложность — источник багов
Чем сложнее программа, тем больше мест, где может сломаться. Баг прячется в неочевидных взаимодействиях, в граничных случаях, которые не держал в голове, в зависимостях, о которых забыл.
Дейкстра говорил: «Тестирование может показать наличие ошибок, но не их отсутствие». Можешь прогнать тысячу тестов — и все пройдут. Но это не гарантирует, что в тысяча первом случае программа не упадёт.
Единственный способ быть уверенным — сделать программу настолько простой, что в ней очевидно нет ошибок. Не «нет очевидных ошибок» (это легко — спрятать баг поглубже), а именно очевидно нет. Когда смотришь и понимаешь: здесь просто негде ошибиться.

Сложность делает код непонятным
Через месяц после написания ты сам не вспомнишь, что тут происходит. Приходит новый человек в команду — не может разобраться. Нужно что-то поправить — боишься сломать.
Это не гипотетическая проблема. Это реальность большинства проектов. Легаси-код — это не старый код. Это непонятный код. Неважно, сколько ему лет. Если он сложный, запутанный, с неявными зависимостями — это легаси с момента написания.
А непонятный код невозможно поддерживать. Каждое изменение — риск. Каждая новая фича — костыль поверх костылей. В какой-то момент проще выбросить и написать заново.
Сложность растёт нелинейно
Добавил одну фичу — усложнил на 10%. Добавил ещё — ещё на 15%. Потом на 25%. Сложность растёт быстрее, чем функциональность. В какой-то момент проект становится неподдерживаемым.
Дейкстра предупреждал: «Цена сложности — экспоненциальная». Сначала кажется, что всё под контролем. Проект растёт, фичи добавляются. А потом вдруг — стена. Любое изменение занимает недели. Баги множатся. Никто не понимает, как всё работает в целом.
И это необратимо, если не бороться с усложнением на каждом шаге. Нельзя накопить сложность, а потом «разгрести». Её можно только не допускать.
Сложность убивает радость
Работать со сложным кодом — мучение. Нет удовольствия. Только раздражение и усталость. Программисты выгорают не от сложных задач, а от сложного кода.
Сложная задача — это вызов. Интересно решить, найти элегантное решение, почувствовать себя умным. Это даёт энергию.
Сложный код — это болото. Лезешь в него, вязнешь, не видишь конца. Это высасывает энергию. После дня работы со сложным кодом чувствуешь себя выжатым. Не потому что много сделал, а потому что потратил много ментальных сил на то, чтобы просто разобраться, что происходит.
Дейкстра это понимал. Он говорил: «Программирование должно быть интеллектуальным удовольствием, а не мучением». А удовольствие возможно только когда работаешь с понятным, элегантным кодом.
История с goto: письмо, которое изменило программирование
1968 год. Дейкстра пишет короткую заметку в Communications of the ACM. Название: «Go To Statement Considered Harmful». На самом деле название придумал редактор — Дейкстра предлагал более мягкое «A Case Against the Goto Statement», но редактор решил добавить драмы.
Это письмо изменило программирование. Серьёзно.

Суть письма
Goto делает программы непонятными. Когда читаешь код с goto, приходится держать в голове весь граф возможных переходов. Невозможно понять кусок кода изолированно — нужно знать, откуда могут прыгнуть и куда могут прыгнуть дальше.
Это нарушает локальность понимания — главный принцип, который Дейкстра считал критичным. Программа должна читаться последовательно, как текст. А goto превращает её в лабиринт.
Представь: читаешь код, строка за строкой. Вдруг — goto label\_17. Где label\_17? Пролистал ниже — нашёл. Что там? А, ещё один goto, теперь на label\_5. Где label\_5? Выше, в начале функции. И так далее.
Чтобы понять последовательность выполнения, нужно мысленно построить граф всех переходов. А если переходов десятки — это невозможно. Голова взрывается. И в этой путанице легко сделать ошибку: забыть обработать какой-то случай, или, наоборот, обработать дважды.
Реакция индустрии
Письмо произвело эффект разорвавшейся бомбы.
Половина программистов согласилась: «Да, чёрт возьми, он прав! Я всю жизнь мучаюсь с чужими goto!»
Другая половина возмутилась: «Как же без goto? Это же основной инструмент! Он нам нужен! Дейкстра хочет лишить нас свободы программирования!»
Были даже письма в журналы с заголовками вроде «Goto Statement Considered Useful». Защитники goto писали, что в некоторых случаях без него не обойтись, что структурное программирование ограничивает выразительность, что опытный программист может использовать goto аккуратно.
Дейкстра отвечал: «Да, опытный программист может использовать goto аккуратно. Но зачем давать инструмент, который так легко использовать неправильно? Зачем полагаться на аккуратность, если можно сделать язык, в котором невозможно написать неаккуратно?»
Что изменилось
В течение десяти лет структурное программирование стало стандартом. Новые языки (Pascal, C, позже все остальные) строились вокруг структурных конструкций: if/else, while, for. Goto либо убрали совсем, либо сделали «плохой практикой».
Сейчас в большинстве языков goto есть технически (в C, C++, Go), но используется крайне редко, в специфичных случаях: выход из вложенных циклов, обработка ошибок в C (где нет исключений), cleanup-код. И даже тогда программисты оправдываются: «Знаю, что goto — зло, но тут без него действительно сложнее».
В современных языках (Python, JavaScript, Java, Ruby, Swift) goto либо нет вообще, либо есть ограниченные аналоги (break, continue с метками).
Урок
Иногда достаточно одного человека, чётко сформулировавшего проблему, чтобы изменить целую индустрию. Дейкстра не придумал структурное программирование (оно зрело в воздухе, многие думали в том же направлении). Но он его сформулировал и дал чёткие аргументы.
И урок не только в том, что goto — плохо. Урок в том, что инструменты формируют мышление. Если язык даёт тебе goto — ты будешь его использовать, даже когда не нужно. Если язык даёт только структурные конструкции — ты будешь думать структурно.
Хороший язык программирования — это не тот, который позволяет делать что угодно. Это тот, который помогает делать правильно и мешает делать неправильно.
Простота и компактность языков программирования
Дейкстра считал: язык программирования формирует мышление. Если язык поощряет сложность — будешь писать сложно. Если помогает быть простым — будет проще.
Он изучал языки и думал: что делает язык хорошим? И пришёл к выводу: компактность базовых концепций. Не количество ключевых слов, а количество фундаментальных идей, которые нужно держать в голове.
Хороший язык — это язык, где мало базовых концепций, но они мощные и комбинируются. Плохой язык — где много разрозненных фич, которые не складываются в систему.
Lisp: элегантность через минимализм
Lisp — один из старейших языков (1958), и Дейкстра его уважал.
Вся программа на Lisp — это список. Данные — списки. Код — списки. Одна основная структура данных для всего. Гомоиконность: код = данные, данные = код. Программа может обрабатывать саму себя.
Минимум синтаксиса. Скобки и всё. Нет отдельного синтаксиса для циклов, условий, функций. Всё — вызовы функций, всё — списки.
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
Читается: определи функцию factorial, принимающую n. Если n <= 1, вернуть 1. Иначе вернуть n \* factorial(n - 1).
Всё ясно. Нет лишнего синтаксического шума. Только логика.
Из этого минимума строится всё: макросы (код, генерирующий код), метапрограммирование, Domain-Specific Languages. Мощь через простоту.
Дейкстра писал: «Язык, в котором программа — это данные, которые можно обрабатывать как программу, — это красиво».
Pascal: язык для обучения правильному мышлению
Никлаус Вирт, создатель Pascal, был учеником и последователем Дейкстры. Pascal создавался в 1970 году как язык для обучения структурному программированию. И в этом его гениальность.
Чистый, ясный синтаксис. Читается почти как псевдокод. Строгая типизация — компилятор ловит ошибки рано, не даёт написать очевидную глупость. Структурное программирование «из коробки». Goto есть, но не поощряется, и без него прекрасно обходишься.
function Factorial(n: integer): integer;
begin
if n <= 1 then
Factorial := 1
else
Factorial := n * Factorial(n - 1);
end;
Всё понятно даже тому, кто Pascal не знает. function — функция. if...then...else — условие. Никаких загадок.
Минимум конструкций, но достаточно для всего. Программы на Pascal понятны, их легко читать, легко учить. Дейкстра одобрял: «Это язык, который учит думать правильно». Если научился писать на Pascal — научился структурировать мысли.
Проблема Pascal: он был слишком прост для промышленного использования. Не хватало средств для работы с памятью, модульности, низкоуровневых операций. Поэтому в индустрии победил C — более гибкий, но и более опасный.
Но Pascal выполнил свою миссию: вырастил поколение программистов, которые думали структурно. И идеи Pascal живут в современных языках.
Oberon: радикальный минимализм Вирта
После Pascal Вирт не остановился. Он думал: а можно ли сделать ещё проще? Убрать всё, что не абсолютно необходимо? И в 1986 году создал Oberon.
Радикальный минимализм. Меньше ключевых слов, чем в Pascal. Убрали for — остался только while. Одна конструкция цикла вместо нескольких. Убрали case — можно обойтись if. Оставили только то, без чего невозможно.
При этом язык мощный: система типов, модули, указатели, расширение типов (прототип ООП). Но каждая фича добавлялась только если была абсолютно необходима.
Идея: один способ делать вещи. Не три варианта цикла — один. Не пять способов объявить переменную — один. Меньше выборов = меньше когнитивной нагрузки.
На Oberon написали операционную систему (Oberon OS). Вся ОС — 200 тысяч строк кода. Для сравнения: современная Linux — миллионы строк. И система работала, была понятной, поддерживаемой. Один человек мог понять её целиком.
Вирт говорил: «Совершенство достигается не тогда, когда нечего добавить, а когда нечего убрать». Oberon был манифестом этой философии.
Дейкстра и Вирт были единомышленниками. Оба верили: язык должен помогать программисту избегать ошибок, а не давать «свободу» стрелять себе в ногу.
Go: современный наследник минимализма
Перенесёмся в 2009 год. Google создаёт новый язык. Роберт Гризмер, Роб Пайк, Кен Томпсон — ветераны индустрии, видевшие, во что превратились современные языки. И решили сделать по-другому.
Go создавался с явной целью: простота для больших команд. Когда код пишут сотни людей, когда проект живёт годами, сложность убивает. Нужен язык, который не даёт усложнять.
Намеренно минималистичный. Авторы отказывались добавлять фичи. Нет наследования — используй композицию. Нет дженериков (долгие годы их не было, добавили только в 2022-м после долгих споров). Нет перегрузки операторов. Нет исключений — только явная обработка ошибок.
Мало ключевых слов — 25 штук. Для сравнения: в C++ больше 80.
Быстрая компиляция. Нет сложного препроцессора, нет перегруженных операторов, нет шаблонов, которые раздувают время компиляции. Проект на миллионы строк компилируется за секунды.
Встроенное форматирование — gofmt. Все программы на Go выглядят одинаково. Нет споров о стиле, где ставить скобки, сколько пробелов. Запустил gofmt — код отформатирован. Всё.
Явные ошибки вместо исключений:
file, err := os.Open("data.txt")
if err != nil {
return err
}
defer file.Close()
Видишь сразу: здесь может быть ошибка, она обрабатывается. Не спрятана в try/catch где-то в другом месте кода.
Критика Go: «слишком просто, примитивно, скучно, нет выразительности». Защитники отвечают: «зато код читается легко, новый человек входит в проект за день, и это важнее крутых фич».
Дейкстра бы оценил. Язык, который намеренно ограничивает выразительность ради понятности — это его философия. Лучше иметь один способ сделать что-то правильно, чем десять способов, девять из которых ведут к ошибкам.
Forth: композиция из простейших элементов
Forth — нишевый язык, но по части минимализма — легенда.
Стековый язык. Вся работа через стек данных. Кладёшь значения на стек, вызываешь операции, они берут данные со стека и кладут результат обратно.
: factorial ( n -- n! )
dup 2 < if drop 1 exit then
dup 1- recurse * ;
Выглядит загадочно, но логика проста: если n < 2, вернуть 1. Иначе вернуть n \* factorial(n-1).
Минимум встроенных слов (операций). Всё остальное определяешь сам. Компилятор Forth — пара килобайт. Умещается в микроконтроллер. Используется в embedded системах, где каждый байт на счету.
Красота Forth: композиция из простого. Есть несколько базовых операций со стеком. Из них строишь более сложные слова. Из них — ещё более сложные. Как Lego: минимальный набор деталей, но можно построить что угодно.
Цена: синтаксис непривычный (обратная польская нотация), порог входа высокий. Не прижился в мейнстриме.
Дейкстра говорил о Forth с уважением: «Язык, который заставляет думать по-другому, — ценен». Forth учит декомпозиции: разбивать сложное на простейшие операции.
Python: читаемость как принцип
Python не такой минималистичный, как Go или Oberon. Но в нём есть важная идея из философии Дейкстры: читаемость важнее краткости.
«Один очевидный способ сделать что-то» — один из принципов Zen of Python (хотя на практике не всегда соблюдается).
Отступы вместо скобок. Код визуально структурирован, нельзя написать неряшливо. Минимум синтаксического шума. Не нужно ставить точки с запятой, объявлять типы (хотя теперь можно через type hints).
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
Читается как обычный текст. Даже непрограммист поймёт, что тут происходит.
Проблема: со временем в Python добавили кучу фич. Декораторы, метаклассы, async/await, type hints, walrus operator (:=). Язык стал мощнее, но и сложнее. Появилось несколько способов делать одно и то же.
Дейкстра бы критиковал это разрастание: «Начинали правильно, но не удержались. Не смогли сказать “нет” новым фичам».
Контрпримеры: языки, которые поощряют сложность
Чтобы понять ценность простых языков, посмотрим на противоположность.
C++: язык-монстр
C++ начинался в 1983 году как «C с классами». Простое расширение. Потом добавили шаблоны (templates). Потом множественное наследование. Потом исключения. Потом RAII. Потом move-семантику. Потом лямбды. Потом variadic templates. Потом concepts. Потом coroutines.
Сейчас C++ — это несколько языков в одном. Можно писать как на C (процедурно). Можно объектно-ориентированно. Можно функционально. Можно в стиле метапрограммирования через шаблоны. Можно смешивать всё это в одном файле.
Результат: невозможно знать весь C++. Даже эксперты знают подмножество. Стандарт языка — больше тысячи страниц. Компиляторы реализуют его по-разному. Ошибки компиляции шаблонов — страницы нечитаемого текста:
error: no matching function for call to
'std::vector<int>::vector(std::vector<int>::iterator,
std::vector<int>::iterator, std::allocator<int>&)'
note: candidate expects 2 arguments, 3 provided
И это ещё простой случай. Бывают ошибки на пол-экрана.
Дейкстра терпеть не мог C++. Говорил: «Язык, в котором больше способов сделать ошибку, чем сделать правильно, — это плохой язык». И добавлял: «C++ делает почти невозможным научить студентов хорошему программированию. Потому что язык сам не знает, что такое хорошо».
Защитники C++ отвечают: «Зато мощный. Можешь делать что угодно. Контроль над памятью, производительность, абстракции высокого уровня».
Дейкстра бы сказал: «Вот именно. Можешь делать что угодно. И это проблема. Хороший язык должен мешать делать плохо, а не давать свободу».
JavaScript: хаос свободы
JavaScript создавался за десять дней в 1995 году. Брендан Айк, под давлением дедлайнов Netscape, накидал язык на скорую руку. И это видно.
Типы приводятся неожиданно:
[] + [] // ""
[] + {} // "[object Object]"
{} + [] // 0 (в некоторых контекстах)
Это не баг, это особенность. Но кто запомнит все правила приведения типов?
== и === — две операции сравнения, работающие по-разному. == приводит типы, === нет. Какую использовать? Сообщество решило: всегда ===, забудьте про ==. То есть в языке есть оператор, который лучше не использовать.
this меняет значение в зависимости от контекста вызова функции. Можно вызвать ту же функцию по-разному — и this будет указывать на разные объекты. Новички ломают голову месяцами.
Асинхронность: сначала через колбэки (callback hell — вложенность на пять уровней). Потом промисы. Потом async/await. Три разных способа, все используются, часто смешиваются в одном проекте.
Куча способов объявить функцию:
function foo() {} // обычная функция
const foo = function() {} // function expression
const foo = () => {} // стрелочная функция
const obj = { foo() {} } // метод объекта
Все работают чуть по-разному (особенно со this). Какую использовать? Зависит от контекста. Но чтобы знать контекст, нужно знать все тонкости языка.
Каждый год новый стандарт (ES2015, ES2016… ES2024) с новыми фичами. Язык растёт, усложняется. И это при том, что базовые проблемы (приведение типов, this) никуда не делись.
JavaScript мощный, гибкий, вездесущий. Но непредсказуемый. Программист должен держать в голове множество исключений и особенностей.
Дейкстра бы схватился за голову: «Язык, где одинаковый код может работать по-разному в зависимости от незаметных деталей, — это катастрофа».
Сообщество JS это признаёт. Поэтому появился TypeScript — надстройка, которая добавляет статическую типизацию и строгость. Попытка исправить хаос, добавив дисциплину.
Perl: когда «свобода» становится проклятием
Perl был популярен в 90-е и начале 2000-х. Его девиз: “There’s more than one way to do it” (TMTOWTDI). Это было гордостью языка. Множество способов решить задачу. Свобода выражения. Программист как поэт.
Дейкстра бы сказал: «Это не свобода, это проклятие».
Когда десять способов сделать одно — каждый программист выбирает свой. Код становится нечитаемым для других. Синтаксис перегружен символами: #x60;, @, %, ->, =~, //, ~~`. Регулярные выражения вшиты в язык и используются везде, даже где не нужно.
, @, %, ->, =~, //, ~~. Регулярные выражения вшиты в язык и используются везде, даже где не нужно.
my @sorted = sort { $a->{value} <=> $b->{value} } @array;
print "$_\n" for grep { $_->{active} } @sorted;
Что тут происходит? Если не знаешь Perl — непонятно. Даже если знаешь — нужно вчитаться.
Можно написать то же самое по-другому:
foreach my $item (@array) {
print $item->{value}, "\n" if $item->{active};
}
Или ещё как-то. Три программиста напишут три разных варианта. Все работают, но читаются по-разному.
Результат: Perl называли «write-only language». Написать можно, прочитать — нет. Даже свой код через месяц не поймёшь. А чужой — тем более.
Одна из знаменитых шуток: «Perl — это язык, на котором каждая программа выглядит как случайная последовательность символов после того, как кот прошёлся по клавиатуре».
Сейчас Perl почти мёртв как основной язык. Не потому что плохой технически, а потому что непонятный. Код на Perl невозможно поддерживать в команде. Новый человек не может влиться. Legacy-проекты на Perl переписывают на что-то более читаемое (Python, Ruby, даже PHP).
Почему языки с простотой побеждают (в долгосрочной перспективе)
Дейкстра предсказывал: языки, которые поощряют сложность, в итоге умирают или маргинализируются. Языки, которые помогают быть простыми, живут и развиваются.
История это подтверждает:
Pascal умер как промышленный язык (слишком прост для реальных задач), но его идеи живут в других языках. Delphi (потомок Pascal) до сих пор используется.
Oberon остался академическим экспериментом, но его принципы вдохновили создателей Go.
C++ жив, потому что нет альтернативы для его ниши (системное программирование, производительность). Но это мучительная жизнь. Программисты бегут оттуда в Rust (который сложный в изучении, но даёт гарантии безопасности) или обратно в C (который проще C++, хотя опаснее).
JavaScript жив, потому что монополия в браузерах. Альтернативы не было (до WebAssembly). Но сообщество пытается укротить его: TypeScript, линтеры, Prettier. Всё это — попытки добавить дисциплину к хаотичному языку.
Perl умер. Его заменили Python и Ruby — языки с более понятным синтаксисом.
Go растёт. За десять лет стал одним из топ-языков для backend, DevOps, системного программирования. Именно потому что простой.
Python популярен, несмотря на медлительность. Потому что читаемый, понятный, легко учится.
Rust растёт, несмотря на сложность изучения. Потому что после того, как выучил, код становится предсказуемым и надёжным.
Почему простота побеждает: практические причины
- Низкий порог входа
Простой язык можно выучить быстро. Go изучается за неделю — и можешь писать продуктивный код. C++ изучается годами — и всё равно не знаешь всего языка.
Это критично для индустрии. Компания может быстро обучить нового человека. Студент может начать писать реальные проекты через месяц, а не через год.
- Легче поддерживать
Код на простом языке понятен через год, через пять лет. Можно вернуться к проекту и разобраться. Можно передать проект другому человеку — он не потратит месяцы на изучение.
Legacy на простом языке — это просто старый код. Legacy на сложном языке — это кошмар, который все боятся трогать.
- Меньше багов
Простой язык = меньше способов ошибиться. Go заставляет обрабатывать ошибки явно — сложно забыть. JavaScript позволяет игнорировать ошибки — и проект падает в продакшене в самый неожиданный момент.
Rust не даёт скомпилировать программу с race conditions. C++ позволяет — и ты ловишь undefined behavior, который проявляется раз в месяц на конкретной конфигурации.
- Быстрее онбординг
Новый человек в команде начинает писать продуктивный код за дни, а не месяцы. Это критично для растущих компаний, где постоянно приходят новые люди.
Простой язык = можно быстро масштабировать команду.
- Меньше холиваров
Когда один способ делать вещи, не спорят о стиле. gofmt в Go решил все споры о форматировании раз и навсегда. В C++ до сих пор воюют о том, где ставить фигурные скобки, как называть переменные, использовать ли auto.
В Python есть PEP 8 (руководство по стилю), и все его придерживаются. В JavaScript каждый проект настраивает ESLint по-своему, и начинаются споры.
Простота = меньше разногласий = больше времени на решение задач.
- Надёжность
Простой код легче проверить, протестировать, доказать корректность (в духе Дейкстры). Сложный код полон скрытых зависимостей, edge cases, неочевидных взаимодействий.
Когда пишешь критичные системы (финансы, медицина, безопасность), простота — это не роскошь, а необходимость. Баг может стоить денег, здоровья, жизней.
Дейкстра и Вирт: союз минималистов
Никлаус Вирт и Эдсгер Дейкстра были не просто коллегами, а единомышленниками и друзьями. Оба европейцы (Дейкстра — голландец, Вирт — швейцарец), оба академики, оба фанаты простоты и элегантности.
Вирт учился у Дейкстры. Читал его работы, переписывался, встречался на конференциях. И создавал языки, следуя философии Дейкстры: простота как основа надёжности.
Pascal (1970) — первый язык Вирта, получивший широкое распространение. Создавался для обучения структурному программированию. Дейкстра одобрил: «Наконец-то язык, который учит думать, а не кодить быстро».
Modula-2 (1978) — следующий шаг. Добавили модульность, раздельную компиляцию. Но сохранили ясность Pascal.
Oberon (1986) — вершина минимализма. Вирт убрал всё, что можно убрать. Остался язык, в котором нельзя написать сложно. Потому что нет инструментов для сложности.
Дейкстра восхищался Oberon. Писал в одной из заметок: «Вирт показал, что можно сделать мощный язык из минимума концепций. Это доказательство того, что простота и выразительность не противоречат друг другу».
Вирт посвящал книги Дейкстре. Считал его наставником в философии проектирования языков.
Их общая философия:
- Язык программирования — это не просто инструмент. Это способ мышления.
- Хороший язык помогает думать ясно. Плохой язык засоряет мышление.
- Ограничения освобождают. Когда язык ограничивает выбор, программист не тратит энергию на выбор. Он тратит её на решение задачи.
- Один способ делать вещи — лучше, чем десять способов. Потому что не нужно помнить, какой способ в каком контексте уместен.
Наследие:
Современные минималистичные языки — прямые наследники этой философии:
Go (2009) — создатели открыто говорили, что вдохновлялись Oberon. Роб Пайк работал с Виртом и знал его идеи. Go — это Oberon для XXI века: минимализм, один способ делать вещи, простота как принцип.
Zig (2016) — новый системный язык. Создатель Эндрю Келли цитирует Вирта и Дейкстру. Идея: сделать системное программирование простым и понятным.
Дейкстра и Вирт доказали: можно создавать мощные инструменты, сохраняя их простыми. Это не противоречие, это искусство.
Почему Дейкстра писал программы на бумаге
Это кажется странностью. Старик, отставший от жизни, который пишет программы ручкой вместо того, чтобы использовать компьютер. Но за этим была глубокая логика.
Проблема с написанием кода сразу на компьютере
Ты пишешь — компилятор ругается на синтаксис. Правишь. Запускаешь — падает. Правишь. Запускаешь — работает, но не то. Правишь. Запускаешь — вроде работает. Запускаешь на других данных — падает.
В этом процессе ты не думаешь о корректности. Ты подбираешь вариант, который не падает. Метод проб и ошибок. Debugging-driven development.
Работает? Отлично, идём дальше. Почему работает? А кто его знает. Главное, что работает.
Результат: программа, которая работает случайно. Работает на тех входных данных, которые ты проверил. А на других — может упасть. Или выдать неверный результат. И ты не знаешь почему, потому что никогда не понимал её по-настоящему.
Подход Дейкстры
Сначала думаешь. Формулируешь задачу чётко. Что должна делать программа? При каких условиях? Что на входе, что на выходе? Какие граничные случаи?
Потом придумываешь алгоритм. Расписываешь шаги. Проверяешь логику на бумаге, вручную, на примерах.
Потом пишешь программу — на бумаге, аккуратно, с отступами. Это заставляет думать о каждой строке. Нельзя быстро накидать и посмотреть, что получится. Нужно сразу писать правильно.
Потом доказываешь, что программа корректна. Математически. Для каждого цикла — инвариант (условие, которое истинно на каждой итерации) и условие завершения (доказательство, что цикл не бесконечный). Для каждой функции — предусловия (что должно быть истинно до вызова) и постусловия (что будет истинно после).
И только после того, как ты уверен, что программа верна на бумаге, ты набираешь её в компилятор.
Это реально работало?
Да. Дейкстра рассказывал: однажды написал программу на бумаге, доказал корректность, набрал в компилятор — работает без единого изменения. Скомпилировалась и запустилась правильно с первого раза.
Коллеги не поверили. Подумали, что он соврал, что на самом деле отлаживал, просто не признаётся.
Но он не врал. Он просто думал достаточно тщательно.
Другой случай: THE operating system, которую он спроектировал. Система была спроектирована полностью на бумаге, с математическими доказательствами корректности каждого уровня. Когда её имплементировали и запустили, она работала сразу. Почти без багов. Потому что баги были найдены и исправлены ещё на стадии проектирования, до написания кода.
Урок для нас
Конечно, мы не будем писать всё на бумаге. Это непрактично в современной разработке. Но идея ясна: думать дешевле, чем отлаживать.
Потратить полчаса на обдумывание решения — лучше, чем потратить три часа на отладку плохо продуманного кода.
Современный аналог: перед тем как писать, набросай план. Псевдокод. Диаграмму. Список шагов. Что угодно, что заставляет думать, а не печатать.
Я сам это практикую. Когда сталкиваюсь со сложной задачей, не открываю сразу редактор. Беру блокнот (или открываю пустой текстовый файл) и пишу:
- Что нужно сделать?
- Какие входные данные?
- Какие выходные данные?
- Какие граничные случаи?
- Как это можно разбить на шаги?
Потом набрасываю псевдокод. Не синтаксически правильный код, а логику:
function processOrder(order):
проверить, что order валидный
если нет — вернуть ошибку
посчитать итоговую стоимость
применить скидки, если есть
зарезервировать товары на складе
если не хватает — вернуть ошибку
создать платёжную транзакцию
отправить подтверждение пользователю
вернуть успех
Смотрю на это. Думаю: всё ли учёл? Нет ли логических дыр? Что может пойти не так?
И только после этого начинаю писать настоящий код. И знаете что? Он получается гораздо лучше. Меньше переделок, меньше багов, меньше «ой, я забыл обработать этот случай».
Дейкстра учил: программирование — это не набор текста. Это мышление. Клавиатура — последний инструмент, а не первый.
И второй урок: если ты не можешь объяснить (себе, резиновому утёнку, коллеге), почему твоя программа работает правильно, — возможно, она не работает правильно. Просто ты ещё не нашёл входные данные, на которых она сломается.
Как писать просто: практические советы
Понимать важность простоты — одно. Писать просто — другое. Дейкстра не только критиковал, но и давал рецепты. Вот конкретные практики, как писать код, который не стыдно показать.
1. Думай, прежде чем писать
Мы это уже обсудили, но повторю: не бросайся сразу кодить. Сначала пойми задачу. Сформулируй её словами. Набросай план.
Пять минут думанья могут сэкономить час отладки.
2. Одна функция = одна задача
Если функция делает несколько вещей — раздели. Дейкстра учил: «Функция должна быть настолько простой, что её корректность очевидна».
Правило большого пальца: если не можешь объяснить, что делает функция, одним коротким предложением — она слишком сложная.
Плохо:
def processUserDataAndSendEmailAndLogToDatabase(user):
# 50 строк кода, которые делают три разных вещи
...
Хорошо:
def process_user_data(user):
# обработка данных пользователя
...
def send_confirmation_email(user):
# отправка email
...
def log_user_action(user, action):
# логирование
...
Каждая функция понятна изолированно. Можно тестировать отдельно. Можно переиспользовать.
3. Давай правильные имена
Половина простоты — в именах. Хорошее имя переменной/функции объясняет, что внутри, без чтения кода.
Плохо:
const d = new Date();
const t = d.getTime();
function p(x) { /* ... */ }
Что такое d? Что такое t? Что делает p(x)? Нужно читать код, чтобы понять.
Хорошо:
const currentDate = new Date();
const timestamp = currentDate.getTime();
function calculateTotalPrice(items) { /* ... */ }
Понятно без комментариев. Код самодокументируется.
Дейкстра говорил: «Программа — это текст, который должен быть понятен человеку. Компилятор — вторичен. Он всё поймёт, как бы ты ни назвал. А вот человек — нет».
Правила именования:
- Переменные — существительные:
user,totalPrice,activeOrders - Функции — глаголы:
calculateTotal(),sendEmail(),validateInput() - Булевы переменные — вопросы:
isActive,hasAccess,canEdit - Избегай сокращений, если они не общеизвестны:
usr→user,calc→calculate
4. Избегай вложенности
Чем больше уровней вложенности (if внутри if внутри цикла внутри функции) — тем сложнее держать контекст в голове.
Правило: не больше двух-трёх уровней. Если больше — рефактори.
Плохо:
def process_order(order):
if order is not None:
if order.is_valid():
if order.has_items():
if order.customer.has_balance():
# делаем что-то
pass
Четыре уровня вложенности. Нужно держать в голове все условия одновременно.
Лучше (early return):
def process_order(order):
if order is None:
return Error("Order is None")
if not order.is_valid():
return Error("Order is invalid")
if not order.has_items():
return Error("Order has no items")
if not order.customer.has_balance():
return Error("Insufficient balance")
# делаем что-то
...
Читается линейно, сверху вниз. Каждое условие проверяется и сразу обрабатывается. Успешный путь — в конце, не спрятан внутри вложенности.
Или вынеси внутреннюю логику в функцию:
def process_order(order):
if not is_order_processable(order):
return Error("Cannot process order")
# делаем что-то
...
def is_order_processable(order):
return (order is not None and
order.is_valid() and
order.has_items() and
order.customer.has_balance())
Теперь process\_order простая, а сложная проверка вынесена в отдельную функцию с понятным именем.
5. Минимизируй состояния
Чем больше изменяемых переменных, тем сложнее отследить, что происходит. Переменная меняется тут, потом там, потом ещё где-то. Через десять строк ты уже не помнишь, какое у неё значение.
Дейкстра любил функциональный подход (хотя термин тогда не был популярен): функции без побочных эффектов, неизменяемые данные.
Плохо:
total = 0
for item in items:
total = total + item.price
if item.discount:
total = total - item.discount
total = total * item.quantity
Переменная total изменяется на каждой строке. Сложно понять, какое значение в какой момент.
Лучше:
def calculate_item_total(item):
price = item.price
if item.discount:
price = price - item.discount
return price * item.quantity
total = sum(calculate_item_total(item) for item in items)
Нет изменений в цикле. Каждая итерация независима. Легче понять, легче тестировать.
Современные языки это поддерживают:
constв JavaScript (не позволяет переприсвоить)finalв Java- immutable коллекции в Clojure, Scala
- ownership и borrowing в Rust (гарантируют отсутствие изменений в определённых контекстах)
Если можешь не изменять — не изменяй. Создай новое значение. Память дешёвая, понимание кода — дорогое.
6. Не оптимизируй раньше времени
«Преждевременная оптимизация — корень всех зол» — сказал Дональд Кнут, друг и коллега Дейкстры.
Сначала напиши просто и понятно. Потом измерь производительность. Если есть проблема — оптимизируй узкое место. Но не жертвуй простотой ради гипотетической скорости.
Плохой пример:
# «Оптимизация»: кэшируем всё
cache = {}
def get_user(id):
if id not in cache:
cache[id] = database.query(...)
return cache[id]
Кажется умным? Но теперь есть глобальное состояние (cache), которое нужно инвалидировать, управлять памятью. Сложность выросла. А проблема производительности, возможно, вообще не существовала.
Дейкстра добавлял: «Простой код часто быстрее сложного. Потому что компилятор его лучше оптимизирует. И потому что в простом коде легче найти настоящее узкое место».
Правило: сначала работающий и понятный код. Потом, если есть проблемы с производительностью, профилируй (измеряй), находи реальное узкое место, оптимизируй его. Не гадай, где медленно. Измеряй.
7. Рефактори безжалостно
Первый вариант почти всегда корявый. Это нормально. Дейкстра писал: «Программирование — это итеративный процесс приближения к простоте».
Написал — работает? Отлично. Теперь посмотри критически:
- Можно ли проще?
- Можно ли убрать лишнее?
- Можно ли переименовать понятнее?
- Можно ли разбить функцию?
Рефакторинг — это не «когда будет время». Это часть работы. Потратил час на рефакторинг — сэкономил десять часов будущим поддерживающим (включая себя через полгода).
Одна из моих практик: после того как фича работает и тесты проходят, я делаю «проход рефакторинга». Иду по коду и спрашиваю себя на каждой функции: «Если бы я встретил этот код впервые, я бы понял, что он делает?» Если нет — переписываю.
8. Пиши для людей, не для компьютера
Компьютер выполнит любой код, лишь бы синтаксически правильный. Но код читают люди. Гораздо чаще, чем пишут.
Статистика: код читается в 10 раз чаще, чем пишется. Значит, оптимизировать нужно для чтения, а не для написания.
Дейкстра: «Программа должна быть литературным произведением». Не в смысле художественности, а в смысле ясности изложения мысли.
Пиши так, чтобы коллега, открыв твой код через год, сразу понял. Или ты сам, когда забыл детали.
Критерий качества: можешь ли ты объяснить код джуну за пять минут? Если да — хорошо. Если нужен час объяснений с рисованием диаграмм — код слишком сложный.
9. Доверяй дискомфорту
Если смотришь на код и чувствуешь: «Что-то не так» — прислушайся. Даже если не можешь объяснить, что именно. Дейкстра называл это «интеллектуальным дискомфортом».
Работает — но некрасиво? Переделай. Потому что «некрасиво» часто означает «сложно, непонятно, хрупко». А это источник будущих проблем.
Твоя интуиция — это накопленный опыт. Если она говорит «тут что-то не так», она, скорее всего, права. Даже если логически ты не можешь сразу объяснить.
Я научился этому на собственной шкуре. Сколько раз писал код, чувствуя «что-то не так, но работает», оставлял как есть. И через месяц-два этот кусок кода становился источником багов. Приходилось переписывать, уже в пожарном порядке.
Теперь: если чувствую дискомфорт — останавливаюсь. Думаю: что именно не нравится? Как можно сделать лучше? И переделываю сразу. Это дешевле.
Простота в тексте: бонус для тех, кто пишет не только код
Дейкстра писал не только программы. Он писал статьи, заметки, письма. И его тексты — образец ясности. Те же принципы, которые работают в коде, работают и в письме.
Одна мысль = одно предложение
Не паковать три идеи в длинное предложение с придаточными, причастными оборотами и вводными конструкциями, которые теряются где-то в середине.
Плохо:
«Программирование, которое является сложной интеллектуальной деятельностью, требующей дисциплины мышления и умения справляться с абстракциями, становится всё более важным в современном мире, где программы управляют всем — от телефонов до самолётов».
Три мысли в одном предложении. Запутанно.
Лучше:
«Программирование — сложная интеллектуальная деятельность. Она требует дисциплины мышления и умения работать с абстракциями. В современном мире программы управляют всем — от телефонов до самолётов».
Три предложения, три мысли. Читается легко.
Короткие абзацы
Один абзац = одна идея. Легче читать, легче понять структуру.
Длинный абзац на экран текста — это стена. Глаз скользит, мозг не цепляется. Короткие абзацы создают ритм, дают передышку.
Смотрите: я в этом тексте использую короткие абзацы. Иногда вообще одно предложение.
Это специально. Чтобы было легче читать, чтобы структура была ясной.
Выкидывать лишнее
«В общем-то», «как бы», «достаточно очевидно», «можно сказать что» — мусор. Убирай. Текст станет чище и энергичнее.
Плохо:
«Можно сказать, что в общем-то достаточно очевидно, что простой код, как бы, легче поддерживать».
Лучше:
«Простой код легче поддерживать».
Закон Оккама в тексте: не умножай слова без необходимости. Каждое слово должно работать. Если можно убрать без потери смысла — убери.
Конкретность
Не «улучшили производительность», а «ускорили на 30%».
Не «много пользователей», а «10 тысяч пользователей».
Не «долго», а «три недели».
Конкретные числа, факты, примеры делают текст убедительным. Абстракции — расплывчатым.
Плохо:
«Наш сервис популярен. Много людей им пользуется. Мы быстро обрабатываем запросы».
Лучше:
«Нашим сервисом пользуется 50 тысяч человек в день. Средняя задержка ответа — 120 миллисекунд».
Чувствуете разницу? Второй вариант доказывает. Первый — просто заявляет.
Логичная структура
Читатель должен видеть, куда ведёт текст. Введение (о чём текст), развитие (суть), вывод (что запомнить).
Как в коде: начало, середина, конец. Не прыгай с темы на тему. Веди читателя за руку.
Используй подзаголовки. Разбивай длинные тексты на разделы. Человек должен понимать, где он находится, сколько осталось, какая сейчас тема.
Этот текст, который вы читаете, структурирован именно так. Есть подзаголовки, есть логика развития мысли. Вы всегда знаете, о чём сейчас речь.
Ясность ведёт к ясности
Дейкстра говорил: «Ясность мышления ведёт к ясности письма. Ясность письма ведёт к ясности мышления». Круг замкнулся.
Когда пишешь просто — думаешь просто. Когда думаешь просто — решаешь сложное. Потому что не тратишь энергию на борьбу с собственной запутанной формулировкой.
Поэтому я рекомендую: пишите. Не только код, но и тексты. Документацию, статьи, заметки для себя. Это тренирует **ясность мышления**. А она нужна везде — в коде, в дизайне, в архитектуре, в общении с командой.
Не находите, что эти принципы буквально один в один повторяют правила инфостиля?
«Ясность мышления ведёт к ясности письма. Ясность письма ведёт к ясности мышления» — Дейкстра
Простота в архитектуре: как строить системы, которые можно понять
Дейкстра писал не только о коде, но и об архитектуре систем. И здесь его идеи ещё важнее. Потому что плохая архитектура убивает проект медленно, но неотвратимо.
THE multiprogramming system: слои простоты
В 1968 году Дейкстра спроектировал операционную систему THE — эксперимент по созданию доказуемо корректной сложной системы.
Идея: разбить систему на уровни (layers). Каждый уровень использует только нижние уровни, не знает о верхних. Каждый уровень можно понять и проверить изолированно.
Уровень 5: Пользовательские программы
Уровень 4: Буферизация ввода-вывода
Уровень 3: Управление консолью
Уровень 2: Управление памятью
Уровень 1: Планировщик процессов
Уровень 0: Базовое управление железом
Каждый уровень — простой. Абстракция скрывает сложность нижних уровней. Можно доказать корректность каждого уровня отдельно, не думая обо всей системе сразу.
Результат: система работала надёжно. Без сбоев. Потому что каждый кусок был понятен и проверен.
Урок для современной архитектуры
Принцип THE system актуален сегодня:
Микросервисы, слои, модули — всё это попытки организовать сложность через декомпозицию. Но часто делают наоборот: создают кучу сервисов, которые завязаны друг на друга неявно. Получается распределённый монолит. Сложность не ушла, а умножилась.
Дейкстра бы сказал: Модуль должен быть понятен сам по себе.
Признаки хорошей архитектуры:
- Ясные границы
Каждый модуль имеет чёткую зону ответственности. Не «модуль, который делает всё, связанное с пользователями», а «модуль аутентификации», «модуль профилей», «модуль подписок».
- Минимум зависимостей
Если чтобы понять модуль А, нужно изучить модули B, C, D, E — архитектура плохая. Хорошая архитектура: каждый модуль зависит от минимума других, через явные интерфейсы.
- Абстракция скрывает сложность
Нижний уровень может быть сложным внутри. Но он предоставляет простой интерфейс верхнему уровню.
Пример: база данных внутри сложная (индексы, транзакции, блокировки). Но клиент видит простой интерфейс: query(), insert(), update(). Вся сложность спрятана.
- Можно понять каждую часть изолированно
Открываешь модуль — понимаешь, что он делает, без чтения половины кодбейза. Это возможно только если зависимости явные и минимальные.
Плохой пример: микросервисы, которые не микро
Компания решает: «У нас монолит, это плохо, давайте на микросервисы!»
Разрезают монолит на 20 сервисов. Но сервисы общаются друг с другом через хитрые схемы. Сервис A вызывает B, который вызывает C, который отправляет событие в D, которое обрабатывается E, который обновляет данные, которые читает A.
Циклические зависимости. Неявные зависимости через общую базу данных. Общее состояние в кэше. События, которые летают в разные стороны, и никто не знает, кто на них подписан.
Результат: вместо одной непонятной системы получили 20 непонятных, плюс сложность сетевого взаимодействия. Сложность умножилась.
Дейкстра бы сказал: «Вы взяли монолит и размазали его по 20 процессам. Сложность не уменьшилась, она стала распределённой. А это хуже, потому что теперь добавились проблемы сети, латентности, частичных отказов».
Хороший пример: независимые модули с ясными границами
Каждый модуль — самодостаточный. Имеет своё хранилище (если нужно), свою логику, свой API. Общается с другими через явные контракты.
Сервис аутентификации знает только о пользователях и токенах. Не знает о заказах, корзинах, подписках.
Сервис заказов знает о заказах. Для работы ему нужна информация о пользователе — получает через API аутентификации. Не лезет в его базу данных.
Сервис уведомлений слушает события («заказ создан») и отправляет email. Не знает логику создания заказа.
Каждый сервис можно понять изолированно. Разработчик, работающий с сервисом заказов, не должен изучать код сервиса аутентификации. Он должен знать только его API.
Это и есть принцип Дейкстры, применённый к распределённым системам: декомпозиция сложности через изоляцию.
Дейкстра vs современные практики разработки
Интересно посмотреть, что бы Дейкстра сказал о современных практиках. Он умер в 2002-м, не видел многого из того, что мы используем сегодня. Но его принципы позволяют оценить эти практики.
Agile / Scrum
Критика:
Дейкстра бы скептически отнёсся к «сначала сделаем быстро, потом исправим». Это антитеза его подходу: сначала думай, потом делай правильно.
Спринты, постоянные изменения требований, приоритет скорости над качеством — всё это поощряет накопление технического долга. «Работает — и ладно». А потом проект становится легаси.
Оговорка:
Он понимал, что требования меняются. Невозможно знать всё заранее. Просто считал: архитектура должна быть настолько гибкой, что изменения не ломают систему. А это возможно, только если система простая и модульная.
Если архитектура хорошая — изменения дёшевы. Если плохая — любое изменение ломает десять других мест.
Вердикт:
Agile как философия «адаптивность важнее жёстких планов» — ок. Agile как «кодим быстро, качество потом» — плохо.
TDD (Test-Driven Development)
Скептицизм:
Дейкстра говорил: «Тесты показывают наличие ошибок, не их отсутствие». Полагаться на тесты вместо понимания корректности — опасно.
Можно написать тысячу тестов — и пропустить edge case, который сломает продакшн.
Признание пользы:
Но он признал бы: тесты — это спецификация поведения. Хорошая вещь. Тесты заставляют думать о контрактах функций: что на входе, что на выходе, что должно быть истинно.
И TDD поощряет писать тестируемый код. А тестируемый код обычно простой: функции с явными входами и выходами, без скрытых зависимостей.
Вердикт:
Тесты нужны. Но недостаточны. Нужно и тесты, и понимание, почему код корректен.
Code Review
Одобрение:
Код должен быть понятен другим. Если ревьюер не понимает — код недостаточно прост.
Code review — это проверка не только на баги, но и на понятность. Если коллега задаёт вопросы «а что тут происходит?» — это сигнал: нужно упростить.
Дейкстра бы добавил: ревью должно быть строгим. Не «выглядит ок, апрувлю». А «докажи мне, что это работает правильно».
Вердикт:
Отлично. Но ревьюеры должны иметь смелость требовать простоты, а не просто проверять, что код компилируется.
Парное программирование
Возможное одобрение:
Два человека думают лучше одного. Меньше шанс написать глупость. Один пишет, другой думает о корректности, задаёт вопросы.
Но с условием: оба должны думать, а не просто печатать.
Парное программирование работает, когда один пишет код, а второй критически его оценивает, задаёт вопросы: «А что если входные данные такие?», «А почему тут именно этот алгоритм?», «А можно ли проще?»
Не работает, когда оба просто смотрят в экран и печатают по очереди. Или когда один диктует, другой механически набирает.
Вердикт:
Хорошая практика, если используется для взаимной проверки мышления, а не просто для совместного написания кода.
Continuous Integration / Deployment
Нейтрально:
Это инструменты. Хороши, если код качественный. Бесполезны (даже вредны), если код — мусор.
«Быстро деплоить плохой код — это быстро ломать продакшн». CI/CD не делает код лучше. Они просто ускоряют процесс — хороший или плохой, какой есть.
Дейкстра бы сказал: сначала научитесь писать правильный код. Потом автоматизируйте деплой.
Вердикт:
Полезные инструменты, но не замена качеству. Автоматизация мусора даёт автоматизированный мусор.
Микросервисы
Критика:
«Вы взяли монолит, непонятный и сложный, и разрезали на десять сервисов, непонятных и сложных. Теперь вместо одной проблемы у вас десять, плюс сетевое взаимодействие, плюс eventual consistency, плюс distributed transactions».
Микросервисы не упрощают систему. Они перемещают сложность из кода в архитектуру. И добавляют новую сложность: сеть ненадёжна, сервисы падают независимо, данные рассинхронизированы.
Признание смысла:
Но признал бы пользу, если сервисы действительно независимы:
- Каждый сервис решает одну чёткую задачу
- Минимум взаимодействий между сервисами
- Ясные границы и контракты
- Можно разрабатывать, тестировать, деплоить независимо
Это редкость. Чаще получается распределённый монолит.
Вердикт:
Микросервисы — не серебряная пуля. Годятся для масштабирования больших команд и нагрузок. Но не делают систему проще. Наоборот, усложняют. Используйте, только если проблемы монолита (развёртывание, масштабирование, независимость команд) реально болят. Иначе монолит простой и модульный — лучше.
Вывод по практикам
Дейкстра не был против инструментов и практик. Он был против бездумного использования.
Любая практика хороша, если помогает писать простой, понятный, надёжный код. И плоха, если поощряет быстроту в ущерб качеству.
Он бы сказал: «Не спрашивайте, какие практики модны. Спрашивайте, помогают ли они вам понимать ваш код. Если да — используйте. Если нет — выбросьте, не важно, насколько они популярны».
Почему это важно: личный опыт
Я тоже начинал с написания программ на бумаге и мысленной отладки. Но не потому, что руководствовался подходом Дейкстры, а по более прозаичной причине — у меня не было компьютера, на котором я мог бы это проверить. Но это дало мне нехилый буст: многие мои программы работали (и до сих пор такое бывает) буквально «с листа», то есть без какой-либо отладки.
Когда компьютер оказался под рукой, я поначалу забил на обдумывание на бумаге. Да и коллеги не понимали, зачем вся эта возня с простотой. Код работает — и ладно. Зачем переписывать, если и так нормально? «Лучшее враг хорошего» и всё такое.
Понял, когда вернулся к своему коду через полгода.
Случай из жизни. Непонятный код
Работал над программой со сложной логикой: серверная часть системы банк-клиент — приём платежей, проверка подписи, рассылка обновлений и всё такое. Написал. Работает. Тесты проходят.
Через полгода пришлось добавлять функциональность. Открыл файл с программой. Функция на 200 строк, с вложенными if’ами на четыре уровня, с переменными типа temp1, result2, flag. Комментариев нет — «код же самодокументируемый» (ну да, конечно).
Пытаюсь понять логику. Час чтения. Рисую схему на бумаге. Ещё час. Начинаю вспоминать: «Ага, вот тут я учитывал случай, когда…» Но детали ускользают. Мой собственный код, а я не могу понять за час.
В итоге потратил день, чтобы разобраться и исправить баг. День на то, что могло занять полчаса, будь код простым.
И подумал: если я, автор, не могу понять через полгода, то кто-то другой не разберётся вообще никогда.
Урок: простота экономит время
С тех пор, когда пишу, спрашиваю себя: «Пойму ли я это через полгода?»
Если нет — упрощаю. Разбиваю функции. Переименовываю переменные. Добавляю комментарии там, где логика неочевидна (хотя если нужны комментарии, может, код слишком сложный?).
Это инвестиция. Трачу лишние 20 минут сейчас на рефакторинг — экономлю часы (или дни) потом.
И знаете что? Это меняет отношение к работе. Когда пишешь код, который не стыдно показать, который сам понимаешь, который элегантен — появляется гордость. Не высокомерие, а простое удовлетворение: «Да, вот это сделано хорошо».
Простой код = меньше стресса
Ещё один эффект, который не ожидал: простой код снижает стресс.
Когда работаешь со сложным, запутанным кодом — всё время тревога. Боишься что-то сломать. Любое изменение — риск. Не понимаешь последствий. Делаешь — и надеешься, что не упадёт.
Это выматывает. Программисты выгорают не от сложных задач, а от сложного кода. От того, что каждый день — борьба с хаосом, а не решение интересных проблем.
Когда работаешь с простым кодом — спокойствие. Понимаешь, что делает каждый кусок. Можешь изменить — и быть уверенным, что не сломал другое. Тесты проходят — и ты знаешь почему, а не просто «повезло».
Это даёт энергию. После дня работы с простым кодом чувствуешь удовлетворение: сделал, понятно, надёжно. После дня со сложным кодом — выжатость: потратил энергию на борьбу с запутанностью, а не на созидание.
Дейкстра это понимал. Он говорил: «Программирование должно быть интеллектуальным удовольствием, а не мучением». И это возможно, только когда работаешь с понятным кодом.
Простота — это забота о других
Ещё один аспект: когда пишешь просто, ты заботишься о тех, кто придёт после тебя.
О коллеге, который будет поддерживать твой код.
О новичке, который будет изучать кодбейз.
О себе в будущем, когда забудешь детали.
Писать просто — это эмпатия. Ты думаешь не только «как бы быстрее написать», но «как сделать так, чтобы другие поняли».
Команды, где люди пишут просто, работают лучше. Потому что нет барьеров понимания. Новый человек входит в проект быстро. Code review проходит легко. Баги находятся и чинятся быстро.
Команды, где пишут сложно, — это боль. Каждый варится в своём куске кода. Никто не понимает чужое. Code review — формальность, потому что разбираться некогда. Баги множатся, потому что никто не видит картину целиком.
Простота — это не личное предпочтение. Это профессионализм.
Финал: урок Дейкстры для нашего времени
Дейкстра умер больше двадцати лет назад. Мир программирования с тех пор изменился радикально. Языки, инструменты, практики — всё другое.
Но проблемы остались те же.
Сложность не исчезла. Она выросла.
Проекты стали больше — миллионы строк кода. Команды стали больше — сотни разработчиков. Системы стали сложнее — микросервисы, распределённые базы данных, согласованность в распределённых системах, облачная инфраструктура.
И в этом хаосе единственное противоядие — простота.
Простота — это не отказ от мощи
Не нужно писать примитивные программы. Нужно писать мощные, но понятные.
Дейкстра решал сложнейшие задачи — операционные системы, алгоритмы, многопоточность. Но решал их просто. Потому что понимал: сложное решение сложной задачи — это провал. Нужно найти простое решение сложной задачи. И это гораздо труднее.
Любой может написать сложно. Это получается само. Нужна дисциплина, чтобы написать просто.
Простота на всех уровнях
Не только код. Архитектура, процессы, коммуникация — везде нужна простота.
Простая архитектура: модули с ясными границами, минимум зависимостей, можно понять каждую часть изолированно.
Простые процессы: не 50 шагов согласования, а чёткие правила, кто за что отвечает.
Простая коммуникация: не многостраничные документы, которые никто не читает, а короткие ясные сообщения.
Сложность множится. Если код сложный, архитектура сложная и процессы сложные — проект обречён. Где-то нужен островок простоты, чтобы хоть что-то было понятным.
Простота требует усилий
Вот в чём парадокс: простота — это труд.
Сложность получается сама. Начинаешь писать — и код усложняется естественным образом. Добавил фичу — стало сложнее. Ещё фичу — ещё сложнее. Энтропия растёт.
Чтобы оставаться простым, нужно напрягаться. Бороться. Постоянно. После каждой фичи спрашивать: можно ли проще? Можно ли рефакторить? Можно ли убрать лишнее?
Это как поддерживать порядок в доме. Беспорядок накапливается сам. Порядок требует усилий. Регулярных, постоянных. Но если не прикладывать усилия — в какой-то момент живёшь в хаосе.
То же с кодом. Технический долг накапливается сам. Чтобы его не было — нужна дисциплина.
Дейкстра учил: дисциплина мышления — основа программирования. Не талант, не опыт (хотя они помогают), а дисциплина. Умение остановиться и подумать. Умение переделать, когда получилось коряво. Умение сказать «нет» новой фиче, если она усложняет без реальной пользы.
Простота = предпосылка надёжности
Его знаменитая фраза: «Простота — это предпосылка надёжности».
Не гарантия. Можно написать простой, но неправильный код. Но сложный код не может быть надёжным. Потому что в сложности всегда прячутся баги. Всегда есть случаи, которые не учёл. Всегда есть взаимодействия, о которых не подумал.
Если хочешь, чтобы система работала надёжно, — сделай её простой. Это не одна из стратегий. Это единственная стратегия, которая работает в долгосрочной перспективе.
Все остальные подходы — больше тестов, больше мониторинга, больше redundancy — это борьба с симптомами. Простота — это лечение причины.
Простота = счастье программиста
И последнее, о чём редко говорят, но что критично важно: простой код делает программистов счастливее.
Работать с понятным, элегантным кодом — удовольствие. Видеть, как система устроена логично, как все части складываются в единое целое. Вносить изменения без страха что-то сломать. Понимать, что делаешь.
Это редкая радость в нашей профессии. Когда дедлайны давят, когда легаси душит, когда выгорание подступает — возможность написать что-то правильно, просто, красиво даёт энергию продолжать.
Дейкстра дал нам язык, чтобы об этом говорить. До него программисты смутно чувствовали: «вот этот код хороший, а этот плохой». После — появились слова и концепции: локальность, минимизация состояния, отсутствие лишнего, доказуемость.
И это знание освобождает. Потому что теперь знаешь, к чему стремиться. Не просто «напиши код, который работает». А «напиши код, который работает и который можно понять».
Призыв к читателю: что делать дальше
Вы дочитали до конца. Это много текста, и если вы здесь — значит, тема резонирует. Хорошо. Теперь самое важное: что с этим делать?
Простота — это выбор, не талант
Вы не обязаны быть гением, как Дейкстра. Не обязаны доказывать корректность программ математически. Не обязаны писать на бумаге (хотя попробуйте иногда — может, понравится).
Но возьмите главное: стремление к простоте как профессиональный принцип.
Это не врождённое качество. Это привычка. Решение, которое принимаешь каждый раз, когда садишься писать код.
Конкретные действия
В следующий раз, когда будете писать код:
- Остановитесь на минуту перед написанием
Не бросайтесь сразу печатать. Подумайте:
- Что должна делать эта функция?
- Какие входные данные, какие выходные?
- Какие граничные случаи?
- Как это можно разбить на шаги?
Запишите план. Псевдокод. Хоть на салфетке. Что угодно, что заставляет думать, а не колотить по клавишам.
- Посмотрите на функцию
Она делает одно или три разных дела? Если больше одного — разбейте.
Можете ли вы объяснить, что она делает, одним предложением? Если нет — слишком сложная.
- Прочитайте переменные
Их имена объясняют, что внутри? temp, data, result2 — плохие имена. userEmail, totalPrice, validatedOrder — хорошие.
Переименуйте. Не жалейте времени. Хорошее имя экономит часы понимания в будущем.
- Проверьте вложенность
Больше трёх уровней if внутри if внутри for? Рефакторите. Early return, вынесение в функции, упрощение логики.
- Оцените зависимости
Функция зависит от десяти других? Использует глобальное состояние? Модифицирует параметры? Это признаки сложности. Можно ли сделать независимее?
- Спросите себя: можно ли проще?
Всегда можно. Не всегда нужно (есть баланс между простотой и функциональностью). Но спросить нужно всегда.
И если ответ «да, можно лучше» — сделайте лучше. Не «потом», не «когда будет время». Сейчас. Потому что «потом» не будет. Потом будет только хуже.
Простота заразительна
Когда в команде кто-то начинает писать просто и чисто, остальные подтягиваются. Потому что никто не хочет быть тем, чей код выглядит грязным на фоне чистого.
Вы можете быть тем человеком, который задаёт планку. Не агрессивно («твой код говно, переписывай»), а примером. Пишите просто. На code review задавайте вопросы: «А можно ли тут упростить?», «А зачем эта переменная?», «А можно ли разбить эту функцию?»
Не критикуйте человека, критикуйте код. И предлагайте альтернативы. «Вот тут сложно. Может, так?» — и показываете более простой вариант.
Люди учатся. Постепенно. И команда становится лучше.
Дайте себе право писать плохо (сначала)
Первая версия почти всегда корявая. Это нормально. Дейкстра сам переписывал программы по несколько раз.
Не бойтесь написать плохо. Бойтесь оставить плохо.
Напишите как получится. Заставьте работать. А потом сделайте проход рефакторинга. Посмотрите критически. Упростите. Переименуйте. Разбейте. Уберите лишнее.
Это часть процесса. Не «когда будет время», а обязательный шаг. Написать → Заставить работать → Сделать понятным.
Учитесь у мастеров
Читайте хороший код. Открывайте исходники проектов, которые славятся качеством. Go standard library, Redis, SQLite, некоторые части Linux kernel (есть куски, которые — шедевры простоты).
Смотрите, как они структурируют код. Как называют. Как разбивают на модули. Учитесь.
Читайте Дейкстру. EWD manuscripts в открытом доступе: https://www.cs.utexas.edu/~EWD/
Начните с коротких заметок. Не все понятны (некоторые очень академичны), но есть жемчужины. Найдёте те, что резонируют.
Читайте Вирта. «Algorithms + Data Structures = Programs» — классика. Учит думать о программировании через призму структур данных и простоты.
Читайте John Ousterhout, «A Philosophy of Software Design» (2018) — современная книга о том же: как писать просто, избегать сложности, проектировать системы, которые можно понять.
Будьте терпеливы к себе
Писать просто — это навык. Он нарабатывается годами. У меня не получалось сразу. До сих пор не всегда получается.
Но каждый раз, когда останавливаешься и думаешь «а можно ли проще?», становишься чуть лучше.
Дейкстра говорил: «Программирование — это деятельность, в которой мы никогда не достигнем совершенства. Но стремление к нему делает нас профессионалами».
Вы не станете идеальным программистом. Но можете стать лучше, чем вчера. И это единственное, что имеет значение.
Что почитать и посмотреть
Если заинтересовались Дейкстрой и философией простоты, вот ресурсы для дальнейшего погружения.
Тексты Дейкстры
EWD manuscripts — все его заметки в открытом доступе:
https://www.cs.utexas.edu/~EWD/
Начните с коротких. Рекомендую:
- EWD316 — «A Short Introduction to the Art of Programming»
- EWD1036 — «On the Cruelty of Really Teaching Computing Science»
- EWD498 — «How Do We Tell Truths that Might Hurt?» (его знаменитые едкие высказывания о языках программирования)
Книги:
- «A Discipline of Programming» (1976) — о том, как писать программы правильно через математические доказательства. Академично, но идеи бесценны.
- «Selected Writings on Computing: A Personal Perspective» (1982) — сборник его эссе.
Статьи:
- «Go To Statement Considered Harmful» (1968) — то самое письмо, изменившее индустрию. Короткое, читается за 10 минут.
- «The Humble Programmer» (1972) — речь при получении Тьюринговской премии. Там вся его философия в концентрированном виде.
Про Вирта и минимализм в языках
Никлаус Вирт:
- «Algorithms + Data Structures = Programs» (1976) — классика. Как думать о программировании через структуры данных.
- «Project Oberon» (2013, переиздание) — книга о создании ОС с нуля. Вся система описана и объяснена. Показывает, что сложное можно сделать понятным.
Про Go:
- «The Go Programming Language» (Donovan, Kernighan) — не только про язык, но и про философию простоты в дизайне.
- Talks by Rob Pike: «Simplicity is Complicated» — про то, как простота в Go достигается сложной работой.
Современные книги о простоте
John Ousterhout, «A Philosophy of Software Design» (2018)
Современная книга о том же, о чём писал Дейкстра: как писать просто, избегать сложности, проектировать системы, которые можно понять. Очень практичная, с примерами из реальных проектов.
Sandi Metz, «Practical Object-Oriented Design» (2018)
Хотя про ООП, но главная тема — простота. Как проектировать классы, которые делают одно, как минимизировать зависимости.
Kent Beck, «Smalltalk Best Practice Patterns» (1996)
Старая книга, но актуальная. Про то, как писать код, который легко читается. Применимо к любому языку.
Статьи и блоги
Rich Hickey (создатель Clojure), talk «Simple Made Easy»
https://www.infoq.com/presentations/Simple-Made-Easy/
Один из лучших докладов о разнице между «простым» и «лёгким». Про то, что простота требует усилий, но окупается.
Casey Muratori, «Semantic Compression»
https://caseymuratori.com/blog_0015
Про то, как упрощать код через правильные абстракции.
Видео
«The Humble Programmer» — чтение лекции Дейкстры
Найдёте на YouTube. Слушать голос человека, читающего его текст — особое удовольствие.
«Simplicity Matters» by Rich Hickey
Ещё один отличный talk о простоте vs сложности в современных системах.
Дейкстра писал в одной из последних заметок (EWD1284, 2000 год):
«Я давно пришёл к выводу, что единственный способ создавать надёжные системы — это делать их настолько простыми, что в них очевидно нет ошибок. Альтернатива — делать их настолько сложными, что нет очевидных ошибок. Первый путь гораздо труднее».
Вот в этом всё. Простота — это труд. Сложность получается сама. Но только простота даёт надёжность, понимание, долгую жизнь кода.
Выбирайте труд. Он того стоит.
Эпилог: Дейкстра был прав
Прошло больше 20 лет после смерти Дейкстры. Индустрия изменилась до неузнаваемости. Но его слова звучат ещё актуальнее, чем при жизни.
Мы строим системы невероятной сложности. Распределённые, микросервисные, с миллионами строк кода. И тонем в этой сложности.
Но есть островки простоты. Проекты, где код понятен. Команды, где новый человек начинает работать продуктивно через неделю, а не через три месяца. Системы, которые работают годами без фатальных падений.
И у всех них общее: они следуют принципам Дейкстры. Может быть, даже не зная его имени. Но принципам.
Простота на каждом уровне.
Локальность понимания.
Минимум зависимостей.
Один способ делать вещи.
Ясные имена.
Функции, которые делают одно.
Код, который объясняет сам себя.
Эти принципы работают. Не потому что красивые теоретически. А потому что проверены практикой — и Дейкстрой в 1960-х, и миллионами программистов после него.
Мир не стал проще
Проблемы, о которых предупреждал Дейкстра, никуда не делись. Наоборот.
Он видел будущее, где программы станут огромными, команды — большими, а сложность — неуправляемой. И говорил: единственный способ справиться — не допускать сложности с самого начала.
Мы не послушали. Создали JavaScript с его хаосом приведения типов. C++ с его тысячестраничным стандартом. Микросервисы, где 20 сервисов общаются через запутанную сеть событий и API. Фреймворки поверх фреймворков. Зависимости, тянущие за собой сотни других зависимостей.
Результат: техническое банкротство многих проектов. Легаси через год после запуска. Команды, которые боятся трогать код. Системы, которые падают, и никто не знает почему.
Дейкстра это предсказал. Говорил: если не будете бороться за простоту, она вас сожрёт. Так и вышло.
Но выход есть
Не всё потеряно. Потому что простота возвращается.
Go показал, что язык может быть минималистичным и при этом мощным. И стал одним из самых популярных языков за десятилетие.
Rust показал, что сложность изучения окупается надёжностью — программы на Rust работают правильно, и это ценно.
SQLite — проект, где один человек (Ричард Хипп) пишет код так тщательно, что база данных работает миллиардах устройств без падений. Потому что простая архитектура, тщательное тестирование, дисциплина.
Redis — хранилище данных, которое славится простотой кода. Создатель (Сальваторе Санфилиппо) сознательно отказывался от сложных фич ради понятности. И Redis работает везде — стабильный, быстрый, понятный.
Эти проекты — доказательство: простота работает. Не теоретически. Практически.
Новое поколение
Появляется новое поколение программистов, которые устали от сложности.
Устали от проектов, где никто не понимает, как всё работает.
Устали от фреймворков, где нужно изучить десяток концепций, чтобы вывести «Hello, World».
Устали от легаси, которое передают как проклятие.
И они ищут другое. Простые инструменты. Ясные архитектуры. Код, который можно понять.
Дейкстра был бы рад. Его идеи наконец находят отклик не только у академиков, но и у практиков.
Что дальше?
Будущее программирования — не в новых суперязыках с тысячей фич. Не в AI, который напишет код за нас (он пишет такой же сложный код, как люди, только быстрее). Не в новых фреймворках, обещающих решить все проблемы.
Будущее — в возвращении к основам. К простоте. К пониманию. К дисциплине мышления.
Языки будут эволюционировать. Появятся новые инструменты. Но принципы останутся:
- Простота — предпосылка надёжности.
- Код пишется для людей, не для компьютеров.
- Думать дешевле, чем отлаживать.
- Каждая строка должна быть там по причине.
- Если не можешь объяснить, как работает — возможно, не работает правильно.
Эти принципы вечны. Потому что они про фундаментальные ограничения человеческого мозга. Мы не можем держать в голове сложные системы. Не могли в 1960-х, не можем сейчас, не сможем через 50 лет.
Единственное решение — делать системы понятными. И это значит — простыми.
Ваша роль
Каждый из нас может внести вклад.
Не нужно быть Дейкстрой. Не нужно создавать новый язык или писать революционные статьи.
Достаточно писать просто в своих проектах.
Делиться знаниями с коллегами.
На code review спрашивать: «А можно ли проще?»
Рефакторить безжалостно.
Не бояться удалять код (лучший код — тот, которого нет).
Учиться у мастеров.
Учить джунов думать о простоте.
И постепенно мир станет чуть лучше. По одному проекту. По одной функции. По одной переменной с хорошим именем.
Последнее слово
Дейкстра уже не с нами. Но его голос звучит в каждой строке хорошо написанного кода.
В каждой функции, которая делает одно и делает правильно.
В каждом модуле с ясными границами.
В каждом проекте, который живёт десятилетиями, потому что его не страшно поддерживать.
Его наследие — это не алгоритм Дейкстры (хотя и это тоже).
Не THE operating system (хотя и это важно).
Его наследие — это идея, что простота — это профессионализм, а не слабость.
Что сложность — это не показатель ума, а признак недоработки.
Что хороший программист не тот, кто пишет умный код, который сложно понять.
А тот, кто пишет простой код, который решает сложные задачи.
Будьте проще.
Мир сложен. Задачи сложны. Не добавляйте сложности туда, где можно обойтись без неё.
Пишите код, который не стыдно показать.
Коллегам. Себе через год. Новичку в команде.
Дейкстре, если бы он был жив.
Он был бы строгим критиком. Задавал бы неудобные вопросы. Требовал бы доказательств корректности.
Но если бы увидел действительно простой код — улыбнулся бы. И сказал бы:
«Вот это правильно. Продолжайте».
Благодарности
Спасибо Эдсгеру Дейкстре — за то, что видел дальше своего времени и не боялся говорить неудобную правду.
Спасибо Никлаусу Вирту — за то, что воплотил идеи Дейкстры в языки, которыми пользовались миллионы.
Спасибо всем программистам, которые борются за простоту в своих проектах. Вы делаете мир лучше, даже если об этом никто не знает.
И спасибо вам, читатель, за то, что дочитали до конца. Это был длинный текст. Но если хоть одна мысль отсюда изменит то, как вы пишете код, — оно того стоило.
Идите и пишите просто.

Эдсгер Дейкстра
1930–2002
«Простота — это предпосылка надёжности»
Конец.