вторник, 1 января 2030 г.

О блоге

Более тридцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). В настоящее время занимаюсь развитием компании по разработке ПО stiffstream, в которой являюсь одним из соучредителей. Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

Так же я пишу о жизни вообще и о нескольких своих увлечениях: о фотографии (включая публикацию своих фотографий, некоторые есть и на ZeissImages), о спорте, особенно о дартсе, и, совсем коротко, о кино.

понедельник, 31 декабря 2029 г.

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

Предлагаю сделать портрет в обстановке, связанной с вашей работой или увлечением. Абсолютно бесплатно. Очень уж мне нравится фотографировать людей в их естественной среде. Происходить это может так...

понедельник, 23 июня 2025 г.

[prog.c++] Есть ли теперь смысл при разработке C++ библиотек придерживаться не самых свежих стандартов?

В заголовок поста вынесен вопрос, который занимает меня в последние пару-тройку лет. A последний год так точно.

Поясню в чем дело.

В C++ на протяжении десятков лет есть специфическая картина: существует официальный С++ на "бумаге", т.е. описанный в соответствующем стандарте, будь то С++98, С++11 или C++23, и есть реальный C++, доступный конкретному разработчику в конкретном компиляторе. И, как правило, в имеющихся в нашем распоряжении компиляторах далеко не все фичи из самого последнего официального стандарта реализованы.

Эта картина особенно важна при разработке кросс-платформенных библиотек. Если ты хочешь, чтобы твою библиотеку использовали разные люди в разных проектах и на разных платформах, то ты вынужден занижать версию стандарта. Например, вместо C++23 (который вроде как уже два года нам доступен) делать библиотеку на C++17 или даже C++14.

При разработке софта для конечного пользователя ситуация, зачастую, гораздо проще: очень часто софт пишется под конкретную платформу и вполне можно заложиться на конкретный компилятор. Но с разработкой библиотек не так. В проекте X библиотека может работать уже в режиме C++23, тогда как в проекте Y -- все еще в C++17.

До недавнего времени я воспринимал такую ситуацию как данность. Типа в разработке прикладного софта для конечного пользователя своя специфика, а при разработке библиотек -- своя. Нужно смириться и получать удовольствие.

Но после C++20 смиряться все сложнее.

C++11 стал совсем другим языком в сравнении с C++98. Два следующих стандарта, C++14 и С++17, инкрементально улучшали C++, но не могу сказать, что они переводили язык на какой-то принципиально другой уровень (даже не смотря на такие крутые фичи C++17 как structured binding и if constexpr). А вот начиная с C++20 все принципиально поменялось:

  • C++20 добавил концепты и operator spaceship (про модули промолчу ибо не пробовал);
  • С++23 добавил deducing this. Не смотря на то, что (на мой взгляд) сделали это через одно место (и можно было бы по-другому), но таки важную реальную проблему эта фича решает;
  • С++26 добавляет compile-time рефексию и, я очень надеюсь, контракты.

Все это вместе, не побоюсь этого слова, делает из C++ совсем другой язык в такой же степени, как C++11 после C++98. Если не в большей.

И вот глядя на все это великолепие в свежих стандартах С++ я не могу найти для самого себя ответ на вопрос: а зачем на при разработке своих библиотек оставаться на C++17/14/11?

Вот реально.

Ладно бы нам за наши OpenSource проекты платили. Но этого нет. И RESTinio, и SObjectizer приносят нам деньги не напрямую, а приводя к нам клиентов через репутацию. Зачастую новые фичи в тот же SObjectizer добавляются "just for fun" -- просто что-то выглядит интересным или представляет из себя вызов, поэтому делаешь это ради получения удовольствия.

И если уж удовольствие от разработки выходит на передний план, то зачем лишаться концептов, контрактов или рефлексии? Ради чего?

Поэтому чем дальше, тем больше мыслей о том, чтобы в следующем году перевести SObjectizer сразу на C++26. На счет RESTinio ситуация посложнее, но если представиться возможность плотно поработать над RESTinio, то и там тоже можно будет сразу же брать С++26.

