Технические заметки
19 subscribers
57 photos
15 videos
30 links
Здесь я делюсь техническим опытом, который приобретаю каждый день на работе и дома.

Сетка: https://set.ki/Nheuhoc
LinkedIn: linkedin.com/in/magrega
Download Telegram
Пришел тут на проект. Сильное легаси. Стали разбираться, разгребать код. Про его качество я умолчу, но вот интересная статистика в целом по количеству строк.

Javascript поражает воображение. Пустых строк и комментов почти столько же, сколько и самого кода.

Html файлы по 6000 строк, в которых всё: JS, CSS, HTML, запросы в БД. Вероятно, что в 2010 не думали о модулярности кода и необходимости его поддерживать спустя годы.

#code #javascript
В своё время для меня стал открытием тот факт, что метод fetch в JavaScript не кидает исключения на 400-х и 500-х ошибках, так как по сути, это всё валидные ответы сервера, и нам, разработчикам, решать, как эти ошибки обрабатывать.
В методе fetch() promise будет отклонён (reject) с TypeError, когда случится ошибка сети или не будет сконфигурирован CORS на стороне запрашиваемого сервера, хотя обычно это означает проблемы доступа или аналогичные — для примера, 404 не является сетевой ошибкой. Для достоверной проверки успешности fetch() будет включать проверку того, что promise успешен (resolved), затем проверку того, что значение свойства Response.ok является true.

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

Да, конечно, вы можете справедливо мне посоветовать Axios, TanStack или RTK Query и будете правы, но когда сам по себе проект является очень маленьким, почти домашним, тащить туда целую библиотеку для запросов кажется избыточным, потому было грустно осознавать, что придется все оборачивать в try\catch.

Однако, поковырявшись в интернетах я набрел на вот такую либу: ky.
По сути, это обёртка над fetch, но с упрощенным синтаксисом и очень широким списком фичей, в виде автоматических повторов запросов, автоматического парсинга JSON, понятных исключений при 400-х и 500-х ошибках и много еще чего другого.

Одна из примечательных возможностей библиотеки, после понятных сообщений при http-ошибках, это модификация тела запроса в определенных условиях. Например, я использовал ky, чтобы обновлять токен, когда он истекает, что делает создание цикла авторизации очень удобным.

Происходит запрос => Токен истек => Запрос рефреш-токена => повторный запрос.
ky.extend({
hooks: {
afterResponse: [
async (request, options, response) => {
if (response.status === 401) {
const newToken = await refreshAuthToken();
request.headers.set('Authorization', `Bearer ${newToken}`);
return ky(request);
}
}
]
}
});

Я наткнулся на этот инструмент, когда изучал JWT-токены. Пытаясь реализовать сценарий работы с ними, мне не понравился подход с Interceptors в Axios. Очень уж громоздко.

#javascript #React #dx
❤‍🔥11🔥1
Внезапно представилась возможность сделать своё расширение для VSCode. Товарищ обратился за советом, хотел узнать, как можно работать с комментариями в .json файлах. Я поделился тем, что знал, и сказал, что есть формат .jsonc, который поддерживает комменты, но парсинг в обычный json он не пройдет. Потолкались идеями, подумали о том, что было бы здорово написать какой-то скрипт, который бы удалял бы комменты, висящие запятые и лишние пустые строки и выдавал это всё в обычный .json.

Сказано - сделано. Скрипт я накидал, но как его встроить в работу? Сразу подумал про магазин расширений в VSCode. По быстрой консультации с DeepSeek выяснил, что нужно сначала сделать учетку на Azure DevOps, там выпустить токен для идентификации своей организации и использовать его в при регистрации в качестве организации в магазине расширений. Практика оказалось гораздо проще: Azure вообще не понадобился. Можно просто создать учетку на сайте магазине расширений и там дропнуть файл с расширением .vsix, после чего он сразу попадает в магазин, валидируется и попадает в индексацию поиска. Azure нужен, чтобы получить упомянутый токен и с его помощью публиковать расширения напрямую из VSCode. Я в рамках своей небольшой задачи обошелся просто загрузкой на сайте.

Сама разработка расширения довольно любопытная. У VSCode есть свои встроенные методы, которыми можно пользоваться.
Например, чтобы добавить команду в палитру команд (ctrl + shift + p), можно воспользоваться методом vscode.commands.registerCommand.
Или, например, чтобы запускать команду на сохранении документа onDidSaveTextDocument, но нужно убедиться, что скрипт стартует вместе с приложением. В package.json в activationEvents добавить "onStartupFinished".

