-
Сбер
Постоянно пользуюсь Сбером, чтобы оплатить что-нибудь по QR-коду. Каждый раз удивляюсь анимации вверху экрана. За каким-то хреном кнопка с QR не зафиксирована, а выезжает справа. Как в рекламных полях, где анимацию делают ради анимации: что-то выехало, покачалось, мигнуло. Типа, управление вниманием.
Зачем? Кто просил эту анимацию? На автомате тычу в левый угол, клик приходится на другой элемент, открывается что-то не то. Ясное дело, все тормозит, потому что параллельно загружаются другие виджеты.
Если функция кнопки известна заранее, а также ее текст и оформление, никакой анимации не нужно. Может, балбесу-дизайнеру из Сбера нравится: у него эмулятор на мощной тачке, запущено одно приложение, все плавно. А потребитель видит слайдшоу и промахивается кнопкой.
Дорогие дизайнеры!
Засуньтеуберите ваши анимации подальше. Они не нужны, вы делаете лишнюю и вредную работу. -
The Mask library for Clojure
(This is a copy of the readme file from the repository.)
Mask is a small library to prevent secrets from being logged, printed or leaked in any similar way. Ships tags for Clojure, EDN and Aero.
Why? Because I’ve been in such a situation three times, namely:
- We don’t mask the secrets.
- Someone logs the entire config.
- Secrets have leaked!
- Rotate all the keys, tokens, etc.
- Change the team and face the same.
This library is an attempt to break this vicious circle.
Installation
Leiningen/Boot:
[com.github.igrishaev/mask "0.1.0"]
Clojure CLI/deps.edn:
com.github.igrishaev/mask {:mvn/version "0.1.0"}
Usage
The
mask.core
namespace providesmask
andunmask
functions. Pass a value tomask
to make it safe for logging or printing in REPL:(in-ns 'mask.core) #namespace[mask.core] (def -m (mask "Secret123")) -m << masked >> (str "The password is " -m) "The password is << masked >>"
Masking is idempotent meaning that you can mask the same value multiple times but the result will be one-level masked value:
(-> -m mask mask mask) << masked >>
To release a value from a mask,
unmask
it:(unmask -m) "Secret123"
Unmasking is idempotent a well:
(-> -m unmask unmask unmask) "Secret123"
Note: the library treats
nil
as an error value that cannot be masked. You’ll get an exception:(mask nil) Execution error (IllegalArgumentException) at ... (core.clj:34). Cannot mask a nil value
Masking an empty value signals you’re doing something wrong. Most likely you’ve missed a corresponding key or an environment variable. Thus, the further work makes no sense.
Spec
The
mask.spec
module provides the::mask
spec that checks if a value is really masked. An example from the tests:(let [config {:username "Ivan" :password #mask "secret"}] (is (s/valid? ::config config))) ;; true
Clojure tag
The built-in
#mask
tag wraps any value with a mask:=> {:token #mask "abc123" :password "SecretABC"} {:token << masked >>, :password "SecretABC"}
EDN tag
There is a
reader-edn
function that acts like an EDN reader for the same tag:(let [source (-> "{:foo #mask 42}")] (edn/read-string {:readers {'mask reader-edn}} source)) ;; {:foo << masked >>}
Aero tag
To extend Aero with the
#mask
tag, import themask.aero
namespace:(require 'mask.aero)
Then read a config with the tag:
;; config.edn {:foo #mask #env "SOME_PASSWORD"} ;; code (aero/read-config (io/resource "config.edn")) ;; {:foo << masked >>}
The Aero dependency is not included. You’ve got to provide it by your own.
Ivan Grishaev, 2023
-
Эй
Почему-то большие компании не могут нормально составить письмо на русском. Будут ошибки или нелепые обороты. Сегодня получил письмо от Дискорда, где ко мне обращаются на “эй”:
Сам ты эй! Понятно, это дурацкая калька с английского hey. Почему не нашли русского чувака, чтобы показать ему перевод перед отправкой? В больших фирмах всегда найдется русский, украинец, поляк или венгр. Даже если он кодер, пусть посмотрит. Он скажет, что обращение на эй в русском не только неестественно, но даже грубо.
Жаль, не сохранил скриншоты Гугла и Godaddy. Иной раз такую дичь присылают, что хватаешься за волосы. Сейчас реже, но все равно.
-
Silent Hill 2
Появился трейлер переиздания Silent Hill 2 — одной из главных игр моего детства. Наверняка вы его смотрели, но вот на всякий случай:
Графика выше всяких похвал, сказать нечего. Однако у меня вопрос — что не так с этим трейлером? Не проматывая вниз, постарайтесь подумать.
-
Праздники
Не люблю праздники посреди недели. Скажем, по четвергам я хожу в зал, но завтра он не работает. Что мешает администратору прийти и открыть дверь, непонятно. И вообще — чем этот четверг отличается от других четвергов? Ничем, кроме дурацкого обычая, что в какой-то день вместо работы валяют дурака.
Хотел переставить на среду, но оказывается, что у жены на работе поменяли смены из-за предпраздничного дня. У старших детей укороченные уроки, и нужно ехать за ними раньше обычного.
Вот и думаешь: стоило ли переставлять кучу дел из-за одного праздника? Кому вообще всрались эти 23 Февраля, 8 Марта, Дни единства и все такое? Кто эти люди, что их празднуют? И как?
Выгоду от праздников получают только дети, потому что не надо идти в школу. Взрослые, даже если они бюджетники, должны что-то переставлять и отрабатывать, что еще то мучение и нарушение ритма.
Считаю, любой праздник должен автоматом переноситься на ближайший выходной — празднуй сколько хочешь, а другим не мешай работать. Исключение из этого правило одно — Новый год, его празднуют строго по календарю. Но это и так очевидно.
-
The DynamoDB library for Clojure
(This is a copy of the readme file from the repository.)
This library is a driver for DynamoDB written in pure Clojure. No AWS SDK, lightweight dependencies, GraalVM-friendly.
Benefits
- Free from AWS SDK. Everything is implemented with pure JSON + HTTP.
- Quite narrow dependencies: just HTTP Kit and Cheshire.
- Compatible with Native Image! Thus, easy to use as a binary file in AWS Lambda.
- Clojure-friendly: supports fully qualified keyword attributes and handles properly them in SQL expressions.
- Both encoding & decoding are extendable with protocols & multimethods.
- Raw API access for special cases.
- Specs for better input validation.
- Compatible with Yandex DB.
-
Удалил
Как и многие, я храню всякий хлам в канале Saved Messages в Телеграме: ссылки, идеи, напоминалки. Иногда перебрасываю креды к сервисам, если лень это делать через гист или менеджер паролей.
Только что случилась забавная вещь. Удаляя сообщение из Saved Messages, не заметил, как кликнул по каналу и удалил его. Нажимая на подтверждение, подумал, что диалог какой-то странный, но палец было не остановить. Вжух — и канала не стало со всеми сообщениями.
-
Баш-скрипты
Программист должен помнить не только о вещах, которые нужно делать. Обратное тоже важно: запоминайте, чего делать не нужно. Плохой опыт — тоже опыт. Он поможет не упасть там, где спотыкаются другие.
Одна из таких вещей — скрипты на баше. Я взял за правило: никаких баш-скриптов. Когда хочется написать на нем что-то длиннее пяти строк, закрывайте ноут и идите за молоком. Потом вернитесь и сделайте правильно — то есть без баша.
Шелл-скрипты хрупки и тяжелы в поддержке. Их трудно читать и отлаживать. Они опираются на сторонние утилиты, которые где-то есть, а где-то нет. Поведение зависит от платформы и флагов.
Шелл — это плавное погружение в смоляную яму, из которой не выбраться. Год-другой — и скрипт занимает экраны, никто не знает, как он работает. Лучше не трогать.
Замечу, что сказанное не относится к случаям, когда вызывают несколько утилит, соединяя их каналы, например:
> 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. База встречается почти в каждом проекте, чего не скажешь про Кафки-Редисы. Эти опционально.
Как-то так.