<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Ихрь постит в уютный бложик: заметки с тегом development</title>
<link>https://ifedyukin.ru/blog/tags/development/</link>
<description>Фрирайтинг, заметки, шитпостинг и все подряд</description>
<author></author>
<language>ru</language>
<generator>Aegea 11.1 (v4098)</generator>

<itunes:subtitle>Фрирайтинг, заметки, шитпостинг и все подряд</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>How to inline TailwindCSS styles to SSR NextJS</title>
<guid isPermaLink="false">77</guid>
<link>https://ifedyukin.ru/blog/all/how-to-inline-tailwindcss-styles-to-ssr-nextjs/</link>
<pubDate>Wed, 04 Oct 2023 11:54:42 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/how-to-inline-tailwindcss-styles-to-ssr-nextjs/</comments>
<description>
&lt;p&gt;By default, NextJS may not include Tailwind styles in the SSR build, and with disabled JavaScript, your site may look ugly like pure HTML.&lt;/p&gt;
&lt;p&gt;To fix this, you need to install the &lt;i&gt;critters&lt;/i&gt; package.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;npm install critters&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And enable &lt;i&gt;optimizeCss&lt;/i&gt; rule in &lt;i&gt;next.config.js&lt;/i&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;experimental: {
    optimizeCss: true,
},&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Source: &lt;a href="https://github.com/vercel/next.js/discussions/12868"&gt;next.js#12868&lt;/a&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>Change SSH port on Ubuntu 22 using Ansible</title>
<guid isPermaLink="false">74</guid>
<link>https://ifedyukin.ru/blog/all/change-ssh-port-on-ubuntu-22-using-ansible/</link>
<pubDate>Wed, 29 Mar 2023 18:46:00 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/change-ssh-port-on-ubuntu-22-using-ansible/</comments>
<description>
&lt;p&gt;After updating Ubuntu 22.10 SSHd uses socket-based activation.&lt;br /&gt;
As a result, the sshd configuration port does not affect the listening port.&lt;br /&gt;
To change the port, we need to perform the following tasks:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;- name: Create config folder
  file:
    path: /etc/systemd/system/ssh.socket.d
    state: directory
    recurse: true

- name: Create config file
  copy:
    dest: /etc/systemd/system/ssh.socket.d/listen.conf
    content: |
      [Socket]
      ListenStream={{ custom_ssh_port }}

- name: Reload systemd manager
  systemd:
    daemon_reload: yes

- name: restart ssh
  service:
    name: &amp;quot;ssh&amp;quot;
    state: restarted

- name: Update connection port
  set_fact:
    ansible_ssh_port: &amp;quot;{{ custom_ssh_port }}&amp;quot;&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>How to fix unknown fragments types in graphql-codegen?</title>
<guid isPermaLink="false">73</guid>
<link>https://ifedyukin.ru/blog/all/how-to-fix-unknown-fragments-types-in-graphql-codegen/</link>
<pubDate>Wed, 15 Feb 2023 23:05:44 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/how-to-fix-unknown-fragments-types-in-graphql-codegen/</comments>
<description>
&lt;p&gt;I spent half of the day setting up graphql-codegen to generate the correct types for fragment queries.&lt;/p&gt;
&lt;p&gt;At first, my code looked like this:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;// file1
export const DIALOG_FIELDS = gql(`
  fragment DialogFields on Dialog {
    id
    name
  }
`);

// file2
export const GET_DIALOG = gql(`
  ${DIALOG_FIELDS}
  query GetDialog($dialogId: ID!) {
    dialog(id: $dialogId) {
      ...DialogFields
    }
  }
`);&lt;/code&gt;&lt;/pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const config: CodegenConfig = {
  schema: CONFIG.apiHost,
  documents: ['src/**/*.{tsx,ts,js,jsx}', '!src/gql/__generated__/*.ts'],
  generates: {
    './src/gql/__generated__/': {
      preset: 'client',
      plugins: [],
      overwrite: true,
      presetConfig: {
        gqlTagName: 'gql'
      }
    }
  },
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, with the &lt;a href="https://www.apollographql.com/docs/react/development-testing/static-typing/"&gt;basic config&lt;/a&gt; I did get an unknown type for both the query and the fragment:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="938" data-ratio="2.1813953488372"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen.png" width="938" height="430" alt="" /&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen-1.png" width="958" height="542" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I found a simple solution for this — we need to remove the string interpolation for the fragment literal from the query, because graphql-codegen &lt;a href="https://stackoverflow.com/a/74094939"&gt;automatically includes&lt;/a&gt; fragments.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;export const GET_DIALOG = gql(`
  query GetDialog($dialogId: ID!) {
    dialog(id: $dialogId) {
      ...DialogFields
    }
  }
`);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As a result, I did get an not unknown type, but the type with no data from the fragment for the query fragment.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen-3.png" width="1266" height="370" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;I lost the most time at this stage, eventually finding a solution in the &lt;a href="https://github.com/dotansimha/graphql-code-generator/discussions/8859#discussioncomment-4891291"&gt;small GitHub thread&lt;/a&gt;. We need to disable fragment &lt;a href="https://the-guild.dev/graphql/codegen/plugins/presets/gql-tag-operations-preset#fragment-masking"&gt;masking feature&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const config: CodegenConfig = {
  schema: CONFIG.apiHost,
  documents: ['src/**/*.{tsx,ts,js,jsx}', '!src/gql/__generated__/*.ts'],
  generates: {
    './src/gql/__generated__/': {
      preset: 'client',
      plugins: [],
      overwrite: true,
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: false
      }
    }
  },
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally the type contains all the information and it is complete!&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image-3.png" width="780" height="318" alt="" /&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Про дебаг в старых Safari</title>
<guid isPermaLink="false">71</guid>
<link>https://ifedyukin.ru/blog/all/pro-debag-v-staryh-safari/</link>
<pubDate>Wed, 01 Feb 2023 18:01:23 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/pro-debag-v-staryh-safari/</comments>
<description>
&lt;p&gt;Знаете, что самое веселое в фронтенд-разработке после похорон IE?&lt;br /&gt;
Дебажить приложения под Safari старых версий — это просто нереально...&lt;/p&gt;
&lt;p&gt;Как я делал с IE? Качал виртуалку с сайта MS с нужной версией IE и спокойно дебажил.&lt;/p&gt;
&lt;p&gt;Как я делаю с Safari? Ищу образ виртуалки, их же официально никто не распространяет, а ушлые ребята распространяют образы только через Patreon, WTF?&lt;/p&gt;
&lt;p&gt;На ноут я не могу поставить виртуалку со старой Mac OS, потому что у меня ARM процессор. На ПК я не могу поставить потому, что у меня там процессор AMD, а почему-то на новых AMD виртуалки как-то криво инструкции транслируют.&lt;/p&gt;
&lt;p&gt;В итоге приходится искать макбуки на Intel, на них ставить виртуалку со старой ОС и там дебажить — во житуха!&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Изначально писал в телеге — &lt;a href="https://t.me/fuckanarium/214"&gt;тык&lt;/a&gt;&lt;/p&gt;
</description>
</item>

