Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!

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

понедельник, 17 января 2022 г.

Как улучшить себе жизнь со скриптами bash/btach

Сегодня я хотел написать этот пост, но краткое вступление раздулось до огроменного ставшего в результате самодостаточным поста. Тут же я поделюсь серией подобных практик в одной узкой области - сборка и поставка приложения. А пост этот был написан с использование другой практики - make manual from chat log. Когда ты с коллегой в чате уже обсудили какой-то вопрос, ты настрочил много текста и теперь хочешь его как-то повторно использовать. Так что этот пост, как и многие другие - во многом вдохновлен обсуждением вопроса в чате, скопипащенный оттуда и немного отредактированный. 

Итак погнали. Я ленив, а потому если команду в консоли bash/batch я ранаю второй раз за сегодня, тут же останавливаюсь и начинаю писать bash/batch скрипт: bash для linux, batch для windows. За последние пару лет эта практика меня сильно прокачала в этих сприктовых языках программирования. Если раньше мне приходилось гуглить как делается в OS так или иная операция или выполняется та или иная команда, то сейчас я гуглю особенности различных языковых конструкций: if/else/while/function/ect, как делаются те или иные expression и так далее. Этот язык программирования (вообще правильно command language) весьма специфический и более того их два диалекта - для windows и для linux. Почему диалекта, потому что очень многое слизано (как мне кажется) у windows с linux. Отличия все же приходится гуглить. 

Раньше я писал скрипты для обоих OS. Но в последнее время склоняюсь в сторону linux, а для тех, кто работает под windows предлагаю для запуска моих bash скориптов использовать что-то типа cygwin или mingw. Очень в немногих местах приходится проверять OS и менять поведение скрипта в зависимости от OS. Но это куда лучше чем писать две версии скриптов, либо лишать windows пользователя инструмента автоматизации. 

Идем дальше. Загнать тот скрипт, который ты обычно ранаешь в консоли в текстовый файл и назвать с расширение bat/sh это первый шаг, но этого недостаточно. Часто новички в DevOps часто используют команду cd для того, чтобы направиться в определенную папку, и там уже выполняют команду. Я же пришел к тому, что каждую команду стоит выполнять в абсолюте.
Все что выполняется относительно, всегда потом дает повод поиграться с ним в самый неподходящий момент.

cd ./some/place/in/project
ls -la
cd ../../..
cat file.txt

В суппорте такой подход требует от меня всегда помнить "где я?". То есть для каждой строчки надо помнить 2 вещи - ЧТО делаем, ГДЕ делаем. А если ты юзаешь всегда только абсолютный путь, тогда все чисто.

ls -la /srv/app/some/place/in/project
cat /srv/app/some/file.txt 

Более того, рекомендовано использовать такие команды даже когда ты работаешь в консоли руками. Если говорить про linux то там в папке твоего юзера есть ~/bash_history файл и в нем все команды когда-либо выполняемые тобой на этой машине. Очень информативно видеть там самодостаточные команды, выполнив которые из любого места системы будет один и тот же эффект.

Но как быть с дублированием? Допустим я не хочу постоянно вбивать /srv/app/some

ls -la /srv/app/some/place/in/project
cat /srv/app/some/file.txt

В скрипте (bash) сделать так

root=$(pwd)
ls -la $root/place/in/project
cat $root/file.txt

В batch вот так 

set root=%cd%
dir %root%\place\in\project
type %root%\file.txt

Следующим шагом я бы улучшил информирование в консоли (bash). 

BLUE=94
GRAY=89
YELLOW=93

color() {
    message=$1
    [[ "$2" == "" ]] && color=$YELLOW || color=$2
    echo "[${color}m${message}"
}

eval_echo() {
    command=$1
    [[ "$2" == "" ]] && color=$BLUE || color=$2
    color "${command}" $color
    echo
    eval $command
}

eval_echo "set root=%cd%"
eval_echo "dir %root%\place\in\project"
eval_echo "type %root%\file.txt"

echo
color "Press Enter to continue"
read

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

 

Согласись так нагляднее, чем видеть все то же самое только без цветных строчек. 

Удобство этого подхода в том, что вместо переменных типа $root будет вставлено реальное значение и на экране напечатается информативная команда, именно та, что будет реально вызвана. 

А если не хочется копипастить из скрипта много кода, то напомню, что самый минималистичный вариант eval_echo это

eval_echo() {
    echo "$1"
    eval $1
}

Обрати внимание, что в этом коде есть ESC символ. 

 

Благодаря этому символу в linux консоли можно рисовать цветом. Под bacth есть подобный подход.

Все это сильно позволяет ускориться во время выполнения обычных рутинных действий: залить ветку на github со всеми subrepo, pull всех subrepo, автоматическая генерация каких-то ресурсов, создание snapshot для всех артефактов, создание запускного jar для каждого компонента с автоматически сгенерированными скриптами запуска, запуск сервера в целях отладки в определенной конфигурации, дамп базы и так далее. Все это делалось вручную раньше, а сейчас на это не тратится время. Время (сильно меньше) тратится на поддержание скриптов. Это экономически выгодно, иначе небыло бы DevOps с CI/CD и мы собирали war вручную или с Ant, заливали бы его в папку Tomcat на сервере, разбирались бы с конфликтом jar, как делали это 10 лет назад. Деплой бы занимал не 1 клик и 10 минут, а 1 час и много ручной работы.

Инженерные практики могут ускорить тебя в 100х раз

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

