
Почему разработчики отказываются от C++ и переходят на C
Прочитал новость: разработчик вредоноса Vidar полностью отказался от C++ в пользу языка C. По его словам, это позволило добиться лучшей стабильности и производительности.
И знаете что? Это удивительным образом совпало с моими мыслями.
Если сформулировать коротко: усложнение языков программирования в конечном счёте ухудшает качество кода. Снижает стабильность. Убивает читаемость. Код становится сложнее поддерживать, сложнее понять, легче добавить ошибку.
Последствия нарушения принципа KISS (Keep It Simple, Stupid) в чистом виде.
И дело не в том, что я или автор Vidar’а не владеем C++ на хорошем уровне. Проблема в самом C++. В бесконечном богатстве возможностей языка.
Это приводит к тому, что просто физически невозможно держать в фокусе все синтаксические конструкции. Каждый раз подглядывать в справочник — не выход. Тем более, если речь идёт о процессе проектирования.
Никлаус Вирт был прав
В этом плане я разделяю позицию гениального Никлауса Вирта о том, что ядро языка должно быть максимально компактным.
Он не только говорил об этом, но и следовал этому принципу в Паскале, Модуле-2 и Обероне. Каждый язык содержал меньшее количество ключевых слов, чем предшественник, сохраняя при этом функциональность и возможность выполнять поставленные задачи.
Вирт понимал: чем проще язык, тем яснее мышление программиста. Тем меньше мест, где можно выстрелить себе в ногу.
К сожалению, «в массы» этот принцип не пошёл.
Игрушки вместо инструментов
Разработчикам нравится играть в новые игрушки, которые появляются в языках программирования.
Итераторы. Декораторы. Замыкания. Тернарные операторы. Однострочники. Классы. Конструкторы и деструкторы. Магические функции. Шаблоны. Лямбды. Концепты. Корутины.
Всё это делает код компактнее. Но… сложнее для понимания.
Почему?
Да потому что все эти фичи маскируют реальную сложность кода.
Создают абстракции, в которых прячутся важные детали работы программы.
Это позволяет даже новичкам замутить и даже заставить работать масштабные программы. Но…
Во многих случаях получаются колоссы на глиняных ногах. Гигантского размера. Тормозные. Глючные.
Они падают от неловкого движения мыши или ввода «не того» символа. Они содержат уязвимости, поэтому их любят хакеры.
Преимущества? Есть. Но какой ценой
Справедливости ради: у современного подхода есть преимущества.
Во-первых, разрабатывать можно быстро. Благодаря фреймворкам-кубикам, прячущим действительно сложные вещи.
Во-вторых, разработкой могут заниматься не слишком квалифицированные люди, которым не придётся много платить.
Ну а то, что качество страдает? Так придумали же SAST, DAST, IAST, OWASP ZAP, юнит-тесты и прочие сканеры ошибок и уязвимостей.
Какую-то часть багов отловит — и ладно.
Остальные в новой версии пофиксим.
Опять же, SDLC — жизненный цикл воплотим. Выпустим мейджор апдейт, денег за него попросим.
Но какой ценой?
Ценой фундаментального качества.
Ценой понимания, что происходит в программе.
Ценой контроля над собственным кодом.
Rust: безопасность через нечитаемость?
Ещё меня расстраивает синтаксис современных языков.
Посмотрите на Rust.
Конструкции просто угнетают своей нечитаемостью:
async fn fetch_data<T: DeserializeOwned>(url: &str) -> Result<T, Box<dyn Error>> {
    let response = reqwest::get(url).await?.json::<T>().await?;
    Ok(response)
}
Для человека, который не пишет на Rust каждый день, это шифр.
DeserializeOwned? Box<dyn Error>? .await??
Каждая конструкция — отдельная абстракция. И чтобы понять код, нужно держать в голове все эти абстракции одновременно.
А имена библиотек? Почему асинхронная либа называется tokio? Какое отношение японская столица имеет к асинхронной диспетчеризации?
Этот язык называют безопасным. И да, borrow checker ловит целый класс ошибок.
Но с точки зрения понимания написанного кода он сильно проигрывает тому же C.
В C ты видишь, что происходит.
Выделил память — освободи. Взял указатель — следи за ним. Никакой магии. Никаких скрытых аллокаций. Никаких «умных» абстракций, которые делают «что-то» за кадром.
Ты контролируешь каждый байт.
И это не недостаток. Это преимущество.
C++: от простоты к хаосу
Вернёмся к C++.
Когда Бьёрн Страуструп создавал C++, он хотел добавить к C объектно-ориентированное программирование. Классы, наследование, инкапсуляцию.
Это было разумно.
Но потом началось.
C++98: шаблоны, STL, исключения.
C++11: auto, лямбды, умные указатели, move-семантика, rvalue-ссылки.
C++14: generic lambdas, auto возвращаемых типов.
C++17: структурные связывания, fold expressions, if constexpr.
C++20: концепты, корутины, ranges, модули.
C++23: ещё больше фич.
С каждой версией язык растёт. Добавляются новые возможности. Новые абстракции. Новые способы сделать то же самое.
И теперь один и тот же код можно написать десятью разными способами.
Какой выбрать?
Использовать старый стиль с указателями? Или умные указатели? Или вообще избегать динамической памяти и использовать std::vector?
Писать цикл for? Или range-based for? Или std::for_each с лямбдой? Или std::ranges::for_each?
Обрабатывать ошибки через коды возврата? Через исключения? Через std::optional? Через std::expected (в C++23)?
Каждый раз — выбор.
И этот выбор отнимает ментальную энергию.
Энергию, которую можно было бы потратить на решение реальной задачи, а не на выбор синтаксической конструкции.
Когнитивная нагрузка
Есть понятие когнитивной нагрузки — количества информации, которое мозг может удержать одновременно.
Чем сложнее язык, тем выше когнитивная нагрузка.
Когда ты пишешь на C++, ты должен помнить:
- правила вывода типов (auto, decltype);
 - правила move-семантики;
 - когда вызываются конструкторы и деструкторы;
 - что делает каждая стандартная библиотечная функция;
 - какие есть особенности шаблонов;
 - как работают лямбды и захват переменных;
 - правила constexpr;
 - и ещё сотни нюансов.
 
