-
JSAM: a simple JSON writer and reader
JSam is a lightweight, zero-deps JSON parser and writer. Named after Jetstream Sam.
- Small: only 14 Java files with no extra libraries;
- Not the fastest one but is pretty good (see the chart below);
- Has got its own features, e.g. read and write multiple values;
- Flexible and extendable.
Installation
Requires Java version at least 17. Add a new dependency:
;; lein [com.github.igrishaev/jsam "0.1.0"] ;; deps com.github.igrishaev/jsam {:mvn/version "0.1.0"}
Import the library:
(ns org.some.project (:require [jsam.core :as jsam]))
Reading
To read a string:
(jsam/read-string "[42.3e-3, 123, \"hello\", true, false, null, {\"some\": \"map\"}]") [0.0423 123 "hello" true false nil {:some "map"}]
To read any kind of a source: a file, a URL, a socket, an input stream, a reader, etc:
(jsam/read "data.json") ;; a file named data.json (jsam/read (io/input-stream ...)) (jsam/read (io/reader ...))
Both functions accept an optional map of settings:
(jsam/read-string "..." {...}) (jsam/read (io/file ...) {...})
Here is a table of options that affect reading:
option default comment :read-buf-size
8k Size of a buffer to read :temp-buf-scale-factor
2 Scale factor for an innter buffer :temp-buf-size
255 Inner temp buffer initial size :parser-charset
UTF-8 Must be an instance of Charset
:arr-supplier
jsam.core/sup-arr-clj
An object to collect array values :obj-supplier
jsam.core/sup-obj-clj
An object to collect key-value pairs :bigdec?
false
Use BigDecimal when parsing numbers :fn-key
keyword
A function to process keys If you want keys to stay strings, and parse large numbers using
BigDecimal
to avoid infinite values, this is what you pass:(jsam/read-string "..." {:fn-key identity :bigdec? true})
We will discuss suppliers a bit later.
Writing
To dump data into a string, use
write-string
:(jsam/write-string {:hello "test" :a [1 nil 3 42.123]}) "{\"hello\":\"test\",\"a\":[1,null,3,42.123]}"
To write into a destination, which might be a file, an output stream, a writer, etc, use
write
:(jsam/write "data2.json" {:hello "test" :a [1 nil 3 42.123]}) ;; or (jsam/write (io/file ...)) ;; or (with-open [writer (io/writer ...)] (jsam/write writer {...}))
Both functions accept a map of options for writing:
option default comment :writer-charset
UTF-8 Must be an instance of Charset
:pretty?
false
Use indents and line breaks :pretty-indent
2 Indent growth for each level :multi-separator
\n
How to split multiple values This is how you pretty-print data:
(jsam/write "data3.json" {:hello "test" :a [1 {:foo [1 [42] 3]} 3 42.123]} {:pretty? true :pretty-indent 4})
This is what you’ll get (maybe needs some further adjustment):
{ "hello": "test", "a": [ 1, { "foo": [ 1, [ 42 ], 3 ] }, 3, 42.123 ] }
Handling Multiple Values
When you have 10.000.000 of rows of data to dump into JSON, a regular approach is not developer friendly. It leads to a single array with 10M items that you read into memory at once. Only few libraries provide facilities to read arrays lazily.
It’s much better to dump rows one by one into a stream and then read them one by one without saturating memory. Here is how you do it:
(jsam/write-multi "data4.json" (for [x (range 0 3)] {:x x}))
The second argument is a collection that might be lazy as well. The content of the file is:
{"x":0} {"x":1} {"x":2}
Now read it back:
(doseq [item (jsam/read-multi "data4.json")] (println item)) ;; {:x 0} ;; {:x 1} ;; {:x 2}
The
read-multi
function returns a lazy iterable object meaning it won’t read everything at once. Also, bothwrite-
andread-multi
functions are pretty-print friendly:;; write (jsam/write-multi "data5.json" (for [x (range 0 3)] {:x [x x x]}) {:pretty? true}) ;; read (doseq [item (jsam/read-multi "data5.json")] (println item)) ;; {:x [0 0 0]} ;; {:x [1 1 1]} ;; {:x [2 2 2]}
The content of the data5.json file:
{ "x": [ 0, 0, 0 ] } { "x": [ 1, 1, 1 ] } { "x": [ 2, 2, 2 ] }
Type Mapping and Extending
This chapter covers how to control type mapping between Clojure and JSON realms.
Writing is served using a protocol named
jsam.core/IJSON
with a single encidng method:(defprotocol IJSON (-encode [this writer]))
The default mapping is the following:
Clojure JSON Comment nil null String string Boolean bool Number number Ratio string e.g. (/ 3 2)
->"3/2"
Atom any gets deref
-edRef any gets deref
-edList array lazy seqs as well Map object keys coerced to strings Keyword string leading :
is trimmedAnything else gets encoded like a string using the
.toString
invocation under the hood:(extend-protocol IJSON ... Object (-encode [this ^JsonWriter writer] (.writeString writer (str this))) ...)
Here is how you override encoding. Imagine you have a special type
SneakyType
:(deftype SneakyType [a b c] ;; some protocols... jsam/IJSON (-encode [this writer] (jsam/-encode ["I used to be a SneakyType" a b c] writer)))
Test it:
(let [data1 {:foo (new SneakyType :a "b" 42)} string (jsam/write-string data1)] (jsam/read-string string)) ;; {:foo ["I used to be a SneakyType" "a" "b" 42]}
When reading the data, there is a way to specify how array and object values get collected. Options
:arr-supplier
and:obj-supplier
accept aSupplier
instance where theget
method returns instances ofIArrayBuilder
orIObjectBuilder
interfaces. Each interface knows how to add a value into a collection how to finalize it.Default implementations build Clojure persistent collections like
PersistentVector
orPersistenHashMap
. There is a couple of Java-specific suppliers that buildArrayList
andHashMap
, respectively. Here is how you use them:(jsam/read-string "[1, 2, 3]" {:arr-supplier jsam/sup-arr-java}) ;; [1 2 3] ;; java.util.ArrayList (jsam/read-string "{\"test\": 42}" {:obj-supplier jsam/sup-obj-java}) ;; {:test 42} ;; java.util.HashMap
Here are some crazy examples that allow to modify data while you build collections. For an array:
(let [arr-supplier (reify java.util.function.Supplier (get [this] (let [state (atom [])] (reify org.jsam.IArrayBuilder (conj [this el] (swap! state clojure.core/conj (* el 10))) (build [this] @state)))))] (jsam/read-string "[1, 2, 3]" {:arr-supplier arr-supplier})) ;; [10 20 30]
And for an object:
(let [obj-supplier (jsam/supplier (let [state (atom {})] (reify org.jsam.IObjectBuilder (assoc [this k v] (swap! state clojure.core/assoc k (* v 10))) (build [this] @state))))] (jsam/read-string "{\"test\": 1}" {:obj-supplier obj-supplier})) ;; {:test 10}
Benchmarks
Jsam doesn’t try to gain as much performance as possible; tuning JSON reading and writing is pretty challenging. But so far, the library is not as bad as you might think! It’s two times slower that Jsonista and slightly slower than Cheshire. But it’s times faster than data.json which is written in pure Clojure and thus is so slow.
The chart below renders my measures of reading a 100MB Json file. Then the data read from this file were dumped into a string. It’s pretty clear that Jsam is not the best nor the worst one in this competition. I’ll keep the question of performance for further work.
Measured on MacBook M3 Pro 36Gb.
Another benchmark made by Eugene Pakhomov. Reading:
size jsam mean data.json cheshire jsonista jsoniter charred 10 b 182 ns 302 ns 800 ns 230 ns 101 ns 485 ns 100 b 827 ns 1 µs 2 µs 1 µs 504 ns 1 µs 1 kb 5 µs 8 µs 9 µs 6 µs 3 µs 5 µs 10 kb 58 µs 108 µs 102 µs 58 µs 36 µs 59 µs 100 kb 573 µs 1 ms 968 µs 596 µs 379 µs 561 µs Writing:
size jsam mean data.json cheshire jsonista jsoniter charred 10 b 229 ns 491 ns 895 ns 185 ns 2 µs 326 ns 100 b 2 µs 3 µs 2 µs 540 ns 3 µs 351 ns 1 kb 14 µs 14 µs 8 µs 3 µs 8 µs 88 ns 10 kb 192 µs 165 µs 85 µs 29 µs 96 µs 10 µs 100 kb 2 ms 2 ms 827 µs 325 µs 881 µs 88 µs Measured on i7-9700K.
On Tests
One can be interested in how this library was tested. Although being considered as a simple format, JSON has got plenty of surprises. Jsam has tree sets of tests, namely:
- basic cases written by me;
- a large test suite borrowed from the Charred library. Many thanks to Chris Nuernberger who allowed me to use his code.
- an extra set of generative tests borrowed from the official
clojure.data.json
library developed by Clojure team.
These three, I believe, cover most of the cases. Should you face any weird behavior, please let me know.
-
Flyway
Волею судеб я использую джавную библиотеку Flyway для миграций. Должен сказать: ее писали клоуны.
Вот как это проверить: запускаем миграции на Postgres 15.8, версия Flyway 7.5.4. Все благополучно работает. А если поднять версию до последней 11.7.2, получим исключение:
Unsupported Database: PostgreSQL 15.8
Не менял абсолютно ничего, никаких настроек, только бампнул версию. И вот пожалуйста.
Внимание, вопрос: что же такого случилось, что Постгрес 15.8 вдруг не поддерживается? Почему спустя три мажорных релиза он отвалился? Слишком новый? Слишком старый? И что делать?
Между прочим, Постгрес 15.8 — относительно свежий релиз (последняя версия, если что — 17). С каких щщей он попал в немилость?
Что творилось в голове у джавистов, которые писали Flyway, я ума не приложу.
Мне приходилось писать свои миграционные движки, и могу сказать: да, это работа не на один день, конечно. Хорошенько все потестить, а потом стабилизация. Но наколбасить 11 мажорных релизов, которые вдобавок тупо не работают — это надо уметь.
Сюда же относится официальный SDK AWS на Джаве, который я уже упоминал. Ощущение, что писали студенты или вроде того. Все аргументы опциональны, все может быть null, в том числе бакет, который читаешь, или файл, в которых пишешь. В рантайме ловишь сто ошибок, что это не может быть нул, то не может и так далее. Про обязательные аргументы в Амазоне не слышали.
Выпустили SDK 2, а там те же самые проблемы.
Словом, ты вырос и сказка кончилась. Программист в корпорации X может получать 400 тыс. долларов в год и писать лютейший быдлокод. А нам, потребителям, с этим жить.
-
Кнопка Summarize
Компания Эпл следует тенденции: сует AI в каждую щель. Так, в почтовом приложении над каждым письмом теперь кнопка “Summarize”, которая, как предполагается, покажет краткую версию письма.
Разумеется, это полная шляпа. Работает только с английским текстом и text/plain. Если вам прислали графоманию на русском, и к тому же в виде HTML-таблиц — кнопка скажет, что “не шмогла”. В целом, эта кнопка скорее не работает, нежели работает.
Все, что остается пользователю — это отключить кнопку, но не все так просто. Я снял нужную галку, кнопка пропала, но потом появилась. Может быть, нужно перезагрузиться, но ситуация уже абсурдна.
Гугл и поддержка Эпла (читай — AI) как дурачки советуют снять эту галку. То, что это не работает, никого не интересует.
Когда уже мир отпустит? Начинает немного раздражать.
-
Ссылки должны быть записаны
Прошлую заметку можно выразить другим тезисом, а именно: каждая ссылка должна быть записана. В файле, в задаче, в заметках, где угодно. Когда это так, нет смысла хранить ее все время открытой.
Например, мне нужно сделать что-то нетривиальное в Постгресе. Путем гугления я нашел два ответа на StackOverflow, два раздела документации и пару чьих-то блогов. Всего шесть ссылок. Можно хранить их открытыми все время, пока работаешь над задачей. А можно добавить их в задачу и спокойно закрыть.
Почему второй способ лучше? По многим причинам, и самая важная – взрослый, организованный подход. Вместо того чтобы сидеть на ссылках, как царь Кощей над златом, мы делимся ими с другими. Возможно, в будущем к этой задаче кто-то вернется и найдет эти ссылки. Их проще скопировать и переслать. Если работа основана на сторонних материалах (код, замеры быстродействия), я считаю правильным ссылаться на источники, чтобы было понятно, откуда решение.
В комментариях кто-то писал: моя задача требует документации, ссылок на то, се, пятое-десятое плюс макеты в Фигме. Так добавь эти ссылки в задачу! Или ты ждешь, что каждый участник будет искать эти ссылки? Это же свинство.
Разумеется, так мало кто делает, ровно как мало кто соблюдает сетевой этикет и правила переписки. Это тот случай, когда нужно не смотреть на других, а самому ставить нормы поведения. Например, добавлять в задачи ссылки на все нужные материалы и просить других делать так же. Может быть, кто-то поймет, что это правильно.
Или ты открыл десяток вкладок и тебя экстренно перекинули на другую задачу, а она тоже требует 10 вкладок. Так и будешь хранить первые десять? Я понимаю, что есть разные профили, окна, группировки… но не говорите, что все это работает как надо, я даже слушать не хочу. Разве не было такого, что браузер вылетел и все забыл? Или обновился и показывает одинокую вкладку “What’s new”?
Запись ссылок важна даже если работаешь в одиночку. Скажем, пилю я свой клиент к Постгресу и мне нужны:
- ссылки на документацию
- ссылки на исходники Постгреса
- ссылки на чужие клиенты, чтобы подсматривать решения
- вопросы на StackOverflow
- всякие мейл-листы.
Поэтому я завожу в репозитории файл
links.md
и пишу туда ссылки.Когда кто-то говорит, что у него пять сотен вкладок, увы, я не верю, что в них возможен какой-то порядок. Это самообман. Все сводится к принципу: записал – отпустил. Пока что-то не записано, оно не свободно. И мы тоже от него не свободны.
-
Табы и закладки
Раз уж заговорили о браузерах, выскажу еще одну мысль.
Меня удивляет, как много обсуждают табы и закладки. Когда выходит какой-нибудь Вивальди и об этом постят новость, комментари сводятся к табам и закладкам. Когда будут вертикальные табы? Когда группировка? Когда автозакрытие как в расширении X? И так далее.
Иногда мне даже жаль разработчиков, потому что сделать так, чтобы понравилось всем, невозможно. Всегда найдется чудак, которому нужны табы по диагонали, в крапинку и с синхронизацией через Амазон в докере. Приходится объяснять, что этого не планируется, а он будет поливать разработчиков дерьмом.
Поэтому расскажу, как управлять табами в любом браузере: будь то Хром, форк Фаекфокса или что угодно. Записывайте: когда у вас много табов, зажмите клавиши Ctrl/Command + W. Все табы закроются. После этого откройте табы, что нужны для текущей задачи. Конец.
Я использую эту схему много лет. Расплодились табы – сношу к чертям и начинаю сначала. Не нужны расширения и синхронизации, вертикальные-горизонтальные и прочий бред.
Уже рассказывал: человек шарит экран, и у него в Хроме 30 вкладок. И не вздумай говорить, что ты ими управляешь!!! Табы сжаты до размеров иконки – как ты найдешь нужный таб? Только перебором: вот этот таб, ой, не тот, ой, другой, ага, вот этот. В чем прикол тыкать каждый раз как слепой щенок? Это как набрать в руки десять предметов и утверждать, что можешь управиться с каждым.
Будь я разработчиком браузера, я бы открыто сказал: ребят, если вы держите 40 вкладок, вам нужно не расширение, а пойти прогуляться.
Сюда же относятся закладки: тратят сотни часов, чтобы найти расширение и настроить синхронизацию. А потом ноют, что в каком-то андроиде не подсосались последние ссылки.
Решение простое: храните ссылки в файлике, который синхронизируется через Dropbox, iCloud или что там у вас. У меня это файлы
music.md
,postgres.md
и другие. Содержимое примерно такое:# Silent Hill 2 Remake OST https://www.youtube.com/watch?v=UFBq69uB-es&list=PLjvrSyTT3pvSbHUpqC3sSC3b9Fq7NuF6b # J. S. Bach - Organ Works - Lionel Rogg - DISC 2/12 https://www.youtube.com/watch?v=rudjAUtfx-g # Toccata & Fugue in C Major, BWV 564 https://www.youtube.com/watch?v=kxtJ_av5NHo # Pink Floyd - Obscured By Clouds (1972) [Full Album] https://www.youtube.com/watch?v=Te_-nISxLVI
Поскольку это маркдаун в Емаксе, то работают всякие плюшки. Клик на ссылку открывает ее в браузере, есть быстрый переход по заголовкам, просмотр оглавления и так далее. Можно делать несколько уровней, например так:
# Ambient ## Portal 2 ## Klaus Schulze # Rock ## Queen ## Pink Floyd
и оно будет красиво отображаться в виде дерева. Но мне достаточно одного уровня. Схема проста как лопата, в ней нечему ломаться. Обновление браузера и расширений на нее не влияют. Что еще нужно?
Конечно, кто-то всплакнет, мол, неудобно на телефоне. Ну и пусть – пока ты не за компом, надо смотреть на солнце, а не тупить в экран. А если не дочитал статью, то кинь ссылку себе в Телеграм, в чем проблема?
Главная мысль этого поста: не быть рабом своих желаний. Хочется такие-сякие табы и закладки – перехочется. Бери то, что не сломается ни при каких обстоятельствах. На долгой дистанции это единственный вариант.
-
Летнее время
Как известно, перевод часов на летнее время несет сплошную пользу. Прямо так хорошо от него, так хорошо, что аж сам себе завидую. И под это дело потерял день рабочего времени, и остальные тоже.
На работе я занимаюсь отчетностью. В Амазоне у меня зашедулено много отчетов, каждый из которых уходит своим потребителям. Большинство из них я сделал прошлой осенью и зимой, когда у заказчика было время +1. А весной произошло вот что.
Если не указать в Амазоне часовую зону cron-выражения, то по умолчанию берется UTC. Это хорошо, потому что точка отсчета фиксирована. Но одно и то же время UTC в зависимости от времени года дает разное локальное время. Например, зимой время 08:00 am UTC будет 9:00 am UTC+1, а летом – 10:00 am UTC+2.
Это значит, что после перевода часов потребители получат отчеты не в 9 часов, а в 10 по местному времени.
Начались жалобы: что-то подумал, что все сломалось, кто-то не успел предоставить отчет к созвону, где-то упал скрипт, который перекладывает отчеты в другое место. Починил так: нужно указать под cron-выражением местную зону, например
Europe/<City>
, и сдвинуть часы так, чтобы они совпадали с зимним временем, по которому работало раньше. На то, чтобы разобраться, задеплоить и проверить, ушел день. В первый раз я поднял часы на +2 вместо +1, и пришлось переделывать.Коснулось и других коллег: Майкл, почему твоя задача запустилась на час позже? И Майклу предстоит то же самое: считать на бумажке часы, деплоить и проверять.
У меня стойкая ассоциация: каждые полгода страна садится голой задницей на гвоздь. За полгода рана заживает, и кажется, что в этот раз обойдется без последствий. Но нет: снова боль, снова проблемы, крики. Никогда такого не было, и вот опять.
Спрашивается: сколько можно? Сколько еще нужно выбросить времени, денег, нервов, здоровья, чтобы чиновников отпустило? Десять лет? Сто лет? Уверен, в будущем над нами будут смеяться: представляете, дети, эти придурки на рубеже тысячелетий гоняли часы туда-сюда, чтобы сэкономить тысячу долларов на 20 миллионов человек. Примерно как раньше сжигали людей, чтобы задобрить бога – с таким же результатом.
Слышал, что за перевод времени люто топил Яков Перельман. Что неудивительно: более яростного человеконенавистника нужно еще поискать. Может, хватит брать с него пример?
-
Иконки в едином стиле
Беда, когда у компании несколько программ, и дизайнера просят сделать иконки “в едином стиле”. Почти всегда получается шляпа.
Каноничный пример – Гугл. Пока у него не было тыщщи сервисов, у каждого была своя симпатичная иконка. У почты – конверт, у звонков – телефонная трубка, у карт – карта. Но потом позвали дизайнера и сказали делать в одном стиле. В результате получились козявки с вариацией цветов: желтый, зеленый, красный, синий.
Поставтье хотя бы две такие иконки рядом, и вы запутаетесь.
Радует, что подобную иконку не сделали Ютубу: проект настолько самобытен, что ни у дизайнера, ни у менеджера не поднимается рука.
Иные иконки вообще лишены смысла, например у Google Authenticator. Старая иконка представляла собой замок сейфа в виде буквы G. Казалось бы, все логично: сейф, безопасность, буква. Новая иконка – это какая-то снежинка. Что хотел сказать автор? Помню как обновил Google Authenticator и десять минут искал иконку сейфа. И до сих пор не могу привыкнуть, что теперь это снежинка.
Более-менее было нормально у Адоба времен CS4. Они стилизировали иконки под периодическую таблицу Менделеева. Смысл в том, что каждый элемент (то есть программа) занимает свою уникальную роль, обладает особыми свойствами. Различать программы помогало следующее:
- на каждой из них было имя программы из двух букв, например Ps, Ai, Ae;
- иконки были разного цвета. Фотошоп – голубой, Иллюстратор – оранжевый, Индиз – малиновый и так далее.
Позже Адоб пошел по пути упрощения: иконки стали почти черно-белыми, а цвет остался только в контуре. Предполагается, что пользователь должен парсить все эти Ps, Ai, Ae, Id, Au, Pr и так далее.
Похоже, никто не может решить задачу иконок “в едином стиле”. А если и может, то не справляется с давлением менеджмента. Так зачем вообще браться за это?
-
ROW CHECK и безопасность
Работал я в одном стартапе на Кложе. Код прошел через десятки разработчиков и представлял лоскутное одеяло: разные подходы и библиотеки. Каждый разработчик городил что-то сбоку, а не исправлял текущее положение дел. Была своя ORM с километрами кода и склейкой SQL-строк. Много там всего было, и в том числе база данных.
Эта база прямо сейчас стоит в памяти. Она тоже прошла через серию разработчиков, каждый из которых знал, как делать правильно. Одни ребята забивали на нормализацию; другие решили, что добавлять колонки утомительно и сделали поле info с типом jsonb, в которое валили все подряд. Были материализованные вьюхи, обновлять которые было затратно, и которые без конца обновлялись из-за косяков в очереди сообщений. Ни одна запись физически не удалялась, а помечалась флагом
is_deleted = true
. Много багов было связано с тем, что данные выбирались без этой проверки.Но это не все. Основатель фирмы считал себя специалистом по безопасности и придумал вот что. В Постгресе есть штука под названием
ROW CHECK
: проверка доступа на уровне записи. Если некая функцияmy_check(row)
возвращаетnull
илиfalse
, то клиент словно не видит этой записи. Это медленно, но работало.Почему директор так сделал? Он хранил в одной таблице данные разных клиентов и опасался, что из-за ошибки в коде один клиент увидит данные другого. Поэтому каждая таблица хранила избыточные айдишки, и в рамках каждого запроса выставлялась переменная
current_owner
. ФункцияROW CHECK
проверяла, что ее значение совпадает с айдишками записи.Воздвигнув этот бастион, директор спокойно сидел под рутовой учеткой, для которой проверки были отключены. На моей памяти было два крупных инцидента, когда он путал окна и выполнял на проде то, что хотел выполнить на тесте. Терялись данные, пропадали целые таблицы.
К счастью, наши часовые зоны сильно отличались, поэтому на починку базы бросали другого человека. По понедельникам он рассказывал, как просиживал выходные за восстановлением бекапов, импортов CSV и отладкой. Были и другие, не столь фатальные случаи, связанные с активностью директора на проде.
Забавно, но наибольший урон базе нанес именно директор, а не хакеры или кривой код. Хакерам, видимо, стартап был не интересен, хотя в самописной ORM были дыры для инъекций. Код, хоть и был не супер, не страдал тем, что читал чужие данные. Свои удаленные – да, но не чужие.
Можно сказать одно: все запреты проверяются на прочность теми, кто их создает. У меня доступ на прод был только для чтения. В другом стартапе права на запись были, но под той учеткой я не ходил. В конце концов, был страх, что за подобный инцидент уводят. А у директора страха не было, вот и результат.
В более широком ключе: запрет хорошо работает только тогда, пока его соблюдают те, кто принимал. Не важно, о чем идет речь: о детях, собаках или машинах. Если говорящая голова топит за очередной запрет, но не готова применить его к своим детям, собакам или машинам, сказанное не имеет смысла.
-
Форки Фаерфокса
Заметка от нечего делать. Пробую форки Фаерфокса: поставил WaterFox и LibreWolf. Первый очень понравился: старый рубленый интерфейс, доступны опции, которых нет в официальной версии. Нет телеметрии и всякого дерьма вроде Покета и синхронизаци. Очень шустрый. Потыкал Ютуб – все ролики работают. LibreWolf вроде тоже неплох, присматриваюсь.
Пока что один минус: не работает webm, но стерпим и это.
Офицальный FireFox в последнее время не радует. Он сливает все больше данных, на сайте меняются формулировки: вместо “не передаем” пишут “улучшаем экспериенс”. Браузер обрастает Покетами-шмокетами, телеметрией и так далее. Нельзя отказаться от обновлений, нужно писать свои полиси.
К счастью, есть достойные форки, которые пилят люди, не нацеленные на прибыль. Их трудом пока что и буду пользоваться.
Пытался сидеть в чистом Хромиуме, но в нем не работает половина Ютуба. Причем даже не видео, а музыка из игр и фильмов. Спасибо, не надо.
От коммерческих хромоподелок вроде Брейва уже тошнит. Тут тебе и крипто-кошелек, и внутренние донаты, и свой поисковик, и то, и се, и облачная учетка, и сторонний VPN по подписке, и целые джунгли впридачу. В Вивальди вообще завезли почтовик и календарь с будильником.
Так что хоть мелкая, но отрада в наши дни: сидеть на форке некогда великого продукта.
-
Cloud-driven development
Расскажу об одной стремной вещи, которую называю “Cloud-driven development”. Это когда разработчик тестирует код не локально, а в облаке.
Выглядит так: нужно забрать откуда-то данные, переколбасить и сложить в другое место. Программист пишет код, деплоит в свое окружение и дергает апишку. Она падает. Он смотрит логи. Правит код, деплоит, дергает. Намылить, смыть, повторить. На двадцатой итерации работает как надо, и он закрывает задачу.
А потом у него отпуск, и мне говорят: поправь. Нужно взять данные из еще одной апишки и положить во второе место. Тестов либо нет, либо самые поверхностные.
Я как-то пытался играть по этим правилам. Быстро выяснилось, что окружение нужно готовить. Ой, у тебя старые сервисы, задеплой к себе эту фигню и эту тоже. У твоей облачной учетки нет пермишенов? Скажи девопсу Джорджу, чтобы добавил. Он в отпуске? Ну, жди. Потом не хватает каких-то креденшелов, токенов-шмокенов, их нужно откуда-то догонять.
Короче, выясняется: разработчик настроил себе уютненькое окружение и ушел в закат. Чтобы сделать то же самое мне, нужна неделя. А ведь мог же поднять Докер и помочь всем. В конце концов, не знаешь Докер, попроси кого-нибудь помочь, а пока что тестируй на локальной базе, локальном S3 – таких заглушек полно.
Кончается тем, что я пишу и Докер, и интеграционные тесты, которые ходят в локальные сервисы, чтобы приблизить локальную разработку к облачной. Причем зачастую в счет личного времени, потому что давай-давай.
Я уж не говорю о том, что Cloud-driven development страшно не эффективен. Каждый деплой занимает в лучшем случае 10-15 минут, и за это время ты все равно не возьмешься за другую задачу. Разработчик смотрит Ютуб или ходить курить. Вроде работает, а на самом деле решето: сплошная пустота.
Я бы вообще не давал разработчикам окружений. Это дорого и вдобавок развращает. Вот тебе Докер с базой, Редисом и Кафкой, пиши, тестируй. Пока локально не заведется, ни о каких облаках не мечтай.
В качестве побочки всплывают проблемы конфигурации. Выясняется, что ни один сервис нельзя направить на локалхост: везде захардкожено что-то вроде
"aws." + region + ".amazon.com"
. Лишний день уходит на то, чтобы сделать хосты конфигурируемыми.Читали же: “
квартирныйоблачный вопрос только испортил их” (с)…