Если кто не знает, VScode написан на Electron. Это фреймворк для написания десктоп приложений на JavaScript, потому мы можем использовать привычный npm при написании расширений.

Само расширение довольно простое: библиотекой strip-json-comments вычищаются комменты, лишние запятые, а потом всё это парсится в JSON, чтобы почистить форматирование.
Посмотреть можно тут: GitHub и VSCode Marketplace.

Есть еще пара идей, как расширить функционал скрипта. Если дойдут руки - поделюсь.

#vscode #Code #javascript #extension #github #json
🤯2👍1🤓1
К сожалению, я из тех людей, кто не умеет отдыхать. Если я больше часа ничего не делаю, то начинаю дрыгать и топать ногой, а вместе с тем и нервничать.

Первое января нового года не было исключением. В качестве подарка на Новый Год я купил себе iPhone с последней версией iOS 26 и обнаружил, что мой сайт-визитка не открывается на нем. Просто белый экран. На компьютерах с Windows, а также на предыдущих версиях iOS всё работает, как и должно.

После часовой сессии гугления и общения с ИИ я сделал несколько открытий:

· Apple не пускают другие браузерные движки на свои телефоны, потому все другие браузеры из App Store по сути тот же Safari (WebKit engine), просто в другом скине. С Safari часто бывают индивидуальные трудности при разработке, но меня смутило, что на компьютере в Chrome сайт открывается, а на телефоне нет. Загадка была разгадана.

· Причина, по которой не работал сайт на iOS 26 – устаревший метод substr в моем JavaScript-коде. Я написал этот сайт года 4 назад, потому не мудрено. Заменил его на substring, и страница загрузилась. Я так понимаю, substr все еще используется для обратной совместимости.

· Оказывается, private key от Putty (.ppk) не тоже самое, что ключи от OpenSSH. В целом не сказать, что это сюрприз, но было удивительно, что у фриварного Putty какие-то свои ключи.

На последнем пункте хочу остановиться чуть подробнее. Пока я ковырял свой сайт и искал причину, почему он не работает, я решил заодно настроить удаленный клиент VS Code по SSH, чтобы работать с кодом сразу на сервере.

Добрые 15 минут я не мог вкурить, почему мой ключ от Putty не работает в VS Code, пока не понял, что нужен OpenSSH.

И вот моя финальная мысль:

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

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

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

#ios #ssh #javascript #frontend #putty #vscode
🤯1
На одном из прежних мест работы, когда в команде появился второй фронтенд-разработчик, довольно быстро встал вопрос о единообразности кода, а следовательно о линтере и форматтере. Для многих разработчиков, кто не особо вдается в глубину, особенно на начальном этапе, линтер и форматтер может зачастую означать одно и тоже. Особенно, когда до определенного момента в некотором смысле так и было.

Всем известный ESLint заведовал всеми правилами, но JavaScript продолжал усложняться: вышел ES6, появилась деструктуризация, стрелочные функции, async/await. Правила линтера для этих сложных вещей конфликтовали с правилами их оформления, это становилось сложно поддерживать.

Проблему решил Prettier в 2017 году. Он забрал на себя всё форматирование и сказал: "Мы придумали лучшие правила, берите и пользуйтесь, хватит спорить из-за стилей.". Действительно, спорить будет ли два таба или четыре пробела в начале строки - контрпродуктивно.

Таким образом, произошло разделение обязанностей. И если правила линтера продолжают быть темой споров, разные команды выкатывают свои конфиги в npm, какие-то наборы правил обрастают фанбазой, то с Prettier договариваться стало проще. Поставил и забыл.

В 2023 году команда ESLint официально перевела все правила форматтинга в "устаревшие" и прямо заявила, что поддерживать их было болью, потому предлагают пользоваться Prettier или ESLint Stylistic.

В моем личном случае мне пришлось это всё разобрать, так как мы стали настраивать линтер для команды. Какие-то наборы правил взяли готовые, что-то написали сами. Пока этой необходимости не возникло, я просто пользовался расширением ESLint в VSCode с какими-то дефолтными правилами, которые шли с CRA, но потом для меня открылся целый мир.

Еще интересный факт, касательно линтера: когда правил много и кодовая база большая, это начинает сказываться на производительности. Редактор кода может тупить, медленнее сохранять файлы, не сразу обрабатывать код и давать подсказки. Один из способов, которым это пытаются решить - переписать линтер и его правила на Rust. Например, Oxlint. Скорость работы в десятки раз выше, но на тот момент, когда я в этом разбирался, не было переписано еще очень большого количества правил, потому мы использовать не стали. Но может кому-то будет интересно.

