• Игрушечный парсер

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

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

    Так что если вы студент(ка) и пишете парсеры, загляните в репозиторий.

    Там простой модуль с комментариями и базовыми парсерами. Также есть два модуля demo1 и demo2. В одном пример с префиксной нотацией, во втором — с инфиксной. Второй пример интересен тем, что там используется рекурсивный парсер, и поэтому нужны конструкции declare и var.

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

    Если будут вопросы, пишите.

  • Поддежка и Джира

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

    Сотрудник отвечает: ах, спасибо, что известили, сейчас поправим. Скажите, какой у вас хром? Я говорю, вот же написал: версия 100500.42. Он такой — отлично, считайте уже сделали.

    Через минуту: еще одна деталь, какая у вас операционная система? Мак, говорю. Отлично, чиним в поте лица.

    Через минуту: а какая версия системы? 14 с копейками. Спасибо, уже патчим прод.

    Через минуту: какая у вас часовая зона? Тут я слегка разозлился и ответил, что хватит приседать на уши. На что сотрудник ответил, что ему нужно заполнить тикет в Джире: там 20 полей, и все обязательны. Уже заполнили 6, осталось 14. Без заполнения он не сможет создать заявку, и она не пойдет программистам. И вообще ничего не будет.

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

    Вот и отлично!

  • Неэффективный ввод и вывод

    Мое частое замечание к коду — неэффективный ввод-вывод. Примеры:

    • чтобы пройтись по строкам файла, человек читает его в память целиком и разбивает символами \r\n. Рано или поздно прилетает CSV на 5 гигабайт, и машине становится плохо.

    • То же самое с джейсоном: есть стрим, но разработчик читает его в гигантскую строку, а потом парсит ее.

    • Нужно записать в файл 100 тысяч строк? Человек джойнит их разделителем, получает километровую строку и пишет в файл.

    • Различные кодирования — base64, gzip и другие — делаются также: данные читаются в память целиком, из них получается результат тоже в памяти.

    • При загрузке файла в S3 он целиком читается в байтовый массив, затем массив передается в запрос.

    При этом разработчик обмазывает код вызовами gc в надежде, что это поможет.

    Сколько подобных ошибок я исправил — не перечесть. В числе прочего был сервис, который падал от недостатка памяти, хотя ее было выделено запредельное количество. Оказалось, разработчик делал все из списка выше. Он получал огромные файлы, читал их в память, парсил, кодировал в JSON и gzip, используя строки и массивы. Когда код падал от OOM, он поднимал лимиты в облаке.

    Это лишний раз подтверждает: сколько памяти ни дай, плохой код сожрет ее всю.

    А решение простое — байтовые и символьные потоки. Ту же Джаву можно ругать за многое, но в ней очень хорошие потоки (абстрактные классы Input- и OutputStream, Reader и Writer). У них много наследников, каждый из которых делает свою работу. Например, буферизирующий поток, который сглаживает неравномерность сети и файлов. Потоки для сжатия, когда пишешь в него, а данные сжимаются в полете. Потоки, связанные с файлами, сокетами или устройствами. Потоки с подсчетом текущей строки и символа, потоки-пайпы (piped) для “переливания” данных между тредами — всего этого навалом.

    Легко найти сторонние потоки для подсчета MD5 и других хешей. Например, пишешь в условный MD5OutputStream, и хеш считается в полете. В конце вызываешь .getHash, и готово.

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

    Уделите время потокам ввода-вывода. Это прям очень полезная вещь.

  • У заказчика праздник

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

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

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

    Честное слово, нам нужны дни, когда нет ни созвонов, ни чатов, ни почты. Дни, когда можно поработать в полном отрыве от команды и вечером написать, как много сделал.

  • Австралийское время

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

    Много воды с тех пор утекло, но порой я скучаю по такому формату. Разница с заказчиком в час-два означает, что ты не делишь с ним головные боли и причуды: планинги, ретро, спринты, тимбилдинги, уведомления в Тимс. Иной раз хочется, чтобы ничего этого не было. Чтобы был только git pull и git push, ну и зарплата. А на тимбилдинге и без меня обойдутся.

    Это так, минутка малодушия. Вздохнул и пошел дальше.

  • Логирование в Джаве

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

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

    Хорошим примером служит история с Log4j. Когда я читал, что было под капотом, вставали волосы во всех местах. Ощущение, что разработчики объехали все сумасшедшие дома, записали пожелания пациентов и выполнили их дословно. Добавьте в шаблоны Тьюринг-полный язык? Хорошая идея. Хочу подгрузку классов по урлам? Считайте, уже сделано. Напишите фасад над фасадом над фасадом? Уже в этом релизе.

    Итог понятен: если громоздить подобные вещи, окажутся уязвимости. В результате целый месяц люди чинили интернет, корпоративный софт, Майнкрафт(!) и все остальное.

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

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

    Долгое время я думал, что logback (вроде бы самая адекватная библиотека логирования) — самостоятельное решение. Оказалось, это всего лишь фасад над уродским slf4j. Представьте себе объем работы и глубину стека вызовов! Один вагон классов для базовой функциональности, второй — чтобы приделать ему человеческое лицо.

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

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

    На мой взгляд, проблема решается тем, чтобы убрать из логирования состояние. Система предлагает интерфейс Logger с методами debug, info, error и другими. Есть реализации этого интерфейса для записи в консоль, файл, системный журнал. Конкретная реализация нас не волнует: мы просто передаем объект logger, трактуя его как экземпляр интерфейса.

    Этот логгер можно (и нужно) передавать в конструктор класса. Например, в каждое подключение к базе можно передать свой логгер. То же самое относится к парсерам, серверам и так далее.

    Это решает проблемы, упомянутые выше. Базовые логгеры для консоли и файла у нас есть. Можно сделать классы-комбо, которые принимают несколько логгеров и пишут сообщения в каждый. Можно сделать асинхронный логгер, можно прикрутить Кафку, CloudWatch, словом — что угодно. Достаточно унаследовать класс от интерфейса Logger и инициировать ресурсы в конструкторе.

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

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

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

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

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

  • Game Center

    В мире Эпла есть одна заноза в заднице, которая называется Game Center. Никто не знает, что это и зачем. Просто каждый раз он вылазит при запуске игры, что-то требует, ознакамливает, уведомляет. Почему нельзя спрятать это поделие под ковер — не ясно.

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

    Почему я должен чем-то помогать? Почему нельзя сделать это автоматом, я без понятия.

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

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

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

    Бред, просто бред и ничего больше.

  • Дизайнеры и фронтендеры

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

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

    С фронтендером то же самое: это обезьянка, которая не пользуется сервисом и не понимает нужды клиентов. Обезьянка внедряет реакты-редуксы, делает ссылкам onClick, чтобы их нельзя было открыть новой вкладке, словом — занимается ерундой, потому что зарплату нужно отрабатывать.

    Простой пример. Сегодня я обновил клиент для VPN. Когда он работает, то в трее висит иконка. Если нажать на нее, выпадет меню, где можно выбрать “disconnect” или “connect”, чтобы по-быстрому отключить-включить текущий сервер. Когда приложение свернуто в трей, оно не отображается в главной панели.

    А что после обновления? Теперь в меню только два пункта: “open” и “quit”. Пункт “open” открывает приложение, и нужно тыкнуть кнопку Disconnect. И теперь приложение всегда висит в панели задач, даже если если свернуто в трей.

    Разве человек, который поменял интерфейс, не идиот? Ничего, что количество кликов стало больше? Как же не писать проклятия в адрес того, кто на ровном месте создал неудобства? И главное, кто просил?

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

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

    Тому дизайнеру и голову не пришло, что клиенты уже давно пользуются приложением и привыкли к нему. Да, может быть та или иная кнопка выглядит не так, как советует условный Мильчин. Ну и что? Люди-то привыкли! Они в гробу видали Мильчина. Твоя задача — поддерживать и улучшать то, что сделали до тебя, а не строить с нуля.

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

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

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

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

  • Выпадашка под пальцем

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

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

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

    Доказательство простое. Во-первых, выпадашка появляется под большим пальцем, а во-вторых, с задержкой. Часто жмешь на нее случайно или в спешке. Например, нужно срочно перекинуть денег на карту ребенку, когда он на кассе, а тут раз — выиграй автомобиль. Нажал и куда-то улетел. Система зафиксировала, что ты перешел, а значит — заинтересован. Просмотр был? Был. Метрики увеличились? Увеличились.

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

    Аналитик мыслит очень примитивно. Составляется тепловая карта и туда, где горячо, ставят выпадашки. При этом аналитик уверен, что переходы вызваны искренним интересом клиента. Или он понимает, в чем дело, но врет начальству.

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

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

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

    Или глупость сегодня тоже считается исследованием?

  • Вставка со стилями (4)

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

    Спрашивается, почему вставленный кусок набран другим шрифтом, цветом и с другим фоном? Откуда эта предпосылка? Какими умозаключениями к этому пришли?

    Самое ужасное, что это поведение везде по умолчанию. Если человек хочет, чтобы документ выглядел нормально, он должен быть продвинутым: понимать, что такое Paste matching formatting, помнить, сколько служебных клавиш зажимать (две, три, четыре), знать о программах вроде Pure Paste.

    Люди, которые программируют поведение как на картинке — когда простая вставка уродует документ — полные отбросы. Других эпитетов у меня нет.

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