Почему я коллекционирую различные ускорялки? Математика проста. Пусть это конкретное решение мне даст +15% в производительности. Какое-то другое решение даст +15%. Еще где-то что-то прочитаю и попробую, снова +15%. Складываем вместе 10 подобных решений и получаем 1.15 ^ 10 = 4.04. То есть 10 подходов ускоряющих тебя всего лишь на 15% в сумме дают + 404%, а это 4х в скорости! Подходов за 10 летний опыт может собраться на порядок больше. И для 50 штук прирост будет уже 1083%, 10x. А и это 15% роста продуктивности это так, самый минимум из того, что новая практика тебе может дать. Часто введение нового подхода сама по себе в разы тебя ускоряет. 

Внимательний читатель заметит, что любой новый подход в разработке облагает автора налогом. Естественно надо закладывать время на суппорт. Часто разработчик принимает решение сам, буду ли заморачиваться с новым кодом и его суппортом или по старинке сделаю, и делает это на основе критерия. Если я скажу, что новый подход даст тебе прирост в производительности в 10 раз, а при этом потребует от тебя всего лишь по 5 минут в день больше времени - дурак не согласится. Но обычно все не так. Времени на реализацию решения надо потратить сегодня 2-3 часа, а потом еще на суппорт решения раз в неделю по 0.5 часа. А прирост будет 10-20%, что уже не так леко прочувствовать, как 2,5 часа. На одной чаше весов - вклад в часах единоразовый с небольшим вниманием, а на другой % прироста производительности. % складываются иначе, чем часы. Копить % выгоднее, чем экономить время. Не всегда, но часто.

Правда, далеко не все дивиденды от инженерных полдходов можно "складывать" как описано выше. Есть независящие инструменты. Ну например я кодирую в Idea (она удобнее), а на сервере у меня все в docker (легче чем инсталить все на host машину). Там на X% ускорился, тут на Y%. Но так как работаю я ИЛИ в идее ИЛИ с докером на сервере, то % берется не от 168 рабочих часах в месяце, а от всего времени в IDE и отдельно всего времени во время обслуживания сервера. Результат складываем и "X% + Y%" дает ((1+X/100)*develop_time + (1+Y/100)*deploy_time). И в этом случае время проинвестированное в разработку и поддержание инструмента стоит оценивать критичнее. Но есть и фундаментальные подходы, скажем как следование принципам BabyStepsRefactoring, CleanCode, UnitTesting, TestFirst. Выгода от использования этих принципов ощущается одновременно и влияет друг на друга, потому формула суммирования будет более вкусной. Возможно не (1+X/100)*(1+Y/100) - это крайность. Но чем более влияния подходов друг на друга, чем чаще они используются, тем ближе мы к пермножению процентов, а не суммированию.

Надеюсь никтон не будет спорить в наше время с тем фактом, что рефакторить код необходимо, если ты хочешь хоть как-то влиять на энтропию системы. Держать код в чистоте - значит помогать себе и другим читателям понимать, что тут происходит. Делать рефакторинг можно грубо зарывшись по уши в код и потом тратя время на исправление всех ошибок компиляции, отловку багов, а можно элегантно - вооружившись компилятором ide и юнит тестами быстро привести код в рабочий вид. Но параллельно с этим всем можно производить рефакторинг маленькими шажками, что позволит исключить дебаг из процесса. А если нужна новая функциональность, то использования подхода TestFirst и сключит дебаг так же из процесса разработки. Привет TDD! Каждая из практик ценна сама по себе, но вмсте они дают возможность избавиться от Debug вообще. 

Debug - самая сложная и неуправляемая, а потому и дорогостоящая часть в разработке. Во время Debug ты мало что понимаешь - только тыцаешь 2-3 клавиши и смотришь в стек в ожидании инсайта. А без юнит тестов рефакторинг опасен, но если ты и осмелишься - много багов уйдет на прод. А без рефакторинга сложно и за CleanCode следить. Так вскоре техдолг не даст менять сисему как того нужно бизнесу. В какой-то момент исправление 1 баги будет порождать 2 новых. Тогда лучшее, что можно сделать - заморозить его. Ну или покрыть модуля автогенерируемыми юнит тестами, затем взяться за рефакторинг модулей с целью навести порядок в модели и сделать код чуть более clean. Затем выкинуть старые автогенерируемые тесты и написать нормальные, читабельные. Этот долг придется выплатить, если хочется продлить жизнь проекту. И лучше тут пользовать подходом ПрицнипСкаута. Сделать место стоянки после себя чище, чем оно было до тебя. Каждый день плати чуть-чуть времени уделяя этим инструментам: CleanCode + Refactoring + UnitTesting. И проект проживет дольше. 

Это один из примеров связки практик. Есть и другие практики. И тот из нас, кто научится использовать их по максимуму будет производительнее чем тот, кто не будет в 100 раз, в 1000 раз, а может и в 10000 раз. Возьмем студента новичка без опыта разработки - сложная задача может просто его застопорить на недели (если вообще задача будет решена), тогда как опытный миддл сделает ее за два дня. Вот и решай на сколько более производительный тот, кто сделал работу в сравнении с тем, кто не сделал ее. Я же верю в то, что хоть в среднем по индустрии Middle не сильно отличается от Senior (так просто устоялось), их производительность может отличаться в весятки, а то и сотню раз. Вопрос в том, остановился ли Senior в развитии на уровне инструментария Middle и дальше качает только SoftSkills или продолжает поиск инструментов его ускоряющих.