• Зачем 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).

    Не обещаю, что будет интересно, скорее сильно на любителя.

  • Copilot и документация

    Вчера провел с коллегой эксперимент.

    Коллега подключил в редакторе Copilot и говорит — смотри, как круто он генерит комментарии к функции! Скажем, для функции generate-time-buckets(account, time-frames) пишет что-то вроде “generate time buckets for given account and time frames”.

    Мда, содержательно: для generate-time-buckets получить “generate time buckets”… Мне такой подход кажется малоинформативным: очевидно, Copilot берет сигнатуру и аргументы, конкатит их, разбивает на лексемы и причесывает, чтобы смотрелось по-человечески.

    Предложил эксперимент: пишем фукнцию, которая тупо складывает два числа: return a + b. Называем функцию delete-file и просим Copilot написать докстринг. Что он пишет? Правильно, “Delete а file”. Есть и второй вариант — “Delete a file from S3”, видимо, для корпоративных клиентов в облаке.

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

  • Зачем спринты?

    За все время работы в айти я не понял, зачем нужны спринты.

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

    Все это кажется мне бредом. Зачем делить время на равные участки? Зачем считать стори-поинты? Какое знание дает цифра 18 стори-поинтов? Чем это хуже, чем 20 поинтов?

    В чем проблема, если задача из одного спринта переносится в другой? Кто от этого пострадал? Если задача не умещается в спринт, то может просто не впихивать невпихуемое?

    Нужно обсудить процесс? Так проведи серию звонков 1 на 1 с разработчиками. Не обязательно ждать ретро.

    В моем понимании процесс управляется релизами. Скажем, поставили мы задачу выкатить новый релиз через два месяца. Договорились, что в релиз войдет то, се, пятое-десятое. Не успеваем? Что-то упрощаем, что-то откладываем в следующий релиз.

    Зачем нам спринты? Почему все следуют карго-культу гуглов-амазонов? Нужно думать своей головой, а не как менеджер Гугла.

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

  • Шкала ООП

    Когда говорят про ООП, упоминают всякие солиды, наследование и прочее. И не говорят вот про что (а надо бы).

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

    Скажем, у нас есть классы Dog и Cat, унаследованные от Animal. Пока мы передаем собак и котов явно, все хорошо. Но когда мы передаем их как животных (Animal), там может быть что угодно. В итоге на одни и те же данные смотришь по-разному.

    Если предположить, что Animal наследуется еще от чего-то, то получится шкала от Object к Dog/Cat, и как смотреть на объект — зависит от контекста. В некоторых случаях делают даун-каст и ап-каст: передают кота в виде объекта, а потом проверяют: если это кот, то одно, если собака, то другое, иначе эксепшен.

    Эту шкалу (ObjectAnimalMammalDog) важно держать в голове и знать, где находишься сейчас. Лично мне она доставляет много хлопот, когда я пишу на ООП-языках, например Джаве. Шкала — это не вкл/выкл, а некая доля, позиция в в дереве, следить за которой сложнее.

    Может быть, кто-то лучше выразил эту мысль, но либо я не слышал, либо забыл.

  • Не люблю регулярки

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

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

    Другой момент — с регуляркой часто оказывается, что ты не все учел. Предположим, нужно искать в тексте числа с плавающей запятой. Вы написали регулярку, которая ищет числа вроде -12.0042. А потом оказалось, что у чисел может не быть целой части, например -.0042. А еще оказались числа в научной нотации: -1.2E9. И регулярку нужно допиливать, допиливать и накидывать тесты.

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

    В одном из проектов у нас была библиотека для генерации регулярок. Это когда декларативно указываешь: либо это слово, либо это, либо то, но не это и не то, и библиотека строит одну регулярку. В ней учитываются общие начала слов, например для fuck и fuckoff получим fuck(?=off). Выбрал такой пример, потому что фильтровали сообщения в чате. Было много других слов, значения которых я не знал и смотрел в словаре.

    Этот подход интересен тем, что регуляркам отводится служебная часть. Конфигурация делается словарями и списками, все ясно и прозрачно.

    Вместо регулярок мне больше нравится парсинг грамматикой. Пару лет назад я прочитал пару статей про комбинаторные парсеры и решил сделать свой. В результате я написал огрызок, который назвал Ostap (потому что великий комбинатор). В библиотеке были простые парсеры и те, что составляются из других (and, or). Можно задать грамматику словарем и получить из нее парсер.

    В результате у меня получилось составить грамматику JSON, и парсер разбирал произвольный JSON-документ. Правда, это было медленней джавного Jackson раз в десять, но за скоростью я не гнался. Почему-то зачесались руки сесть и переписать его на Джаве и собрать парсеры для JSON, INI, Tolm и других форматов.

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

  • Jira

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

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

    Из сегодняшнего: заполняю тикет, прикладываю кусочек кода. Пишу:

    {code:clоjure}
    (dеfn do-some-shit [a b]
      (do-this {:a b}))
    {code}
    

    Сохраняюсь и вижу:

    Unable to find source-code formatter for language: clоjure.
    Available languages are: actionscript, ada, applescript, bash,
    c, c#, c++, cpp, css, relange, go, groovy, haskell, html,
    java, javascript, js, json, lua, none, nyan, objc, perl,
    php, python, r, rainbow, ruby, scala, sh, swift, visualbasic,
    xml, yaml(dеfn do-some-shit [a b]
      (do-this {:a b}))
    

    Иными словами: сообщение о том, что подсветка Clоjure не поддерживается, внедрилось прямо в код! Причем даже без переноса строки: код следует сразу за ...yaml.

    Сначала я проклял всеми словами фронтендеров, но посмотрел в Network и убедился: это серверный рендер. Клиент посылает фрагмент, а сервер возвращает HTML. Поскольку это не JSON, некуда положить нотис о том, что язык не поддерживается, и ребята такие — давайте просто засунем его в код.

    Каким же мудаком надо быть, чтобы протащить это в прод — я просто не знаю. И куда глядела армия QA, разные бета-тестеры и вообще сами сотрудники фирмы? Фееризм космического дна.

  • Сишные строки

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

    Беда в том, что, читая эту строку, никогда не знаешь, где конец. Сколько байтов резервировать — 32 или килобайт? Немало ошибок в сишных программах связано с тем, что неверно определяется конец строки.

    Интересно наблюдать сишные строки в бинарном протоколе Postgres. В оригинальной его части строки передаются как в Си — то есть неизвестной длины с нулем на конце. Скажем, сообщение StartupMessage строится по принципу

    u s e r n a m e 0 d a t a b a s e 0
    

    А в расширениях, которые появились позже, строки передаются нормально: сначала длина в int32, потом байты без нуля:

    0 0 0 5 h e l l o
    

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

    Чтение строки известной длины — это две строчки кода. Чтение неизвестной строки — это цикл, выделения буферов, конкатенация и прочий бред.

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

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

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

  • Бусти и почта

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

    Дело было так: захожу через Гугло-учетку, все нормально, пришло письмо “здравствуй, дорогой Ivan”. А как начал читать, появилась плашка: чел, ivan@grishaev.me — это твой емейл?

    Вы серьезно? Гугл уже подтвердил, что это моя почта, что вам еще нужно? Как вы это представляете: зашел через Гугл, а почта не моя? Тыкаю Yes, чтобы отвязался. Перехожу в соседнюю вкладку, а там вылезла та же плашка. На этот раз я промахнулся и нажал No. В результате моя почта пропала из профиля, и Бусти пишет — пока не подтвердишь почту, покоя не дам.

    Хоть я и не видел кода Бусти, легко понять, почему так происходит. Когда создается учетка, у нее стоит флаг “почта не подтверждена” — нормальное поведение для регистрации по email. Но разработчики не учли, что в случае с Гуглом почта подтверждается автоматом и выносят мозг подтверждением.

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

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

  • Приглашаю на митап

    В следующий четверг (28 ноября) Health Samurai проводят очередной кложурный митап. Я буду рассказывать про свою библиотеку PG2 — это которая клиент к Постгресу. Начало в 19:00, нужна регистрация. Ссылка на страницу организаторов.

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

    • как устроен Postgres Wire Protocol
    • зачем браться за свое решение, когда есть другие, проверенные годами
    • сопоставление типов Postgresql и Java/Clоjure
    • заморочки с парсингом
    • мысли об API и дизайне

    Подойдет всем, кто как-то связан базами данных. Запись на Ютубе будет через несколько дней после митапа.

Страница 1 из 87