Всё это одновременно.
А теперь представь: ты пишешь на C.
Ты должен помнить:
- базовые типы данных;
 - указатели;
 - функции;
 - структуры;
 - правила области видимости.
 
Всё.
Остальное — библиотеки. Которые написаны на том же C и работают предсказуемо.
Когнитивная нагрузка в разы меньше.
А значит, больше ментальных ресурсов на решение задачи. На продумывание архитектуры. На поиск элегантного решения.
Читаемость — это не роскошь
Код читают чаще, чем пишут.
По разным оценкам, соотношение чтения к написанию — 10:1.
То есть на каждую минуту написания кода приходится 10 минут чтения.
Читаемость критически важна.
И здесь C выигрывает.
Возьмём простой пример: работа со списком.
C++:
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto result = std::accumulate(numbers.begin(), numbers.end(), 0,
    [](int sum, int n) { return sum + n * n; });
Что происходит? Вроде ясно… если ты знаешь, что такое std::accumulate, как работают итераторы, что такое лямбда.
C:
int numbers[] = {1, 2, 3, 4, 5};
int result = 0;
for (int i = 0; i < 5; i++) {
    result += numbers[i] * numbers[i];
}
Что происходит? Абсолютно ясно. Даже если ты не программист.
Цикл. На каждой итерации берём число, возводим в квадрат, прибавляем к результату.
Никакой магии. Никаких абстракций.
Абстракции: друзья или враги?
Скажете: «Но абстракции — это хорошо! Они позволяют не думать о деталях!»
Да. Иногда.
Абстракции хороши, когда они скрывают несущественные детали.
Например, функция malloc() абстрагирует детали работы с операционной системой. И это правильно — мне не нужно знать, как ядро управляет памятью.
Но абстракции плохи, когда они скрывают существенные детали.
Когда ты вызываешь std::vector::push_back() — что происходит?
Может, просто добавление элемента. А может, реаллокация всего массива. Копирование элементов. Освобождение старой памяти.
Ты не знаешь.
Абстракция скрыла. И если ты не залезешь в документацию (или исходники), ты не поймёшь, почему программа тормозит.
В C таких сюрпризов нет.
Ты сам управляешь памятью. Сам решаешь, когда выделять, когда освобождать, когда копировать.
Да, это требует больше кода. Но это даёт контроль и понимание.
Производительность и предсказуемость
Разработчик Vidar’а упомянул производительность.
И это не случайно.
C++ с его абстракциями часто создаёт неожиданные overhead’ы.
Виртуальные функции — таблица виртуальных методов, дополнительная индирекция.
Исключения — раскрутка стека, дополнительные структуры данных.
Умные указатели — счётчики ссылок, атомарные операции (в std::shared_ptr).
RAII — конструкторы и деструкторы, которые вызываются неявно.
Каждая абстракция имеет цену.
Иногда цена мала. Иногда — значительна.
Проблема: ты не всегда знаешь заранее, какая будет цена.
В C всё явно.
Нет виртуальных функций — нет дополнительных вызовов.
Нет исключений — нет раскрутки стека.
Нет умных указателей — нет счётчиков ссылок.
Каждая операция предсказуема.
Ты видишь код — и понимаешь, сколько он будет работать.
Стабильность через простоту
Разработчик Vidar’а также упомянул стабильность.
И это логично.
Чем проще код, тем меньше мест, где может спрятаться баг.
В C++ баг может быть:
- в неправильном использовании move-семантики;
 - в некорректном захвате переменных в лямбде;
 - в неожиданном вызове деструктора;
 - в race condition в std::shared_ptr;
 - в исключении, которое вылетело не оттуда, откуда ожидалось;
 - в template metaprogramming, который скомпилировался, но делает не то;
 - в неявном преобразовании типов;
 - и ещё в тысяче мест.
 
