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

О блоге

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

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

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

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

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

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

вторник, 27 октября 2020 г.

[work] Наткнулся тут на невероятное...

В поисках информации о "спиральной динамике" нашел такого автора на vc.ru: Максим Цепков. А среди его статей ознакомился вот с этой: "Реальность цифрового мира: проекты делает некомпетентная команда". И вот в этой статье практически сходу споткнулся о пару-тройку моментов, которые заставили всерьез задуматься о том, а не сказочный ли пишет все эти буквы? Уж простите мне мой французский, но запись в блог сразу после прочтения, еще под свежими впечатлениями.

Итак, цитата:

Получается, что нам нужно делать проекты некомпетентной командой. В то время в любом учебнике по менеджменту написано: обязательным условием для успеха проекта является компетентная команда, поэтому определите, какие компетентные специалисты вам нужны, найдите их и привлеките к проекту. Это оказывается невозможным. Как можно ли в таких условиях успешно делать проекты?

Оказывается, для этого уже давно есть методы. Первые из них появились, естественно, в IT как раз тогда, когда было осознанно, что регламенты и правила не работают, а ключевым фактором успеха является человеческий фактор. Об этом есть классическая книга Тома ДеМарко «Peopleware» (1987), которая в русском переводе так и называется «Человеческий фактор».

И как ответ на эту ситуацию появился Agile. Он взлетел именно поэтому, что появился во время прихода персоналок. Персоналки ведь пришли не домой, они сделали компьютеры доступными для мелких и средних компаний. И потребовалось массово автоматизировать бизнес, возник дефицит разработчиков. Их не было, выпуск институтов не увеличился. И именно Scrum и Agile обеспечили решение этой проблемы, позволив успешно делать проекты некомпетентной командой. Это был один из ключевых факторов их успеха и широкого распространения. В России, кстати, мы это не очень ощутили, потому что именно тогда, в период появления персоналок, развалилась оборонка и много квалифицированных инженеров ушли в айтишники, они демпфировали этот кризис.

Что меня здесь смущает?

Первое. Agile и персоналки. Если я правильно помню, то первопроходцы Agile делали приснопамятную систему расчета компенсаций для Крайслера в 1993-1999 годах как раз таки не для персоналок вообще, а для мейнфреймов. Могу ошибаться, поэтому если кто-то владеет информацией лучше меня, то поправте плиз.

Далее, период развития интереса к Agile, начиная с появления Agile Manifesto -- это уже нулевые годы. Когда у персоналок был не то, чтобы приход. Они уже давно пришли. А приход тогда как раз был у Web-а. И именно в конце 1990-х и начале нулевых стартовал и нарастал массовый переход от десктопа к Web-у. В условиях когда не было ни толком развитых Web-технологий, ни опытных Web-разработчиков, зато спрос на Web-разработку был сумасшедший. Так если уж благодаря чему-то Agile и взлетел, так это благодаря Web-разработке.

Второе. Что-то мне трудно припомнить, чтобы в 1990-е в России после распада оборонки квалифицированные инженеры массово уходили в айтишники. При том, что как раз в оборонке до 1990-х огромное количество разработчиков и трудилось. А потом осталось без куска хлеба. В прямом смысле. И поэтому уже существующие профессиональные кадры разбегались и выживали кто как может: кто-то уехал, кто-то переквалифицировался на бух- и складской учет (dBase/FoxBase/Clarion, а затем и 1C с Галактикой и Ко), кто-то ушел в преподавание, кто-то смог уйти в сисадмины (в банках в то время был большой спрос), кто-то тупо торговал широпотребом на рынке.

Так что как-то с моими воспоминаниями ну никак не бьется. Но я и не из России. Поэтому опять же, вопрос к более знающим людям: действительно ли в 1990-х квалифицированные инженеры из оборонки в РФ уходили в айтишники?

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

понедельник, 26 октября 2020 г.