PS. Простите, что поминаю Rust в суе, но после добавления в C++ рефлексии тема выбора между Rust и C++ становится еще более сложной, а аргументов в пользу Rust-а -- еще меньше. При этом, если бы мне пришлось программировать не на C++, а на чистом Си, то я бы однозначно предпочел бы Rust.

вторник, 17 июня 2025 г.

[prog.c++] Еще раз про noexcept в C++: это очень специфическая гарантия отсутствия исключений

Увидел вот такое в ленте LinkedIn:

У этого "откровения" полсотни лайков и несколько репостов. Ужас.

noexcept в C++ -- это очень специфическая гарантия того, что функция не бросает исключений. Более точно было бы сказать так: когда вы вызываете noexcept-функцию, то вы можете не беспокоится об исключениях, потому что либо не будет исключений, либо не будет больше вашей программы.

И уж совсем дико читать про "Makes code safer and more predictable" и, особенно, про "Prevents unexpected termination during exception propagation". К сожалению, пока в C++ недостаточно средств для того, чтобы noexcept было удобно использовать для написания безопасного и предсказуемого кода. Но это тема отдельного большого разговора.

PS. Несколько лет назад написал большой текст об опыте работы с исключениями на примере проекта aragata. Очень хочу переработать его в более современную версию. Надеюсь выкроить для этого время. Но если кто-то не хочет ждать, то не поленитесь сходить по этой ссылке.

понедельник, 16 июня 2025 г.

[prog.thoughts.flame] Прочел давеча статью про вайб-кодинг с использованием LLM

На RSDN-е поделились ссылкой на статью "Вайб-кодинг: практика, о которой почему-то не говорят" в которой рассказывается про опыт разработки некого проектика на Go посредством "искусственного интеллекта" (Sonnet 3.7).

К сожалению, статья слишком поверхностная, чтобы сделать какое-то определенное заключение. Как минимум, не хватает ни описания задачи, ни примеров кода (особенно в стиле "вот как пишет неопытный начинающий разработчик, а вот что генерирует LLM").

Кажется, что люди делали какой-то интеграционный проект, вроде сбора телеметрии с облачных серверов с последующим укладыванием снятых данных в БД и мониторингом всего этого процесса. И что делалось это путем объединения уже существующих подходов и технологий, а язык программирования использовался только лишь как "клей" чтобы соединить воедино готовые компоненты и сервисы.

На ум приходит прежде всего то, что программисты давным давно уже знакомы с ситуациями, когда какие-то рутинные и хорошо известные вещи начинают автоматизироваться: ты только выбираешь значения тех или иных параметров, а все остальное генерируется автоматически.

Первый грубый пример, который сходу вспоминается, -- это мода на всяческие Wizard-ы, которые начали появляться в IDE когда стало возникать само понятие IDE (середина-конец 1990-х). Помнится, в каком-нибудь условном Borland C++ 2.0 кучу кода нужно было написать вручную прежде чем у тебя появится примитивное приложение с одним окошком. А в каком-нибудь Borland C++ 4 или VisualStudio 98 ты кликнул на несколько кнопок в Wizard-е и получил готовый каркас приложения с тем самым одним окошком.

Еще один вспомнившийся пример: простые декларации модели данных средствами ActiveRecord в Ruby-On-Rails скрывают за собой кучу ORM-кода.

Более сложный пример, который, возможно, не все сочтут релевантным, -- это генераторы парсеров, вроде yacc/bison, coco/r, ragel или ANTLR. Ведь чтобы вручную написать разбор даже относительно несложной грамматики придется написать не одну сотню строк кода. Тогда как в случае с bison-ом ты описываешь лишь то, что тебе нужно, на специальном DSL, и получаешь здоровенную простыню автоматически сгенерированного кода.

Т.е. программисты давным давно повторяют один и тот же трюк: сперва решают задачу врукопашную, затем накапливают опыт, выделяют повторяющиеся паттерны, а потом создают инструмент, который делает рутинную часть работы вместо нас.

