-
Файл с процентом
Узнал, что в Teams нельзя прикрепить файл со знаком процента. Также запрещены решетка, двоеточие и другие спецсимволы (см. картинку). Испытал смешанные чувства.
С одной стороны, это бред. Когда файлы хранят в облаке, никто в здравом уме не назначает им исходное имя. Файлы лежат как бинарные блобы с машинными уидами, а имя хранится в базе в текстовой колонке. Будь там процент, решетка, эмодзи или DROP TABLE STUDENTS — нормальной системе это без разницы.
С другой стороны, “не все так однозначно” (с). Я уже рассказывал, как потерпел неудачу из-за пробела в папке S3. Половина клиентов ищут папку Daily Reports, другая половина — Daily%20Reports. Класс java.util.URI просто валится, если дать ему адрес с пробелом.
Отсюда ясно, почему запрещены спецсимволы: с ними возрастает вероятность факапа. Не в этом сервисе, так в другом. Проще запретить, чем разгребать последствия: удаленные по ошибке данные, инъекции в путях и прочие штучки.
Так что хотел подтрунить над идиотской ситуацией, а потом раз — и как тот парень, который в цирке не смеется.
-
Кложурная Слака
В кложурной Слаке новости. Если коротко, заканчивается бесплатный период, и сообщество решает, куда переезжать. Я там набросил на вентилятор, и делюсь с вами мыслями. Это было коротко, а теперь долго.
Все вы знаете про Слак — бизнес-мессаджер, платформу команд. Слака очень популярна в Америке, люди залипают в ней как мы в Телеграме. Там и работа, и разные сообщества. Но есть нюанс: в бесплатной версии очень короткая история поиска, буквально неделю или около того. В результате то, что обсуждали месяц назад, уже не находится. Народ пишет ботов, которые архивируют каналы в статичный веб, но это так себе.
В кложурной слаке зарегана 21 тысяча человек, из них 2.600 активны. За каждого активного пользователя Слак просит почти пять долларов в месяц, в результате месячная плата составляет 19 косарей. До того, как узнать эту сумму, я думал выступить спонсором: перечислять долларов 500 в месяц в общий фонд. Но собрать 19 тысяч будет тяжеловато, да и не ясно, за что столько платить.
Пару лет назад Шон Корфилд, один из топовых разрабов, выбил из Слаки годовой грант: мы некоммерческое комьюнити и все такое. Потом в Слаке сменилось руководство, но Шон снова получил грант. И вот опять в Слаке перестановки, и на этот раз на запросы никто не отвечает. Платный тариф истекает в конце декабря, и идет обсуждение, куда переезжать.
Независимо от того, что решат, важно подумать вот о чем. Слака — это классическая бесплатная доза. Ладно один год платного тарифа, ладно второй — но так не может продолжаться вечно. Менеджмент без конца меняется, и каждый новый обнуляет договоренности. Поддержка некоммерческих групп не оговорена на сайте, и надеяться на вечное продление глупо. Все частные договоры ситуативны и справедливы лишь в моменте.
Просто чтобы посмотреть на реакцию, я предложил Телеграм. А что такого? Есть же русское сообщество в Телеге, и хорошо себе живет. Есть и англоговорящая группа человек на 250.
Как и ожидалось, нашлись те, кого беспокоит, что Телеграм — это Russian thing. Ничего, что основатель уехал в Дубай двенадцать лет назад? Кроме того, я могу понять, когда россиянин не ставит американский софт, опасаясь санкций. Но почему американец опасается софта, который написан русскими? Уверен, что все сливается в КГБ? Кто-то много смотрел телевизор.
Другая вещь, которая бросилась в глаза — в этом вашем Телеграме нет модерации. Западный человек настолько привык, что каждая платформа модерируется, что без нее чувствует себя голым. Это что, каждый может написать то, что думает? Людям даже не приходит в голову, что модерацией должно заниматься сообщество: ставить своих анти-спам ботов, дать админские права людям в разных часовых поясах. Модерировать должны вы, а не индусы на аутсорсе, которые грепают текст по ключевым словам, и которым завернуть контент проще, чем оставить.
Ясно дело, ни на какой Телеграм я не надеюсь. В Слаке обсуждают Зулибы и прочую дрянь с клиентами на Электроне. Всерьез предлагают Дискорд. На этом фоне мое предложение смотрится неудачной шуткой. Я не возражаю. Мне было интересно вбросить тему, на которую у людей вагон предрассудков и посмотреть со стороны.
-
Боязнь исключений
Многие программисты боятся исключений. Как я об этом узнал? Примерно раз в месяц в кложурной Слаке и Телеграме задают вопрос: что мне использовать вместо исключений? Им советуют возвращать мапы с полем error, разные типы-обертки, макросы. Есть несколько библиотек, имитирующих монады. Это когда поделил на ноль, и вместо исключения приходит объект Failure.
Что тут сказать… можно прятаться от проблемы разными способами. Не смотреть на нее, не называть вслух или креститься. Можно писать программу так, что даже в худшем случае она не упадет. Только вопрос — зачем?
Везде, где пытаются избежать исключений, я вижу обратное. Проблему зарывают в песок, и об ошибке никто не знает. Если не пожалуется пользователь или не придет менеджер, о проблеме не узнают. Или узнают в пятницу вечером. Я таких осиных гнезд вскрывал ого-го сколько.
Когда используют разные обертки вместо исключений, для меня это признак низкого уровня команды. В коллективе не понимают, как поступать с исключениями, как их собирать и расследовать. В такой команде пишут плохой код, вот и все.
Исключения не нужно прятать. Наоборот, как только что-то идет не так, нужно кидать их с как можно более понятным сообщением и данными для отладки. Для этого нужно победить свои страхи: признать, что код не идеален, в нем баги, и экстренно на них реагировать.
Терпимость к отклонениям должна быть нулевая. Зарывание ошибок в мапу — путь в никуда.
-
Уведомления о куках
В позднем СССР сложилась интересная ситуация. Воевать в Афганистане можно было с 18 лет, а пить спиртное — только с 21. Другими словами, не возбранялось легально убивать людей, а вот пить водку — подожди еще три года.
Мне это напоминает современный веб. Сегодня каждый второй сайт кричит, что он использует куки: всплывашки и выпадашки всех мастей. По требованию еврочиновников меня предупреждают, что сайт гоняет куки — мелкие статичные строки. Это при том, что браузеры существуют не первый год, и проблемы кук уже решены. Можно легко посмотреть, что передается в куках, удалить их, выставить запрет не передачу и так далее.
При этом еврочиновники не считают нужным уведомить пользователя, что любой современный сайт — это рантайм Джаваскрипта с почти неограниченными возможностями. В частности, меня не уведомляют о том, что:
-
сайт может вывалить 50 мегабайтов скриптов, сделать 250 запросов;
-
сайт может ходить на другие сайты, подтягивать виджеты, которые тянут другие скрипты, и так рекурсивно;
-
на сайте может быть пять трекеров, которые сливают всю подноготную о системе: юзер-агент, разрешение экрана, айпишник, локаль и прочее. Из них формируется устойчивый отпечаток, по которому за клиентом следят в других системах. Эти данные продаются третьим фирмам.
-
на сайте контекстная реклама;
-
сайт трекает движение мыши, шлет события на каждый клик
-
в сайт встроены анти-блокировщики, которые замедляют загрузку;
-
сайт может быть частью ботнета (координатором или жертвой);
-
сайт может трекать пользователя через данные в LocalStore и кастомные заголовки. Кук нет, отслеживание есть;
-
функции трекинга уже зашиты в апишку браузера и Джаваскрипта (Beacon API, Geolocation API, etc)
-
и еще штук двадцать пунктов.
Повторюсь, со всем этим у еврочиновников проблем нет, предупреждать не нужно. А вот куки им не понравились.
На этот счет у меня есть еще одна мысль, но о ней — в следующий раз.
-
-
Приши, улучшай
Кто читал Ильяхова, знает формулу “пиши, сокращай”. Суть в том, что хороший текст не пишут сразу. Пишется болванка, полная косяков, а потом по ней многократно проходят: здесь снимают стружку, тут, наоборот, доливают свинца. Только тогда текст становится сильным.
То же самое с кодом: недостаточно его написать и проверить. Когда тесты написаны и код рабочий, нужно пройтись по нему и причесать. Дать нормальные имена, вынести анонимные функции на верхний уровень. Одни участки кода разнести на промежуточные шаги, чтобы не было слишком плотно. Другие, наоборот, сократить.
Здесь нет точных правил, важно чутье — как воспримет этот код другой человек. Будет ему ли понятна логика, насколько легко внести изменения.
И если есть программисты, которые комитят рабочий код, то со второй фазой все очень плохо. Кажется, что когда код заработал, у программиста щелкает какое-то реле — работа сделана, закрывай таску. Потратить двадцать минут на причесывание кода — не знаем, не слышали.
И да, этим страдают в том числе кложуристы. Кто-то напел им, что на Кложе получается божественный код, который хорош сам по себе. Иммутабельность, лисп, дэ-эс-эль, бла-бла.
Читаю чужой код: коллекцию пропускают через пачку
map
иpartition-by
. В каждой из них — анонимная функция со вложенными reduce и другими анонимными функциями. Партицирование замкнуто на другой коллекции, которая что-то достает из первой. Неделимый блок кода размером с экран. Функция в функции внутри функции.Нечитаемый ад. Писал его не молодой программист, надо полагать, с опытом. И то же самое: как только он дошел до стадии “работает”, то оформил PR — и получил апрув коллег.
Давайте не будем так делать. Если код работает, это не значит, что задачу пора закрывать. От двадцати минут, потраченных сверху, вреда не будет — прод не сгорит, менеджер подождет. Зато спасет день коллеги, который вкатывается в проект.
-
Две системы клавиш
Это короткая заметка, которую хотелось бы развить в будущем.
Есть только два редактора, в которых хоткеи сделаны правильно — это Вим и Емакс. Не то чтобы я маньяк, но других вариантов не вижу.
Возьмем современный редактор и посмотрим на хоткеи. Нажатие на кнопку приводит к печати символа, и это логично. Чтобы вместо печати было действие, кнопку нажимают со служебной клавишей, скажем Ctrl, Alt, Command. Все это тоже логично.
Но вот беда — многие комбинации заняты дефолтами! Ctrl-Q, T, N, O, P — все это системные действия. Еще десяток клавиш уходит на навигацию каретки, еще десяток — на удаление строк, слов и символов, и привет — Ctrl уже исчерпан. Попутно нужно учесть, что на Маке роль Ctrl играет Command, поэтому удвоения емкости это не дает.
Остается Alt, но на него могут быть повешены хоткеи терминала и операционки, и в итоге какой-нибудь Alt-N просто не дойдет до редактора. Ну и часто на Альт тоже много чего навешено.
Только в двух редакторах подумали о том, как решить проблему комплексно: это Vim и Emacs. В первом разграничили режимы ввода и команд. Я пытался в Вим, но не зашло. Уважаю тех, кто сидит в нем, потому что нравится сама концепция.
В Емаксе хоткеи сделали последовательностями, или цепочками. Например,
C-x f
открывает файл,C-x o
открывает новое окно,C-x C-c
—закрывает Емакс. Подход с цепочками позволяет делать домены хоткеев, когда наC-x
вешаются базовые функции, наC-h
— все, что имеет отношение к справке, наC-x r
— операции с прямоугольниками и так далее. Все это легко наращивается вглубь, не мешая остальным.Недавно загуглил, как вызвать в Идее выпадашку с методами. Знаете как? Command + F12! Если учесть, что F-клавиши на маке работают с зажатым Ctrl, получается три кнопки одновременно! Пользователь Идеи должен быть осьминогом, чтобы с этим совладать. А в Xcode на полном серьезе одна из функций навешена на Shift+Alt+Command+V. Я уже не помню какая, но точно помню подсказку в меню.
Не то чтобы это большая проблема; можно продуктивно работать в любом редакторе. Но повторюсь, системно к проблеме хоткеев подошли только в двух редакторах: Виме и Емаксе. Все остальное — ситуативно и нерасширяемо.
-
Выпрямить данные
В кложурном чатике скинули очередную задачу: дана такая-то мапа, нужно перегнать ее в другую мапу. Примерный исходник:
(var DATA {:kek {:foo {:type :LOL} :bar {:type :KEK}} :owo {:pip {:type :AGA}}})
Ожидание:
{:LOL {:foo :kek} :KEK {:bar :kek} :AGA {:pip :owo}}
Это очень упрощенный пример. Грубо говоря, нужно вывернуть вложенную мапу наизнанку: поместить наверх то, что сейчас внизу.
Большинство кложуристов подходят к этой задаче как есть. Они берут мапу и прогоняют ее через комбо
map
,mapcat
и анонимных функций. Кто-то накручиваетreduce
внутриreduce
. В целом работает, но как это читать и отлаживать — я не знаю.Я тоже скинул решение, точнее его часть, и оно зашло: понаставили пальчиков и огней. Раз так, стоит рассказать подробней, тем более что я замечаю, что никто так не делает.
Итак, если посмотреть на исходную мапу, станет ясно, что когда-то она была плоской таблицей:
kek foo LOL kek bar KEK owo pip AGA
Из-за того, что в левой части были повторы, кто-то решил избавиться от них группировкой. Но данные никуда не пропали: мы по-прежнему знаем, у кого какой атрибут. Посмотрев на самый вложенный элемент, легко проследить его путь. Например, обнаружить, что AGA начинается с owo.
Так вот, чтобы переколбасить эту мапу во что-то другое, нужно сперва выправить данные — привести их к таблице. Поможет макрос for. Он принимает несколько коллекций и строит декартово произведение их элементов. Примечательно, что каждая следующая коллекция может быть получена из предыдущих элементов. Код ниже строит ленивую таблицу:
(for [[k1 submap1] DATA [k2 submap2] submap1 [_ item] submap2] [k1 k2 item]) ([:kek :foo :LOL] [:kek :bar :KEK] [:owo :pip :AGA])
Теперь когда мапа развернута, можно сгруппировать ее по-другому. Как именно — это уже тривиальное дело, потому что, имея плоские данные, это делается на раз-два. Кроме того, нужно спросить себя — действительно ли нужна новая группировка? Может быть, лучше оставить как есть? Возможно, на той стороне тоже хотели бы плоские данные.
Я как-то рассказывал о нелепой ситуации со вложенностью. На одной стороне человек потеет, чтобы построить из списка вложенную мапу вида:
{:node shit :children [{:node crap :children [{:node fuck :children [...]}]}]}
А на второй стороне другой человек потеет, чтобы обойти ее как список. Оба пишут быдлокод и матерятся, а ради чего — не ясно. Такую структуру даже не каждый кложурист обойдет, потому что не все знают про tree-seq и зипперы.
Возвращаясь к теме переколбаса данных: хорошо бы запомнить аналогию. Плоские данные — это как лист бумаги, из которого можно сложить самолет, кораблик или лягушку-квакушку. Но переход от одной фигуры к другой происходит через развертку — то есть откату к нулевому состоянию. Только потом можно переходить к новому.
Наверняка найдется японец, который может сложить самолет в лягушку, минуя лист. Точно так же можно написать быдлокод, который переколбасит одну мапу в другую. Но это сложно, не очевидно, хрупко. Лучше избегать.
Видеть простейшую форму и возвращаться к ней, когда что-то идет не так — важный навык.
-
Файловые пути
У меня пожелание: давайте не будем строить файловые пути конкатенацией строк. Не будем сами и не позволим другим. Как увидите в ревью что-то вроде
file_path = some_dir + "/" + file_name
, сразу пишите комментарий: не клей строки, используй функции, для этого предназначенные.
Достоверно известен случай, когда человек потерял бизнес из-за ошибки в файлах. Это был небольшой хостинг, и чел выполнил на всех машинах
sudo rm -rf $FOO/$BAR
. Беда в том, что переменные не подхватились, и получилосьsudo rm -rf /
.Вы скажете, что есть флаг
-e
, чтобы вылететь с ошибкой, если переменная среды не задана. Ну, допустим. Однако ничто не мешает сделать такую же ошибку в коде. Например, кложуристы (которые, как известно, боги программирования), строят пути конкатенацией строк:(str some-dir "/" subdir "/" file-name)
И не знают, что функция
str
молча пропускает нуллы. Это значит, еслиfile-name
равен nil, то путь получится/some/dir/subdir/
. Если передать его в функцию удаления, можно удалить всю папку.По закону подлости, если что-то “можно”, то оно случается. Сегодня отлаживал этот баг. Человек строит путь примерно так:
(str "/tmp/" (get-dir-name ...))
Этот путь передается в функцию, которая рекурсивно удаляет папку. Но во-первых, функция удаления была с багом и ничего не удаляла. А как только я починил, выяснилось: при особых условиях
(get-dir-name...)
выдает nil, и путь получается /tmp/. Функция исправно удаляет весь /tmp, в котором масса нужных файлов.Мораль: работая с путями как строками, легко отстрелить ногу. Вдвойне печально, что это делают снова и снова. Я с этим борюсь, и вы помогайте.
Если речь про Кложу без библиотек, испольуйте
io/file
:(io/file "/tmp" subdir filename) java.io.File<"/tmp/subsir/filename.txt">
Если один из аргументов nil, оно свалится с ошибкой. А так есть либы вроде
babashka/fs
. -
Зачем OpenAPI?
Чего не могу понять, так это одержимость OpenAPI. Казалось бы, нужна апишка на сайте — ну, сделай как тебе удобно. Но люди берут OpenAPI, крафтят спеку, генерят по ней контроллеры, схемы, тесты. Превозмогают, потеют и потом рассказывают: смотрите, наша апишка по стандарту OpenAPI.
А кого волнует этот ваш OpenAPI? Расстрою: никому не интересно, какая у вас апишка. Пользователю все равно, что гоняетя под капотом. Для программистов на Питоне, как правило, пишут клиентские библиотеки. Вызывая метод
client.get_user(id=42)
, программист в гробу видал, что там у вас —GET
,POST
, джейсон или XML. Никто на это не смотрит.Если точнее, на это смотрят только кложуристы, потому что для них клиентских библиотек никто не пишет. Но кого интересуют проблемы кложуристов? Они сами напишут клиент поверх чего нужно.
За много лет я не припомню, чтобы от OpenAPI была какая-то польза. А вот проблем — целый мешок. Это стандарт, которому нужно следовать; это определенные инструменты, которые навязывают игру. Инструмент X написан на Руби, ставь его и миллион пакетов. Инструмент Y написан на Ноде, ставь ее тоже и качай половину npm. Я неделю настраивал swagger в докере, чтобы он показывал веб-страничку со спекой. Команда привязала гирю к ноге и удивляется: почему разработка идет так медленно?
Когда мне нужна апишка, я делаю простой RPC: команда-параметры, команда-параметры. Все в теле запроса, а не как в REST, где один параметр в заголовке, второй в адресной строке, третий черт знает где. В теле гоняю либо JSON, либо message pack в зависимости от content type.
Это просто, это быстро, это прозрачно. В коде большая мапа вида
{action {doc ... schema-in schema-out handler ...}}
По текущей команде я вынимаю схему, проверяю вход, вызываю функцию
handler
с параметрами. Если дебаг, то проверяю выходные данные. Один раз настроил этот словарь и потом только наращиваешь.Если нужна документация, пишется код, который пробегает по словарю и рендерит markdown-файл. В нем список команд, описание из поля
doc
и схемы ввода-вывода. Если нужно, md-файлик рендерится в HTML или PDF.Но серьезным людям этого не понять. Им нужна OpenAPI-спека, чтобы что-то генерить и чему-то соответсвовать. Пишутся запредельные объемы тулинга под OpenAPI. Бывает, в Кложу приходит бывший рубист и заводит песню: мол, в моих Рельсах есть библиотека, которая по спеке сгенерит контроллер и модели, напишет тесты, а у вас в Кложе ничего нет… блин, потому я и довольный, что нет.
На самом деле я был разок в проекте на Кложе, где по OpenAPI-спеке генерили код. Два слова: это ужасно. Ни при каких обстоятельствах не сяду за это снова. Генерация — это стремно, это хрупко, это километровые диффы. Духота, трение и тошнота.
И никому не прихоит в голову спросить — зачем? Какую проблему ты решаешь своим OpenAPI? Зачем соответствовать чужому стандарту, который не контролируешь? Чтобы что?
-
Видео с митапа о Postgres
Появилась запись митапа:
Примерно час я рассказываю про всякие технические кишки, потом дискуссия с Николаем (CTO Самураев) и Владимиром (разработчиком JDBC-драйвера для Postgres).
Не обещаю, что будет интересно, скорее сильно на любителя.