• Значения-классы

    В очередной раз читаю спор а-ля “динамическая типизация против статической”. Заметил вот что.

    Попадаются отчаянные джависты, которые говорят: даже простые значения нужно делать классами, чтобы не допустить ошибки. Например, телефон должен быть не строкой, а классом Phone, который оборачивает строку; почта — классом Email, имя — Name и так далее.

    Идея в том, что если метод принимает имя, телефон и почту, то строки легко перепутать: передать почту вместо имени или телефон вместо почты. А если параметры типизированы — Name, Email, Phone, — то ошибки быть не может.

    То же самое предлагается делать с числами: температура по Фаренгейту — один класс, по Цельсию — другой. Для коров и лошадей тоже разные классы: HorseNumber и CowNumber. У таких классов свои методы сложения и вычитания, чтобы можно было сложить коров только с коровами, но не дай бог с лошадьми.

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

    Но нашлись лазейки. Иные джависты делают записи с одним полем value, примерно так:

    public record Phone(value String) {};
    
    Phone phone = new Phone("223-322");
    

    Примеры, что они приводят, на первый взгляд красивы. Сигнатура как в примере ниже

    someMethod(Name name, Email email, Phone phone)
    

    действительно выглядит лучше, чем String, String, String.

    Однако первая беда в том, что ничто не мешает мне выполнить такой код:

    Phone phone = new Phone("test@hello.com");
    

    и передать этот псевдо-телефон в метод. Можно добавить валидацию в конструктор, но в любом случае она сработает в рантайме — компилятор ничего не знает о том, что строка test@hello.com — это неправильный телефон.

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

    UserPhone ph = new UserPhone(phone.value());
    

    Джавистам, которые мечтают о таком подходе, я бы пожелал столкнуться с ним на практике. Чтобы у них был проект с классами ЧислоЯблок, ЯблокВЯщике, ЧислоЯщиков, ВозрастПользователя, ВозрастСотрудника, КуритИлиНет, МужчинаИлиЖенщина и так далее. Как говорится, бойся своих желаний!

    Кстати, чтобы избежать путаницы в параметрах, они должны поддерживать передачу по имени, например так:

    new User(name="...", phone="...", email="...");
    

    В Джаве такого нет; там приняты билдеры:

    User.builder().name("...").phone("...").email("...").build();
    

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

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

  • Книга о Postgres и JSON

    Поздравьте меня, что ли: в эту пятницу я закончил писать черновик новой книжки. Это будет книга о Postgres. Вся она посвящена одной специфичной теме: работе с JSON. Расскажу, как хранить в Postgres сложные документы, индексировать их, версионировать, делать отчеты, работать с ними из Python, искать разными способами и многое другое.

    Главы

    1. Введение в документы
    2. Базовые возможности JSON
    3. JSON в таблицах
    4. Индексирование JSON
    5. Ограничения в документах
    6. Язык путей JSONPath
    7. Отчеты и функции
    8. Функции на языке Python
    9. Версионирование и архивация документов
    10. Релевантный поиск

    “Черновик” означает, что текст полностью готов, но пропущены места с кодом. Буду их заполнять. Далее верстка, обложка и печатная подготовка.

    Будет в следующем году, скорее всего летом.

  • Новая модель

    Каждый раз, когда выходит новая языковая модель, повторяется одно и то же. Постят график с гантелями, где модель якобы обходит конкурентов в бенчмарках. При этом все смотрят на график как на что-то само собой разумеющееся. Никто не спросит, как вообще эти гантели читать? Левый конец — то, что было раньше, правый — сейчас? Зачем точка посередине? Что это за бенчмарки такие? Чем один отличается от другого? Я понимаю, есть ребята, которые в теме, но если пишешь для всех, можно и объяснить.

    Ну и примеры. Какое мне дело, что в бенчмарке X модель набрала на пять баллов больше? Где примеры того, в чем конкретно она улучшилась?

    Каждый релиз LLM — это взрыв на фабрике гантелей. Армия чудаков растаскивает графики по соцсетям, делая вид, что понимает их.

  • Еще про API

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

    Часть параметров нужно взять из урла, например айдишки сущностей. Другие данные – из заголовков. Если это GET/HEAD, взять параметры query string. Если POST – читать multipart или парсить json. В итоге первые 5-10 строк кода уходят на то, чтобы выковырять данные из разных мест.

    Всегда поражало: почему никто не видит проблемы? Почему не заслать все в джейсоне через POST? Ради чего размазывать поля по разным местам?

    Я хочу, чтобы апишка была описана данными, например словариком с полем action и params. Все в одном месте, и дальше этот словарик разруливается по полю action. Под каждый словарик пишется схема и документашка. Что еще нужно?

    У этого подхода миллион преимуществ, вот хотя бы некоторые:

    • не нужно парсить урлы и query-параметры. Парсинг урлов, вычленение айдишек с приведением к числу – это сложно;

    • не нужно клеить урлы на клиенте. Это лютейшая боль: зачем вам /api/v1/users/123/orders/456 вместо {:user_id 123 :order_id 456}?

    • сообщенька отделяется от транспорта; становится неважно, как ее передали: по HTTP или иначе.

    • следствие 1: легко добавить новый транспорт. Фронтенд захотел веб-сокеты – пожалуйста, это делается за день. Напомню, что в веб-сокете никаких урлов нет, только данные.

    • следствие 2: очереди. Тяжелые сообщения можно складывать в кафки-реббиты.

    • следствие 3: тесты и сценарии. Можно подготовить файл, где каждая строка – сообщенька. После чего сказать: выполни их по порядку. В результате система придет в нужное состояние.

    • пакетный режим: тот же JSON RPC позволяет заслать несколько сообщений за раз. Их можно обработать параллельно.

    • бывает, один обработчик должен вызвать другой. Ты же не будешь слать сам себе HTTP-запрос! Подготовил сообщеньку и скормил ее функции, которая их разруливает.

    Замечу, что под сообщением я имею в виду не очереди задач, а нечто другое. Сообщение – это набор данных, которые описывают действие. Удобно, когда все это хранится рядом.

    Я уж молчу о том, что в классическом REST вечно не хватает методов или они неочевидны. В одной фирме коллеги чуть не подрались, когда спорили, что использовать для изменения: PUT или PATCH. Самая жесть – это запросы, которые меняют сразу несколько сущностей. Там вся концепция ресурсов идет лесом.

    Сообщения удобней для понимания. Что легче читается: POST /api/v1/orders или CreateOrder? Другой пример: PATCH /api/v1/orders/123456/cancel или CancelOrder? То-то же.

    Сообщения используются не только в вебе. Например, любое обращение к Postgres – это обмен сообщеньками. У них общий контейнер: метка, длина и произвольное тело, которое парсится в зависимости от метки. Как только написал парсер сообщений, считай, клиент почти готов.

    В Кассандре, Кафке то же самое: заголовок/cooбщенька, заголовок/cooбщенька.

    Поэтому я уверен: нужно думать над сообщениями, а транспорт всегда найдется. Это вторичная вещь. Жаль, этого не понимают REST-маньяки со своими сваггерами, постманами, бест-практис и так далее.

  • Европейский закон о зарплате

    Пишут, что в 2026 году в Европе введут новые правила о зарплате. Вот неполный их список:

    • зарплата должна быть указана в любом объявлении о найме;

    • соискателя обязаны уведомить о зарплате еще до интервью и подписания договора;

    • разглашение зп не может быть поводом для увольнения или других воздействий;

    • сотрудникам можно запрашивать информацию о зарплатах других сотрудников;

    • компании штатом более 250 сотрудниками должны публиковать отчеты о зп в разрезе гендера.

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

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

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

    Кстати, в этом коренная проблема Европы – тотальное регулирование. Именно поэтому Европа смотрит американский Нетфликс, ищет в американском Гугле, читает американский Твиттер, чатится в американском Вацапе, вызывает американский Убер и так далее.

    Где хостится европейский стартап? В американских AWS, Google Cloud, Azure. Куда едут условные финн Торвальдс и голландец Ван Россум? Где зарегистрированы фонды Линкуса и Питона? В Америке.

    Чтобы создать сервис, который захватит мир, нужно нарушить чьи-то права. В Америке обе стороны (фирма и работник) идут на это добровольно, и в результате американские сервисы повсюду. Европейские – нет.

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

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

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

  • Полоса прокрутки (2)

    Небольшое продолжение про Ютуб — и больше поднимать эту тему не буду.

    Всякими махинациями я добился того, чтобы полоса была под видео, а не на нем. Для этого классу ytp-chrome-bottom добавляется свойство bottom: -60px или около того. Но проблема пришла откуда не ждали, и даже не одна.

    Дело в том, что тулбар появляется только когда наводишь курсор на плеер. Так вот: если тулбар вне плеера, то наведение мыши на него ничего не дает — он остается невидимым. Простыми словами, перестает работать.

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

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

    Ну и в целом сложность: штук двадцать вложенных дивов yt-playeryt-main-containeryt-main-inneryt-video-containeryt-video-node и так далее. Каждый что-то перехватывает и наследует. Дебажить этот цирк — то еще удовольствие.

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

  • Полоса прокрутки

    Некоторое время назад я жаловался на плеер Ютуба, мол, полоса прокрутки и кнопки залезают на видео. На это можно повлиять при помощи своего css-файла. Для Фаерфокса и его форков инструкция такая:

    • Определить папку с профилем. Для этого нажать Help -> More Troubleshooting Information -> Profile Folder -> Show in Finder

    • Создать в ней папку chrome

    • В ней создать файл userContent.css с таким содержанием:

    @-moz-document domain(youtube.com) {
     .ytp-chrome-bottom {
         position: relative !important;
         bottom: -15px !important;
     }
    }
    
    • Зайти в about:config и задать свойсво toolkit.legacyUserProfileCustomizations.stylesheets в true

    • Перезапустить браузер.

    В результате тулбар окажется сверху с зазором в 15 пикселей. Картинку прилагаю:

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

    В общем, когда стили заработали, я удивился как бухгалтер, у которого сошелся годовой отчет.

    Впечатления от новой полосы интересные. Чувство такое, что вместо 90% контента теперь видишь все 100%. Внезапно, надписи внизу читаются; у людей есть колени и ноги; у ведущего в углу кадра есть лицо. Раньше ничего этого не было, а теперь — пожалуйста.

    Могу спокойно читать субчики в “Зеленом слонике”, а раньше не мог. То-то же.

    Для Хрома и форков инструкции нет, но полагаю, найдутся расширения.

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

  • Оружия черепашек-нинздя

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

    Итак, первый уровень, изи: Леонардо, который в голубой повязке, владеет двумя мечами. Это всем понятно.

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

    Теперь третий уровень, хард: что у Донателло, который в фиолетовой повязке? Простолюдин скажет, что у него палка, и от этого хочется плакать. Это бо! – или, чтобы было понятнее, шест бо. Посох из металла или с его элементами, чтобы не сломаться и сильнее бить противника. Бо полезен в бою, в быту, в походе, словом, универсальная для Востока вещь.

    Последний уровень, найтмеар: что у Рафаэля, черепашки в красной повязке? Когда говорят, что трезубец, хочется кататься по полу от отчаяния. Это саи. С-а-и! Один сай, два сая, многое саев. Гугл-док не понимает и предлагает замену, но вы-то понимаете! Такие кинжалы с двумя лепестками по краям. Идея в том, чтобы принять меч противника между зубцами и резко повернуть. Твой рычаг больше, чем у противника, и он теряет оружие.

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

  • Дизайн REST API

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

    Во-первых, версия v1 в урле. Она всегда меня забавляла. Я работал во многих стартапах, и ни один из них не сменил версию. Гораздо чаще стартапы закрываются и продаются. Я считаю, апишка должна быть одна, и со временем какие-то методы добавляются, какие-то снимаются с обслуживания. А в той нумерации, что на скриншоте, слишком много пафоса. Примерно как объявить счетчик с шестью нулями для значения, которое в лучшем случае достигнет двойки. Даже если предположить, что появится новая апишка, просто сделай другой урл: /api/grpc или /api/graphql.

    Далее, иерархия. Те апишки, которые работают с матчем, требуют код турнира. Нужно передать две айдишки: турнира и матча. Зачем? Разве может матч относиться к разным турнирам? Это банальная избыточность, и делается она ради красоты. Должен передаваться только матч, а по нему легко найти турнир и проверить права.

    Еще одна забавная вещь: метод PUT не может покрыть все изменения, поэтому в ход идут костыли: частички :report, :reopen на конце урла. Это некрасиво, потому что по-хорошему операция определяется HTTP-методом, а тут урлы. Тот случай, когда шубу заправляют в трусы, а она не лезет.

    Ну и расцветка Сваггера как у цыган: зеленый, голубой, оранжевый, красный… все собрали.

    На мой взгляд, нормальная апишка должна быть по принципу RPC. Один урл с двумя параметрами: action и params. Метод один – POST. Внутри словарик с функциями и схемами, по которому раскидываются запросы. Все. Документашка со схемами строится одной командой.

    Апишка – довольно простая вещь. Она не должна быть сложной и требовать разного тулинга. Тот REST, что имеем сегодня – это вавилонская башня на ровном месте. Как и в библейской истории, разработчики сами не понимают друг друга. Но продолжают и продолжают строить эту башню.

  • Просто берите SQLite

    Тезис к размышлению: при помощи Postgres и SQLite можно решить (почти) все насущные задачи. Без распределенных кэшей, очередей, облачных паб-сабов и так далее.

    Насчет Постгреса все ясно: я уже писал о нем отдельно. А что SQLite?

    Замечаю одно и то же: иной раз требуется хранить в памяти какие-то данные. Сначала это словарь вида id -> сущность. Потом нужен поиск по полю сущности. Строится обратная мапа поле -> список сущностей. Потом нужная вторая сущность и перекрестные ссылки. Код превращается в ад.

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

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

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

    Если данные не помещаются в памяти, можно скинуть их на диск. Красота же?

    Есть и другие рецепты использования SQLite, например использовать его для обмена данными. Каждый, кто сталкивался с сериализацией, знает, какая это боль и сколько в ней подводных камней. Один из способов их обойти – кидаться файлами SQLite.

    За счет SQLite можно выкинуть просто нелепое количество кода и самописных решений. Разумеется, не всегда – но по моему опыту, очень-очень часто.

Страница 10 из 111