И есть у меня подозрение, что компания H3LLO.CLOUD, в которой проводился описанный в статье эксперимент, уже приблизилась в своих задачах к тому порогу после которого подобная автоматизация рутины становится не только возможной, но и неизбежной.

Только вот вместо собственноручного написания условных Wizard-ов для своих задач, они использовали LLM в качестве такого Wizard-а.


Не могу не заострить внимание на нескольких фразах из статьи, касательно которых хочется сказать пару слов.

Рутинные задачи, написание бойлерплейта, стандартных функций — всё это автоматизируется. Время освобождается. Фокус смещается с написания строк кода на архитектуру, декомпозицию задач, правильную постановку технического задания, которое теперь превращается в промптинг.

Как-то я со времен университета нахожусь при мнении, что ключевой фактор -- это снижение семантического разрыва между тем, что тебе хочется иметь, и возможностями того инструмента, которым ты обладаешь. Чем меньше такой разрыв, тем меньше рутины и того самого бойлеплейта.

Грубо говоря, если у вас задача просуммировать значения в столбцах матрицы 1M на 1M, то придется написать кучу бойлерплейта на чистом Си и обойтись всего лишь несколькими строчками в каком-то специализированном DSL.

И до недавнего времени задача разработчиков (по крайней мере той части разработчиков, которая занимались языками программирования и фреймворками) состояла именно в том, чтобы дать прикладным программистам те самые высокоуровневые, заточенные под задачу инструменты (в виде продвинутых ЯП, специализированных DSL или же фреймворков для ЯП общего назначения).

Сейчас же, судя по тексту обсуждаемой статьи, устранение рутины и бойлерплейта перекладывают не столько на инструменты, позволяющее обычному человеку писать меньше, сколько на генерацию кода посредством AI.

Т.е. высокоуровневыми инструментами становятся не ЯП/DSL/фреймворки, а генеративные модели, которые запросто выплевывают мегатонны кода на уже существующих языках общего назначения. А исходником становится текст промпта для AI.

Что-то мне не кажется, что это хорошо. Ну да поделать с этим все равно ничего не могу. Да и желания такого нет. Посему будем посмотреть к чему это приведет.

Еще одна сторона медали: языки программирования -- это таки формализованные штуки. И к ним хоть как-то применимы методы формальной проверки корректности. Если же мы начинаем оперировать промптами, написанными на естественном языке, то как будет проверяться корректность исходного промпта?

У новых людей меняется отношение к коду. Он становится расходным материалом, а не домашним питомцем — если что-то пошло не так, часто проще откатиться, доработать промпт и сгенерировать заново (иногда и 10 раз), чем пытаться исправить сложный баг в машинном коде.

А вот тут у меня возникают опасения.

Сейчас мы копаемся в коде для того, чтобы заставить его работать правильно и надежно. Для чего нужно понимание этого самого кода. И если возникает какой-то сложный баг, то это требует пристального внимания, т.к. скорее всего мы чего-то не учли. А когда баг обнаруживается, иногда приходится что-то кардинально пересматривать, в том числе и на уровне архитектуры или применяемых алгоритмов и/или структур данных. При этом, по хорошему, найденная и исправленная проблема покрывается дополнительными тестами дабы обнаружить регрессию в будущем.

Причем не всегда речь идет о багах. Временами проблема может быть в неожиданных просадках производительности, слишком длительной блокировке каких-то ресурсов и т.п. Грубо говоря, сложность какой-то операции, о которой мы не задумывались, может оказаться O(n*n), и это проявляется только в каких-то специфических сценариях у VIP-заказчика.

Если же код -- это расходный материал, и мы не вникаем в причины сложных и неожиданных ситуаций, а тупо меняем описание задачи, чтобы получить очередную простыню кода, в которую мы даже не будем особо заглядывать, то откуда будет браться уверенность в "правильности" и "надежности"?

Это первый момент, связанный с данной цитатой.

Второй же момент возникает из того, что автор статьи пишет в других местах:

