
Простота — это сложно: уроки Дейкстры о том, как писать код, который не стыдно показать
Эдсгер Дейкстра — один из тех людей, чьи мысли о программировании звучат так, будто он видел будущее и не особо им восхищался. Он умер в 2002-м, но если бы увидел современный фронтенд со стеком из двадцати фреймворков, пятидесяти зависимостей и конфигом на триста строк — наверное, просто тихо закрыл бы ноутбук и ушёл пить джин.
Дейкстра был фанатиком простоты. Не в смысле «упрощённости» или «примитивности», а в смысле элегантности. Он считал, что программист должен стремиться к тому, чтобы решение было настолько прозрачным и очевидным, что в нём просто не осталось места для ошибки. «Простота — это предпосылка надёжности», — писал он. И добавлял с характерной для него язвительностью: «Если вы не можете объяснить свою программу за пять минут, возможно, вы сами не понимаете, что она делает».
Он ненавидел goto. Написал знаменитое письмо «Go To Statement Considered Harmful», которое разозлило половину программистов того времени. Не потому что goto — это «плохая команда сама по себе», а потому что она делает код непрозрачным. Ты читаешь программу сверху вниз, и вдруг — прыжок. Куда? Зачем? При каких условиях? Приходится держать в голове весь граф переходов. Это сложно. А сложность — источник багов.
Дейкстра говорил: программирование — это борьба со сложностью. Не создание сложности (хотя многие этим занимаются), а укрощение её. Задачи сами по себе сложные. Мир сложный. Но решение должно быть настолько простым, насколько возможно. Не проще (это уже упрощение, потеря функциональности), а именно настолько простым, насколько задача позволяет.
Он любил повторять фразу, которую приписывал Эйнштейну (хотя авторство спорно): «Всё должно быть сделано настолько просто, насколько возможно, но не проще». Вот в этом «но не проще» — вся соль. Простота — это не отказ от функциональности, это кристаллизация сути. Выпаривание всего лишнего до тех пор, пока не остаётся чистая логика, которая решает задачу и ничего больше.
Я наткнулся на его тексты (EWD — Edsger W. Dijkstra manuscripts, коллекция его заметок, которые он печатал на машинке и рассылал коллегам) несколько лет назад и залип. Потому что он писал о том, что я смутно чувствовал, но не мог сформулировать. Это ощущение, когда смотришь на код и понимаешь: вот тут что-то не так. Работает, тесты проходят, но внутри скребёт. Дейкстра называл это «интеллектуальным дискомфортом». Когда решение не элегантно, ты это чувствуешь. Даже если не можешь сразу объяснить почему.
И вот что интересно: он не просто критиковал плохой код. Он пытался понять, почему мы пишем сложно. Почему программисты так любят городить конструкции, добавлять слои абстракций, плодить зависимости. И пришёл к выводу: потому что простота требует усилий. Гораздо проще накидать решение «в лоб», чем найти элегантное. Простота — это результат работы, думанья, рефакторинга. Сложность получается сама, простоту нужно выстрадать.
Он говорил: «Вопрос не в том, можете ли вы написать программу, которая работает. Вопрос в том, можете ли вы доказать, что она работает правильно». И для этого программа должна быть понятной. Не только компьютеру (ему всё равно, хоть на ассемблере), но и человеку. Потому что только понятную программу можно проверить, отладить, изменить, поддерживать.
«Простота — это предпосылка надёжности» — Эдсгер Дейкстра
Так вот. Дейкстра был прав. Простота — это не роскошь, не «когда будет время причесать». Это основа. Код должен быть простым не потому, что это красиво (хотя и это тоже), а потому что только простой код можно понять, а значит — контролировать.
И это касается не только кода. Текстов тоже. Решений тоже. Архитектуры тоже. Везде, где есть сложность, нужно бороться за простоту.
Давайте разберёмся, кто такой был этот человек, что он имел в виду, когда говорил о простоте, и почему языки программирования, которые помогают быть простыми, — это благословение.
Кто такой Дейкстра: коротко о главном
Чтобы понимать, почему к его словам стоит прислушиваться, нужно знать, кто это был.
Эдсгер Вибе Дейкстра (1930–2002) — нидерландский учёный, один из основателей computer science как научной дисциплины. Не просто программист, а теоретик, который закладывал фундамент того, как мы думаем о программировании.
Алгоритм Дейкстры (1956). Поиск кратчайшего пути в графе. Если вы хоть раз пользовались навигатором — скажите спасибо Дейкстре за его элегантное и простое решение сложной задачи. Именно таким он видел идеальный код.
Структурное программирование (1960-е). Боролся с goto, продвигал идею, что программа должна строиться из простых блоков: последовательность, ветвление, цикл. Сейчас это кажется очевидным, но тогда было революцией. Он доказал, что этих трёх конструкций достаточно для любой программы, и они делают код понятным.
Семафоры и мьютексы (1965). Решал проблему синхронизации процессов. Придумал концепцию семафоров — элегантный механизм, который до сих пор используется в многопоточном программировании.
THE multiprogramming system (1968). Операционная система, которую он спроектировал как доказательство концепции: можно создать сложную систему, которая будет доказуемо корректной. Использовал иерархическую структуру, где каждый уровень можно понять и проверить независимо. Это была демонстрация того, что простота и строгость работают даже в больших проектах.
Тьюринговская премия (1972). «Нобелевская премия» в computer science. Получил за вклад в развитие языков программирования и операционных систем. В речи при вручении говорил — конечно же — о простоте и дисциплине мышления.
EWD manuscripts (1959–2002). Больше тысячи заметок, которые он писал всю жизнь. Печатал на машинке, нумеровал (EWD1, EWD2… EWD1318), рассылал коллегам. Там всё: технические идеи, философские размышления, критика индустрии. Многие до сих пор актуальны. Читаются как эссе умного и язвительного человека, который видит проблему глубже остальных.
Преподавание. Работал в университетах Нидерландов и США. Считал, что учить программированию — это учить думать. Не синтаксису, не фреймворкам, а дисциплине мышления. Говорил студентам: «Не спрашивайте, работает ли программа. Спрашивайте, почему она должна работать».
Язвительность и принципиальность. Он не боялся говорить неудобные вещи. Критиковал языки программирования, критиковал индустрию за гонку за фичами вместо надёжности, критиковал коллег за неряшливость мышления. Его не всегда любили, но всегда уважали. Потому что он был последователен и глубок.
Одна из его самых известных и спорных цитат: «Использование COBOL калечит ум; обучение ему должно рассматриваться как уголовное преступление» и «BASIC калечит мозг программиста». Звучит резко. Но важен контекст: Дейкстра говорил не о людях, которые используют эти языки, а о том, как инструменты формируют мышление. COBOL и BASIC того времени поощряли плохие практики: спагетти-код, отсутствие структуры, goto повсюду. Человек, который учился программировать на таком языке, усваивал плохие паттерны мышления. Дейкстра считал, что язык должен учить думать правильно, а не просто позволять что-то написать. Это была критика инструментов, а не людей.
Главная идея всей его жизни: программирование — это интеллектуальная деятельность, требующая дисциплины и строгости. Код — это не «что-то, что работает», а формальное доказательство решения задачи. И чтобы доказательство было возможным, код должен быть простым.
Вот такой человек. Когда он говорил о простоте — это не были пустые слова. Это был выстраданный опыт создания сложных систем и понимание, что единственный способ справиться со сложностью — это радикальная простота на каждом уровне.
Как писал код сам Дейкстра
Чтобы понять его философию не абстрактно, а на практике, посмотрим на пример. В своих заметках Дейкстра использовал собственную нотацию — guarded commands (охраняемые команды). Вот как он записывал алгоритм Евклида для нахождения наибольшего общего делителя:
do x > y → x := x − y
□ y > x → y := y − x
od
Что тут происходит? do...od — это цикл. Внутри — два варианта, разделённые символом □ (который означает недетерминированный выбор). Каждый вариант имеет охрану (условие слева от стрелки) и действие (справа). Цикл выполняется, пока хотя бы одна охрана истинна. Когда x = y, обе охраны ложны, цикл завершается, и в x (или y) лежит НОД.
Почему это красиво? Потому что корректность очевидна из структуры. Не нужно трассировать выполнение. Не нужно проверять граничные случаи. Ты смотришь на код и видишь, что он делает и почему это правильно:
- На каждой итерации либо
x, либоyуменьшается (но остаётся положительным) - НОД(x, y) не меняется при вычитании меньшего из большего
- Когда
x = y, это и есть НОД
Три строки. Никакой магии. Никаких скрытых состояний. Программа — это одновременно и алгоритм, и доказательство его корректности. Вот что Дейкстра понимал под простотой.
Сравните с типичной реализацией, которую напишет программист «в лоб»:
def gcd(x, y):
while y != 0:
temp = y
y = x % y
x = temp
return x
Работает? Да. Эффективнее? Да (использует остаток от деления вместо вычитания). Но почему это работает — уже не так очевидно. Нужно знать свойства операции mod, нужно мысленно прокрутить несколько итераций, нужно убедиться, что цикл завершится. Корректность здесь — результат анализа, а не свойство структуры.
Дейкстра не говорил, что его версия лучше для продакшена. Он говорил, что программист должен сначала написать версию, корректность которой очевидна, и потом, если нужно, оптимизировать — сохраняя понимание того, почему оптимизированная версия тоже корректна.
Что Дейкстра понимал под простотой
Слово «простота» превратилось в штамп. Его используют все для обозначения буквально всего. Но у Дейкстры была чёткая концепция, что это такое. Разберём её на составляющие.
Простота ≠ примитивность
Это первое, что нужно понять. Простое решение может быть глубоким, мощным, универсальным. Но оно прозрачно. Ты смотришь — и понимаешь, как оно работает. Не нужно держать в голове десяток контекстов.
Пример: алгоритм Дейкстры поиска кратчайшего пути. Идея проста и элегантна — от стартовой точки расходимся волной, выбирая каждый раз ближайшую непосещённую вершину. Но эта идея лежит в основе решения сложной задачи, которая используется везде: от 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++. Даже эксперты знают подмножество. Стандарт языка — больше тысячи страниц. Компиляторы реализуют его по-разному. Ошибки компиляции шаблонов — страницы нечитаемого текста.
Дейкстра терпеть не мог 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). Это было гордостью языка. Множество способов решить задачу. Свобода выражения. Программист как поэт.
Дейкстра бы сказал: «Это не свобода, это проклятие».
Когда десять способов сделать одно — каждый программист выбирает свой. Код становится нечитаемым для других. Синтаксис перегружен символами: $, @, %, ->, =~, //, ~~. Регулярные выражения вшиты в язык и используются везде, даже где не нужно.
Можно написать одно и то же по-разному. Три программиста напишут три разных варианта. Все работают, но читаются по-разному.
Результат: Perl называли «write-only language». Написать можно, прочитать — нет. Даже свой код через месяц не поймёшь. А чужой — тем более.
Одна из знаменитых шуток: «Perl — это язык, на котором каждая программа выглядит как случайная последовательность символов после того, как кот прошёлся по клавиатуре».
Сейчас Perl почти мёртв как основной язык. Не потому что плохой технически, а потому что непонятный. Код на Perl невозможно поддерживать в команде. Новый человек не может влиться. Legacy-проекты на Perl переписывают на что-то более читаемое.
Почему простота побеждает
Дейкстра предсказывал: языки, которые поощряют сложность, в итоге умирают или маргинализируются. Языки, которые помогают быть простыми, живут и развиваются.
Низкий порог входа. Простой язык можно выучить быстро. Go изучается за неделю. C++ — годами.
Легче поддерживать. Код на простом языке понятен через год. Legacy на простом языке — просто старый код. Legacy на сложном — кошмар.
Меньше багов. Меньше способов ошибиться. Go заставляет обрабатывать ошибки явно. JavaScript позволяет игнорировать — и проект падает в продакшене.
Быстрее онбординг. Новый человек продуктивен за дни, а не месяцы.
Меньше холиваров. Когда один способ делать вещи, не спорят о стиле.
Дейкстра и Вирт: союз минималистов
Никлаус Вирт и Эдсгер Дейкстра были не просто коллегами, а единомышленниками и друзьями. Оба европейцы (Дейкстра — голландец, Вирт — швейцарец), оба академики, оба фанаты простоты и элегантности.
Вирт учился у Дейкстры. Читал его работы, переписывался, встречался на конференциях. И создавал языки, следуя философии Дейкстры: простота как основа надёжности.
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")
# делаем что-то
...
Читается линейно, сверху вниз. Каждое условие проверяется и сразу обрабатывается. Успешный путь — в конце, не спрятан внутри вложенности.
5. Минимизируй состояния
Чем больше изменяемых переменных, тем сложнее отследить, что происходит. Переменная меняется тут, потом там, потом ещё где-то. Через десять строк ты уже не помнишь, какое у неё значение.
Дейкстра любил функциональный подход: функции без побочных эффектов, неизменяемые данные.
Если можешь не изменять — не изменяй. Создай новое значение. Память дешёвая, понимание кода — дорогое.
6. Не оптимизируй раньше времени
«Преждевременная оптимизация — корень всех зол» — сказал Дональд Кнут, друг и коллега Дейкстры.
Сначала напиши просто и понятно. Потом измерь производительность. Если есть проблема — оптимизируй узкое место. Но не жертвуй простотой ради гипотетической скорости.
Дейкстра добавлял: «Простой код часто быстрее сложного. Потому что компилятор его лучше оптимизирует. И потому что в простом коде легче найти настоящее узкое место».
7. Рефактори безжалостно
Первый вариант почти всегда корявый. Это нормально. Дейкстра писал: «Программирование — это итеративный процесс приближения к простоте».
Написал — работает? Отлично. Теперь посмотри критически:
- Можно ли проще?
- Можно ли убрать лишнее?
- Можно ли переименовать понятнее?
- Можно ли разбить функцию?
Рефакторинг — это не «когда будет время». Это часть работы.
8. Пиши для людей, не для компьютера
Компьютер выполнит любой код, лишь бы синтаксически правильный. Но код читают люди. Гораздо чаще, чем пишут.
Статистика: код читается в 10 раз чаще, чем пишется. Значит, оптимизировать нужно для чтения, а не для написания.
Критерий качества: можешь ли ты объяснить код джуну за пять минут? Если да — хорошо. Если нужен час объяснений с рисованием диаграмм — код слишком сложный.
9. Доверяй дискомфорту
Если смотришь на код и чувствуешь: «Что-то не так» — прислушайся. Даже если не можешь объяснить, что именно. Дейкстра называл это «интеллектуальным дискомфортом».
Работает — но некрасиво? Переделай. Потому что «некрасиво» часто означает «сложно, непонятно, хрупко». А это источник будущих проблем.
Твоя интуиция — это накопленный опыт. Если она говорит «тут что-то не так», она, скорее всего, права.
Простота в тексте: бонус для тех, кто пишет не только код
Дейкстра писал не только программы. Он писал статьи, заметки, письма. И его тексты — образец ясности. Те же принципы, которые работают в коде, работают и в письме.
Одна мысль = одно предложение
Не паковать три идеи в длинное предложение с придаточными, причастными оборотами и вводными конструкциями, которые теряются где-то в середине.
Плохо: «Программирование, которое является сложной интеллектуальной деятельностью, требующей дисциплины мышления и умения справляться с абстракциями, становится всё более важным в современном мире, где программы управляют всем — от телефонов до самолётов».
Три мысли в одном предложении. Запутанно.
Лучше: «Программирование — сложная интеллектуальная деятельность. Она требует дисциплины мышления и умения работать с абстракциями. В современном мире программы управляют всем — от телефонов до самолётов».
Три предложения, три мысли. Читается легко.
Короткие абзацы
Один абзац = одна идея. Легче читать, легче понять структуру.
Длинный абзац на экран текста — это стена. Глаз скользит, мозг не цепляется. Короткие абзацы создают ритм, дают передышку.
Смотрите: я в этом тексте использую короткие абзацы. Иногда вообще одно предложение.
Это специально.
Выкидывать лишнее
«В общем-то», «как бы», «достаточно очевидно», «можно сказать что» — мусор. Убирай. Текст станет чище и энергичнее.
Плохо: «Можно сказать, что в общем-то достаточно очевидно, что простой код, как бы, легче поддерживать».
Лучше: «Простой код легче поддерживать».
Закон Оккама в тексте: не умножай слова без необходимости. Каждое слово должно работать.
Конкретность
Не «улучшили производительность», а «ускорили на 30%». Не «много пользователей», а «10 тысяч пользователей». Не «долго», а «три недели».
Конкретные числа, факты, примеры делают текст убедительным. Абстракции — расплывчатым.
Логичная структура
Читатель должен видеть, куда ведёт текст. Введение (о чём текст), развитие (суть), вывод (что запомнить).
Как в коде: начало, середина, конец. Не прыгай с темы на тему. Веди читателя за руку.
Ясность ведёт к ясности
Дейкстра говорил: «Ясность мышления ведёт к ясности письма. Ясность письма ведёт к ясности мышления». Круг замкнулся.
Когда пишешь просто — думаешь просто. Когда думаешь просто — решаешь сложное. Потому что не тратишь энергию на борьбу с собственной запутанной формулировкой.
Поэтому я рекомендую: пишите. Не только код, но и тексты. Документацию, статьи, заметки для себя. Это тренирует ясность мышления. А она нужна везде — в коде, в дизайне, в архитектуре, в общении с командой.
Не находите, что эти принципы буквально один в один повторяют правила инфостиля, хотя написаны задолго до его появления?
«Ясность мышления ведёт к ясности письма. Ясность письма ведёт к ясности мышления» — Дейкстра
Простота в архитектуре: как строить системы, которые можно понять
Дейкстра писал не только о коде, но и об архитектуре систем. И здесь его идеи ещё важнее. Потому что плохая архитектура убивает проект медленно, но неотвратимо.
THE multiprogramming system: слои простоты
В 1968 году Дейкстра спроектировал операционную систему THE — эксперимент по созданию доказуемо корректной сложной системы.
Идея: разбить систему на уровни (layers). Каждый уровень использует только нижние уровни, не знает о верхних. Каждый уровень можно понять и проверить изолированно.
Уровень 5: Пользовательские программы
Уровень 4: Буферизация ввода-вывода
Уровень 3: Управление консолью
Уровень 2: Управление памятью
Уровень 1: Планировщик процессов
Уровень 0: Базовое управление железом
Каждый уровень — простой. Абстракция скрывает сложность нижних уровней. Можно доказать корректность каждого уровня отдельно, не думая обо всей системе сразу.
Результат: система работала надёжно. Без сбоев. Потому что каждый кусок был понятен и проверен.
Признаки хорошей архитектуры
Ясные границы. Каждый модуль имеет чёткую зону ответственности. Не «модуль, который делает всё, связанное с пользователями», а «модуль аутентификации», «модуль профилей», «модуль подписок».
Минимум зависимостей. Если чтобы понять модуль А, нужно изучить модули B, C, D, E — архитектура плохая. Хорошая архитектура: каждый модуль зависит от минимума других, через явные интерфейсы.
Абстракция скрывает сложность. Нижний уровень может быть сложным внутри. Но он предоставляет простой интерфейс верхнему уровню. Пример: база данных внутри сложная (индексы, транзакции, блокировки). Но клиент видит простой интерфейс: query(), insert(), update(). Вся сложность спрятана.
Можно понять каждую часть изолированно. Открываешь модуль — понимаешь, что он делает, без чтения половины кодбейза. Это возможно только если зависимости явные и минимальные.
Антипример: микросервисы, которые не микро
Компания решает: «У нас монолит, это плохо, давайте на микросервисы!»
Разрезают монолит на 20 сервисов. Но сервисы общаются друг с другом через хитрые схемы. Сервис A вызывает B, который вызывает C, который отправляет событие в D, которое обрабатывается E, который обновляет данные, которые читает A.
Циклические зависимости. Неявные зависимости через общую базу данных. Общее состояние в кэше. События, которые летают в разные стороны, и никто не знает, кто на них подписан.
Результат: вместо одной непонятной системы получили 20 непонятных, плюс сложность сетевого взаимодействия. Сложность умножилась.
Дейкстра бы сказал: «Вы взяли монолит и размазали его по 20 процессам. Сложность не уменьшилась, она стала распределённой. А это хуже, потому что теперь добавились проблемы сети, латентности, частичных отказов».
Микросервисы не упрощают систему. Они перемещают сложность из кода в архитектуру. И добавляют новую сложность. Используйте, только если проблемы монолита (развёртывание, масштабирование, независимость команд) реально болят. Иначе простой модульный монолит — лучше.
Почему это важно: личный опыт
Я тоже начинал с написания программ на бумаге и мысленной отладки. Но не потому, что руководствовался подходом Дейкстры, а по более прозаичной причине — у меня не было компьютера, на котором я мог бы это проверить. Но это дало мне нехилый буст: многие мои программы работали (и до сих пор такое бывает) буквально «с листа», то есть без какой-либо отладки.
Когда компьютер оказался под рукой, я поначалу забил на обдумывание на бумаге. Да и коллеги не понимали, зачем вся эта возня с простотой. Код работает — и ладно. Зачем переписывать, если и так нормально? «Лучшее враг хорошего» и всё такое.
Понял, когда вернулся к своему коду через полгода.
Случай из жизни. Непонятный код
Работал над программой со сложной логикой: серверная часть системы банк-клиент — приём платежей, проверка подписи, рассылка обновлений и всё такое. Написал. Работает. Тесты проходят.
Через полгода пришлось добавлять функциональность. Открыл файл с программой. Функция на 200 строк, с вложенными if’ами на четыре уровня, с переменными типа temp1, result2, flag. Комментариев нет — «код же самодокументируемый» (ну да, конечно).
Пытаюсь понять логику. Час чтения. Рисую схему на бумаге. Ещё час. Начинаю вспоминать: «Ага, вот тут я учитывал случай, когда…» Но детали ускользают. Мой собственный код, а я не могу понять за час.
В итоге потратил день, чтобы разобраться и исправить баг. День на то, что могло занять полчаса, будь код простым.
И подумал: если я, автор, не могу понять через полгода, то кто-то другой не разберётся вообще никогда.
Урок: простота экономит время
С тех пор, когда пишу, спрашиваю себя: «Пойму ли я это через полгода?»
Если нет — упрощаю. Разбиваю функции. Переименовываю переменные. Добавляю комментарии там, где логика неочевидна (хотя если нужны комментарии, может, код слишком сложный?).
Это инвестиция. Трачу лишние 20 минут сейчас на рефакторинг — экономлю часы (или дни) потом.
И знаете что? Это меняет отношение к работе. Когда пишешь код, который не стыдно показать, который сам понимаешь, который элегантен — появляется гордость. Не высокомерие, а простое удовлетворение: «Да, вот это сделано хорошо».
Простой код = меньше стресса
Ещё один эффект, который не ожидал: простой код снижает стресс.
Когда работаешь со сложным, запутанным кодом — всё время тревога. Боишься что-то сломать. Любое изменение — риск. Не понимаешь последствий. Делаешь — и надеешься, что не упадёт.
Это выматывает. Программисты выгорают не от сложных задач, а от сложного кода. От того, что каждый день — борьба с хаосом, а не решение интересных проблем.
Когда работаешь с простым кодом — спокойствие. Понимаешь, что делает каждый кусок. Можешь изменить — и быть уверенным, что не сломал другое. Тесты проходят — и ты знаешь почему, а не просто «повезло».
Простота — это забота о других
Ещё один аспект: когда пишешь просто, ты заботишься о тех, кто придёт после тебя.
О коллеге, который будет поддерживать твой код. О новичке, который будет изучать кодбейз. О себе в будущем, когда забудешь детали.
Писать просто — это эмпатия. Ты думаешь не только «как бы быстрее написать», но «как сделать так, чтобы другие поняли».
Команды, где люди пишут просто, работают лучше. Потому что нет барьеров понимания. Новый человек входит в проект быстро. Code review проходит легко. Баги находятся и чинятся быстро.
Простота — это не личное предпочтение. Это профессионализм.
Финал: урок Дейкстры для нашего времени
Дейкстра умер больше двадцати лет назад. Мир программирования с тех пор изменился радикально. Языки, инструменты, практики — всё другое.
Но проблемы остались те же. Сложность не исчезла. Она выросла.
Проекты стали больше — миллионы строк кода. Команды стали больше — сотни разработчиков. Системы стали сложнее — микросервисы, распределённые базы данных, облачная инфраструктура.
И в этом хаосе единственное противоядие — простота.
Простота — это труд
Вот в чём парадокс: простота — это труд.
Сложность получается сама. Начинаешь писать — и код усложняется естественным образом. Добавил фичу — стало сложнее. Ещё фичу — ещё сложнее. Хаос растёт.
Чтобы оставаться простым, нужно напрягаться. Бороться. Постоянно. После каждой фичи спрашивать: можно ли проще? Можно ли рефакторить? Можно ли убрать лишнее?
Это как поддерживать порядок в доме. Беспорядок накапливается сам. Порядок требует усилий. Регулярных, постоянных. Но если не прикладывать усилия — в какой-то момент живёшь в хаосе.
То же с кодом. Технический долг накапливается сам. Чтобы его не было — нужна дисциплина.
Дейкстра учил: дисциплина мышления — основа программирования. Не талант, не опыт (хотя они помогают), а дисциплина. Умение остановиться и подумать. Умение переделать, когда получилось коряво. Умение сказать «нет» новой фиче, если она усложняет без реальной пользы.
Что делать дальше
В следующий раз, когда будете писать код:
Остановитесь на минуту перед написанием. Подумайте: что должна делать эта функция? Какие входные данные, какие выходные? Какие граничные случаи?
Посмотрите на функцию. Она делает одно или три разных дела? Если больше одного — разбейте.
Прочитайте переменные. Их имена объясняют, что внутри?
temp,data,result2— плохие имена. Переименуйте.Проверьте вложенность. Больше трёх уровней? Рефакторите.
Спросите себя: можно ли проще? Всегда можно. И если ответ «да, можно лучше» — сделайте лучше. Не «потом». Сейчас.
Простота заразительна
Когда в команде кто-то начинает писать просто и чисто, остальные подтягиваются. Потому что никто не хочет быть тем, чей код выглядит грязным на фоне чистого.
Вы можете быть тем человеком, который задаёт планку. Пишите просто. На code review спрашивайте: «А можно ли тут упростить?» Не критикуйте человека, критикуйте код. И предлагайте альтернативы.
Люди учатся. Постепенно. И команда становится лучше.
Дейкстра уже не с нами. Но его голос звучит в каждой строке хорошо написанного кода.
В каждой функции, которая делает одно и делает правильно. В каждом модуле с ясными границами. В каждом проекте, который живёт десятилетиями, потому что его не страшно поддерживать.
Его наследие — это идея, что простота — это профессионализм, а не слабость. Что сложность — не показатель ума, а признак недоработки. Что хороший программист не тот, кто пишет умный код, который сложно понять. А тот, кто пишет простой код, который решает сложные задачи.
«Если через 10 лет, когда вы будете делать что-то по-быстрому и грязно, вы внезапно представите, что я смотрю вам через плечо, и скажете себе: “Дейкстре бы это не понравилось” — что ж, для меня этого бессмертия достаточно»
Пишите код, который не стыдно показать.

Эдсгер Дейкстра 1930–2002
Что почитать
Тексты Дейкстры — 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?» (его знаменитые едкие высказывания о языках)
- «Go To Statement Considered Harmful» (1968)
- «The Humble Programmer» (1972) — речь при получении Тьюринговской премии
Про Вирта и минимализм:
- «Algorithms + Data Structures = Programs» (1976)
- «Project Oberon» (2013) — книга о создании ОС с нуля
Современные книги о простоте:
- John Ousterhout, «A Philosophy of Software Design» (2018)
- Sandi Metz, «Practical Object-Oriented Design» (2018)
Доклады:
- Rich Hickey, «Simple Made Easy» — один из лучших докладов о разнице между «простым» и «лёгким»