<item>
<title>Про Apple и поддержку старых фич</title>
<guid isPermaLink="false">70</guid>
<link>https://ifedyukin.ru/blog/all/pro-apple-i-podderzhku-staryh-fich/</link>
<pubDate>Mon, 14 Nov 2022 21:11:46 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/pro-apple-i-podderzhku-staryh-fich/</comments>
<description>
&lt;p&gt;Айти — уникальная штука по количеству ошибок с обратной совместимостью, причем даже в глобальных продуктах с кучей пользователей.&lt;/p&gt;
&lt;p&gt;У меня в работе (да и не только в работе) очень много всего автоматизировано: переключения между проектами, автозаполнения форм, переводчики по месту, хитрые поиски и линки, генерилки паролей и временных почт, подключалки к серверам, всякие скрипты уровня «сходи туда, скачай то, заскриншоть, отправь в телеграм» и т. д.&lt;/p&gt;
&lt;p&gt;Так вот после обновления Mac OS (напомню, лучшая ОС в мире) у меня отвалилась половина скриптов потому, что Apple удалила из стандартной сборки системы python 2 (которому сто лет в обед) и php (зачем он там был, я уже и не вспомню). Пришлось что-то переписать, что-то обновить, о чем-то забыть...&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/2022-11-14-21.09.23.jpg" width="960" height="1280" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Вот Apple может взять и релизнуть сильно ломающие изменения, а некоторые мои знакомые в своих проектах не могут удалить одно мелкое поле, которым пользуются ну 2% пользователей. Более того, я завсегдатай срачей в тематических чатах про поддержку старых браузеров.&lt;/p&gt;
&lt;p&gt;Кажется, всякое старье все-таки можно (и нужно) выпиливать, пользователи не тупые и умеют гуглить, пусть разбираются. Всем бы у Apple поучиться!&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Больше постов в &lt;a href="https://t.me/fuckanarium"&gt;телеграме&lt;/a&gt;, там шитпостинг ложится лучше&lt;/p&gt;
</description>
</item>

<item>
<title>Как я Алису научил открывать домофон</title>
<guid isPermaLink="false">69</guid>
<link>https://ifedyukin.ru/blog/all/kak-ya-alisu-nauchil-otkryvat-domofon/</link>
<pubDate>Fri, 02 Sep 2022 23:45:46 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/kak-ya-alisu-nauchil-otkryvat-domofon/</comments>
<description>
&lt;p&gt;У меня установлен умный домофон, вызовы на который можно принимать с телефона, смотреть через приложения записи камер и открывать через него же дверь.&lt;/p&gt;
&lt;p&gt;Это очень удобно, но телефон я не всегда ношу с собой по дому, да и когда готовлю, например, и требуется открыть дверь, то становится довольно неудобно — руки заняты.&lt;/p&gt;
&lt;p&gt;Когда я задумался, как бы решить эту проблему, то в голову пришла мысль, что таймеры во время готовки я выставляю через Алису, было бы круто так же открыть и домофон (и входную дверь тоже, но это другая история).&lt;br /&gt;
«Алиса, открой домофон», — бах, дверь открыто — красота! Нужно делать.&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Во-первых, я изначально заложил 95% на успех, так как домофон у меня через приложение может открываться с телефона.&lt;br /&gt;
Во-вторых, мне банально интересно было потыкаться в навыки Алисы и даже 5% неуспеха меня не смущали.&lt;br /&gt;
В-третьих, мне никак не хотелось идти вечером пятницы пить пиво.&lt;/p&gt;
&lt;p&gt;Опираясь на первый пункт, я подумал, что можно было бы зареверсить эндпоинты всякими HttpToolkit’ами, но прежде решил погуглить и не зря. Нашел репозиторий на гитхабе с интеграцией домофона в какую-то аппку для умных домов, в одном из файликов было описано взаимодействие с апи провайдера.&lt;/p&gt;
&lt;p&gt;Я, недолго думаю, постманом получил нужные токены, протыкал ручки — вроде работает. Быстренько накидал простой сервис на ноде, который на запрос по определенному эндопоинту с сикретом (куда же без сесурити) дергает эндпоинт провайдера, в случае неудачи пытается обновить токен и дернуть его еще раз.&lt;/p&gt;
&lt;p&gt;Затем пошел на &lt;a href="https://dialogs.yandex.ru/"&gt;Диалоги&lt;/a&gt;, где сделал приватный диалог для Алисы, который перенаправил в ту же ручку. Внутри сервиса дописал пару проверок, чтобы во входящих интентах было «домофон», затем дергался эндпоинт провайдера, а в ответ Алисе уходило «Открываю».&lt;/p&gt;
&lt;p&gt;Проверил — работает! Только расстроило, что в диалогах инпут длинный. То есть Алиса слушает это как «Алиса, скажи домофону, чтобы открылся» или около того — явно в команде должно звучать кодовое слово навыка.&lt;/p&gt;
&lt;p&gt;Обошел это через создание шортката в сценариях для всех своих станций и... эврика! Теперь Алиса открывает домофон по фразе «Открой домофон». Пятница прошла довольно-таки неплохо!&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Изначально тут вообще должен был быть здоровый гайд с примерами кода и кучей скриншотов, но в итоге код получился очень типовой, рокетсайнса никакого, так что убрал все лишнее и поделился чисто тем, что есть.&lt;/p&gt;
&lt;p&gt;Видео с демкой залил в телегу — &lt;a href="https://t.me/fuckanarium/102"&gt;тык&lt;/a&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>How to run TS-node with ES modules and CommonJS?</title>
<guid isPermaLink="false">67</guid>
<link>https://ifedyukin.ru/blog/all/how-to-run-ts-node-with-es-modules-and-commonjs/</link>
<pubDate>Mon, 29 Aug 2022 20:26:01 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/how-to-run-ts-node-with-es-modules-and-commonjs/</comments>
<description>
&lt;p&gt;If you’ve got ERR_REQUIRE_ESM, ERR_UNKNOWN_FILE_EXTENSION for TS files, ERR_MODULE_NOT_FOUND for TS files with ES modules and other errors related to «CJS to ESM migration problem» in your NodeJS project you can fix it by following these steps.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Update package.json to define modules system&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;/code&gt;&lt;/pre&gt;&lt;ol start="2"&gt;
&lt;li&gt;Update tsconfig.json to support ESM in TS-node&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;target&amp;quot;: &amp;quot;esnext&amp;quot;,
    &amp;quot;emitDecoratorMetadata&amp;quot;: true,
    &amp;quot;module&amp;quot;: &amp;quot;esnext&amp;quot;,
    &amp;quot;moduleResolution&amp;quot;: &amp;quot;node&amp;quot;,
    &amp;quot;types&amp;quot;: [&amp;quot;node&amp;quot;],
    &amp;quot;resolveJsonModule&amp;quot;: true,
    &amp;quot;esModuleInterop&amp;quot;: true,
    &amp;quot;forceConsistentCasingInFileNames&amp;quot;: true
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;ol start="3"&gt;
&lt;li&gt;Run your entry point script using node and ts-node as node loader&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;node --experimental-specifier-resolution=node --loader ts-node/esm src/index.ts&lt;/code&gt;&lt;/pre&gt;&lt;ol start="4"&gt;
&lt;li&gt;Enjoy!&lt;/li&gt;
&lt;/ol&gt;
</description>
</item>