#javascript #eslint #formatter #linter #Oxlint #Prettier
🔥2
К лучшему или нет, но зима заканчивается, а рабочие обороты увеличиваются. Времени на посты становится меньше, но я приготовил кое-что прикольное.

Я купил себе как-то часы Mi Band 8 на смену своим пожилым Mi Band 2, доставшимся мне от жены. Хотелось просто добавить цвета с кастомными циферблатами к подсчету шагов и калорий. На них нашелся циферблат, который мне невероятно сильно понравился. От картинки чувствовался такой осенний, немного хеллоуинский вайб с примесью какой-то диснеевской сказки. Но был у них баг один, дни недели отставали на один. В понедельник показывали воскресенье.

У восьмой версии часов оказался какой-то совершенно непонятный алгоритм счета калорий, который насчитывал мне по сожженных 400 ккал пока я просто сижу за компом. Решил вернуть. Взял 7 версию, на которой старый алгоритм подсчета, меня это устроило, но того циферблата на них не было. Ну что ж, полез курить форумы.

Накурился и узнал такую вещь:
Файлы циферблатов идут в формате .bin, но по сути они открываются обычным ZIP-архиватором. А внутри...
🎉 JavaScript 🎉

Вы не ослышались, ребята! Внутри .json-файлы манифестов, куча ассетов в видео картинок и javascript-код.

Потому, смачно похрустев пальцами, я полез исправлять циферблат и пересобирать его для 7 версии Mi Band. Обратно всё собирается программой ZMake, точнее, адаптируются картинки под ZeppOS.

Такие вот дела.

#javascript #zeppos #miband
🔥4
Важность понимания сложности алгоритмов

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

В какой-то момент стали поступать жалобы от пользователей на то, что приложение вылетает на андроид-устройствах. Сами устройства довольно старые: RFID-считыватели на Android 6 и Android 14. После диагностики поняли, что суть в том, что тупо кончается оперативная память и приложение падает. Сразу оговорюсь, что к падению приложения причастен был и ряд других проблем, но в рамках темы рассмотрим только алгоритм.

Суть: в приложении есть функционал выгрузки части базы данных на устройство для оффлайн работы. При выгрузке из БД (у нас couchdb 1.7.2 😢) тянется 130000 документов. Пару секунд приложение пыжится, потом ломается. Предлагаю рассмотреть изначальный код:
obj_arr.forEach(obj => {
if (obj.m_type === OT_BAG) {
obj.art_count = obj_arr
.filter(x => (x.m_type === OT_ART) && (x.container === obj.id))
.length;
}
});

Как мы видим, внутри цикла forEach на каждой итерации вызывается filter, который проходит по тому же массиву. У нас квадратичная сложность вычисления O(n²).

Для тех, кто не в теме, приведу пример: представим, что у нас есть массив, в котором 1000 элементов. Проходя по нему одним циклом мы имеем линейную сложность вычисления O(n), потому что мы пройдем по всем элементам без исключения один раз. В данном случае n - количество элементов в массиве (1000). Количество операций равно количеству элементов.

А теперь представим, что проходя по одному элементу, мы вызываем еще один цикл, который проходится по тому же массиву (цикл в цикле). Сколько будет операций? На каждую операцию будет тоже O(n) вычислений. То есть, 1000 * 1000 = 1000000 операций. То есть, O(n * n) = O(n²).

Однако, если мы не будем вызывать цикл в цикле, а найдем способ вытащить один цикл из другого (а еще лучше, воспользуемся хеш-таблицами 😉), то наша сложность снова станет линейной. И вместо миллиона операций на массив мы вернемся к 1000, что является тысячекратным упрощением.

В общем, решили мы это так:
let bag_art_count = {};

rows.forEach(r => {
const value = r.value || {};
if (value.m_type === OT_ART && value.container) {
bag_art_count[value.container] =
(bag_art_count[value.container] || 0) + 1;
}
});

let obj_arr = rows.map(r => {
const obj = Object.assign(
{ _id: r.value.tag.substr(0, 24), id: r.id },
r.value
);

if (obj.m_type === OT_BAG) {
obj.art_count = bag_art_count[obj.id] || 0;
obj.is_suited = (obj.art_count === 0);
}

return obj;
});

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

#javascript #algo #android
🔥31