Понимание архитектуры и умение читать чужой код становятся критически важными.
Ценность навыка написания кода снижается в пользу навыка чтения чужого кода.

Что-то я весьма скептически отношусь к такой вещи, как "умение читать чужой код".

Если писать код нас худо-бедно учат, то вот с развитием навыка "чтения кода", боюсь, есть большие проблемы.

Попробуйте ответить сами себе: насколько вы любите задачи вида "вот написан код, что получится в результате его работы?

Насколько хорошо вы решаете такие задачи?

А если код не одна страничка текста, а 150-200-250 и более строк?

Почему некоторые разработчики говорят, что они не умеют делать софт без отладчика, потому что отладчик для них -- это основной инструмент для разбирательства с чужим кодом? А зачастую и со своим 🙁

Лично я придерживаюсь мнения, что многие программисты весьма хороши в написании кода (особенно если их сильно бить по рукам во время обучения), но вот в чтении кода мы в массе своей более чем посредственны. Потому что мы не компьютеры, мы не можем в своей голове прокрутить кучу вариантов исполнения и еще больше связанных с исполнением деталей. Если бы могли, то мы бы и программировали бы с гораздо меньшим количеством ошибок.

Но этого не происходит.

Поэтому-то мне кажется, что когда AI начнет генерировать для нас все больше и больше кода, то мы не сможем достаточно тщательно этот код вычитывать на предмет потенциальных проблем.


Если же все-таки попробовать подвести некий итог от прочитанного, то есть ощущение, что прикладное программирование с пришествием AI должно очень серьезно измениться. Под прикладным я понимаю решение задач, необходимых конечным пользователям программного обеспечения, типа "посмотреть историю движения остатков по складам за три последних квартала" или "сделать интеграцию с сервисом N для того, чтобы видеть последние M товаров, интересных пользователям пришедшим с площадки K".

Другой вопрос: а что будет с теми, кто занимается не рутиной, а какими-то уникальными велосипедами? Например, сможет ли AI в обозримое время заменить тех, кто сделал Roaring Bitmap?

Вот не знаю. Впрочем, таких велосипедостроителей и раньше много не требовалось.

В общем, не знаю чего ожидать. Есть ощущение, что ничего хорошего.

PS. Убежден в том, что развитие тех самых IDE из середины 1990-х с их Wizard-ами, автоматизированными рефакторингами, интеллектуальным автодополнением и мгновенной навигацией по коду сделало ситуацию в программизме только хуже: стало больше говнокодеров и, соответственно, говнокода. А производство кода посредством AI лишь усугубит дело -- это как самые продвинутые IDE на конских дозах стероидов + массовое непонимание того, что творится в сгенерированном коде. Так что да, есть ощущение, что ничего хорошего таких как я не ждет.

среда, 4 июня 2025 г.

[prog.c++] Осваиваю C++ные концепты: одна или две константны внутри типа

Недавно довелось столкнутся с задачкой, в которой пользователь должен определить тип с перечнем свойств. Что-то вроде traits из нашего проекта RESTinio. При этом внутри такого типа обязательно должна быть константа high_watermark типа std::size_t. Например:

struct my_traits {
  ... // Какие-то определения типов.

  // Максимальный размер при достижении которого нужно провести чистку данных.
  static constexpr std::size_t high_watermark = 64 * 1024 * 1024;
};

А кроме этого может быть определена и вторая константа, low_watermark. Т.е. класс свойств может выглядеть и так:

struct my_traits {
  ... // Какие-то определения типов.

  // Максимальный размер при достижении которого нужно провести чистку данных.
  static constexpr std::size_t high_watermark = 64 * 1024 * 1024;

  // Размер при достижении которого чистку данных следует остановить.
  static constexpr std::size_t low_watermark = 16 * 1024 * 1024;
};

При использовании таких типов свойств нужно было как-то понять, есть ли в свойствах константа low_watermark. Если есть, то нужно было использовать именно ее значение. А если нет, то оставалось высчитывать нижний порог из high_watermark.