[prog.c++] Особенность aggregate initialization в современном C++, о которой я и не подозревал

Некоторое время назад попробовал разобраться с тем, как работает трюк, описанный в статье "Reflection in C++14". И очень долго тупил и не понимал, как же так получается, что попытки увеличивать количество параметров в выражении aggregate initialization ведут к должному результату... Т.е. сперва делается T{a1}, затем T{a1, a2}, затем T{a1, a2, a3} и т.д. И останавливается это все на aN, где N и есть количество полей в структуре T.

А тупил и не понимал потому, что считал, что в выражении aggregate initialization нужно передавать начальные значения для всех полей структуры T. Т.е., если в T четыре поля, то и в aggregate initialization нужно отдать именно четыре значения. Не больше, не меньше. А именно четыре.

Ну и вот оказалось, что в главном-то я и не прав. В aggregate initialization можно отдать и меньше значений. Вот для иллюстрации работающий пример (убедиться можно на wandbox):

#include <iostream>

struct Demo {
    int a_;
    int b_;
    int c_;
    int d_;
};

std::ostream & operator<<(std::ostream & to, const Demo & d) {
    return (to << '(' << d.a_ << ',' << d.b_ << ',' << d.c_ << ',' << d.d_ << ')');
}

int main() {
    {
        Demo d{};
        std::cout << "First: " << d << std::endl;
    }
    {
        Demo d{10};
        std::cout << "Second: " << d << std::endl;
    }
    {
        Demo d{104215};
        std::cout << "Third: " << d << std::endl;
    }
}

Вот уж, что называется, век живи, век учись, а помрешь все равно дураком ;)

среда, 21 октября 2020 г.

[prog.c++] json_dto-0.2.11 released

json_dto is a thin wrapper around RapidJSON library we wrote several years ago. It's surprising for us that this small bicycle is used by someone outside our team. Sometimes users show us use-cases we just weren't thinking about. And we add new features to json_dto to fulfill expectations.

The last updates to json_dto add a possibility to customize formatting of (de)serialized values by specifying custom Reader_Writers. For example let's imagine that we have a struct with a `std::map<std::uint32_t, int>` field:

struct example_data {
   std::map<std::uint32_tint> m_weights;
   ...
};

It's impossible to write a simple (de)serialization code for example_data in the form:

struct example_data {
   std::map<std::uint32_tint> m_weights;
   ...

   template<typename Io> void json_io(Io & io) {
      io & json_dto::mandatory("weights", m_weights)
         ...
         ;
   }
};

because RapidJSON expects string values as keys, but json_dto (de)serializes `std::uint32_t` as integers. And the naive implementation of `json_io` shown above leads to run-time error during serialization.

Now json_dto allows to use custom formatters for fields to be (de)serialized:

struct simple_reader_writer {
   // Those methods will be used for (de)serializing of map's keys.
   void read(
      json_dto::mutable_map_key_t<std::uint32_t> & key,
      const rapidjson::Value & from) {
      ... // Deserializing key from a string.
   }
   void write(
      const json_dto::mutable_map_key_t<std::uint32_t> & key,
      rapidjson::Value & to,
      rapidjson::MemoryPoolAllocator<> & allocator) {
      ... // Serializing key as a string.
   }

   // Those methods will be used for (de)serializing of map's values.
   void read(
      int & v,
      const rapidjson::Value & from) {
      json_dto::read_json_value(v, from); // Just reuse json_dto.
   }
   void write(
      const int & v,
      rapidjson::Value & to,
      rapidjson::MemoryPoolAllocator<> & allocator) {
      json_dto::write_json_value(v, to, allocator); // Just reuse json_dto.
   }
};

struct example_data {
   std::map<std::uint32_tint> m_weights;
   ...

