пятница, 13 декабря 2024 г.

[prog.self.reflection] Официальный ответ на претензию "Да вовсе ты и не крутой программист"

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

На что официально заявляю: я не считаю себя крутым программистом и никогда ничего подобного о себе не говорил. Ну на сколько помню.

Если это не так, и я где-то что-то подобное ляпнул, то дайте, плз, ссылку. В назидание.

Более того, если говорить о том, как я сам себя оцениваю, то это будет что-то чуть повыше среднего. В верхние 20% точно не попадаю.

Отчасти это компенсируется тем, что я еще немного могу в тестирование и документирование. Т.е. делаю разное, хоть и посредственном уровне.

Вот в чем, скорее всего, превосхожу многих, так это в упорстве при достижении целей. Что и позволяет "брать и делать", даже если задача находится за пределами моих текущих возможностей.

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

четверг, 12 декабря 2024 г.

[prog.flame] Иногда просто поражаешься трудолюбию программистов

Недавно довелось увидеть что-то вроде вот такого:

void data_holder::validate_data()
{
  std::cout << "*** Data validation: 1/15 (checking) ***" << std::endl;
  check();
  std::cout << "*** Data validation: 2/15 (sorting) ***" << std::endl;
  sorting();
  std::cout << "*** Data validation: 3/15 (deduplication) ***" << std::endl;
  deduplicate();
  std::cout << "*** Data validation: 4/15 (normalization) ***" << std::endl;
  normalize();
  ... // И так еще несколько строк пока не будет сделан шаг 15 из 15.
}

Вот честно, удивлен такому трудолюбию, т.к. меня бы быстро задолбало выписывать однотипные строки печати в std::cout с инкрементом значений в них. Особенно с учетом того, что количество этих шагов увеличивается по мере развития проекта :)

Так что я бы чуть ли не сразу написал бы что-то вроде:

void data_holder::validate_data()
{
  constexpr int total_steps = 15;
  auto inform = [step = int{1}](const char * name) mutable {
    std::cout << "*** Data validation: " << step << "/" << total_steps
      << " (" << name << ") ***" << std::endl;
    ++step;
  };

  inform("checking");
  check();
  inform("sorting");
  sorting();
  inform("deduplication");
  deduplicate();
  inform("normalization");
  normalize();
  ... // И так еще несколько строк пока не будет сделан шаг 15 из 15.
}

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

Ну и да, исходный пример заставляет вспомнить народную мудрость "простота хуже воровства".

среда, 11 декабря 2024 г.

[prog.flame] Комментарии в коде нужны. Без них тяжено, проверено на собственном опыте

Недавно в LinkedIn поделился эмоциями о том, что в большинстве случаев при работе с внешними заказчиками в коде нет комментариев. Вообще нет комментариев. И как же после этого приятно заглядывать в код, в котором комментарии есть.

К моему удивлению, там случился срач в котором мне пытались доказать, что комментарии врут, поэтому их писать не нужно.

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

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

  • почему это сделано именно так, а не иначе?
  • зачем вообще это было сделано?

Никакой "типа самодокументирующийся код" не может ответить на эти вопросы. А уж плохо написанный код не сможет вменяемо рассказать даже о том что именно было реализовано.

Первоначально я хотел на этом и закончить, но встретил в статье про переиздание знаменитой книги "Мифический человеко-месяц" вот такой абзац:

Однако есть и другая точка зрения на этот вопрос. Например, Мартин (Роберт, а не Джордж), автор книги «Чистый код», утверждает, что комментарии — это зло. Даже будучи среди исходного кода они всё равно могут стать неактуальными. «Не трать время на написание комментариев», – говорит Мартин. Вместо этого он предлагает более тщательно подходить к процессу именования переменных, методов и классов. Если всё делается правильно, то и необходимость в комментариях отпадает.

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

Disclaimer. Приведенный код -- это черновик, который еще не компилировался и не тестировался.

Итак, вот фрагмент без комментариев:

template<typename T>
void
doAttemptToSwitchFromReadOnlyToReadWrite(T * objectToLock)
{
   auto & globalList = getGlobalListFor(objectToLock);
   std::unique_lock globalListLock{ globalList._lock };
   auto it = globalList._objects.find(objectToLock);
   if(it != globalList._objects.end())
   {
      auto & lockInfo = it->second;
      if(AccessMode::ReadOnly != lockInfo._currentMode)
         throw std::runtime_error{
               "doAttemptToSwitchFromReadOnlyToReadWrite: "
               "lockInfo._currentMode != AccessMode::ReadOnly "
               "in the global lock list"
            };

      if(1u == lockInfo._ownersCount)
      {
         lockInfo._currentMode = AccessMode::ReadWrite;
      }
      else if(isWaitingQueueEmpty(lockInfo))
      {
         OwnerCountForLockingUpgrateTrx ownerCounterTrx{ lockInfo };

         const auto requestResult = makeWaitingAccessorInfoThenWait(
               globalListLock,
               lockInfo,
               AccessMode::ReadWrite);
         if(AccessRequestResult::granted != requestResult)
            throw PossibleDeadlock{
                  "doAttemptToSwitchFromReadOnlyToReadWrite: "
                  "unable to upgrade ReadOnly lock to ReadWrite lock even "
                  "after waiting"
               };

         ownerCounterTrx.commit();
      }
      else
      {
         throw PossibleDeadlock{
               "doAttemptToSwitchFromReadOnlyToReadWrite: "
               "there is no possibility to upgrade ReadOnly lock "
               "to ReadWrite lock"
            };
      }
   }
   else
   {
      throw std::runtime_error{
            "doAttemptToSwitchFromReadOnlyToReadWrite: there is no "
            "information about objectToLock in the global lock list"
         };
   }
}

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