Никогда не думал, что это случится со мной, но, похоже, я выгорел. А ещё мне стрёмно. Да, это ещё одна статья про выгорание.
Я тут на днях смотрел на свою RSS-читалку и заметил, что под тегом «C++» у меня где-то три сотни непрочитанных статей. Я не прочитал ни одной статьи по плюсам с прошлого лета, и мне офигенно. Я не написал ни строчки осмысленного кода на плюсах за последние три месяца, с тех пор, как распустили отдел, где я работал, и мне просто супер. Я позволил себе хотеть больше никогда не писать на плюсах, и у меня появились крылья.
Только стало страшно, потому что это давно уже стало куском моей самоидентификации. Я писал на плюсах лет 17, это почти две трети моей жизни, и как-то очень стрёмно всё это выкидывать. Всё моё сеньёрство-помидорство, львиная часть моего опыта — она там, в наступании на плюсограбли. Кто я без своего костюма?
Короче, да, я выгорел. И я не знаю, что делать дальше.
Я очень люблю плюсы. Помню, как схомячил учебник Стивена Праты за неделю поездок в школу в седьмом классе (ну хоть что-то хорошее от дороги в полтора часа в один конец). Помню, как читал распечатку туториала по ACE (помнит ли сейчас кто-то ACE?) на какой-то школьной экскурсии классе в девятом. Потом у меня появился КПК, куда я мог залить chm'ку Саттера и читать её на пьянке у друга на даче (куда меня почему-то больше не приглашали) классе в 11-м. Примерно тогда же я уже и начал зарабатывать первые программистские деньги — снова спасибо плюсам. Мои самые светлые воспоминания того периода — бессонные ночи и встречи рассвета под чтение того же Саттера, Мейерса, Александреску, каких-то статей на RSDN, под написание собственного кода. Я был наивным и не думал о будущем, но я ощущал, что оно у меня есть, и что я его кую вот прямо сейчас. Что все эти бессонные ночи и кодомарафоны на сутки и больше — это моё.
Я люблю то время и благодарен мирозданию за то, что оно у меня было.
Но теперь пришла усталость.
Просто надоело ходить по минам. Нет, не по минному полю, а именно по минам, потому что на минном поле есть участки без мин. 17 лет назад, когда я начинал, писать свой вектор было весело и прикольно, и я мог это сделать за полчаса, а сейчас мне нужно несколько дней и постоянная консультация со стандартом. Тварь я дрожащая или право имею? Получу UB тут или нет? А тут? А тут? Да что там вектор, мы не так давно с khim довольно долго обсуждали, как написать строку, и едва смогли определиться с тем, каким может (или должен) быть тип элементов строки, чтобы не было алиасинга.
Просто надоело смотреть на чужие культи. На моей позапрошлой работе они все были так молоды, вся жизнь впереди хорошие чуваки, и они хорошо знают своё дело — data science, статистику, предметную область, что угодно — но на плюсах они просто отстреливают себе ноги. Тяжело смотреть. И это бессмысленно. Там не нужно то, что плюсы могут дать, там не нужна высокая производительность (всё равно они пишут клей), не нужна близость к железу. К счастью, очередной пришедший тимлид это понял и перевёл всех на питон. Правда, тогда мне пришлось уйти, питон — не мой язык.
А, да, производительность — эту самую топовую производительность плюсы тоже не могут дать.
Сравнить два POD одним memcmp
? Нельзя.
Использовать строки, где внутри char*
, не думая об алиасинге? Нельзя.
Передавать unique_ptr
в функцию, рассчитывая, что оверхеда не будет, как говорят из каждого утюга? Нельзя.
Написать свой вектор для trivially copyable/constructible/etc types, который бы не рассчитывал на то, что компилятор вырежет часть необходимых по стандарту вещей? Нельзя.
Вообще писать современный код, пытаясь при этом быть идиоматичным, но не рассчитывая на то, что компилятор свернёт все эти слои шаблонных конструкций? Нельзя.
Нельзя. Нельзя. Нельзя. Спасибо, но рассчитывать на достаточно умный компилятор приятнее в других языках.
Что там ещё? Автовекторизация? Держи карман шире! Даже в простейших случаях уровня «пробежаться по строке и подсчитать количество вхождений символа» (ассоциации подкидывают название файла laba1.cpp
) рукописный ассемблер оказывается быстрее того, что генерирует gcc -O3 -march=native
с нестандартными расширениями и дёрганьем интринсиков (а что остаётся от плюсов с интринсиками? шедулинг регистров?). Незначительно, на 2-5%, но и я не спец в ассемблере на этом уровне. Без интринсиков и оставаясь исключительно в рамках стандарта? Тогда проигрыш в 2-3 раза. Ладно, это с кодом, про это я скоро напишу отдельную статью.
Даже выделить место под тривиальный тип и выmemcpy
ть туда набор байт — нельзя без дополнительных приседаний, лайфтайм так не начнётся. До C++20. В C++20 начнётся, спасибо, починили, но когнитивная нагрузка языка от этого только выросла.
Когнитивная нагрузка вообще только вырастает, даже когда какие-то вещи исправляют, потому что я же, мать его, специалист, я же должен знать, что починили, когда починили, и как было раньше. Да и C++ хорош поддержкой легаси, а это значит, что ты с этим легаси можешь встретиться. Например, в прошлом месяце ко мне обратился приятель с вопросом о том, как что-то сделать в C++03.
И эта когнитивная нагрузка — она не потому, что задачи того требуют. Это не внутренняя непосредственная сложность предметной области. Это просто, ну, так исторически сложилось.
Смотреть на трёхэтажные конструкции из костылей, кстати, тоже надоело. Было 20 способов инициализации, добавили uniform initialization syntax, стал 21 способ инициализации, но это всё ещё недостаточно uniform, поэтому в C++20 мы сделаем uniform из обычных круглых скобочек. Кстати, кто-нибудь сходу помнит правила выбора конструкторов с initializer list? Что-то там про неявные преобразования с потерей точности, но если значение известно статически, то…
Я постарался выработать правило — если мне сходу неочевидно, что код делает, если мне приходится вспоминать стандарт, если я чувствую неуверенность, то не надо так писать код. Поэтому у нас есть uniform initialization syntax, но я пишу std::vector<int> foo(n, val)
, без всяких фигурных скобочек, потому что я не хочу думать, как интерпретация зависит от определения n
и val
. И мне стыдно так писать, потому что все посоны на раёне пишут посты в блогах, где всё красиво, с фигурными скобочками, со всеми новыми финтами.
То есть, конечно, кто-то может сказать, что чего там эта неуверенность, надо просто код писать и оттачивать навык. Но я не хочу оттачивать навык интерпретации талмуда на полторы тысячи страниц. В других языках это не нужно, даже там, где монады и зигогистоморфные препроморфизмы. Да и 10 лет назад я вполне успешно писал продакшен-код за живые деньги на этих же плюсах, а опыта у меня было точно меньше. Значит, наверное, дело таки не только во мне.
Да и проблемы возникают не только у меня.
- Вот NeoCode написал классную статью про концепты в C++20.
requires C1<T::type> || C2<T::type>
не то же самое, чтоrequires (C1<T::type> || C2<T::type>)
— не прелесть ли? Ну это чтоб не расслабляться, видимо. (╯°□°)╯︵ ┻━┻ - Или вот чувак описывает, почему при переходе на gcc-10 сломался vim. Спойлер: потому что там UB, которым компилятор пока что не пользуется, да и то не факт. Починено вот так: «The workaround vim uses to avoid these failures is to disable buffer overflow checks from being emitted by using -D_FORTIFY_SOURCE=1 define». Это vim, да. Ъ-юникс-хакеры, трушнее некуда.
- Или вот ESR (если эти инициалы о чём-то говорят) пишет, что C кончается.
- Или вот веселуха, например.
- Или вот хороший перевод. И да, я видел код, который ломался из-за похожих вещей.
- Или на днях у чуваков из PVS Studio была статья, где в комментах подискутировали, когда можно использовать
memcpy
/memset
, а когда нельзя. Если разработчики статического анализатора ошибаются в таких довольно прямолинейных вещах, то кто и когда тогда вообще не ошибается? - Или вот, кстати,
memset
в плюсах таки пользоваться нельзя. Никогда. Лично мне потребовалось поковырять стандарт примерно час, чтобы убедить себя, что нельзя (как же так, я всегда думал, что можно!). Чуваки на канале про плюсы на фриноде сначала сказали, что можно, потом обсуждение свернулось в сторону «прост не используйmemset
и не думай)))». Потом, правда, ещё один чувак таки попытался разобраться в проблеме, и попытался доказать, чтоmemset
на плюсах использовать таки можно, потому что алиасинг (и что?). Но на практике, конечно, всем пофиг.
А сколько CVE и прочих уязвимостей и падений появилось из-за того, что где-то не так с указателем поступили, где-то проверка не там была, где-то… Чёрт, как жаль, что я не сохраняю эти вещи.
Короче, это была усталость.
Потом я понял, что мне страшно. У меня развился паралич.
Прошлым летом я устроился на совершенно шикарную работу, моими коллегами были умные, шарящие люди, ежедневно читающие блоги плюсистов и новые пропозалы в стандарт, обсуждающие на ланче что-то из программирования и каких-то новых фишек, а не то, как они провели выходные или какие планы на следующие. Для которых «так нельзя, это потенциальное UB» — не пустой звук, а повод потратить два дня на реализацию фичи вместо двух часов и искренне поблагодарить меня, что я обратил внимание. Но даже они делали ошибки. Я делал ошибки. Мы все делали ошибки. Они не знали то, что я считал базовыми знаниями. Я не знал то, что они считали базовыми знаниями. Никто ничего не знает. Если они не могут, то кто может?
Мне страшно. Мне хочется забиться в угол и плакать. Я не могу быть уверенным как минимум в половине строк, что я пишу. У меня есть чувство, что я строю фекалодендритные конструкции, а убеждение хотя бы самого себя в том, что написанное имеет смысл, занимает неоправданно много времени. Что бы я ни делал, в моём коде будут UB. Я ни на что не могу повлиять. Психологи говорят, что выученная беспомощность тут где-то рядом, так что написание кода на плюсах для психики не очень полезно.
Иронично, что больше всего уверенности у меня в наименее нужных в среднем продакшен-коде вещах — что-то там на темплейтах этакое намутить, на constexpr
вот. Может, это потому, что вся эта мета-ерунда куда ближе к так близкому сердцу принципу «если оно компилируется, то оно работает».
А, кстати о темплейтах. Рабочий проект, где каждый .cpp
-файл компилируется по 5-7 минут даже без оптимизаций? Время до первой диагностики компилятора в те же 5 минут? Пердёж компилятора на десятки мегабайт в случае ошибок? Да, я сохранял в файл и замерял ради интереса. Потребление памяти компилятором в 5-10 гигов на файл? Билдсервер с 32 ядрами и 64 гигами памяти, на которой нельзя запускать больше чем этак 8 параллельных потоков компиляции? Проект на несколько десятков kloc, собирающийся на ней полчаса? Получите, распишитесь.
И тулинг. Мне куда проще находить, на что у меня тратится память, в том же хаскеле, который, как известно, только для факториалов и годится. Системы сборки? Ха. Апгрейд компилятора для прода? Жди лет пять после релиза стандарта. Пакетный менеджер? Ха-ха. Reproducible builds? Ха-ха-ха. Все места, где я работал, на это либо вообще забивали, либо вкладывали какое-то совершенно неадекватное количество ресурсов. Я понимаю, почему так происходит, у этого всего есть абсолютно логичные и объективные причины, по-другому и выйти не могло, но я устал так жить.
Это был страх от языка, но я пропустил его через себя. Три месяца назад меня уволили, я пришёл домой, расслабился, вот уже три месяца пишу код для себя (не на плюсах!), сделал пару мелких проектов и начал один покрупнее, и мне хорошо.
Было хорошо.
Совсем недавно я поймал и осознал страх другого рода, куда более экзистенциальный.
Кто я такой? Я программист на плюсах. Я пишу код на плюсах две трети жизни. Это действительно стало огромной частью моей самоидентификации. Почему я претендую на синиора? Потому что я успел отстрелить себе миллион ног и натаскать свою нейросеть в черепушке определять, какая нога отстрелена на этот раз, по отсвету дульной вспышки. Потому что все интервью, касающиеся языка, я проходил, как тут говорят, with flying colors. Потому что я могу даже не готовиться к интервью, и всё равно его пройду. Потому что я могу показать особо интересные куски кода на гитхабе, и для меня скипнут как минимум телефонное интервью, а иногда скипали и техническое собеседование, разговаривая только о жизни или в лучшем случае о дизайне систем, релевантных для места работы.
А так придётся выкидывать весь этот опыт в корзину и начинать почти с нуля. Ну, да, у меня есть какое-то там системное мышление, я дизайнил компиляторы, я дизайнил распределённые системы с противоречивыми требованиями, это, наверное, тоже кусок синьористости, но если меня разбудить ночью и спросить «почему ты претендуешь на синьора», я отвечу «потому что сиплюсплюс». Ко мне обращаются коллеги, когда там какой-то адовый баг или что-то не работает или надо что-то сделать на темплейтах или хорошее код-ревью бы. Это давно уже так — помню, ещё где-то на третьем курсе, когда я отрабатывал пропуски по физкультуре заплывами в бассейне, ко мне подплыл какой-то чувак и спросил: «а это ты 0xd34df00d? у меня тут вопрос по boost…»
Это просто прорва времени и сил, которая выкидывается вникуда. Это непереносимый опыт (каламбур, ага).
Ну вот пойду я сейчас собеседоваться на чистого хаскелиста какого-нибудь, или вообще в ресёрч. Что я там скажу? На кого вообще претендовать? На каком основании? Что-то там типы что-то там алгебра что-то там математика. Но разве это сравнится по сложности со стандартом плюсов? Разве сравнятся мои тамошние навыки с моим опытом в плюсах? 17 лет плюсов против этак лет 8-10 хаскеля. Против этак лет трёх какой-то математики (примат в вузе не считается).
Кто я, если выкинуть плюсы?
И, может, выкидывать это всё — ошибка. Меня ждут минимум в двух очень хороших местах, одно из них неплохо смотрится в резюме, в обоих местах готовы платить какие-то баснословные деньги, но просто не могу больше этим заниматься. Кто-то наверняка скажет, что я с жиру бешусь, особенно сейчас, когда неопределённость на рынке труда повыше. Наверное, кто-то будет прав.
Короче, да, мне немного страшно, и я устал. Я немножко стал динозавром. Когда-то я непонимающе смотрел на людей, которые в гробу видали все новые вещи, а теперь я замечаю что-то похожее в себе. Я не нахожу в себе силы читать новые вещи о C++, и только о C++. За последние полгода я поковырял и неплохо проработал два новых для меня языка, я ежедневно либо читаю всякие матанокнижки либо решаю задачки, мой текущий проект — игрушечный proof-of-concept-язык, но меня тошнит при одной мысли о том, чтобы прочитать ещё один пропозал или даже ещё одну пережёванную статью-рерайт cppreference о каких-нибудь нововведениях в C++20.
Собственно, сначала я думал назвать эту статью «я стал динозавром», но проблема в другом. Те люди — они, кажется, вполне себе счастливы на своём C++03 или C99, или где там у них произошла кристаллизация, а я никак не счастлив и там. Проблема в том, что, получается, моему опыту, моим навыкам, вот этому всему — этому пора на свалку. А если этому пора на свалку, то не пора ли и мне?
И да, я ни в коем случае не хочу сказать, что плюсы — отстой. Вовсе нет, плюсы прекрасны, это я устал и кончился. Проблема не в тебе, проблема во мне.