<item>
<title>Запустили сайт NИTИ</title>
<guid isPermaLink="false">64</guid>
<link>https://ifedyukin.ru/blog/all/niti-site/</link>
<pubDate>Tue, 07 Jun 2022 14:52:33 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/niti-site/</comments>
<description>
&lt;p&gt;Мы с ребятами делаем &lt;mark&gt;в Рязани&lt;/mark&gt; очень крутой движ — NИTИ, я относительно недавно, но все же. Это тусовки с электронной (и не только) музыкой, крутая площадка, диджейские подкасты, музыка и еще много всего крутого.&lt;/p&gt;
&lt;p class="note-main"&gt;Пару дней назад у объединения была, так сказать, годовщина. В честь этого события мы анонсировали масштабный оупен-эйр и... барабанная дробь... &lt;a href="https://niti.team"&gt;сайт&lt;/a&gt;!&lt;/p&gt;
&lt;p class="note-aside"&gt;Оупен-эйр — это мы в лесу будем делать &lt;a href="https://niti.team/events/uniti"&gt;тусовку&lt;/a&gt; с баром, чилл-зоной, арт-объектами, лайн-апом на десяток часов и кучей замечательных людей.&lt;/p&gt;
&lt;p class="note-main"&gt;У нас была просто замечательная команда из трех человек, которой мы запустили сайт с нуля, у нас только концепт какой-то был и пара референсов, меньше, чем за месяц.&lt;/p&gt;
&lt;p class="note-aside"&gt;Наша команда была прямо фулл-ремоут, с полностью асинхронной коммуникацией и всего двумя созвонами за все время: в самом начале и в самом конце, чтобы контент поправить.&lt;/p&gt;
&lt;p&gt;Работали по настоящему аджайлу, можно сказать, только спринты были по паре дней, утрирую, но как-то так.&lt;/p&gt;
&lt;p&gt;Очень клевые дизайнерские идеи, очень интересные хаки в верстке, чтобы эти идеи ожили, мобильная версия с заделом под приложение — всем рекомендуем ее на телефонах сохранять на рабочий стол, чтобы оно в стэндалон режиме открывалось.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="1440" data-ratio="1.9834710743802"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/niti.png" width="1440" height="726" alt="" /&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image-2.png" width="1004" height="1580" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Технологически весь сайт построен на NextJS, чтобы быстро работало и вообще на острие технологий.&lt;br /&gt;
К слову, на релизе этот самый Next съел всю оперативу на сервере и вместо картинок юзеры получали 502 ошибку, но теперь все статически экспортируется — рантайм не нужен!&lt;/p&gt;
&lt;p&gt;На графике посещений очень ярко выражен момент, когда мы сайт в инстаграме и телеграме анонсировали, красота! А вот та просадочка в начале — это как раз 502 ошибки, хорошо, что поймали быстро.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/niti-site.png" width="2264" height="774" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Осталось заделать наикрутейший интернет-магазин, но пока это в бэклоге. Сейчас в топ-приоритетах — собрать сет на грядущий движ в &lt;s&gt;штанах&lt;/s&gt; лесу!&lt;/p&gt;
</description>
</item>

<item>
<title>Как хостить MySQL на маленьких VPS</title>
<guid isPermaLink="false">62</guid>
<link>https://ifedyukin.ru/blog/all/kak-hostit-mysql-na-malenkih-vps/</link>
<pubDate>Tue, 22 Mar 2022 17:20:29 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/kak-hostit-mysql-na-malenkih-vps/</comments>
<description>
&lt;p&gt;Недавно я начал хостить бложик (apache + mysql + nginx + пара сервисов на nodejs) на самом дешевом дроплете от Digital Ocean (надо ж кредитные токены DO куда-то потратить).&lt;/p&gt;
&lt;p&gt;Внезапно столкнулся с проблемой, что контейнер MySQL периодически умирает от OOM киллера, MySQL с небольшим количеством данных съедала ну очень много памяти в простое.&lt;/p&gt;
&lt;p&gt;Платить больше я не хотел, а воспоминания, что когда-то MySQL крутилась у меня на прямо маленьких VPS никак не давала покоя.&lt;/p&gt;
&lt;p&gt;Оказалось, что есть такая штука, как &lt;tt&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.0/en/performance-schema.html#:~:text=The%20MySQL%20Performance%20Schema%20is,of%20the%20server%20at%20runtime."&gt;Performance Schema&lt;/a&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The MySQL Performance Schema is a feature for monitoring MySQL Server execution at a low level.&lt;br /&gt;
The Performance Schema provides a way to inspect internal execution of the server at runtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Дальше я не особо читал, конечно, погуглил чуть, решил поэкспериментировать.&lt;br /&gt;
Добавляем в конфиг в секцию &lt;tt&gt;[mysqld]&lt;/tt&gt;  строчку, чтобы отключить эту фичу (я просто примонтировал новый конфиг), перезагрузил сервис и бах — случилась магия и теперь MySQL кушает гораздо меньше ресурсов. А мониторить мне пока не к чему.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/Screenshot-2022-03-22-at-17.08.52.png" width="1966" height="616" alt="" /&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Как блог в DO переезжал</title>
<guid isPermaLink="false">61</guid>
<link>https://ifedyukin.ru/blog/all/kak-ya-v-do-pereezzhal/</link>
<pubDate>Thu, 17 Mar 2022 15:46:50 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/kak-ya-v-do-pereezzhal/</comments>
<description>
&lt;p&gt;Недавно Mastercard и Visa работать перестали для международных покупок, значится. Понял я, что пришло время переезжать на Digital Ocean, так как у меня там был некоторый предоплаченный период.&lt;/p&gt;
&lt;p&gt;С одной стороны я сразу пожалел, что не написал 7 лет назад какой-то плейбук, чтобы все развернуть. С другой — у меня почти все заводилось через `docker-compose up`.&lt;/p&gt;
&lt;p&gt;Как раз подчистил ненужные сервисы, забэкапил все, перетащил через `scp` всякую рантайм-шляпу типа логов, терять же не хочется, запустил — все отлично работает, остался рад, ушел спать.&lt;/p&gt;
&lt;p&gt;Ночью мне написали, что часть постов не грузится, я решил посмотреть, что произошло и... я не понял, что произошло, ведь я фронтендер! Да и до сих пор не понимаю. На графиках прикольно видно мои потуги что-то починить и как все ломалось.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image-1.png" width="1976" height="1250" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;В какой-то момент у меня начала падать база, что-то в ней побилось. Побилось, кажется, из-за того, что у одного из PHP-скриптов Эгеи не было прав, а потом уже и сам контейнер в базой не мог писать на диск, так как там прав тоже не хватало — на предыдущей VPS у меня чудесно uid’ы юзеров совпали, и все хорошо было.&lt;/p&gt;
&lt;p&gt;В итоге потратил некоторое время на то, чтоб права поправить, обновил MySQL наконец-то, раз 5 переустановил Эгею из бэкапов — косячил в скрипте, некоторые UTF-8 символы криво переносились.&lt;/p&gt;
&lt;p&gt;Но, как итог — все работает, причем даже постабильнее чем было и ресурсов меньше ест. Не знаю только, завелась ли почта.&lt;/p&gt;
&lt;p&gt;Какие выводы? Выводов нет. Надо только нормально все автоматизировать или хотя бы записывать, а не вспоминать что там было 3 года назад, и за правами сразу следить.&lt;/p&gt;
</description>
</item>