В C багов тоже хватает. Но они проще.
Забыл освободить память — утечка.
Разыменовал нулевой указатель — segmentation fault.
Вышел за границы массива — undefined behaviour.
Проблемы очевидны. Их легче найти. Легче исправить.
В C++ проблема может быть неочевидной. Спрятанной за несколькими уровнями абстракций.
Код выглядит правильно. Компилируется. Но не работает. Или работает непредсказуемо.
И ты тратишь часы на отладку, пытаясь понять, где в цепочке шаблонов, конструкторов, неявных преобразований и move-семантики что-то пошло не так. Это не программирование, это шаманство. Танцы с бубнами вокруг пылающего костра синтаксических конструкций и абстракций.
Простота делает баги видимыми.
Сложность — прячет их.
Проблема «правильного» C++
Скажете: «Но если писать на C++ правильно, используя modern C++, следуя best practices, то всё будет хорошо!»
Вот именно. Если.
Проблема в том, что «правильный» C++ — это движущаяся цель. То, что считалось best practice в C++98, устарело в C++11. То, что рекомендовали в C++11, пересмотрели в C++17. В C++20 появились новые парадигмы, которые снова меняют «правильный» стиль.
Каждые несколько лет правила игры меняются.
И если ты работаешь с legacy кодом (а кто не работает?), ты видишь смесь стилей.
Код на C++98 с голыми указателями и new/delete. Код на C++11 с умными указателями и лямбдами. Код на C++17 с std::optional и структурными связываниями.
Всё в одном проекте.
И чтобы понять, что происходит, ты должен знать все эти стили. Все эти парадигмы.
В C таких проблем нет.
C89 и C99 отличаются минимально. C11 добавил немного фич, но базовый язык тот же.
Код, написанный 30 лет назад, выглядит похоже на код, написанный сегодня.
Стабильность. Предсказуемость. Преемственность.
Размер стандарта
Показательный факт.
Стандарт C11: около 700 страниц.
Стандарт C++20: более 1800 страниц.
Почти в три раза больше.
И это не потому, что C++ делает что-то принципиально другое. Нет, он делает то же самое, но предлагает десятки способов это сделать.
Можешь ли ты держать в голове 1800 страниц спецификации?
Нет.
Никто не может.
Даже эксперты по C++ признают: язык стал слишком большим. Никто не знает его полностью.
А как ты можешь эффективно использовать инструмент, который не понимаешь полностью?
Время компиляции
Ещё один аспект: скорость компиляции.
C++ с его шаблонами, особенно heavy template metaprogramming, компилируется мучительно долго.
Проекты на сотни тысяч строк могут компилироваться десятки минут.
Да, C++20 обещал модули, которые должны ускорить компиляцию. Но на практике их поддержка в компиляторах и toolchain’ах всё ещё неполная.
C компилируется быстро.
Очень быстро.
Проект аналогичного размера — секунды, может быть, минуты, но не десятки минут.
Быстрая компиляция — это не просто удобство. Это короткий цикл обратной связи. Написал код — скомпилировал — проверил — исправил. Чем быстрее этот цикл, тем быстрее ты находишь ошибки. Тем продуктивнее работа.
Размер бинарников
И ещё: размер исполняемых файлов.
C++ с STL, исключениями, RTTI создаёт большие бинарники.
Даже простая программа «Hello, World» на C++ с iostream может весить сотни килобайт.
На C аналогичная программа — десятки килобайт.
Для десктопных приложений это, может, не критично. Особенно на фоне Electron-приложений размером по полгига.
Но для embedded систем, где каждый килобайт на счету? Для вредоносов, которые хотят быть незаметными? Для утилит, которые должны быть лёгкими и быстрыми?
Размер имеет значение.
Контроль vs удобство
В программировании всегда есть компромисс.
Между контролем и удобством.
Языки высокого уровня (Python, JavaScript, Java) дают удобство. Автоматическая память, garbage collection, богатые стандартные библиотеки.
Но жертвуют контролем. Ты не знаешь точно, когда память освободится. Не контролируешь размещение данных в памяти. Не можешь выжать максимум производительности.
C даёт контроль. Полный. Ты управляешь каждым байтом. Каждой аллокацией. Каждой операцией. Но это требует дисциплины. Внимания. Понимания.
C++ пытается быть посередине. Дать и контроль (как в C), и удобство (абстракции, STL, умные указатели). Но в результате получается гибрид, который:
- сложнее C (теряем простоту);
 - медленнее компилируется;
 - создаёт большие бинарники;
 - менее предсказуем;
 - требует знания огромного языка.
 
