• Баш-скрипты

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

    Одна из таких вещей — скрипты на баше. Я взял за правило: никаких баш-скриптов. Когда хочется написать на нем что-то длиннее пяти строк, закрывайте ноут и идите за молоком. Потом вернитесь и сделайте правильно — то есть без баша.

    Шелл-скрипты хрупки и тяжелы в поддержке. Их трудно читать и отлаживать. Они опираются на сторонние утилиты, которые где-то есть, а где-то нет. Поведение зависит от платформы и флагов.

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

    Замечу, что сказанное не относится к случаям, когда вызывают несколько утилит, соединяя их каналы, например:

    > ps aux | grep java
    

    Это совершенно нормально. Я имею в виду скрипты, где шелл выступает в роли настоящего языка программирования, то есть:

    • активно используются переменные;
    • циклы, ветвления, переходы;
    • объявление и вызов функций;
    • импорт других скриптов.

    Все вместе это становится таким адом, что лучше уволиться, чем ворошить осиное гнездо (или, в английской идиоме — тараканье).

    Кусок скрипта откуда-то из интернета:

    no_more_args=1 ;
      -*)
       if [ "x$no_more_args" = "x1" ] ;
    then
        dirs[$num_dirs]="$1";
        let "num_dirs++"
    else
     if [[ "x${1:1:1}" != "x-" && "x$1" =~ "x-.*a.*" ]] ;
     then
         no_dots=0;
     fi
        opts="$opts $1";
       fi
      *)
       dirs[$num_dirs]="$1";
       let "num_dirs++"
     esac
     shift
    

    Для меня он выглядит как китайская грамота. Что такое [ "x$no_more_args" = "x1" ]? Что такое [[ "x${1:1:1}" != "x-" && "x$1" =~ "x-.*a.*" ]]? Как это читать и поддерживать?

    Во времена Unix шелл-скрипты были прорывом, с этим никто не спорит. Однако время не стоит на месте, и созданы более удобные инструменты. Вот несколько примеров.

    Если с проектом нужно делать много мелких вещей, подойдет make. Напомню, make — это набор именованных действий. Кроме вызова по отдельности их можно комбинировать, например собрать бек и фронт за раз. В любом проекте я завожу Makefile и помещаю туда все, что придет в голову, например:

    • сборку проекта
    • прогон тестов и миграций
    • очистку временных файлов и папок
    • запуск и остановку Докера
    • много чего еще

    Пример большого Make-файла можно посмотреть в репозитории с книгой.

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

    Да, Питон требует окружения. Однако если у вас сложные скрипты, он окупается. Установку Питона и его барахла поместите в Makefile.

    Для управления машинами лучше писать плейбуки на YAML. Кроме Ansible, полно других утилит для конфигураций.

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

    Еще одна интересная мысль — генерация шелла из другого языка. Подобно тому, как из TypeScript производят JavaScript, можно произвести шелл из условного Лиспа или Питона. Когда я работал в Exoscale, у нас была библиотека для генерации шелла на Кложе. Я ей не пользовался, но само наличие говорит о том, что в руководстве понимали риски ручного скриптописания.

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

    В общем, чем раньше вы замените шелл-скрипт на что-то профильное, тем лучше.

  • Спасибо, что

    Кто читает Ильяхова, знает про “заранее спасибо”. Это когда пытаются манипулировать — свалить задачу, важное дело — и как бы проявляют вежливость.

    Сделай до понедельника, заранее спасибо. Напиши за ночь, заранее спасибо. Заполни сто полей, подтверди почту и телефон, заранее спасибо. Словом, все то, после чего думаешь “нет уж, спасибо”.

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

    У “заранее спасибо” есть другая форма: “спасибо, что”. Как и в случае с оригиналом, она вызывает одну мысль — я и не собирался. Лучше объясни, что делать, а не благодари.

    — Спасибо, что закрыли дверь!

    — Поставь доводчик.

    — Спасибо, убрали за собой!

    — Что убирать? Куда убирать? Как быть с жидкостями?

    Оказалось, “спасибо, что” есть и в английском языке. Когда-то давно я переписывался с коллегой, и он сказал “…and thank you for doing bla-bla-bla”. Я слегка удивился: вроде ничего не делал, а коллега благодарит. Начал было объяснять, но потом понял — это просто формулировка. Поручение в замысловатой форме, а thank you для того, чтобы я не обиделся. Типа такая вежливость. Bla-bla-bla я в итоге сделал, но душок от манеры речи остался.

    Надо ли говорить, что подобные вещи хоть в русском, хоть в английском языке смотрятся плохо. Общее между ними одно — манипуляция. Просьбу всегда следует выражать явно. Затасканная фраза вместо прикрытия дает фальшь.

  • Собеседование сегодня

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

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

    Итак, цель собеседования в том, чтобы определить, подходит кандидат или нет. Однако редко кто из собеседующих понимает суть процесса. Кандидату задают расплывчатые вопросы, ответы на которые прикидывают “на глазок”. Часто бывает, что один неудачный ответ затмевает остальные и собеседующий помнит только его. Или наоборот: кандидат не умеет писать код, но понравился эйчару манерой общения.

    Мне кажется, на собеседовании должна быть только конкретика. Вопросы про слабости и неудачи, видение себя через пять лет и прочее я считаю дурным тоном. Сюда же относятся расплывчатые технические вопросы, например сравнение архитектур, Linux vs Windows и другие темы, где главное — как подвешен язык.

    На предпоследнем собеседовании меня спросили, как работает компьютер. Я наплел всякую лажу про регистры, сумматоры и прерывания процессора, переходы и вот это все. Прошел (хотя не взяли по особым причинам). Но если бы не наплел, что бы это изменило?

    На последнем собеседовании спросили, какую проблему в айти я бы решил, если б имел неограниченные ресурсы. Тоже наплел ахинею про зависимости, пакеты и прочее. Прошел, вот сижу и пишу на Кложе. Спрашивается, если бы не наплел, хуже бы тогда писал?

    Еще одна категория вопросов, которые не стоит задавать — это родимые травмы языков. Например, что получится, если сложить объект и массив в Javascript? Не заглядывая в консоль Хрома, подумайте и объясните, какой будет результат и почему не иначе:

    {} + {}
    

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

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

    nope = lambda: pass
    

    Другой вариант этой задачи: лямбда, которая всегда бросает исключения:

    riser = lambda x: raise Exception(x)
    

    Прежде чем читать далее, предлагаю подумать и вам.

    Ответ: код не запустится из-за ошибки синтаксиса. Анонимная функция в Питоне устроена так, что в теле может быть только выражение. В нашем случае pass и raise — это операторы, а не выражения, отсюда ошибка компиляции. Чтобы первая лямбда работала, нужно заменить pass на None.

    Разумеется, ни один кандидат не ответил без подсказок, а я гордился, что знаю такое поведение. Хотя очевидно, это бред: что мешает поправить компилятор так, чтобы pass в лямбде возвращал None? Это дело нескольких строк, просто никому нет дела. Поэтому не парьте мозги окружающим.

    В крупные компании, куда стоят толпы народу, практикуют метод “нам не нужны неудачники” из известного анекдота. Не смог перестроить красно-чёрное дерево на бумаге? Пошел вон. И неважно, что в будущем будешь перекладывать протобуфы из Кафки в базу — нужно как-то отсеять желающих. Эйчаров на всех не хватает, а искусственный интеллект, как обычно, заработает не сейчас, а в скором будущем (сейчас он работает, но неправильно: выбирает белых образованных мужчин).

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

    Знакомый собеседовался в Яндекс на высокую должность. Архимаг по алгоритмам дал задачу на поиск строки с мудреными ограничениями. Главный критерий был в том, чтобы решить за O(N), а все очевидные решения давали квадрат. Эту задачу мы решали всем чатом, но так и не нашли ответа с O(N). Должности в Яндексе знакомый не получил.

    Оглядываясь на этот эпизод, пытаюсь понять, какой в этом смысл? Зачем мучить человека одной задачей в течение часа? Разве сотрудник Яндекса работает в таких же условиях? — его заводят в комнату на час, и если он не найдет алгоритм, увольняют? Уж наверное он общается с коллегами, ходит в курилку и все такое. Почему кандидат, который был в шаге от решения, не имел возможности даже пройтись по комнате? Все тот же критерий неудачника.

    Требовать от кандидата решить задачу на время значит поставить его в неудобное положение. Каждому известно, что мозг работает в двух режимах: сфокусированном и рассеянном. Их комбинация и продолжительность зависят от генетики. Возможно, кандидат нашел решение сразу после ответа “мы вам перезвоним”. Может быть, ему нужно сходить до туалета, чтобы смочить лицо. Или в магазин за молоком. Может, его знакомый уже решал такую задачу и может за пять минут поделиться опытом. Но собеседующего это не волнует: часы затрекал, таску закрыл.

    Бинарная оценка — решил/не решил — по своей сути не отличается от оценки по размеру груди, знаку зодиака или цвету волос кандидата. Так можно, если вы условный Яндекс. Но важно понимать, что в основе этой градации лежит дурацкий принцип и пренебрежение к людям.

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

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

    for x in range(1, 101):
        elif x % 3 == 0:
            print("Fizz")
        elif x % 5 == 0:
            print("Buzz")
        if x % 3 == 0 and x % 5 == 0:
            print("FizzBuzz")
        else:
            print(x)
    

    Первое же число, которое делится на 3 и 5 — это 15 — даст сбой: для него сработает первая ветка, и вместо FizzBuzz получим Fizz. В этом нет ничего страшного, гораздо важнее, как довести ошибку до кандидата и оценить его действия.

    Можно сказать, что в коде ошибка и предложить ее найти. Другой вариант — скинуть выхлоп правильного FizzBuzz и предложить написать тест. Это интересный случай, потому что навык писать тесты обязателен для программиста. Скорей всего, тест вынудит кандидата изменить код, чтобы вместо печати он строил список строк. Продвинутый кандидат перехватит вывод консоли в файл или поток.

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

    Дополнительно можно спросить про SQL и оператор JOIN. Если кандидат выбрал пользователей и профили одним запросом с LEFT JOIN, это вообще круто. Отпадают все сомнения.

    Если FizzBuzz приелся, замените его на другое задание с подвохом — факториал. Написать его можно сотней способов — apply, loop, recur — и каждый дает пищу для размышлений (например, хвостовая рекурсия: что такое, примеры). Подвох в том, что факториал нуля равен единице, и редкий кандидат помнит об этом. Как и в случае с FizzBuzz, предложите найти ошибку. Как будет лучше ее исправить? Добавить if/else или вести счетчик от нуля? Поправить аккумулятор? И конечно, написать и запустить тесты.

    В таких заданиях я смотрю не столько на решение, сколько на принцип работы кандидата. Настроена ли у него среда разработки? Если он претендует на Кложу, а пишет код в блокноте, мягко говоря, это сомнительно. Как он работает с REPL-ом? Как выполняет код из редактора? Как запускает тесты — и пишет ли их в принципе? Простой FizzBuzz даст на порядки больше информации, чем зубодробилка с O(N).

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

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

    В заключение — краткие тезисы.

    • От кандидата вас интересует его последний год. Прерывайте, если он рассказывает, как заводил Doom на утюге в 11 лет. Это никому не интересно.
    • Программиста берут, чтобы он писал код и тесты. Еще до собеседования попросите кандидата поделиться кодом. Подойдут гитхаб или приватные гисты. Если кода нет вообще никакого, это странно.
    • Дайте простую задачу, в которой, тем не менее, легко ошибиться. Оцените, как кандидат найдет ошибку и исправит ее.
    • Считаю, что тесты обязательны. Программист, который с ними не знаком, претендует в лучшем случае на мидла.
    • Полезно узнать навыки SQL. База встречается почти в каждом проекте, чего не скажешь про Кафки-Редисы. Эти опционально.

    Как-то так.

  • Аватар 2

    Что ж, сходил я на второй Аватар. Красиво, качество картинки просто космос. Хоть фильм и затянут, есть моменты, когда наслаждаешься кадрами, ни о чем не думая. Качество съемок, рендера, анимации тянет на миллион по какой угодно шкале.

    Удивляет другое: если на фильм тратят космические деньги — сотни миллионов долларов, — то почему нельзя написать хороший сценарий? Чтобы он был целостный, а не как лоскутное одеяло — с лишними ответвлениями, забытыми персонажами и слабыми местами. Это правда тяжело понять.

    Еще первый Аватар журили за избитый сюжет. Герой попадает к дикарям с корыстными целями, но переходит на их сторону. До какого-то момента он двойной агент, далее его раскрывают и выгоняют. Тогда он находит супер-девайс — меч, эликсир, дракона — и возвращает доверие. Происходит битва, и вождь как нельзя кстати гибнет. Герой становится вождем, получает королевство и царскую дочку.

    При всей банальности у этого сюжета неоспоримый плюс — он целостный и законченный. Все персонажи приходят к своему финалу (победа, любовь, поражение), не остается открытых вопросов, внутренний и внешний конфликты решены. Зрители довольные расходятся по домам.

    Во втором Аватаре пошли по принципу “добавим больше всего”. Получилось скомканно и путано; возникают вопросы о нестыковках. Перечислю некоторые из них.

    В начале фильма показывают сильную женщину, главу поселения. Сразу ясно: она главная, и даже злодей в ее подчинении. При первой встрече она лупит боксерскую грушу, показывая — смотрите, я баба с яйцами. Очевидно, она должна быть в финале, чтобы ответить за вторжение. Что имеем в итоге? Ее показывают пару раз и забывают. Зачем вводить персонажа, если он не доходит до финала и не влияет на сюжет? Банальная глупость.

    Далее: злодей, противник героя. Он настолько примитивен, что просто руки опускаются. Едва подорвался с больничной койки, как побежал убивать Джейка. Почему так глупо? Неужели его нисколько не коснулись вопросы самоидентификации: кто я, в каком мире я живу и для чего? Зачем убивать какого-то типа, который не причинил мне вреда? И это при том, что злодей прекрасно понимает — он просто копия того вояки, другое существо со своей волей.

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

    Есть еще один персонаж, за счет которого можно было бы усложнить сюжет — это Паук, человек, который живет с Нави. Это классический двойной агент вроде Маугли — вроде бы свой, но помани пальцем, и он перейдет к врагу. Паренек не сдает Нави явно, но работает против них: учит злодея штучкам и языку, переводит на допросах. Из-за него гибнет старший сын. Что в итоге? Паука освобождают и принимают обратно. Молодец, че.

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

    Еще одна нелепость — нет единства конфликта. В первой части люди добывали минерал, который стоил конских денег (8 миллионов за кило). Куда делся этот минерал в новой части, не ясно. Ладно, дана другая установка — переселение всех жителей Земли на Пандору. Однако в середине фильме оказывается, у людей новый промысел: ловля китов. Их рыбий жир дает вечную жизнь и стоит на Земле миллиарды. Получается нестыковка: в чем главная угроза — экспансия людей или убийство китов? С чем мы боремся? Я понимаю, что технически можно и то, и другое, но для сюжета это вредно: должна быть одна проблема.

    Нелепые ответвления сюжета. Дети героя — подростки, и конфликт старших братьев уже оттягивает на себя внимание. Внезапно приемная дочь становится экстрасенсом, впадает в обмороки и прочее. В нелепой сцене она видит мать, которую куда-то засасывает. Эта история должна разрешиться во что-то серьезное, но на деле оказывается банальностью: девочка управляет растениями, которые губят единственный батискаф. В конце она позвала светлячков, чтобы осветить путь побега, но это тоже слабо. Тему экстрасенсов надо было либо убрать, либо развить за счет чего-то другого.

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

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

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

    Пока на экране показывали медуз и карасей, я все думал о том, как сложится финал. И надумал по крайней мере три концовки, которые опишу ниже.

    Первая связана с идентификацией злодея. Как я писал выше, он крайне туп — при всем интеллекте человека действует как биоробот. Его путь надо было сделать похожим на путь героя из первой части. Сначала он ловит героя как человек без особого успеха. Затем изучает повадки и язык Нави. Далее наступает внутренний конфликт — он не понимает, кто он: человек или Нави. Воспоминания полковника теряются, их заменяют новые ощущения.

    В штабе видят, что злодей неуправляем и его пытаются убить. Злодей переходит на сторону Нави и вместе с героем берет город. В эпичном финале убивают бабу с яйцами, что снимает вопрос о ее нужности. Бывший злодей гибнет или жертвует собой, искупляя причиненное им зло. Его подельники образуют в лесу поселение. Чтобы помочь им выжить и разнообразить род, часть племени героя переходит к ним. В эпилоге показано, что на Земле отказались от программы клонировать Нави. Экспансию на Пандору откладывают, потому что ни одна попытка не окупила себя.

    Эта концовка просто супер. Все конфликты решены, все приходят к финалу, ничего лишнего. Что мешало сделать так же?

    Вторая концовка связана с Пауком. Парнишка должен был сыграть роль Дарта Вейдера — сперва помогает творить зло, но в итоге встает на путь истинный и искупляет грехи. В финальной битве злодей побеждает, надежды нет. Его сынуля вертит головой, борется внутри. Затем затаскивает папулю в тонущий батискаф и говорит: я помогал тебе творить зло, исправляю вину. Все это с пафосом, под крики и слезы. Есть шанс спастить самому, но это дело чести. Злодей мертв, предатель пожертвовал собой, все сходится, конфликт закрыт.

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

    Все это придумал обычный программист из Воронежа. Почему сценаристы из Голливуда со ставкой миллион в час не могли так же?

    Несколько слов в завершение. Великий писатель Антон Чехов высказал простой тезис: если на стене висит ружье, в финале оно должно выстрелить. Это касается буквально каждой мелочи в произведении. Показал персонажа — либо он одержит победу, либо умрет. Показал чудо-кристалл — либо он поможет герою, либо навредит злодею. У кого-то приступы ясновидения — в финале это расстроит замыслы врага. Все в том же роде, исключений не бывает. Все идет к своему финалу: люди, предметы, явления.

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

  • Возня с файлами (2)

    Это продолжение прошлого поста на тему синхронизации файлов. Небольшое дополнение и руководство как все настроить.

    Одно время я бекапил файлы в S3 при помощи aws cli — утилиты на питоне. Она работает просто: указываешь источник и приемник, и файлы переливаются из первого во второе. Источником и приемником могут быть локальная папка или бакет S3. В случае с S3 клиент посылает HEAD-запросы, чтобы сберечь трафик.

    Схема хороша тем, что протокол S3 поддерживает масса сторонних сервисов, например Яндекс.Облако, Digital Ocean, Exoscale (где автор имел честь работать) и другие. Если что-то пойдет не так, переезд сводится к изменению урла в настройках.

    Для айфона нашлось приложение S3 Manager, написанное на коленке одним добрым человеком. Показывает файлы из облака, умеет сохранять локально. Спартанское, немного топорное, но в этом и прелесть. Работает с Яндексом, проверял лично.

    Недостаток aws cli в том, что это односторонняя синхронизация. Сюда же относятся утилиты вроде rsync и аналоги. Их объединяет то, что одна из сторон считается верной, а другая подстраивается под первую. Например, если синхронизировать папки A и B, то файлы из А окажутся в В. Однако если в B были новые файлы, то в лучшем случае они игнорируются, а в худшем удаляются. Двойной синк по принципу сначала из А в В, а потом наоборот, эту проблему не решает.

    Забегая вперед, скажу, что в итоге поставил Syncthing, который пробовал еще год назад. Однако в процессе экспериментов узнал о замечательной программе Unison, и теперь коротко ее опишу.

    Это консольная утилита, написанная на OCaml. Работает на каждом утюге, включая Солярис. Синхронизирует два пути с учетом слияния файлов. Для этого программа ведет свой реестр и по нему сверяеся, что делать с файлами.

    Путь может быть удаленным (на другом машине). В этом случае операции с файлами протекают по SSH. У программы два режима: интерактивный (ручной) и автоматический. В первом случае, если встретится конфликт, программа запрашивает действие. Понравился богатый выбор, в том числе разрешение конфликтов при помощи сторонних утилит (kdiff, meld), что очень удобно.

    В автоматическом режиме (для скриптов и crontab) интерактивного ввода нет, все разруливается флагами.

    Unison оставил прятное впечатление. Видно, что программе много лет: огромное число функций, подробная докуметация. Но смутило, что Unison не работает в режиме демона и нуждается в регулярном запуске по крону. Плюс не очень подходит к Винде из-за SSH — как там его настроить даже не представляю.

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

    Итак, понадобится VPS с большим диском. Путем гугления я вышел на AlphaVPS, где за три евро в месяц можно взять машину с диском на 256 гигабайт (а за три с половиной — на 512). Диски, ясное дело, не SSD, но в моем случае это не важно.

    Заказываем машину с Ubuntu 22, заходим под рутом. Создаем пользователя:

    useradd -s /bin/bash -d /home/ivan/ -m -G sudo ivan
    passwd ivan
    

    Перекидываем SSH-ключ:

    ssh-copy-id -i ~/.ssh/ivan.pub ivan@123.123.123.123
    

    В ~/.ssh/config прописываем алиас

    Host alpha
        HostName 123.123.123.123
        IdentityFile ~/.ssh/ivan
        User ivan
    

    Далее заходим по ssh alpha и вводим команды:

    echo "deb https://apt.syncthing.net/ syncthing stable" | sudo tee /etc/apt/sources.list.d/syncthing.list
    curl -s https://syncthing.net/release-key.txt | sudo apt-key add -
    sudo apt update
    sudo apt install syncthing
    

    Чтобы Syncthing запускался в автозагрузке, добавим его в systemctl:

    sudo systemctl enable syncthing@ivan.service
    

    Остается включить:

    sudo systemctl start syncthing@ivan.service
    

    Syncthing настраивается через веб-интерфейс, однако он доступен только с локального хоста. Поднимаем SSH-тоннель:

    ssh -N -L 18384:localhost:8384 alpha
    

    Теперь откройте браузер на странице localhost:18384 — появится интерфейс Syncthing на удаленной машине. Ну а дальше вы сами знаете: создаете папки, связываете машины друг с другом и так далее.

    На текущий момент у меня VPS за три евро и три компа: два мака и винда. Вроде бы работает, файлы подхватываются. К сожалению, Syncthing не так гибок в плане текстовых файлов, как Unison: умеет только брать файл А или В, а про слияние сторонней тулзой ничего не знает. Однако для текстовых файлов есть git.

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

  • Обновления

    Современный софт огорчает просьбами обновиться. С какой-то поры этой заразы стало так много, что не знаешь, куда деваться. Открыл Siblime Text, а он предлагает обновиться:

    Открыл Dash, программу офлайн-документации, а она предлагает обновиться:

    Открыл Wireshark для просмотра трафика, а она предлагает обновиться:

    Firefox в конец обнаглел: теперь обновления нельзя отключить. Ставить обязательно: либо с подтверждением, либо без него. Справа постоянно висит нотификация.

    Туда же скатился Телеграм: появляется блямба “обновить”, которая молозит глаза. Конечно, никто не принуждает, дело добровольное. Просто попробуй посиди с ней — долго не протянешь.

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

    Почему-то ни один диалог не отвечает на вопрос: какая польза мне в обновлении. Программа работает, покрывает все нужды, жалоб нет. С какой стати тратить время на обновление? Если найдется баг, я выберу “О программе” → “Проверить обновления” и тогда обновлюсь.

    Причина в том, что разработчики забывают об этике. Некрасиво, когда программа делает то, о чем ее не просили. Это понимали еще пятьдесят лет назад во времена Unix. А сегодня понятия цифровой этики не существует. Так быть не должно.

    Вспомнился частный случай с Гуглом — организации, которую трудно заподозрить в желании сделать кому-то удобно. Когда браузер Хром вошел в фазу взрывного роста, он обновлялся даже не каждый день, а несколько раз в день. В результате обновления сделали полностью скрытыми от пользователя. Это потребовало усилий, но они того стоили. Уж если корпорация решила за меня, что и как обновлять, то и спрашивать согласия не нужно. Иначе зачем?

    Ужасно, но подход с обновлением переходит в консольные утилиты, например pip и npm. Когда работал с Питоном, жутко бесило сообщение pip: воу-воу, у тебя версия, 2.5.1, а последняя — 2.5.3, поставь немедленно! Перед каждой командой pip ломился в сеть с GET-запросом, ожидая ответа. Это такая тупость, что даже руки опускаются. Ясное дело, что обновления отключаются какими-то переменными среды, которые вечно гуглишь на StackOverflow.

    В свое время я ставил программы для работы с текстом, например Optima. Минимальный интерфейс, интеграция с Главредом, красиво. Но уже на второй день он распахнул диалог с предложением обновиться. У меня в таких случаях одна мысль — пошел на х..й. Грубовато, но это первое, что приходит в голову. Вздумал прервать меня за работой? Отправляйся в ведро: “приложения” → Optima → “удалить”.

    Идеально в этом плане ведет себя Emacs. Каждый раз я ставлю его с опозданием на две-три мажорных версии, а он хоть бы слово сказал. Поставил 25.1, хотя доступен 28.2 — значит, так надо: версия 25.1 просто работает. Сложно что ли сделать так в других программах?

    Бесконечное стремление вперед просто вымораживает. Если бы от него был толк! Сколько я вижу бажного кода, написанного в последних версиях JetBrains! Сколько уродской верстки, сделанной в свежем In Design! Сколько вырвиглазного текста, набранного в последнем Ворде!

    Обновившись по самые помидоры, программист по-прежнему не пишет тесты к своему коду. Подставляет параметры в SQL функцией format. Посылает тысячу запросов к базе в цикле. Что, IDE не подсвечивает? Подожди обновлений — может, со следующим релизом подсветит.

    В общем, разработчики: успокойтесь с обновлениями. Вашими усилиями почти не осталось софта, который работает молча. Дайте уже спокойно поработать.

  • Как я провел 2022 год

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

    После увольнения из Exoscale я устроился стартап Clashapp (ныне Huddles). Это мобильное приложение с короткими видео и встроенной валютой. Вместе с коллегами пилили бекенд на Кложе. В техническом плане проект был очень продвинут, многие вещи взял на заметку. Тимлидом у нас был Eric Dvorsak, и работать с ним было одно удовольствие.

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

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

    Работа в Австралии отличается графиком. Разница с офисом семь часов, пересекаюсь с руководителем буквально один час в день. Это первый случай в моей практике, когда разница столь велика. Есть и плюсы, и минусы. С одной стороны, приучает работать автономно, задавать вопросы, проверять задачи на точность. Полезно иметь пул задач, чтобы в случае затыка с одной переключиться на другую.

    С другой стороны, пересечение всего лишь час в день — неописуемое благо. Отбрехался с утра — и свободен: никаких ретро, синков, тренингов, медитаций, эстимейтов, постмортемов и что там еще. Никто не потревожит, никто не вломится в зум. Все асинхронно, красота!

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

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

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

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

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

    Помогаю выпускнице, с которой делали диплом, найти работу Питон-джуниором. Ох непросто стало сейчас.

    В планах на 2023 год — выпустить книжку.

    Поздравляю всех с наступившим Новым годом. Желаю больше стойкости к стрессу и информационному шуму. Меньше пропускать через себя, чтобы не сойти с ума. Оставаться людьми. Это, пожалуй, самое важное.

  • Clojure Coding Guide

    TL;DR: this is a detailed description of how to write good Clojure code. It’s based on my 8 years of experience with Clojure for both commercial purposes and side projects as well. Some parts of this document repeat the well-known guide by Bojidar. Other parts instead break the conventional rules in Clojure development. For such cases, I give an explanation of why they are what they are. Everything written below has come from practice, and I hope you’ll find it useful.

    Table of Contents

    Parentheses

    Let’s start with something obvious yet worthy of repeating. When writing Lisp code, don’t balance parentheses as you did before in Python or JavaScript. Not like this:

    (time
     (doseq [a [1 2 3]]
       (let [b (* a a)]
         (println b)
         )
       )
     )
    

    But this:

    (time
     (doseq [a [1 2 3]]
       (let [b (* a a)]
         (println b))))
    

    The same rule applies to collections. The following code fragments look weird in Clojure:

    (def mapping {
       :name "Ivan"
       :email "test@test.com"
    })
    
    (def numbers [
      1,
      2,
      3
    ])
    

    It is a strong rule for every Lisp dialect and you’ve got to get on with it. The sooner you get an editor powered with a plugin to manage parenthesis the better it is for you as a programmer. Emacs + Paredit is a good choice but it’s a matter of preference.

    Read more →

  • Кто виноват

    Наверное, вы слышали: чтобы получить правильный ответ, задавайте правильный вопрос. И наоборот: неправильный вопрос займет массу времени и сил, но ответа не даст. Удивительно, что многие вопросы, что мы задаем, поставлены неверно. Один из них звучит как в заголовке — кто виноват?

    Стоит чуть-чуть подумать, как станет ясно, что это неправильный вопрос. Так ли важно, кто именно виноват? Что это дает? Как виновность поможет исправить ситуацию и предотвратить ошибку в будущем?

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

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

    Когда сотрудник Гитлаба удалил базу с прода, выяснилось много вещей. Бекапы не работали, оповещения об ошибке бекапов тоже не работали. Пока петух не клюнул, никто не волновался.

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

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

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

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

    Вася уронил прод и он виноват. Но отвечает техдиректор, потому что у Васи в принципе не должно быть доступа к проду.

    Оператор взорвал реактор и он виноват. Отвечают те, кто проектировал, вводил в эксплуатацию и тестировал.

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

    Пьяный устроил дебош в клубе. Он виноват, но отвечает охрана, которая его пропустила.

    Сотрудник слил базу клиентов. Он виноват, но отвечает фирма — юрлицо. Будьте любезны штраф в десять тысяч за каждую строку. Сливы сразу прекратятся.

    Водитель в Воронеже сбил насмерть коляску с грудным ребенком. Оказалось, у него десятки неоплаченных штрафов за последние полгода. Он виноват. Но пусть начальник ГИБДД объяснит, почему водителю не аннулировали права.

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

    Бывает, что ответственный известен, но сыскать с него ничего нельзя. Это значит, игра заведомо проиграна. В следующий раз не ведитесь.

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

  • Интернет и геополитика

    На заре интернета было забавное по текущим временам мнение. Якобы он станет средой, которая объединит людей, отбросив границы и политические взгляды. Что противоречия физического мира отпадут, и все найдут согласие. Что коллективный разум возобладает над политиками и превзойдет их.

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

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

    Тезис о едином пространстве разваливается на глазах. Все меньше остается сервисов, куда можно попасть, не испытывая ограничений. Твиттер(1) и Инстаграм(2) заблокированы Россией. На Госуслуги и Налог.ру нельзя зайти, если ты не в России. Если уехал, ставь московский ВПН, что до недавнего времени звучало анекдотом.

    Порнхаб в одной стране разрешен, в другой запрещен, в третьей — только с авторизацией через местную соцсеть. Grammarly не работает с российскими IP, нужен ВПН. Украинские сайты показывают россиянам плашку с расчлененкой.

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

    Гугл и Ютуб боятся ВПН как огня. С ним Ютуб показывает капчу — не дай бог ты пройдешь мимо Большого Брата. Фейсбук, Твиттер и аналоги блокируют Тор. Им лучше знать, что безопасно, а что нет.

    Про Китай писать нет смысла.

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

    Фейсбук и Твиттер открыто занимают позиции в социальных конфликтах США: выборы, BLM и так далее. Получить бан за поддержку Трампа теперь в порядке вещей. Не лучше ситуация и у нас, когда ВК и Мейл.ру сливают данные о тех, кто состоит в группах “оппозиции”.

    В той или иной мере огораживается каждый второй сайт. Со включенным ВПН нельзя передать показания в энергослужбу или заказать б/у книгу. Знакомый справедливо заметил, что сегодня проще иметь два ноута — настроенный под иностранный сайты и для внутренних нужд.

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

    Очевидно, никакого свободного интернета нет, более того — не интернет двигает обществом, а двигают им. Современная дурь оседает в интернете и приводит к ограничениям по IP, странам, действиям режима и остальному.

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

    1, 2 – считаются экстремистскими организациями или вроде того.

Страница 14 из 73