<item>
<title>Второй выпуск подкаста смонтирован</title>
<guid isPermaLink="false">56</guid>
<link>https://ifedyukin.ru/blog/all/vtoroy-vypusk-webpurple-podkasta/</link>
<pubDate>Wed, 19 May 2021 11:07:00 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/vtoroy-vypusk-webpurple-podkasta/</comments>
<description>
&lt;p&gt;Я как-то прошляпил в какой-то момент публикацию этого черновика, а сейчас до него дошел. Не пропадать же добру, правильно?&lt;/p&gt;
&lt;p&gt;Андрей Смолко — главный знаток спеки JS в Рязани рассказывает про то, как от проектирования кораблей он перешел к разработке веб-сервисов, учился в Европе и много чего интересного про синтетические данные.&lt;/p&gt;
&lt;p&gt;Подкаст в аудио-формате и на других площадках — &lt;a href="https://anchor.fm/webpurple/episodes/S01E02----------JS-e116ij7"&gt;анкор&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Фидбэк, предложения, пожелания, залететь в следующий выпуск — &lt;a href="https://forms.gle/5x192yAGfgK1SAdw8"&gt;форма&lt;/a&gt;.&lt;/p&gt;
&lt;div class="e2-text-video"&gt;
&lt;iframe src="https://www.youtube.com/embed/7KccAfKyq5E?enablejsapi=1" allow="autoplay" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;
0:00 — Приветствие&lt;br /&gt;
1:33 — Экскурс в историю&lt;br /&gt;
3:12 — Как поехать учиться в Европу&lt;br /&gt;
9:47 — Про магистерскую диссертацию, исследования и первые программы&lt;br /&gt;
14:37 — Как вернулся в Россию, в Рязань&lt;br /&gt;
17:00 — Про модели кораблей, электронную навигацию и работу в Санкт-Петербурге&lt;br /&gt;
21:10 — Первый продакшен код&lt;br /&gt;
22:55 — Как Андрей стал программистом и переехал в Рязань&lt;br /&gt;
28:30 — Как начал читать спеку JS&lt;br /&gt;
37:44 — Кто вдохновляет&lt;br /&gt;
42:55 — Бэкенд или фронтенд&lt;br /&gt;
46:09 — Компетенции и собеседования&lt;br /&gt;
55:12 — Про проект и синтетические данные&lt;br /&gt;
1:08:12 — Завершение&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Как babel-плагин замедлил приложение в 2 раза</title>
<guid isPermaLink="false">55</guid>
<link>https://ifedyukin.ru/blog/all/kak-babel-zamedlil-prilozhenie-v-2-raza/</link>
<pubDate>Thu, 04 Mar 2021 19:11:13 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/kak-babel-zamedlil-prilozhenie-v-2-raza/</comments>
<description>
&lt;p&gt;&lt;b&gt;TL;DR:&lt;/b&gt; в &lt;mark&gt;@babel/plugin-transform-classses&lt;/mark&gt; есть &lt;a href="https://github.com/babel/babel/issues/11356"&gt;баг&lt;/a&gt;, который вызывает падение перформанса.&lt;/p&gt;
&lt;p&gt;Вчера всплыла неожиданная проблема, которая изначально звучала как «часть приложения работает медленнее после оптимизаций сборки проекта». Перформанс панель показывала неутешительные результаты, причем, посмотрев на коллстек, найти даже минимальную зацепку я не смог.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image.png" width="1130" height="293" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Экспериментально удалось выяснить, что проблема возникает чисто из-за каких-то зависимостей.&lt;/p&gt;
&lt;p&gt;С ними в проекте недавно была проведена работа по дедубликации лок-файла и дерева зависимостей, вместе с чем обновились некоторые библиотеки. Минус 7000 строк лока, разбор проблемы обещал быть веселым.&lt;/p&gt;
&lt;p class="note-main"&gt;Проблему искал сначала мой коллега, затем я. Не знаю, сколько времени на это было потрачено, но причиной оказался один плагинчик — &lt;mark&gt;@babel/plugin-transform-classses&lt;/mark&gt;. Откатил версию, зафиксировал ее, и все заработало.&lt;/p&gt;
&lt;p class="note-aside"&gt;Из-за отсутствия зацепок исследование проводилось методом исключения, благо, модули babel были близко к началу очереди.&lt;/p&gt;
&lt;p class="note-main"&gt;Как оказалось, с 1 апреля 2020 года в репозитории есть репорт этой проблемы — &lt;a href="https://github.com/babel/babel/issues/11356"&gt;babel#11356&lt;/a&gt;.&lt;/p&gt;
&lt;p class="note-aside"&gt;Сейчас разбираться, че там внутри плагина поменялось мне лень, конечно, но когда-нибудь обязательно покопаю, когда-нибудь.&lt;/p&gt;
&lt;p&gt;В нашем случае замедление было в среднем в 2+ раза. Некоторым повезло меньше, после обновления Angular можно &lt;a href="https://github.com/angular/angular/issues/41038"&gt;поймать&lt;/a&gt; и более ощутимые просадки.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/kak-babel-zamedlil-prilozhenie-v-2-raza.png" width="1199" height="460" alt="Картинка из обсуждения в репозитории Angular'а" /&gt;
&lt;/div&gt;
&lt;p&gt;В очередной раз поймал себя на мысли, что в 2021 году babel уже не нужен. Пора начать выпиливать его из проектов!&lt;/p&gt;
&lt;p&gt;Сейчас придумал теорию заговора, что babel так с адептами ООП борется просто.&lt;br /&gt;
Как там было? Если хочешь, чтобы перестали использовать deprecated метод, добавь в него sleep.&lt;/p&gt;
</description>
</item>

<item>
<title>Пилотный выпуск подкаста в паблике</title>
<guid isPermaLink="false">54</guid>
<link>https://ifedyukin.ru/blog/all/podcast-pilot-published/</link>
<pubDate>Sun, 28 Feb 2021 16:26:39 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/podcast-pilot-published/</comments>
<description>
&lt;p&gt;Зарелизил, так сказать, пилотный выпуск подкаста. Со звуком проблемы, но это пока, дальше — лучше!&lt;br /&gt;
Пока доступно на &lt;a href="https://www.youtube.com/watch?v=k1MjM5H0UII"&gt;ютубе&lt;/a&gt; и &lt;a href="https://anchor.fm/webpurple/episodes/S01E01-------Gatsby-er4jdg"&gt;анкоре&lt;/a&gt;, доставка на другие площадки — в процессе.&lt;/p&gt;
&lt;p&gt;Фидбэк и предложения можно в комменты ниже, в комменты на ютуб или анонимно — в &lt;a href="https://forms.gle/TfFBZgj9pAq1gyeo7"&gt;форму&lt;/a&gt;.&lt;/p&gt;
&lt;div class="e2-text-video"&gt;
&lt;iframe src="https://www.youtube.com/embed/k1MjM5H0UII?enablejsapi=1" allow="autoplay" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;
0:00 — Приветствие&lt;br /&gt;
2:12 — Стартапы и COVID-19 в Германии&lt;br /&gt;
5:42 — Социализация после переезда&lt;br /&gt;
10:42 — Велосипед и учеба на права в Берлине&lt;br /&gt;
13:55 — DevOps, инфраструктура и облака&lt;br /&gt;
15:51 — Основное направление работы&lt;br /&gt;
17:01 — Про Ruby&lt;br /&gt;
18:07 — Глобальная клаудификация&lt;br /&gt;
21:12 — Постоянное обучение и выгорание&lt;br /&gt;
25:21 — Инженерная тусовка и атмосфера&lt;br /&gt;
28:11 — Почему бы не начать свой стартап&lt;br /&gt;
30:36 — История сайта WebPurple&lt;br /&gt;
35:00 — Технический стек для своего стартапа&lt;br /&gt;
41:24 — Завершение&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Обходим блокировку аналитики AD блокерами</title>
<guid isPermaLink="false">53</guid>
<link>https://ifedyukin.ru/blog/all/ad-blockers-workaround/</link>
<pubDate>Mon, 22 Feb 2021 13:14:12 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/ad-blockers-workaround/</comments>
<description>
&lt;p&gt;AD-блокеры — несомненно крутая штука, тем не менее, они блокируют скрипты аналитики, которые не всегда являются злом.&lt;/p&gt;
&lt;p&gt;Например, &lt;a href="https://www.cloudflare.com/web-analytics/"&gt;аналитика&lt;/a&gt; от Cloudflare — про приватность и легкость, данных лишних не собирает, на сторону их не продает. Потому обойти ее блокировку различными расширениями не так уже и подло.&lt;/p&gt;
&lt;p&gt;Блокировщики работают в несколько этапов: блокируют подгрузку файла скрипта аналитики и блокируют запросы к серверам аналитики, к которым эти скрипты и обращаются.&lt;/p&gt;
&lt;p&gt;Для обхода блокировки нам понадобится одна вещь — прокся с возможностью замены части контента в ответе, например, nginx.&lt;/p&gt;
&lt;p&gt;Добавляем два локейшена в конфиг:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;# сам скрипт аналитики
location /analytics-script.js {
  # очень важно, чтоб отрабатывал sub_filter, т.к. если сервер отдаст gzip, то замена не сработает
  proxy_set_header Accept-Encoding &amp;quot;&amp;quot;;

  # заменяем в скрипте оригинальный адрес на наш, проксированный
  sub_filter 'https://cloudflareinsights.com/cdn-cgi/rum'  'https://домен/analytics-api';
  sub_filter_types *;
  sub_filter_once off;

  proxy_hide_header Content-Security-Policy;
  proxy_pass https://static.cloudflareinsights.com/beacon.min.js;
}