А контроль? Он есть, но спрятан за слоями абстракций. И чтобы его использовать, нужно понимать, что эти абстракции делают.
Философия Unix: делай одно, но хорошо
В философии Unix есть принцип: каждая программа должна делать одно дело, но делать его хорошо.
Можно применить к языкам программирования.
C делает одно дело: даёт тонкий слой абстракции над ассемблером. Позволяет писать эффективный, портируемый код, сохраняя контроль.
Делает это хорошо. Просто. Понятно. Стабильно.
C++ пытается делать всё:
- объектно-ориентированное программирование;
 - обобщённое программирование (шаблоны);
 - функциональное программирование (лямбды, ranges);
 - метапрограммирование (template metaprogramming, constexpr);
 - низкоуровневое программирование (как в C);
 - высокоуровневое программирование (STL, абстракции).
 
Но чем больше задач решает инструмент, тем менее эффективно он решает каждую.
Принцип KISS в действии
Keep It Simple, Stupid.
Этот принцип применим не только к коду, но и к выбору инструментов.
Чем проще инструмент, тем:
- легче его освоить;
 - легче его использовать правильно;
 - меньше способов ошибиться;
 - яснее последствия действий;
 - предсказуемее результат.
 
C — простой инструмент.
Небольшой язык. Понятные правила. Предсказуемое поведение.
C++ — сложный инструмент.
Огромный язык. Множество правил и исключений. Неочевидное поведение в corner cases.
Выбор очевиден.
Если задача не требует сложных абстракций C++ (а большинство задач не требует), зачем платить цену сложности?
Когда C++ оправдан
Справедливости ради: есть задачи, где C++ имеет смысл.
Если тебе нужны:
- сложные структуры данных с автоматическим управлением ресурсами (STL контейнеры);
 - полиморфизм и наследование (ООП);
 - generic программирование на уровне библиотек (шаблоны);
 - и при этом производительность, близкая к C.
 