Попробовал применить для этой цели концепты, благо в проекте C++20. Получилось так, как показано под катом.

Это лучшее, что удалось придумать. К сожалению, в возможностях C++20 пока еще не сильно копенгаген, только учусь. Поэтому интересно, а можно ли было сделать лаконичнее и понятнее?

Полный код под катом, а проверить его можно, например, на Wandbox.

Upd. Под катом так же и второй вариант, более компактный за счет отсутствия одного из концептов и использования if constexpr внутри вспомогательной функции low_watermark.

Upd. Более безопасно использовать std::same_as вместо std::is_same_v.

воскресенье, 1 июня 2025 г.

[life.cinema] Очередной кинообзор (2025/05)

Традиционный обзор отсмотренных за минувший месяц фильмов. Традиционно в начале каждого из списоков то, что понравилось больше, тогда как в конце то, на что можно не тратить свое время. Сразу скажу, что ничего выдающегося не попалось, а упомянутые ниже сериалы лучше бы и не смотреть вовсе.

Фильмы

Багровая отмель (Fear Below, 2025). Очень просто, без изысков, без стремления удивить. Но настолько олдскульно, что получаешь удовольствие. Именно это мне и понравилось, но фильм сам по себе средненький и ничем не выдающийся.

Громовержцы* (Thunderbolts*, 2025). Первые 2/3 мне отлично зашли, т.к. там было столько иронии над супергеройской темой, что я уж подумал, что Марвелл отважилась отстебать своё же супергеройское кино. Потом начался закос под серьезность, что сильно подпортило впечатление, но самый-самый конец и титры чуть-чуть ситуацию выправили. Получился забавный эксперимент, который вполне можно и посмотреть.

Тайна в её глазах (Magpie, 2024). Развязка хорошая, но то, что ей предшествовало выглядело скучным и не вполне естественным.

Источник вечной молодости (Fountain of Youth, 2025). Красочно, динамично, но очень уж все наивно и простенько. Такое ощущение, что это фильм для семейного просмотра с детьми младшего школьного возраста.

Дыхание шторма (Last Breath, 2025). Взяли невероятную историю, подтянули хороших актеров, сделали классную картинку... Но самым интересным и впечатляющим оказались кадры из семейной кинохроники прототипа главного героя, показанные в самом конце. Смотреть можно не потому, что хороший фильм, а потому, что в нем рассказывается про уникальный случай.

Вне юрисдикции (Exterritorial, 2025). Люди честно пытались снять бодренький фильм. Но результат не цепляет. Возможно потому, что в сценах с мордобоем даже мне заметны моменты, где актриса недостаточно убедительна. А из-за этого и происходящее не удается воспринимать всерьез.

Дроп (Drop, 2025). Такой фильм мог бы зацепить, если бы возникло ощущение реальности происходящего. Но такого ощущения не возникло. Какое-то все слишком гламурное и нарочитое. В общем, не зашло.

Объект преследования (Target, 2023). Поначалу было вполне себе нормально. В финале сотворили что-то невообразимое, что основательно подпортило впечатление от кино.

Курьер (El correo, 2024). Вроде бы бодренько. Но не зацепило, как-то шаблонно все.

Под огнем (Warfare, 2025). По шуму вокруг этого фильма ждал сильно большего. Можно сказать, что разочарован. И качеством, и происходящим на экране. Слишком уж много вопросов действия на экране вызывают.

Последний подозреваемый (Zheng jiu xian yi ren, 2023). Редкий маразм + обилие чрезмерного кривляния, характерного для азиатского кино. Смело можно проходить мимо.

Сериалы

Операция "Карпаты" (первый и второй сезоны, 2024). По большей части возникает ощущение "нам втирают какую-то дичь", но зато все динамично, нет сериальной затянутости.

Мосгаз. Дело № 11. Розыгрыш (2025). Не рекомендую. Может быть по уровню маразма и не сравнялись с "Мосгаз. Последнее дело Черкасова", но все равно слишком часто возникало ощущение, что нам втирают какую-то дичь.