# проксируем вызовы к оригинальному api аналитики
location /analytics-api {
  proxy_pass https://cloudflareinsights.com/cdn-cgi/rum;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Подключаем аналитику не с домена Cloudflare, а со своего домена.&lt;/p&gt;
&lt;p&gt;Таким образом мы загружаем и скрипт аналитики со своего домена, и аналитику он отправляет на наш домен, а внутри уже все проксируется в Cloudflare.&lt;/p&gt;
&lt;p&gt;AD блокеры ничего не блокируют, мы получаем аналитику. Запросы куда-то еще уходят неявно.&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Есть одна небольшая проблема — у всех запросов один IP, IP прокси, почему-то аналитика Cloudflare не учитвает заголовки и не имеет задокументированных способов переопределить IP юзера, как это сделано в Google аналитике.&lt;/p&gt;
&lt;p&gt;Закинул в коммьюнити &lt;a href="https://community.cloudflare.com/t/web-analytics-ip-override/246614"&gt;топик&lt;/a&gt; на эту тему, посмотрим, как решится.&lt;/p&gt;
</description>
</item>

<item>
<title>Применяем команды iOS</title>
<guid isPermaLink="false">50</guid>
<link>https://ifedyukin.ru/blog/all/ios-actions-location/</link>
<pubDate>Sun, 25 Oct 2020 19:05:43 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/ios-actions-location/</comments>
<description>
&lt;p&gt;Относительно недавно я перешел на iOS, и меня долго мучало, куда бы приладить команды, которые позволяют что-то автоматизировать. Чего я только в них прикрутить не пытался, в моих случаях все упиралось в то, что триггер телеграма с отправкой сообщений попросту не работает. Но это, конечно, совсем другая история!&lt;/p&gt;
&lt;p&gt;Сегодня я по некоему наитию вспомнил, что пока катался по городам, то и дело забывал обновлять &lt;a href="https://aegeus.ifedyukin.ru/blog/all/location-on-the-page/"&gt;геолокацию&lt;/a&gt; у себя на сайтике, потому что надо ручками жать кнопку в телеграм-боте. И вот тут-то я и подумал, что надо прикручивать автообновление локаций в фоне.&lt;/p&gt;
&lt;p&gt;Хотелось бы сказать, что я не написал ни строчки кода для внедрения этой фичи, но нет, пришлось дописать в бота один GET-эндпоинт, который достает из QS координаты, проверяет токен и делает все то же самое, что делал бот.&lt;/p&gt;
&lt;p&gt;Говорим iOS’у запускать каждый вечер экшен, в котором он получает геопозицию, достает из нее координаты и дергает GET-эндпоинт с пробросом координат в него. Метод запроса и экшены были подобраны так, чтобы iOS ничего не показывал и не требовал никаких действий со стороны пользователя.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="591" data-ratio="0.46171875"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/shortcuts-1.jpg" width="591" height="1280" alt="" /&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/shortcuts-2.jpg" width="591" height="1280" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Ошибки state-менеджмента React-компонентов</title>
<guid isPermaLink="false">49</guid>
<link>https://ifedyukin.ru/blog/all/react-state-issues/</link>
<pubDate>Mon, 15 Jun 2020 19:01:52 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/react-state-issues/</comments>
<description>
&lt;p&gt;При работе с React у многих разработчиков, у которых с ним менее года опыта (или более, но понимания некоторых аспектов нет) возникает проблема с пониманием состояния компонента.&lt;/p&gt;
&lt;p&gt;Когда были классовые компоненты, многие хранили в стейте вообще все, даже какие-то служебные флаги, которые можно было бы в this записать, и которые на рендер компонента никак не влияют.&lt;/p&gt;
&lt;p&gt;Это было проблемой, но решалась она быстро. С появлением хуков ситуация ухудшилась.&lt;/p&gt;
&lt;p&gt;Если в классовых компонентах каждый метод — какой-то шаг лайфсайкла, то с хуками концепция и подход абсолютно другой. Отсюда вырастает много ляпов, которые не сразу заметны и которые разработчики объяснить не могут, но проблемы, возникающие из-за таких ляпов превращаются в серьезную головную боль.&lt;/p&gt;
&lt;p&gt;Парочка самых распространенных, что я видел: Эффект, который слушает проп и обновляет в след за ним стейт без каких-либо преобразований. Что-то вроде такого:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const component = ({ prop }) =&amp;gt; {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() =&amp;gt; {
    setSavedProp(prop);
  }, [prop]);
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Больше логики никакой, никаких подводных камней. Мало того, что можно было бы прямо в useState прокидывать проп, так все гораздо проще — можно было бы использовать сам этот проп, вместо его сохранения в стейт! Вероятно, там когда-то был какой-то процессинг, может быть, какие-то условия, но больше этого нет, зато есть несколько ненужных обновлений компонента.&lt;/p&gt;
&lt;p&gt;Эффект, который слушает изменения стейта, чтобы… вызвать коллбэк.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const component = ({ prop, onPropChange }) =&amp;gt; {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() =&amp;gt; {
    onPropChange(savedProp);
  }, [savedProp]);

  return (
    &amp;lt;Child prop={prop} onPropChange={setSavedProp} /&amp;gt;
  )
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тут все вроде бы тоже понятно, но стейт не используется ни для чего, кроме триггера коллбэка. Правильнее прокинуть просто коллбэк и… все, а не триггерить несколько апдейтов просто так. Так мало того, часто забывают что-то еще в зависимости эффекта прокинуть, и он работает совсем не так, как должен.&lt;/p&gt;
&lt;p&gt;Почему-то за последние полгода столкнулся с кучей таких ошибок, и не только я — в различных чатиках тоже проскакивало частенько. То ли дело в разработчиках, то ли в хуках.&lt;/p&gt;
</description>
</item>

<item>
<title>Проблема FrontOps’а</title>
<guid isPermaLink="false">48</guid>
<link>https://ifedyukin.ru/blog/all/frontops-trouble/</link>
<pubDate>Sun, 07 Jun 2020 18:59:53 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/frontops-trouble/</comments>
<description>
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Фронтопс — перенос девопс практик на фронтенд, так как у фронтенда есть своя специфика.&lt;/li&gt;
&lt;li&gt;Основная проблема — многие фронтендеры привыкли работать с хорошими инструментами, не зная, как они работают внутри, не понимая, что происходит с кодом после того, как он написан.&lt;/li&gt;
&lt;li&gt;Каждому фронтендеру нужно понимать, как собирается его проект, что происходит после сборки, как и в каком виде код доходит до пользователя, иметь представление об используемых и существующих инструментах&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/frontend-devops.png" width="1600" height="747" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Предыдущие два года на более, чем двух проектах я занимался такой деятельностью, которую обычно называют «FrontOps». Давайте разберемся, какие у фронтопса проблемы и как их решать.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Дисклеймер:&lt;/b&gt; По теме я не раз говорил на митапах &lt;a href="https://webpurple.net"&gt;Webpurple&lt;/a&gt;, но структурировать все в текст так и не вышло нормально. Пост — просто изливание кучи мыслей, не структурирован, хотя планировалось нечто большое на несколько иную тему, в черновиках лежал больше, чем полгода, но, как &lt;a href="https://marinintim.com/2020/publish-anyway/"&gt;говорил Тим&lt;/a&gt;, надо признаться, что черновики не допишутся и публиковать как есть, вот публикую. Ниже все исключительно ИМХО.&lt;/p&gt;
&lt;p&gt;Повествование я начну как обычно — с небольшого отступления в прошлое. Когда я начинал работать как разработчик, я никогда не был против задач не для разработчика (как сейчас считается): сервер настроить, сконфигурировать прокси, сборщик из пачки sh-скриптов подкрутить, еще что-то. Да, эти задачи я решал постольку-поскольку, не использовал всякие крутые штуки и инструменты, до поры до времени ни про вагрант, ни про ансибл не слышал, но задачи решал и мне в целом нравилось.&lt;/p&gt;
&lt;p&gt;Что касается специализаций и развитий навыков, я приверженец T-shape подхода: в основном я фронтендер, но и в DevOps с бэкендом могу. Вот благодаря этому пересечению с девопсом я гордо могу назвать себя FrontOps’ом.&lt;/p&gt;
&lt;h2&gt;Появление FrontOps&lt;/h2&gt;
&lt;p&gt;Вторая часть слова — Ops. Когда говорят про Ops, вспоминается DevOps. Несколько обидно, что почему-то в &lt;b&gt;Dev&lt;/b&gt;Ops не всегда включают FrontEnd, &lt;s&gt;шутка про формошлепов&lt;/s&gt; на это есть свои причины, но начнем с того, что такое DevOps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;DevOps&lt;/b&gt; — методология взаимодействия специалистов по разработке (Dev) со специалистами по информационно-технологическому обслуживанию (Ops) и интеграцию их рабочих процессов друг в друга для обеспечения качества продукта.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ru.wikipedia.org/wiki/DevOps"&gt;Wikipeadia — DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;В диалогах я слышал что-то вроде: «Какие еще оперейшены во фронтоне?! Че там оперировать, статика же?», — наверно, поэтому как-то это все в отдельное направление и выделилось местами. Изначально, девопс — это больше про бэкенд и все, что около него, как-то так сложилось исторически, во всяком случая я чаще сталкивался с таким, что в рамках девопс-процессов о фронтенде никто особо не думал, и это как-то само должно было решаться. Ведь что нужно бэкенду? Кластер серверов, лоадбалансинг, умный деплоймент с нулевым даунтаймом, скейлинг и много всего сложного и интересного. А что на фронтенде обычно было? Отдать статику (часто через тот же бэкенд) и все, но сейчас времена другие.&lt;/p&gt;
&lt;p&gt;В итоге, отпочковался фронтопс. &lt;b&gt;Фронтопс&lt;/b&gt; — ничто иное, как &lt;b&gt;перенос девопс-практик на фронтенд&lt;/b&gt;. Те же практики, те же решаемые задачи, просто с некоторой спецификой что ли. Девопс (и фронтопс, выходит, тоже) подразумевает работу по некоторому циклу из определенных шагов с использованием определенных техник и инструментов.&lt;/p&gt;
&lt;h2&gt;Проблема FrontEnd ~ Ops&lt;/h2&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/devops-phase.png" width="1280" height="725" alt="" /&gt;
&lt;/div&gt;
&lt;p class="foot"&gt;Create... а дальше?&lt;/p&gt;
&lt;p&gt;После того, как код написали, с ним еще много интересного происходит до того, как он начнет решать задачи пользователя и бизнеса, да и в процессе выполнения этих задач интересное не заканчивается. На бэкенде, разработчики в большинстве своем представляют, что с кодом происходит, после того, как он скомпилировался, так как по-другому там жить довольно сложно, надо все-таки понимать, кто там jar’ник запустил и какие и куда пакеты летают, бэкендерам даже надо понимать, что такое TCP, например (!!!).&lt;/p&gt;
&lt;p&gt;В мире фронтенда ситуация несколько иная. На один мой комментарий в ревью «этот код не соберется вот по этой причине, вот линк, почитай, интересная штука» мне отвечали «я простой разработчик, это меня не касается» (утрирую, но примерно так было). WTF? В тот момент я понял, что что-то не так.&lt;/p&gt;
&lt;p&gt;Откуда растут ноги этой проблемы? Из времен, когда разработка фронтенда заключалась в запуске сервера статики (и то не всегда) и изменения файликов, которые тут же отображаются в браузере, а деплой заключался в том, чтобы отдать файлики бэкендерам, а они там в jsp перегонят и сами разберутся, или загрузить файлики через FTP на другой сервер. Просто, круто, DX на высоте, код написал и все.&lt;/p&gt;
&lt;p&gt;Мир меняется, требования растут, технологии усложняются. Сначала начали гонять файлы через различные инструменты, которые код бандлят и транспилируют, потом этапы стали сложнее и дольше, а файлов стало больше, добавились дополнительные этапы, куча библиотек, фреймворки. С одной стороны, сейчас ситуация совсем другая, но с другой — для многих разработчиков все остается так же с небольшим усложнением:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;`git clone` — оп, код у меня;&lt;/li&gt;
&lt;li&gt;`npm install` — ну, зависимости у меня, погнали;&lt;/li&gt;
&lt;li&gt;`npm start` — ммм, *клик-клац-клик*, все, написал;&lt;/li&gt;
&lt;li&gt;`git commit -a &amp;&amp; git push` — отлично, таску в резолв.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="width:100%;height:0;padding-bottom:56%;position:relative;"&gt;&lt;iframe src="https://giphy.com/embed/QNFhOolVeCzPQ2Mx85" width="100%" height="100%" style="position:absolute" frameBorder="0" class="giphy-embed" allowFullScreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;С точки зрения разработчика ничего особо не поменялось, но появилась какая-то магия, которая где-то там что-то делает. Разработчик не знает, как оно работает внутри и что происходит с кодом после пуша, да даже не после пуша, а после сохранения файлика, кто над этим файликом колдует на его компьютере, во что превращается этот файлик. Рассмотрим выдуманную историю. В один момент было решено переехать из одного облака в другое и допилить пару крупных фич. Для этого надо в зависимоти от инстанса приложения нужно обращаться к различным сервисам, чуть-чуть подпилить сборку проекта, чтобы он собирал все в несколько бандлов и парочку шрифтов подключить.&lt;/p&gt;
&lt;p&gt;Вроде бы задача понятна, но для многих разработчиков фронтенда она нерешаема по одной простой причине: &lt;b&gt;они слишком отдалились от инструментов разработки и не успели подстроиться под их изменения&lt;/b&gt;, да и думать об этом часто не нужно, и «вообще пусть это девопсы делают, оперейшены все-таки». Но откуда девопс-инженерам знать о том, как код должен собираться вебпаком? А тем более о всяких узких местах, типа хранений шрифтов на том же домене, чтобы лишний dns-запрос не делался?&lt;/p&gt;
&lt;p&gt;Много разработчиков на C++ вы знаете, которые не понимают, как линкуются библиотеки и не могут написать make-файл? А фронтендеров таких много, и это часто не проблема фронтендера, да и не проблема это, просто у нас много инструментов, которые легко устанавливаются и все делают сами.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Хорошо, когда инструменты настолько удобны, что легко решают все проблемы за тебя. Плохо, когда ты не представляешь, как они это делают.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Решаем проблемы&lt;/h2&gt;
&lt;p&gt;Фронтопс — это не только девопс + фронтенд, фронтопс — то, что нужно знать фронтенд разработчику, для того, чтобы быть действительно фронтенд разработчиком, ведь закомиченный код — далеко не результат работы. Нужно понимать, что с кодом происходит, куда он потом помещается и как доходит до пользователя.&lt;/p&gt;
&lt;p&gt;Если вы понимаете, что происходит с вашим кодом в вашем проекте, напишите к этому документацию, устройте пару созвонов, где покажете остальным участникам команды, что и как просиходит в процессе сборки, непрерывных интеграций, деплоя.&lt;/p&gt;
&lt;p&gt;Если вы не понимаете этого, изучите проект и инструменты, которые в нем используются, почитайте документацию к ним, спросите более опытных тиммейтов, сделайте собственный стартер или пет-проджект, накрутите там крутой CI/CD, оптимальную сборку.&lt;/p&gt;
&lt;p&gt;Изучение «оперейшенов» на базовом уровне занимает не так много времени, но это очень ценный и полезный опыт, который положительно скажется на кругозоре и понимании того, как вообще работает проект и какую роль в нем занимает фронтенд.&lt;/p&gt;
&lt;h2&gt;Инструменты, о которых нужно иметь представление&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Вебпак, бабел, бандлеры, таск-раннеры (&lt;a href="https://webpack.js.org/guides/getting-started/"&gt;Webpack&lt;/a&gt;, &lt;a href="https://babeljs.io/docs/en/"&gt;Babel&lt;/a&gt;, &lt;a href="https://docs.npmjs.com/misc/scripts"&gt;npm-scripts&lt;/a&gt;, &lt;a href="https://gulpjs.com/"&gt;Gulp&lt;/a&gt;) — изучите принцип работы сборщиков и компиляторов JavaScript, TypeScript, CSS, поймите разницу между Rollup и Webpack, изучите возможности npm-scripts и Gulp (хоть его и мало кто использует сейчас).&lt;/li&gt;
&lt;li&gt;Форматтеры, линтеры, статические анализаторы (&lt;a href="https://eslint.org/"&gt;ESlint&lt;/a&gt;, &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;, &lt;a href="https://github.com/stylelint/stylelint"&gt;stylelint&lt;/a&gt;, &lt;a href="https://www.sonarqube.org/"&gt;SonarQube&lt;/a&gt;) — хватит спорить на ревью о том, где должна стоять запятая, хватит мутировать иммутабельные значения, автоматизируйте проверку этого.&lt;/li&gt;
&lt;li&gt;Git и хуки (&lt;a href="https://github.com/typicode/husky"&gt;Husky&lt;/a&gt;, &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt;, &lt;a href="https://try.github.io/"&gt;Learn Git&lt;/a&gt;) — изучите Git, для вас не должны быть проблемой ребейзы, которые заканчиваются конфликтами, используйте хуки для автоматического запуска проверок.&lt;/li&gt;
&lt;li&gt;Тестирование (&lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;, &lt;a href="https://jasmine.github.io/"&gt;Jasmine&lt;/a&gt;, &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt;) — изучите инструменты тестирования, способы их использования, виды тестов.&lt;/li&gt;
&lt;li&gt;Дополнительные инструменты для проверок проекта (&lt;a href="https://github.com/ai/size-limit"&gt;size-limit&lt;/a&gt;, &lt;a href="https://github.com/GoogleChrome/lighthouse-ci"&gt;Lighthouse CI&lt;/a&gt;) — проверяйте перформанс-метрики вашего проекта прямо на этапе непрерывных интеграций.&lt;/li&gt;
&lt;li&gt;CDN, файлы, сервера и прокси (&lt;a href="https://nginx.org/ru/"&gt;nginx&lt;/a&gt;, &lt;a href="https://aws.amazon.com/ru/s3/"&gt;S3&lt;/a&gt;, &lt;a href="https://aws.amazon.com/ru/cloudfront/"&gt;CloudFront&lt;/a&gt;, &lt;a href="https://cloudinary.com/"&gt;Cloudinary&lt;/a&gt;) — изучите, как файлы могут храниться и раздаваться.&lt;/li&gt;
&lt;li&gt;Мониторинг (&lt;a href="https://www.nginx.com/products/nginx-amplify/"&gt;Amplify&lt;/a&gt;, &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;) — настройте мониторинги, отслеживайте, что происходит с кодом при использовании.&lt;/li&gt;
&lt;li&gt;Деплой, сервера, докер (&lt;a href="https://github.com/ansible/ansible"&gt;Ansible&lt;/a&gt;, &lt;a href="https://docs.docker.com/get-started/"&gt;Docker&lt;/a&gt;, &lt;a href="https://habr.com/ru/post/122445/"&gt;SSH&lt;/a&gt;) — изучите подходы к деплою, варианты запуска приложений, научитесь работать с SSH и консолью — это действительно нужный навык.&lt;/li&gt;
&lt;li&gt;CI/CD (&lt;a href="https://docs.gitlab.com/ee/ci/"&gt;GitLab CI&lt;/a&gt;) — изучите принципы и подходы к организации CI/CD, сделайте полезный пайплайн для своего проекта или оптимизируйте существующий.&lt;/li&gt;
&lt;/ul&gt;
</description>
</item>

<item>
<title>UI library based on Chakra-UI bundling</title>
<guid isPermaLink="false">47</guid>
<link>https://ifedyukin.ru/blog/all/ui-lib-chakra-bundling/</link>
<pubDate>Thu, 09 Apr 2020 18:49:15 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/ui-lib-chakra-bundling/</comments>
<description>
&lt;p&gt;Recently I tried to build some UI components library that based on &lt;a href="https://github.com/chakra-ui/chakra-ui"&gt;Chakra-UI&lt;/a&gt;. At first my library didn’t contain react components and compiled using typescript and it worked perfectly. But after I added React components based on Chakra-UI and migrated build to webpack, I’ve got the next error from react hooks that were called inside Chakra-UI hooks such as useTheme:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
By the way, Chakra-UI repository has this issue about this error - ((https://github.com/chakra-ui/chakra-ui/issues/495 #495)).&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What is the reason for this error? useTheme hook and Chakra-UI library in whole use react library. As a result library imports react and react-dom. When webpack bundling your library that includes Chakra-UI, it includes react and react-dom to bundle of your library. In brief, you import react to your project, also you import your library with Chakra-UI that contains react too, as a result — you have at least two React copies. And in fact, it’s incorrect hook call.&lt;/p&gt;
&lt;p&gt;How to fix it? Use only single react instance in your app. If you’re using webpack you can mark react and react-dom libraries as external libraries for your components library:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;externals: {
  // mark &amp;quot;react&amp;quot; and react-dom as external packages that will import using commons
  react: 'commonjs react',
  'react-dom': 'commonjs react-dom'
},
output: {
  path: path.resolve('./'),
  libraryTarget: 'commonjs2', // set library target
  filename: '[name].js',
  library: '',
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now bundle of your library doesn’t contain react and react-dom. We marked libraries as external and have pure require calls but doesn’t have library in bundle:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;/***/ &amp;quot;react&amp;quot;:
/*!************************!*\
  !*** external &amp;quot;react&amp;quot; ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require(&amp;quot;react&amp;quot;);
/***/ }),

/***/ &amp;quot;react-dom&amp;quot;:
/*!****************************!*\
  !*** external &amp;quot;react-dom&amp;quot; ***!
  \****************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require(&amp;quot;react-dom&amp;quot;);
/***/ })&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way also can help you if you’re using hooks and creating your own UI library without Chakra-UI.&lt;/p&gt;
</description>
</item>