Тогда C++ — разумный выбор.
Но это не большинство задач.
Для системного программирования, embedded, драйверов, ядра ОС, высокопроизводительных серверов, небольших утилит — C достаточно.
И даёт преимущества: простоту, предсказуемость, контроль, скорость компиляции, размер бинарников.
Индустрия и мода
Но почему тогда индустрия продолжает усложнять языки?
Потому что это выгодно.
Новые фичи — это маркетинг.
«Смотрите, в новой версии языка X появились корутины / pattern matching / async-await / [вставить модную фичу]!»
Это создаёт хайп. Привлекает разработчиков. Оправдывает конференции, книги, курсы.
Простота не продаётся.
«Наш язык такой же, как 10 лет назад, просто стабильный и надёжный» — это скучно.
Никто не напишет статью. Не выступит на конференции.
Сложность продаётся.
«Изучи 10 новых фич C++23!» — это интересно. Кликабельно. Монетизируемо.
И разработчики языков играют в эту игру.
Каждая новая версия — новые фичи. Больше, сложнее, круче.
Но код от этого лучше не становится.
Становится сложнее. Менее понятным. Менее надёжным.
Пирамида абстракций
Современная разработка — это пирамида абстракций.
Внизу — железо.
Выше — ассемблер.
Выше — C.
Выше — C++ (или другой системный язык).
Выше — фреймворки.
Выше — высокоуровневые языки (Python, JavaScript).
Выше — ещё более высокоуровневые фреймворки.
Чем выше поднимаешься, тем меньше контроля.
Тем больше «магии». Тем меньше понимания, что происходит внизу.
И это нормально для многих задач.
Если ты пишешь веб-приложение, тебе не нужно думать про управление памятью. Python + Django — отличный выбор.
Но когда тебе НУЖЕН контроль?
Когда производительность критична. Когда размер бинарника важен. Когда надёжность — вопрос жизни и смерти (embedded в медицине, авиации).
Тогда спускайся вниз по пирамиде.
К более простым, более контролируемым инструментам.
К C.
Обучение: начинать с простого
Ещё один аспект: обучение программированию.
Многие университеты начинают с C++ или Java.
Это ошибка.
Начинающему программисту нужно понять фундаментальные концепции:
- как работает память;
 - что такое указатель;
 - как функции вызываются;
 - как данные хранятся.
 
C учит этому напрямую.
Ты видишь память. Управляешь ей. Понимаешь, что происходит.
C++ прячет это за абстракциями.
Студент может написать программу на C++, не понимая, что такое указатель. Что такое стек и куча. Как работает память.
Он использует std::vector, std::string, умные указатели — и ему кажется, что он понимает.
Но он не понимает фундамента.
И потом, когда нужно оптимизировать. Или отладить seg fault. Или написать low-level код.
Он беспомощен.
Потому что не понимает, что происходит под капотом.
Начинать нужно с C.
Понять основы. Научиться работать с памятью вручную. Понять указатели, структуры, функции.
А потом, если нужно, переходить к более высоким абстракциям.
С пониманием, что они делают. И какой ценой.
Минимализм как философия
В мире, где всё стремится к усложнению, минимализм — это бунт.
Отказ от лишнего. Фокус на существенном. Ясность вместо украшательства.
C — это минимализм в программировании.
Никаких лишних фич. Никаких «а давайте добавим ещё одну возможность».
Только то, что действительно нужно:
- базовые типы данных;
 - указатели;
 - функции;
 - структуры;
 - препроцессор (да, спорный, но полезный инструмент).
 