   template<typename Io> void json_io(Io & io) {
      io & json_dto::mandatory(
              // Explicit specification of custom Reader_Writer for that field.
              // The usage of apply_to_content_t tells that custom formatter
              // should be applied to every item of the container.
              json_dto::apply_to_content_t<simple_reader_writer>{},
              "weights", m_weights)
         ...
         ;
   }
};

So now keys of `example_data::m_weights` will be (de)serialized as strings.

But custom Reader_Writers allow to go further. Let's imaging that `example_data` contains yet another `std::map<uint32_t, int>`:

struct example_data {
   std::map<std::uint32_tint> m_weights;
   std::map<std::uint32_tint> m_colors;
   ...
};

where keys of `example_data::m_colors` should be represented in the form `#xxxxxx`, where `xxxxxx` is hexadecimal representation.

We can create another custom Reader_Writer:

struct color_hex_reader_writer {
   // Those methods will be used for (de)serializing of map's keys.
   void read(
      json_dto::mutable_map_key_t<std::uint32_t> & key,
      const rapidjson::Value & from) {
      ... // Deserializing key from a string.
   }
   void write(
      const json_dto::mutable_map_key_t<std::uint32_t> & key,
      rapidjson::Value & to,
      rapidjson::MemoryPoolAllocator<> & allocator) {
      ... // Serializing key as a string.
   }

   // Those methods will be used for (de)serializing of map's values.
   void read(
      int & v,
      const rapidjson::Value & from) {
      json_dto::read_json_value(v, from); // Just reuse json_dto.
   }
   void write(
      const int & v,
      rapidjson::Value & to,
      rapidjson::MemoryPoolAllocator<> & allocator) {
      json_dto::write_json_value(v, to, allocator); // Just reuse json_dto.
   }
};

and use it for (de)serialization of `example_data::m_colors`:

struct example_data {
   std::map<std::uint32_tint> m_weights;
   std::map<std::uint32_tint> m_colors;
   ...

   template<typename Io> void json_io(Io & io) {
      io & json_dto::mandatory(
              json_dto::apply_to_content_t<simple_reader_writer>{},
              "weights", m_weights)
         & json_dto::mandatory(
              json_dto::apply_to_content_t<color_hex_reader_writer>{},
              "colors", m_colors)
         ...
         ;
   }
};

The full working example can be seen here.

PS. I don't like to answer questions like "Is json_dto better than nlohmann::json, cereal or any other similar library?" We started to use RapidJSON several years ago and we didn't know about many of the JSON-libraries well known today. At some time we decided to simplify our marriage with RapidJSON and wrote json_dto. Since then it's easier for us to continue to use json_dto than to switch to any other library. So if you are happy with nlohmann::json there is no need to see for something else. But if you have to stay out of nlohmann::json for any reason there is json_dto ;)

PPS. We love reinventing bikes and we know how to do it: RESTinio and SObjectizer are also out of our garage.

понедельник, 12 октября 2020 г.

[prog.c++] The follow-up for "What's new in rotor v0.09" article

There is a new article "What's new in rotor v0.09" that tells several things about yet another C++ actor framework named "rotor". As for me it's a good example of how different implementations of Actor Model could be. And it's a good reason to write some words about SObjectizer's features to show how the same things were addressed a long time ago.

Before I start, it's necessary to note that some of the decisions described below are rather philosophical than technical ones. It also means that some decisions taken from philosophical standpoints had a significant impact on technical aspects of SObjectizer's internals and API, and on applications built on top of the SObjectizer.

четверг, 8 октября 2020 г.

[prog.c++] Наш RESTinio упомянули в докладе про I/O Objects из Networking TS на CppCon2020

При просмотре доклада "The Networking TS from Scratch: I/O Objects" от Robert Leahy уронил челюсть на пол при появлении вот этого слайда:

Отрадно. Не зря, выходит.

Пользуюясь случаем хочу напомнить, что если вам не хватает чего-то в RESTinio или же вам что-то не нравится в RESTinio, то об этом можно рассказать нам. Возможно, мы сможем вам помочь.