<item>
<title>NextJS, первый взгляд</title>
<guid isPermaLink="false">46</guid>
<link>https://ifedyukin.ru/blog/all/nextjs-begin/</link>
<pubDate>Fri, 28 Feb 2020 18:45:08 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/nextjs-begin/</comments>
<description>
&lt;p&gt;На днях одно (пока) небольшое приложение перевел на NextJS, ибо нужен качественный SSR для SEO, а самостоятельно писать кучу хелперов слишком дорого по времени.&lt;/p&gt;
&lt;p&gt;На первый взгляд все оказалось очень круто, что интересно, почему-то даже некоторые интерактивные элементы начали работать быстрее, не разбирался пока, какая магия их ускорила, но не отметить не мог. Миграция тоже прошла гладко, подключение к проекту было самым крутым из всех инструментов: установить через yarn, удалить конфиг webpack’а, — именно последний пункт больше всего понравился.&lt;/p&gt;
&lt;p&gt;Я сразу подумал, что все слишком хорошо, чтобы быть правдой, в одном чатике отметили, что через недельку я буду бомбить на Next, но пока держусь и странность всего одну заметил — роутинг.&lt;/p&gt;
&lt;p&gt;Казалось бы, роутинг должен быть сильнее всего проработан в фреймворке такого типа, но то ли я не понял, как им правильно пользоваться, то ли оно как-то странно сделано. Ладно, со ссылками пришлось повозиться, ибо в одном случае href — тот url, на который непосредственно идет редирект, а в другом случае — slug страницы, который я определил в роутере, а непосредственно ссылку прокидывать нужно в проп as.&lt;/p&gt;
&lt;p&gt;Но вот если пушить что-то в url с динамическим роутом, то получается что-то странное. Пушу не явный конечный url, а slug описанный в роутинге, нужна квери-стринга? Ок, квери можно прокинуть в отдельный объект, они даже до чилда долетят, но вот в url они не попадут, бери ручками и прокидывай.&lt;/p&gt;
&lt;p&gt;Тут у меня прямо явный диссонанс:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ожидание: сейчас я в метод пуш прокину объект квери и текущий адрес — хоба, квери обновлен, строка адреса хранит состояние, все счастливы.&lt;/li&gt;
&lt;li&gt;Реальность: прокидываю реальный адрес, прокидываю шаблон адреса, прокидываю квери-объект, новый полный адрес вместе с квери, который сам леплю чем-то, — тоже все счастливы, конечно, но большей ценой.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Действий немного, но хотелось бы каких-то изящных абстракций из коробки, а не руками их делать.&lt;/p&gt;
</description>
</item>