Всё.
Этого достаточно для решения практически любой задачи системного уровня.
Linux написан на C. PostgreSQL написан на C. Redis написан на C. Git написан на C.
Самые надёжные, быстрые, проверенные временем проекты написаны на C.
Не потому, что авторы не знали C++. А потому что осознанно выбрали простоту.
Ментальная модель
Когда ты программируешь, в голове формируется ментальная модель того, что делает код.
Чем проще язык, тем точнее ментальная модель соответствует реальности.
В C:
Ты видишь malloc(100) — понимаешь: выделяется 100 байт на куче.
Ты видишь ptr->field — понимаешь: разыменование указателя и доступ к полю структуры.
Ты видишь цикл for — понимаешь: повторение блока кода N раз.
Ментальная модель = реальность.
В C++:
Ты видишь auto x = vec.begin() — что такое x? Iterator. Какой именно? Зависит от vec. Что он делает при инкременте? Зависит от реализации.
Ты видишь std::move(obj) — что происходит? Вызывается move-конструктор. Или нет, если он не определён. Тогда copy-конструктор. Или ошибка компиляции.
Ты видишь { — фигурную скобку — что она означает? Блок кода? Инициализацию? Uniform initialization? Aggregate initialization?
Ментальная модель ≠ реальность.
Между твоим пониманием и тем, что реально происходит — пропасть.
Заполненная неявными преобразованиями, перегрузками операторов, template magic, оптимизациями компилятора.
Эта пропасть — источник багов.
Отладка: чем проще, тем легче
Когда программа не работает, нужно найти причину.
В C отладка относительно проста.
Ставишь breakpoint. Смотришь значения переменных. Смотришь указатели. Проходишь по коду step-by-step.
Что видишь в отладчике = что происходит в программе.
В C++ отладка — квест.
Step into в функцию — попадаешь в недра STL. В шаблонный код с непроизносимыми именами типов.
Смотришь на stack trace — десятки фреймов из библиотечного кода, которые не относятся к твоей логике.
Пытаешься понять, почему деструктор вызвался не там, где ожидал. Пытаешься разобраться, почему move-семантика сработала не так.
Между твоим кодом и реальным исполнением — слои абстракций. Которые отладчик показывает. Но которые мешают понять суть проблемы.
Зависимости и экосистема
C имеет минимальные зависимости. Стандартная библиотека C (libc) есть везде. Это часть любой POSIX-системы. Написал код на C — он скомпилируется на Linux, macOS, BSD, даже на экзотических платформах.
C++ зависит от тяжёлой стандартной библиотеки. libstdc++ или libc++ — большие библиотеки. Не всегда доступны на минималистичных системах. И если ты используешь современные фичи C++17/20 — тебе нужен новый компилятор. Которого может не быть на целевой системе.
Портируемость C — его сила.
Код, написанный 20 лет назад, компилируется сегодня на любой платформе.
Портируемость C++ — вопрос.
Особенно если используешь суперпередовые фичи.
Время жизни знаний
Ты учишь язык программирования. Тратишь месяцы, годы. Как долго эти знания будут актуальны?
Знания C актуальны десятилетиями.
C89 стандарт — 1989 год. 35 лет назад. И он всё ещё валиден. Код на C89 компилируется и работает.
Выучил C — эти знания с тобой на всю карьеру.
Знания C++ устаревают быстро.
C++11 изменил язык кардинально. C++17 добавил новые парадигмы. C++20 — ещё больше.
То, что ты выучил 5 лет назад, уже не best practice. Нужно постоянно учиться заново. Следить за новыми стандартами. Обновлять знания.
Инвестиция в изучение C — долгосрочная.
Инвестиция в изучение C++ — требует постоянного обновления.
Сообщество и культура
В сообществах C и C++ разная культура.
C-программисты, как правило:
- ценят простоту;
 - понимают, что происходит на низком уровне;
 - пишут явный, понятный код;
 - не гонятся за модой;
 - используют проверенные подходы.
 
C++-программисты часто:
- гонятся за новыми фичами;
 - спорят о best practices (которые меняются каждые несколько лет);
 - используют сложные абстракции, чтобы показать владение языком;
 - увлекаются template metaprogramming и другими «умными» техниками.
 
Конечно, это стереотипы. Есть прагматичные C++-программисты и есть любители усложнять на C.
Но тенденция есть.
C притягивает минималистов.
C++ притягивает тех, кто любит сложность и мощь.
Безопасность через ясность
Часто говорят: «C небезопасен. Buffer overflow, use after free, все эти уязвимости».
Это правда. C не защищает от ошибок программиста.
Но вот парадокс: когда ты знаешь, что язык не защищает — ты осторожнее. Ты проверяешь границы массивов. Валидируешь входные данные. Думаешь о том, что может пойти не так. Ты несёшь ответственность.
C++ создаёт иллюзию безопасности.
«У меня std::vector, он автоматически управляет памятью — значит, я в безопасности».
Но это не так. Уязвимости никуда не делись. Они просто спрятались в других местах:
- use after move;
 - iterator invalidation;
 - data races в многопоточном коде;
 - исключения, нарушающие invariants;
 - undefined behaviour в шаблонах.
 
Иллюзия безопасности опаснее, чем осознание опасности. Когда ты знаешь, что на минном поле — идёшь осторожно. Когда думаешь, что в безопасности — расслабляешься и подрываешься.
Что говорят мастера
Интересно, что многие мастера индустрии высказываются в пользу простоты.
Линус Торвальдс, создатель Linux, известен критикой C++. Ядро Linux написано на C. Осознанно.
Его аргументы: C++ слишком сложен, создаёт плохие абстракции, приводит к раздутому коду.
Роб Пайк, один из создателей Go, работал над Unix и Plan 9 (написаны на C). Go создавался как реакция на сложность C++.
Кен Томпсон, создатель Unix и C (вместе с Денисом Ритчи), говорил о важности простоты.
Эти люди не новички. Они создали операционные системы, языки программирования, инструменты, которыми мы пользуемся каждый день.
И они выбрали простоту. Не потому, что не понимают сложные концепции. А потому что понимают их слишком хорошо. Понимают, какую цену платишь за сложность.
Rust: попытка решить проблему
Справедливости ради: Rust — это попытка создать безопасный системный язык.
Решить проблемы C (небезопасность) без проблем C++ (сложность).
Получилось ли?
Частично.
Rust действительно безопаснее. Borrow checker ловит целый класс ошибок на этапе компиляции.
Но ценой сложности.
Rust, возможно, даже сложнее C++. Кривая обучения крутая. Синтаксис тяжёлый.
И главное: Rust решает проблемы, которые в C решаются дисциплиной.
Если ты внимателен, проверяешь границы, валидируешь данные — большинство проблем C не возникает.
Rust форсит это на уровне языка. Что хорошо для больших команд, где нельзя полагаться на дисциплину каждого.
Но для небольших проектов или опытных разработчиков — это overhead.
Ты платишь сложностью языка за безопасность, которую мог бы обеспечить сам.
Ирония: от C к C++ к C
Забавная ирония индустрии.
1970-е — 1980-е: C доминирует. Системное программирование, Unix, всё на C.
1990-е — 2000-е: C++ набирает популярность. «C устарел, нужны ООП и абстракции».
2010-е: Modern C++, всё больше фич. C++ становится мейнстримом в системном программировании.
2020-е: Часть разработчиков возвращается к C. Или переходит на минималистичные языки (Go, который проще C++).
Полный круг.
Индустрия заново открывает ценность простоты.
После десятилетий усложнения, накопления абстракций, раздувания языков — возвращение к основам.
Разработчик Vidar — не исключение. Это тренд.
Небольшой, пока незаметный. Но он есть.
Вывод: простота — конкурентное преимущество
Итак, почему разработчики отказываются от C++ и переходят на C?
Потому что простота — это сила.
Сила понимания того, что делает код.
Сила контроля над каждым аспектом программы.
Сила предсказуемости поведения.
Сила стабильности и надёжности.
Сила быстрой компиляции и маленьких бинарников.
Сила ясности кода, который легко читать и поддерживать.
C++ предлагает мощь.
Но эта мощь приходит с высокой ценой: сложность, когнитивная нагрузка, скрытые детали, хрупкие абстракции.
Для многих задач эта цена не оправдана.
Особенно когда есть простая альтернатива — C.
Разработчик Vidar это понял. Отказался от C++ в пользу C. Получил стабильность и производительность.
Linux, PostgreSQL, Redis, Git — написаны на C. И работают десятилетиями. Надёжно. Быстро.
Принцип KISS работает не только в коде, но и в выборе инструментов.
Никлаус Вирт был прав: ядро языка должно быть компактным.
Простота — не примитивность. Простота — это ясность. Фокус на существенном. Отсутствие лишнего.
Может быть, пора и тебе пересмотреть свой выбор?
Если ты пишешь на C++, спроси себя:
- использую ли я все эти фичи?
 - нужны ли мне эти абстракции?
 - понимаю ли я, что происходит под капотом?
 - не трачу ли я больше времени на борьбу с языком, чем на решение задачи?
 
Если хоть на один вопрос ответ «нет» — возможно, стоит попробовать C.
Не навсегда. Не для всех проектов.
Но попробовать. Написать небольшую программу. Утилиту. Библиотеку.
Почувствовать разницу. Ясность. Контроль. Простоту.
И, возможно, ты тоже вернёшься к истокам.
К языку, который не мешает думать. К языку, который делает одно дело — но делает его хорошо.
К C.
P.S. Это не призыв отказаться от всех современных языков и писать только на C.
Это призыв задуматься.