Магия руби

August 30, 2016

Когда я изучаю что хранится в базе данных, то очень люблю использовать рейлсовые конструкции примерно такого вида User.<...>.group(:region).count. Однако для рубишных массивов не знал изящного решения, как посчитать сколько раз встречается каждый элемент, и много раз писал примерно так:

[1, 2, 3, 1].group_by { |x| x }.map { |k, v| [k, v.count] }.to_h
# => {1=>2, 2=>1, 3=>1}

Выглядит не очень красиво.

Наш коллега Владислав Белов предложил отличный вариант (правда нужны руби >= 2.2):

[1, 2, 3, 1].group_by(&:itself).transform_values(&:count)
# => {1=>2, 2=>1, 3=>1}

Красота :cat: :cat: :cat:

Новая блестящая идея

August 29, 2016

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

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

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

Красота кода

August 26, 2016

После того как я поработал в проекте из нескольких десятков миллионов строк на C++, представления о красоте програмного кода у меня сильно поменялись. Чисто технически невозможно сделать так, чтобы на таких масштабах сотни разработчиков создавали "красивый код" с точки зрения перфекционизма. Я долго думал над метафорой, которая бы точнее описывала мое сегодняшнее отношение к данному вопросу, пока нашел только такую.

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

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

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

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

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

Маленькая тайна грепа

August 25, 2016

Есть вещи, которые очень легко гуглятся, но иногда в голову не приходит их гуглить. Например я всегда считал, что такая конструкция, вполне нормальная практика:

ps auxww | grep ruby | grep -v grep

Чтобы найти процесс с руби, нужно сначала исключить процесс с грепом, который ищет процесс с руби. Ну вы понимаете.

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

ps auxww | grep [r]uby

В таблице процессов отображается grep [r]uby, а ищем мы регексп, в котором первая буква r и потом строка uby. Таким образом процесс самого грепа не попадает в поиск. Теперь я ищу только так.

Крон, Великий и Ужасный

August 24, 2016

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

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

PATH=/root/bin:/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
23 7 * * 1-5 cd /root/blog && (echo STARTED_`date "+\%Y-\%m-\%d__\%H-\%M-\%S"` && git pull && NOCACHE=1 ./publish next && echo FINISHED_`date "+\%Y-\%m-\%d__\%H-\%M-\%S"`) >> /root/crontab.log 2>&1 

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

Большие апдейты

August 23, 2016

Один долгий руби процесс у нас пишет логи своего выполнения в базу.

Сделано было просто и прямолинейно, создали колонку log:text в таблице скажем jobs и вот такой метод логирования:

class Job
  def log(msg)
    self.log += msg + "\n"
    safe!
  end
end

Конструкция исправно работала пару лет и вдруг закончилось дисковое пространство на боксе с базой. Добавили. Через некоторое время снова закончилось. Начали разбираться.

Оказалось, что кто-то добавил отладочной информации и количество записей в лог резко выросло. Поле log стало занимать 2-3 мегабайта, новые строки добавляются примерно раз в секунду, а Postgres устроен так, что UPDATE = INSERT + DELETE, DELETE же не сразу же освобождает место. То есть мы стали со скоростью несколько мегабайт в секунду забивать диск, автовакуум не успевал чистить.

Починили легко. Вынесли лог в отдельную модель и вместо обновления теперь создаем новую строку в таблице. Добавили пару индексов для скорости доступа и все стало хорошо - место на диске заканчиваться перестало. Это решение успешно работает до сих пор.

Переписывать нельзя модифицировать

August 22, 2016

В какой-то книжке прочитал, что программу дешевле переписать с нуля, если нужно модифицировать более 30% исходников.

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

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

Лига лени в гит-конфиге

August 19, 2016

Чем старше я, тем ленивей становлюсь. Боюсь представить, что будет дальше.

Недавно в моем .gitconfig появилась команда, которую я стараюсь использовать пореже... Но она такая клевая... :heart_eyes_cat:

[alias]
  # перенес на несколько строк для наглядности, в оригинале она у меня в одной
  y = !(git add -A . && 
        git commit -amsaved_at_`date +%Y-%m-%d__%H:%M:%S` &&
        git pull --rebase && git push && 
        echo "" && git log -1 --stat) || git pull --rebase

Команда комитит и пушит все что есть, а также подтягивает свежие изменения.

Название алиаса от слова sYnc. Букву s я не стал использоваться, чтобы не путать с git st => git status.

Бдительность не бывает лишней

August 18, 2016

Как-то раз я копировал postgres-базу с продакшена на тестовый сервер.

Продакшен работал уже на версии 9.4 и там даже добавили таблицу с jsonb-колонкой, тестовый сервер был все еще 9.3. Этот факт у меня был где-то на краю сознания, поэтому я решил проверить как перенеслась эта таблица.

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

Мораль это истории такова: не стоит терять бдительность при переносе базы, мало ли что.

Как помыть слона

August 17, 2016

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

В такие моменты обычно вспоминаю замечательную книгу Дизайн пользовательского интерфейса. Искусство мыть слона.

Чтобы хорошо вымыть слона, очень полезно начинать с самой грязной его части, иначе к концу мытья грязь успеет снова распространиться по слону.

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