<item>
<title>Текущая локация на странице</title>
<guid isPermaLink="false">44</guid>
<link>https://ifedyukin.ru/blog/all/location-on-the-page/</link>
<pubDate>Tue, 07 Jan 2020 18:35:37 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/location-on-the-page/</comments>
<description>
&lt;p&gt;Как-то внезапно зашел разговор о том, что прикольно на своем сайте иметь свое текущее местоположение с точностью до города. И, как оказалось, самым сложным является получение этого местоположения.&lt;/p&gt;
&lt;p&gt;Самый первый вариант, который я рассматривал — по cron’у дергать “Find my phone” и ориентироваться на эти координаты, однако, этот вариант не особо мне понравился, ибо, во-первых, надо раскручивать API, которое может неожиданно поменяться, во-вторых, в моменты между городами cron также отрабатывает, и потом на сайте красуется какое-то неожиданное местоположение, но основная причина — сложно и ничего непонятно.&lt;/p&gt;
&lt;p&gt;Второй вариант — сделать простой сервис с get и post ручками, развернуть его где-то в Heroku и все. Сервис я такой сделал, положил его на &lt;a href="https://github.com/ifedyukin/share-your-location"&gt;GitHub&lt;/a&gt;. Из интересного, post-метод работает через Telegram-бота — я просто кликаю кнопку и… все, ну и в MongoDB ведется лог местоположений, интересно будет потом посмотреть.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image-20200107160823273.png" width="1286" height="1842" alt="" /&gt;
&lt;/div&gt;
&lt;p class="foot"&gt;Интерфейс бота&lt;/p&gt;
&lt;p&gt;Сервис работает как часы (еще бы одна RW-сущность плохо работала), хостится на Heroku. Осталось только интегрировать его с сайтом, но тут интересно, что сайт-то полностью статический, делать запросы лишние не очень хочется, ибо на Heroku инстансы поднимаются дольше, чем пользователи задерживаются на моем сайте. Потому все вынес в compile time, оказалось, из GraphQL все довольно удобно доставать.&lt;/p&gt;
&lt;p&gt;В gatsby-nodes.js добавляем хук, создаем ноду в GraphQL&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const fetch = require('node-fetch');
const config = require('../config');

exports.sourceNodes = async ({
  actions: { createNode },
  createContentDigest
}) =&amp;gt; {
  const result = await fetch(config.locationUrl);
  const locationData = await result.json();

  createNode({
    ...locationData,
    id: 'author-location',
    parent: null,
    children: [],
    internal: {
      type: 'AuthorLocation',
      contentDigest: createContentDigest(locationData)
    }
  });
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В компоненте страницы достаем это и рендерим.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;query RootPage {
    authorLocation {
      city
      country
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В целом механизм работы такой:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Отправляю боту местоположение.&lt;/li&gt;
&lt;li&gt;Он проверяет, что оно новое, сохраняет в MongoDB.&lt;/li&gt;
&lt;li&gt;Дергает webhook Netlify.&lt;/li&gt;
&lt;li&gt;Netlify стартует пересборку сайта.&lt;/li&gt;
&lt;li&gt;Gatsby делает запрос к сервису, получает новое местоположение.&lt;/li&gt;
&lt;li&gt;Сборка.&lt;/li&gt;
&lt;li&gt;Деплой.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Просто, работает как часы, осталось не забывать нажимать эту кнопку.&lt;/p&gt;
</description>
</item>


</channel>
</rss>