<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitri Pisarev 🇷🇺</title>
    <description>The latest articles on DEV Community by Dmitri Pisarev 🇷🇺 (@dimaip).</description>
    <link>https://dev.to/dimaip</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F386641%2F223efc81-77f0-4fe7-ab3f-dce488c0dd36.jpg</url>
      <title>DEV Community: Dmitri Pisarev 🇷🇺</title>
      <link>https://dev.to/dimaip</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dimaip"/>
    <language>en</language>
    <item>
      <title>Annoying TypeScript discriminating union gotcha</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Wed, 14 Apr 2021 06:28:28 +0000</pubDate>
      <link>https://dev.to/dimaip/annoying-typescript-discriminating-union-gotcha-3p16</link>
      <guid>https://dev.to/dimaip/annoying-typescript-discriminating-union-gotcha-3p16</guid>
      <description>&lt;p&gt;Can you spot what's wrong with this code?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k7jC-Jrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7vnfvzljv6bz8y1tc7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k7jC-Jrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7vnfvzljv6bz8y1tc7z.png" alt="Discriminating union gone wrong"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.typescriptlang.org/play?ts=4.2.3#code/C4TwDgpgBACgTgezAZygXigbwFBT1AS2QEE44BDEALigDNyAbZCAGl31oDsaAKAN0YBXCDWTA4BTgHMAlOgB8UPggIATbAF8oAHyzs8RUhWpRxwtvjrco-ISKhiJ0gNoBdOWkXK1m7NgDGCJxiUADCCAC2YEEQnMDoNpiGZJQsVho08EjIHoo4loHB8f6MDABG5P4A1gk8uXqWlgS0NsnGcvmNXVw8zgDk5MiqtKp97vqNWhBM0J1dlj19AIwATADMfTITlhoTuxpAA"&gt;https://www.typescriptlang.org/play?ts=4.2.3#code/C4TwDgpgBACgTgezAZygXigbwFBT1AS2QEE44BDEALigDNyAbZCAGl31oDsaAKAN0YBXCDWTA4BTgHMAlOgB8UPggIATbAF8oAHyzs8RUhWpRxwtvjrco-ISKhiJ0gNoBdOWkXK1m7NgDGCJxiUADCCAC2YEEQnMDoNpiGZJQsVho08EjIHoo4loHB8f6MDABG5P4A1gk8uXqWlgS0NsnGcvmNXVw8zgDk5MiqtKp97vqNWhBM0J1dlj19AIwATADMfTITlhoTuxpAA&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>Jest: shared async code between test blocks</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Tue, 26 May 2020 17:09:00 +0000</pubDate>
      <link>https://dev.to/dimaip/jest-shared-async-code-between-test-blocks-453k</link>
      <guid>https://dev.to/dimaip/jest-shared-async-code-between-test-blocks-453k</guid>
      <description>&lt;p&gt;I want to remember this day as the first day I asked something at StackOverflow and receive a brilliant and non-trivial answer!&lt;/p&gt;

&lt;p&gt;Check it out, a way to provision a group of tests with some test data asynchronously while getting some data from the provisioning process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setupTestContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testContext&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some group of tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;someData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="nx"&gt;setupTestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// context is filled with data at this point&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/62019169/jest-shared-async-code-between-test-blocks/62027162#62027162"&gt;https://stackoverflow.com/questions/62019169/jest-shared-async-code-between-test-blocks/62027162#62027162&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jest</category>
      <category>tdd</category>
    </item>
    <item>
      <title>React quiz: spot a bug</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Sat, 16 May 2020 11:48:27 +0000</pubDate>
      <link>https://dev.to/dimaip/reactjs-quiz-spot-a-bug-40pj</link>
      <guid>https://dev.to/dimaip/reactjs-quiz-spot-a-bug-40pj</guid>
      <description>&lt;p&gt;A few month ago I've posted this trick snippet on Twitter... And then I even forgot what the problem was myself!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTokenize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;someTokenizeFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;setTokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someTokenizeFunction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Can you spot a bug? Write in the comments!&lt;/p&gt;

&lt;p&gt;Could make a nice interview question, huh?&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>Getting a PWA to self-update</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Fri, 15 May 2020 11:23:02 +0000</pubDate>
      <link>https://dev.to/dimaip/getting-a-pwa-to-self-update-19b9</link>
      <guid>https://dev.to/dimaip/getting-a-pwa-to-self-update-19b9</guid>
      <description>&lt;p&gt;So people have asked me to share how to make sure your PWA automatically updates itself, even on #iOS 12 (which preserves the state of your app even when quitting it).&lt;/p&gt;

&lt;p&gt;In my case the app is stateless so I can allow myself to just reload the app without showing any prompts for update to the user. If your app is more complex you can show a prompt in every place I do &lt;code&gt;location.reload&lt;/code&gt;, all other things should be relevant.&lt;/p&gt;

&lt;p&gt;Here's the point to listen if the new ServiceWorker is available: &lt;a href="https://github.com/dimaip/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/serviceWorker.js#L26"&gt;https://github.com/dimaip/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/serviceWorker.js#L26&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When installing SW I do skipWaiting to make it take control immediately, without waiting for all tabs to be closed. Also I do &lt;code&gt;clients.claim&lt;/code&gt; in order to take control of all the tabs that have no SW installed yet for some reason: &lt;a href="https://github.com/dimaip/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/service-worker.js#L6-L11"&gt;https://github.com/dimaip/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/service-worker.js#L6-L11&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On every route transition I compare the current app's version with the version returned by app's version endpoint: &lt;a href="https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/Routes.js#L24"&gt;https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/Routes.js#L24&lt;/a&gt;&lt;br&gt;
If the versions differ I reload. This is especially important on iOS 12 where there's no way to make the app to reload from user actions.&lt;/p&gt;

&lt;p&gt;On every deploy I tag a new version with &lt;code&gt;yarn version --patch&lt;/code&gt;. Here's how I expose it server-side: &lt;a href="https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/server.js#L73"&gt;https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/server.js#L73&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also display the version in the app, quite helpful when debugging: &lt;a href="https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/containers/Main/BurgerMenu.js#L87"&gt;https://github.com/psmb/calendar/blob/ea68923793e8975a483aff8605f52e5c913b9344/app/containers/Main/BurgerMenu.js#L87&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! On every route transition I can be sure that the users have the latest version of the app.&lt;/p&gt;

&lt;p&gt;Make sure you also read this related post how to deploy an app with code-splitting, or else you won't get very far with auto-updates! &lt;a href="https://dev.to/dimaip/what-you-should-consider-before-deploying-an-app-with-code-splitting-1n76"&gt;https://dev.to/dimaip/what-you-should-consider-before-deploying-an-app-with-code-splitting-1n76&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The most common React mistake</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Fri, 15 May 2020 11:12:04 +0000</pubDate>
      <link>https://dev.to/dimaip/the-most-common-react-mistake-3a9m</link>
      <guid>https://dev.to/dimaip/the-most-common-react-mistake-3a9m</guid>
      <description>&lt;p&gt;Have you ever ran into this warning?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: Can’t perform a React state update on an unmounted component.  This is a no-op, but it indicates a memory leak in your application. To  fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I bet this is the most common ReactJS newbie mistake... Forgetting to clean up your effects.&lt;/p&gt;

&lt;p&gt;Stumbled over it again and here's how I fixed it: &lt;a href="https://github.com/dimaip/calendar/commit/c20274917feff86af859f7252b717b84936ba865"&gt;https://github.com/dimaip/calendar/commit/c20274917feff86af859f7252b717b84936ba865&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>What You should Consider before Deploying an App with Code Splitting</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Sat, 25 Apr 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/dimaip/what-you-should-consider-before-deploying-an-app-with-code-splitting-1n76</link>
      <guid>https://dev.to/dimaip/what-you-should-consider-before-deploying-an-app-with-code-splitting-1n76</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_hBq3t6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/837032/80278193-fc3da380-86fc-11ea-810c-c7c7b92aa0f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_hBq3t6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/837032/80278193-fc3da380-86fc-11ea-810c-c7c7b92aa0f3.png" alt="Some exceptions from Sentry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently I had to publish &lt;a href="https://c.psmb.ru"&gt;my first ever PWA with code-splitting&lt;/a&gt; (&lt;a href="https://github.com/dimaip/calendar"&gt;here&lt;/a&gt; is the source). Quite quickly I figured out I had no idea what I was doing… Maybe I am not the only one who didn’t consider that deploying apps with code-splitting is not all that trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Keep previously deployed JS chunks. If you can’t, prefetch + force-update app on failed imports&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Context
&lt;/h2&gt;

&lt;p&gt;Nowadays code splitting for JavaScript apps has become mainstream. It is trivial to achieve and dramatically improves initial load time for your app. Webpack provides code-splitting out of the box on dynamic imports.&lt;/p&gt;

&lt;p&gt;Imagine you have a React web app and you would like &lt;code&gt;SomeVeryHeavyComponent&lt;/code&gt; to be loaded only when the user navigates to that route. Here’s how you would achieve it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SomeHeavyComponent = React.lazy(
  () =&amp;gt; import('./SomeHeavyComponent')
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s it. Webpack would extract it into a separate JS bundle so your app assets would look somehow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main.a3e4.js &amp;lt;-- main bundle
0.ef23.js &amp;lt;-- 'SomeHeavyComponent' bundle

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The Issue
&lt;/h2&gt;

&lt;p&gt;So far so good. But now comes the time to deploy our app to production. You build your app assets and put them to some static web hosting.&lt;/p&gt;

&lt;p&gt;Users start using your app, perhaps installing it on their phone, if it is a PWA.Then you discover a bug in your app. You quickly fix it, rebuild the app and put the new assets online, replacing the old ones.&lt;/p&gt;

&lt;p&gt;And here comes the boom! You start getting exceptions of this kind popping up in Sentry (you do monitor your JS apps, right?!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChunkLoadError (error: (built/0.ef23)
Loading chunk 6 failed. (error: https://your.app/built/0.ef23.js)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What happened? Somebody had the previous version of your app running in the browser (or cached with a service-worker). When navigating to the route that required &lt;code&gt;SomeHeavyComponent&lt;/code&gt;, the app tried to load it and failed. Well of course, we removed those old assets and they are no longer available.&lt;/p&gt;

&lt;p&gt;Had we had assets always named the same way (e.g. &lt;code&gt;0.js&lt;/code&gt; instead of &lt;code&gt;0.ef23.js&lt;/code&gt;), we would have gotten a different exception, along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError __webpack_require__ (webpack/bootstrap)
Cannot read property 'call' of undefined

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That happens because &lt;code&gt;SomeHeavyComponent&lt;/code&gt; might have changed, and Webpack no longer finds what it expected to see in it.&lt;/p&gt;

&lt;p&gt;Let’s get this problem solved!&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1. Keep previous versions of assets
&lt;/h2&gt;

&lt;p&gt;The only no-compromise solution is to &lt;strong&gt;keep all ever deployed assets forever&lt;/strong&gt; (or at least for a long enough time). It would obviously help to prevent the aforementioned problem and keep the users happy.&lt;/p&gt;

&lt;p&gt;There is a small consideration of disk space, but the way bigger problem is that &lt;strong&gt;most deployment tools just don’t support such an approach&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, Vercel (ex. ZEIT) &lt;a href="https://github.com/zeit/now/discussions/4140"&gt;claims&lt;/a&gt; that it is not what their users would expect (your users never do code-splitting, huh?).&lt;/p&gt;

&lt;p&gt;On the contrary, AWS Amplify Console works correctly out of the box (though it performs considerably slower than Vercel both in terms of delivery and build times).&lt;/p&gt;

&lt;p&gt;I would love to gather more data on what deployment platforms support keeping previously deployed assets available, so &lt;strong&gt;please comment if you know how other platforms behave in this regard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can always build a custom deployment pipeline that would support keeping previously deployed assets, but in many cases it is just not worths the effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PROS&lt;/strong&gt; : the most reliable solution&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CONS&lt;/strong&gt; : not many platforms support it out of the box&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 2. Catch exceptions and force-reload app
&lt;/h2&gt;

&lt;p&gt;If we can’t afford to keep previous versions of assets deployed, we can at least catch those loading mistakes and force-reload the app. Since dynamic imports return just a Promise, it is very easy to do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SomeHeavyComponent = React.lazy(
  () =&amp;gt; import('./SomeHeavyComponent')
     .catch(e =&amp;gt; window.location.reload())
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Of course, your app should be able to self update its service worker on reload. It is actually rather tricky to do it and it deserves a dedicated article which I may write some day. For now read this Twitter thread:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So people have asked me to share how to make sure your PWA automatically updates itself, even on &lt;a href="https://twitter.com/hashtag/iOS?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#iOS&lt;/a&gt; 12 (which preserves the state of your app even when quitting it). Thread follows.&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev 🇷🇺 (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/1250009587866009601?ref_src=twsrc%5Etfw"&gt;April 14, 2020&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But this approach has one serious down side: if your app is stateful, it would be hard to keep the app’s state after force-update. E.g. imagine writing some Tweet and getting Twitter to force-reload on you, that would be some drama!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PROS&lt;/strong&gt; : works with all deployment platforms&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CONS&lt;/strong&gt; : horrible UX for stateful apps&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3. Pre-cache all JS bundles with a service worker
&lt;/h2&gt;

&lt;p&gt;Alternatively, another technique could be to pre-cache all JS bundles with a service worker on initial page load.&lt;/p&gt;

&lt;p&gt;This technique is very easy to implement with Workbox, in just one line of code with the help of &lt;code&gt;workbox-webpack-plugin&lt;/code&gt; Webpack Plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;precacheAndRoute(self.__WB_MANIFEST);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is usually a good idea to do prefetching in any case, the only consideration here is bandwidth. If your app is really large, are you sure your user would be happy you’d download all of its assets at once plus keep them in phone memory?&lt;/p&gt;

&lt;p&gt;But this technique is not 100% reliable in preventing the aforementioned exceptions, so it should still be combined with the previously described technique of catching dynamic import exceptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PROS&lt;/strong&gt; : prefetching makes sense in many cases anyways&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CONS&lt;/strong&gt; : does not conserve bandwidth&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I find it super strange that such a crucial topic of deploying web apps does not get enough attention. I sincerely hope that deployment platforms like Vercel will get their shit together and provide an option to keep previously deployed assets available. For the time being, some apps can get away with prefetching all JS chunks and reloading on dynamic exceptions.&lt;/p&gt;

&lt;p&gt;Please retweet if you found this article helpful!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How I shot myself in a foot while deploying an app with code splitting &lt;a href="https://t.co/qZ1d3xcBWk"&gt;https://t.co/qZ1d3xcBWk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev 🇷🇺 (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/1254013100380106755?ref_src=twsrc%5Etfw"&gt;April 25, 2020&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>coresplitting</category>
      <category>webpack</category>
      <category>react</category>
    </item>
    <item>
      <title>7 Steps From jQuery Plugins to React Development</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Wed, 15 Feb 2017 00:00:00 +0000</pubDate>
      <link>https://dev.to/dimaip/7-steps-from-jquery-plugins-to-react-development-4p61</link>
      <guid>https://dev.to/dimaip/7-steps-from-jquery-plugins-to-react-development-4p61</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note, this post is from 2017, it might feel slightly outdated, but I believe the main idea is still relevant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JavaScript single-page applications (SPAs) have made a huge leap in the last couple of years. We talk about them, we write them, they got all the attention and buzz you can think of. But even in 2017, not everything is a SPA. So what do you do when you need to add some interactive widget to a mostly static backend-driven website? For all those sliders, tabs, feedback forms and so on, is imperative jQuery fiddling still the best way to go?&lt;/p&gt;

&lt;p&gt;Here I’m offering you an overview of the architectural steps between a jQuery plugin and a full-fledged SPA. At each step you decide if pros outweigh the cons and move on. The trick is to stop early, bringing in as many abstractions as you need and not more.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1. You might not need jQuery
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OxEJ2I4t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975317/5cdfd962-f397-11e6-91e3-867508639da9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OxEJ2I4t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975317/5cdfd962-f397-11e6-91e3-867508639da9.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing we should do is to remember that jQuery is not a synonym of JavaScript (anymore), and that it’s perfectly possible to go without it. There’s a great website to prove the point: &lt;a href="http://youmightnotneedjquery.com/"&gt;youmightnotneedjquery.com&lt;/a&gt;Ah, but what about jQuery plugins, surely we can’t live without them?! Well, no, actually there’s almost certainly a vanilla JS plugin for every need, look up here: &lt;a href="http://youmightnotneedjqueryplugins.com/"&gt;youmightnotneedjqueryplugins.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shave off tens of KB of JS code. And no, it’s not only about download time, it’s also about JS parsing time and execution time, those actually do matter on older mobile devices&lt;/li&gt;
&lt;li&gt;Stay closer to the DOM and browser APIs. This will make you a better JavaScript developer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More verbose, low-level code. You loose familiar abstractions&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 2. Switch to ES6
&lt;/h1&gt;

&lt;p&gt;ES6 is nice, it has modernised JavaScript to look as fresh as those other hipster languages like Python. Arrow functions, destructuring, smart variable declarations etc. Don’t get too excited about ES6 classes though, they are still a wrapper over JS’ prototype-based inheritance, so make sure you understand how they work before using them!Don’t worry about browser support, as most of ES6 is traspileable to ES5 by Babel. But yes, that would require an extra build step.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More expressive language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extra build step&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 3. Use ES6 modules
&lt;/h1&gt;

&lt;p&gt;What’s even better than new ES6 language constructs is the ability to break your code apart into modules that can require each other. In addition to that, you can import 3rd party code from npm. You can use Webpack to bundle your modules into a single file. Use UMD format to be able to consume the resulting bundle both from the browser or as a CommonJS module from Node or from another Webpack app.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more adding extra dependencies via script tags into global scope by hand&lt;/li&gt;
&lt;li&gt;Your dependencies won’t collide in the global scope&lt;/li&gt;
&lt;li&gt;Modular code is easier to manage and reuse, no more of those mile-long js files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need to configure Webpack&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 4. View as a function of the state
&lt;/h1&gt;

&lt;p&gt;Now that we have modernised JavaScript a little, let’s look at architectural patterns of how to use it.&lt;/p&gt;

&lt;p&gt;The most important thing React taught us is how cool it is to have the view as a function of app’s state. This way we have just one path to both create and update the view: the render function. Will this be useful for simple JS widgets? Hell yes! No more getting a certain DOM node by some attribute from a soap of html tags, and imperatively modifying it! Let me give you an example.&lt;/p&gt;

&lt;p&gt;To start with, let’s define a little helper function that would help us to create DOM nodes. Its API would look like this: &lt;code&gt;el('h1', {class: 'headline'}, 'I'm a header')&lt;/code&gt; that would render such DOM node: &lt;code&gt;&amp;lt;h1 class="headline"&amp;gt;I'm a header&amp;lt;/h1&amp;gt;&lt;/code&gt;. My very naive implementation looks like &lt;a href="https://github.com/psmb/typo-reporter/blob/da7ffc6ec6b6d968d72cc4e2dbfa6a5229a2c3ff/src/el.js"&gt;this&lt;/a&gt;, but I’m sure you can do better in a couple of minutes.&lt;/p&gt;

&lt;p&gt;Now that we have such helper at hand, we can nicely render some DOM nodes as a function of our state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var rootNode = document.createElement('div');
document.body.appendChild(rootNode);

var state = {
  counter: 0
};

function setState(newState) {
  state = Object.assign({}, state, newState);
  // remount the whole node tree on every change
  mount();
}
function increment() {
  setState({counter: state.counter + 1});
}
function render(state) {
  return el('div', {}, [
    el('h1', {}, 'Demo'),
    el('button', {onClick: increment}),
    el('div', {class: state.counter &amp;gt; 10 ? 'plenty' : 'a-few'}, state.counter)
  ]);
}
function mount() {
  // sorry, delete children properly IRL
  rootNode.innerHTML = '';
  var renderedNodeTree = render(state);
  rootNode.appendChild(renderedNodeTree);
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again, this is super naive, but we got what we wanted: on every update to the state we re-render the whole view. The cool part of this is that our view now is completely declarative and has the full power of JavaScript at our disposal: conditions, loops, event handlers, anything that comes to your mind.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View as a function of the state. Declarative rendering. The same code path for updates and rendering&lt;/li&gt;
&lt;li&gt;No dependencies! No extra code download and parsing!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Will eventually become slow. But this is not really something to worry about from the start, don’t do premature optimisations before you hit the problem!&lt;/li&gt;
&lt;li&gt;Elements loose state on every update. That’s a major bummer for input fields! We’ve got to do something about it!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 5. Virtual DOM
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FvivNfS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975671/9e4b3d32-f398-11e6-9118-c1d05bce3774.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FvivNfS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975671/9e4b3d32-f398-11e6-9118-c1d05bce3774.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve been hit by one of the two cons of the previous solution, it’s time to move on. All of our problems come from full re-rendering on every update, so to solve them we need a way update the DOM elements without re-creating them if not necessary. And that’s what virtual DOM is for.Virtual DOM keeps a virtual representation of DOM nodes in addition to DOM nodes themselves. When asked to update the state, it renders the new virtual elements tree and then decides which elements in the DOM can be updated, and which need to be thrown away and recreated. It’s fun to write your own virtual DOM implementation, but we are not going to do it right now. Instead, we can take one of the existing ones, e.g. &lt;a href="http://maquettejs.org"&gt;maquettejs.org&lt;/a&gt;. It does its job in return for 3KB of extra download size.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View as a function of the state, but now it’s going to perform well with tonnes of nodes and more importantly it will keep the state of DOM elements on the update.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The external dependency of 3KB added. You need to decide if you can live with that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 6. Componentization
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f4LvmYC6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975738/c2e929d8-f398-11e6-85e1-774f1db366bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f4LvmYC6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975738/c2e929d8-f398-11e6-85e1-774f1db366bf.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I guess we got all we needed to make a replacement for small jQuery widgets. But if you widget would begin to grow, you might want to break it up into smaller reusable components. That’s exactly the idea behind React, so if you have gone this far, it might make sense to refactor your widget to a React component. Depending on your target browser support, you might get away with something way more lightweight than React itself by using one of its clones, e.g. Preact. The core of Preact weights 3KB, and if you add another 10KB on top you would get compatibility with existing React ecosystem via the preact-compat shim.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break your monolith code into smaller reusable components with a well-designed API, props validation and many bells and whistles.&lt;/li&gt;
&lt;li&gt;Take advantage of enormous React ecosystem: borrow components, use devtools, performance optimisation tools and what not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the state gets dispersed among different components, it becomes harder to share and synchronise&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 7. Shared state management
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2wmddDXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975802/f92ca86c-f398-11e6-9400-151a6e809b0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2wmddDXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.githubusercontent.com/assets/837032/22975802/f92ca86c-f398-11e6-9400-151a6e809b0a.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So once we broke our app apart into small reusable pieces, it becomes harder for components to synchronise shared state. Usually in React the pattern is to put the shared state in the top most component among those that are going to need it and pass the state down via props. Those long chains of props can become a nightmare to manage, so that’s multiple state management solutions for React appeared. The most simple one of them is Redux. The idea is really trivial: you extract the whole of your app’s state to the top of your app, put it into context and access it via a helper function from the context in your child components. You mutate the state via dispatching actions and processing them via pure functions called reducers.Redux would cost you about 5KB, but I guess you can fit the same logic even in fewer lines of code. The real Preact+Redux app may easily fit under 10KB of JS.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State updates are declarative, allowing to easily trace updates in your code (and bugs)&lt;/li&gt;
&lt;li&gt;Redux itself has a nice ecosystem of plugins and devtools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yet one more dependency, one more abstraction to master&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step X. Beyond JavaScript
&lt;/h1&gt;

&lt;p&gt;I won’t write in detail about it here, but just so you know, there is a world of web development beyound JavaScript. &lt;a href="http://elm-lang.org"&gt;Elm&lt;/a&gt;, the inspirer of Redux, and Facebook’s next toy, &lt;a href="https://facebook.github.io/reason/"&gt;ReasonML&lt;/a&gt; are among a few. Static type checking, immutabillity and functional programming paradigms are among the things to expect in that outer space. But we’ve gone too far from our comfy jQuery shire, haven’t we?&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Remember, every abstraction, every dependency comes at a cost. Start from the basics, learn the low-level APIs and gradually climb up the abstraction steps.&lt;/p&gt;

&lt;p&gt;Recently I had to create a simple &lt;a href="https://github.com/psmb/typo-reporter"&gt;“report a typo on a page”&lt;/a&gt; widget. I had to stop at Step 4, as adding a 3KB of virtual DOM on top of 2KB of my own code was just not worth it.&lt;/p&gt;

&lt;p&gt;Even if you are developing a small 2KB widget and not a Facebook-like SPA, your code doesn’t have to suck and you should not treat the decisions you take lightly.&lt;/p&gt;

&lt;p&gt;P.S.: If you’ve gone this far, watch &lt;a href="https://www.youtube.com/watch?v=mVVNJKv9esE"&gt;this talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vdom</category>
      <category>react</category>
      <category>redux</category>
    </item>
    <item>
      <title>Neos CMS Goes for a Full UI Rewrite with React and Redux</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Sun, 13 Mar 2016 00:00:00 +0000</pubDate>
      <link>https://dev.to/dimaip/neos-cms-goes-for-a-full-ui-rewrite-with-react-and-redux-35d0</link>
      <guid>https://dev.to/dimaip/neos-cms-goes-for-a-full-ui-rewrite-with-react-and-redux-35d0</guid>
      <description>&lt;p&gt;&lt;a href="https://neos.io"&gt;Neos&lt;/a&gt; is a modern content management system, known for its flexibility and ease of use. Behind the project we have 19 active team members spread across 3 agile teams, and 85 contributors to the project in total, and if you ever visit a Neos event or a code sprint, you will soon find out that we are more like a family, than a corporation. In fact Neos is a rare case when large open source project is not being backed by any commercial company.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f3Zd44wy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://dimaip.github.io/assets/neos-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f3Zd44wy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://dimaip.github.io/assets/neos-ui.png" alt="Current Neos UI"&gt;&lt;/a&gt;&lt;em&gt;Current UI of Neos&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But don’t worry, I won’t spend the rest of the article worshiping our product or describing all of its features (even though it totally deserves it).&lt;/p&gt;

&lt;p&gt;I have some other story to tell you, namely &lt;strong&gt;how we approached the rewrite of Neos UI with React, Redux, and the rest of modern and shiny JS stack of 2016&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The web is full of Redux tutorials and great learning materials, but &lt;strong&gt;it is much harder to find real open source projects of our scale to be written with modern JS stack&lt;/strong&gt; (oh, I have overlooked that &lt;a href="https://github.com/Automattic/wp-calypso"&gt;Calypso&lt;/a&gt; also uses Redux, thought it had flux). In this write-up I will try to do two things at once: give you a brief walkthrough of our codebase, alongside some theory behind the parts of the stack that we have chosen. Be warned though, &lt;strong&gt;we are currently in the very beginning of the rewrite, so the code that you will see is pretty much WORK IN PROGRESS&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision
&lt;/h2&gt;

&lt;p&gt;Undertaking a complete UI rewrite was not an easy decision to make. You see, by now we have one of the most intuitive UIs in the content management world, mostly stable and complete. It was written in EmberJS 1.x and for its time was pretty neatly built. But with time &lt;strong&gt;things started to get out of hand&lt;/strong&gt; , the complexity of it multiplied and development of new interface features started to cost more and more. Touching one piece of it could backfire in other least places, we had no interface tests so refactoring it was not easy too, and the whole thing just didn’t feel predictable and fun to work with any longer. The last drop was a difficulty of upgrading it to Ember 2.x, too many things had changed during the time and we wanted to rethink multiple things anyways.&lt;/p&gt;

&lt;p&gt;To evaluate the decision, two amazing core team developers, &lt;a href="https://twitter.com/WilhelmBehncke"&gt;Wilhelm Behncke&lt;/a&gt; and &lt;a href="https://twitter.com/inkdpixels"&gt;Tyll Weiß&lt;/a&gt;, had spent a few days under cover to built a proof-of-concept prototype, which was able to convince the rest of the team that we should go for it.&lt;/p&gt;

&lt;p&gt;Last week we had a code sprint in Dresden where more developers joined the rewrite effort, and now we have 6 people (&lt;a href="https://twitter.com/WilhelmBehncke"&gt;@WilhelmBehncke&lt;/a&gt;, &lt;a href="https://twitter.com/inkdpixels"&gt;@inkdpixels&lt;/a&gt;, &lt;a href="https://twitter.com/DerGerDner"&gt;@DerGerDner&lt;/a&gt;, &lt;a href="https://twitter.com/skurfuerst"&gt;@skurfuerst&lt;/a&gt;, &lt;a href="https://twitter.com/MarkusGoldbeck"&gt;@MarkusGoldbeck&lt;/a&gt; and &lt;a href="https://twitter.com/dimaip"&gt;me&lt;/a&gt;) actively working on it and about 5 more feeling intrigued and wanting to join our efforts too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lets Pretend This is a Tutorial…
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lhl4Dt_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://dimaip.github.io/assets/modal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lhl4Dt_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://dimaip.github.io/assets/modal.png" alt="The AddNodeModal dialog that we are going to implement"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;The AddNodeModal dialog that we are going to implement&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I will try to make code walkthrough look more like a tutorial. As a kind of tutorial assignment, I will be using the feature on which I was working during last week. &lt;strong&gt;Our task would be to create a dialog for creating nodes&lt;/strong&gt; (i.e. pages or content elements in Neos), that will provide you with a choice of all possible page types that are allowed to be created in the given place, and that would finally send the command to the server API, creating a new node of the chosen type. Let’s call it &lt;code&gt;AddNodeModal&lt;/code&gt;.&lt;/p&gt;


  &lt;p&gt;Warning! This walkthrough presupposes you know some React and Redux essentials and will not help you getting started from zero ground.&lt;/p&gt;


&lt;h3&gt;
  
  
  React Components
&lt;/h3&gt;


  &lt;p&gt;All of our React components are divided into two types: &lt;strong&gt;presentational components&lt;/strong&gt; and &lt;strong&gt;container components&lt;/strong&gt;. Presentational components are small reusable pieces of the interface like Buttons, Modals, Icons or even Trees.
Presentational components are encapsulated into container components, that provide more dedicated app logic, that is generally not meant to be reusable. Containers may connect to app state via &lt;a href="https://github.com/reactjs/react-redux"&gt;react-redux&lt;/a&gt; @connect decorator. Usually, they don’t render data directly, but pass it down to presentational components.&lt;/p&gt;


&lt;p&gt;So to render our AddNodeModal we would need a couple of components: Dialog, Button, Icon, Headline and Grid (to nicely layout buttons into multiple rows). Luckily all of the needed components were already created by somebody else, so we can just play a bit of Lego composing our piece of UI out of existing components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PackageFactory/PackageFactory.Guevara/blob/9e06fdd96c1627a262c42b8405c1f128de972fa4/Resources/Private/JavaScript/Host/Containers/AddNodeModal/index.js"&gt;AddNodeModal container component&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  State
&lt;/h3&gt;


  &lt;p&gt;The main reason for the switch to this new stack was the desire to give more predictability and integrity to the UI. You see, our case is slightly complicated by the fact that we have the same data distributed across multiple places: the navigation tree, inline editing etc. Before we did not have a unified data model, and all of this modules functioned independently, carefully glued together by some state syncing code. Yes, that was kind of a nightmare.
That is why here from the start we for having all data clearly normalised and stored in the state. But that includes not only the content data, but also the state of the UI itself: all trees, panels, user preferences and so on now have a dedicated place in the application state.&lt;/p&gt;


&lt;p&gt;For our AddNodeModal we would need two things stored in the state: reference node, relative to which the new node would be created, and an insertion mode (inside, before, after). Let’s store these two values at &lt;code&gt;UI.AddNodeModal.referenceNode&lt;/code&gt; and &lt;code&gt;UI.AddNodeModal.mode&lt;/code&gt; inside the state.Our dialog will show up when we put some node into &lt;code&gt;referenceNode&lt;/code&gt;, and disappear once we clear that value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reducers
&lt;/h3&gt;


  &lt;p&gt;The idea behind Redux is to join app state into one single state tree, and manipulate it via a side-effect free function, that takes previous state and returns the new state, based on an action that describes the manipulations that we want to apply to it. The reducer may be split into multiple reducers, for the sake of modularity. The state itself is kept in the store and not in the reducer, the reducer is just a simple function, remember?
Actions that manipulate the state may be likened to C (Command) in CQRS (Command-Query Responsibility Segregation). You may record and later replay actions to get a kind of Event Sourcing.&lt;/p&gt;

  &lt;p&gt;To manipulate state efficiently we use our own library called plow-js, which has that scent of functional programming to it. Check it out, it is really cool!
You might have noticed that we do not use the usual switch statement block in the reducers, and describe them via map handlers instead. Nothing fancy about it, just our taste preference.&lt;/p&gt;


&lt;p&gt;So to manipulate the state we would need to create a reducer handling two actions: OPEN and CLOSE. OPEN would set &lt;code&gt;referenceNode&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt; to provided values, CLOSE would clear the value of &lt;code&gt;referenceNode&lt;/code&gt;, closing the dialog. Nothing difficult so far, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PackageFactory/PackageFactory.Guevara/blob/9e06fdd96c1627a262c42b8405c1f128de972fa4/Resources/Private/JavaScript/Host/Redux/UI/AddNodeModal/index.js"&gt;UI.AddNodeModal reducer&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Selectors
&lt;/h3&gt;


  &lt;p&gt;It is a general recommendation to keep data in the state normalised, just like in a relational database. This way it is easier to manipulate it, without worrying that some parts of data get out of sync. But often you need to have data gathered from multiple places in the state, and that is when selectors come to the rescue. Selectors are functions that take the state and return the needed part of it. We use a very nice selector library called reselect. It helps you to create more complex selectors by combining simple selectors and also helps to make them more performant by automatic memoization.&lt;/p&gt;


&lt;p&gt;We had no difficulty in getting &lt;code&gt;referenceNode&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt; from the state, but now we have a bigger challenge coming. We need to get a list of allowed nodetypes for the reference node and mode. For that, we need to combine data from multiple places across the state: nodeType data, nodeType constraints, referenceNode, mode, parent and grandparent node to given referenceNode and so on. But that’s not all, now we need to group allowed node types and sort them in the right order. You see, quite a complex logic that is comprised of multiple simple selectors, each of which needs independent testing and performance optimization.&lt;/p&gt;

&lt;p&gt;So we got the list of allowed node types, nicely grouped and sorted. Now it is time to add some behavior to them that would actually create nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PackageFactory/PackageFactory.Guevara/blob/9e06fdd96c1627a262c42b8405c1f128de972fa4/Resources/Private/JavaScript/Host/Selectors/CR/Constraints/index.js"&gt;Constraints selectors&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Side-effects
&lt;/h3&gt;


  &lt;p&gt;Redux architecture mainly focuses on the client state and does not consider effects, such as asynchronous requests to the server. There is no consensus on the best practices here, but for our case, we chose &lt;a href="https://github.com/yelouafi/redux-saga"&gt;redux-saga&lt;/a&gt; library. It uses generators and looks really fancy at first sight, but we found most freedom in using it. Basically, it watches for one of your actions to happen and then executes some code, which may be asynchronous and as effect trigger other actions.&lt;/p&gt;


&lt;p&gt;We have a fancy new server API to describe the desired actions we want to perform on the server. Any action we want to take is encoded as a change object, e.g. &lt;code&gt;Create&lt;/code&gt;, &lt;code&gt;Move&lt;/code&gt;, &lt;code&gt;Property&lt;/code&gt; and so on. For our task of creating nodes, we need to choose between actions &lt;code&gt;Create&lt;/code&gt;, &lt;code&gt;CreateAfter&lt;/code&gt; and &lt;code&gt;CreateBefore&lt;/code&gt; actions based on &lt;code&gt;mode&lt;/code&gt; state. After we construct correct change object, we need to send it as a parameter to &lt;code&gt;Changes.add&lt;/code&gt; action creator, and it would be transparently picked up by the changes saga and sent to the correct API endpoint on the server. On success saga fires a &lt;code&gt;FINISH&lt;/code&gt; action, on failure &lt;code&gt;FAIL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PackageFactory/PackageFactory.Guevara/blob/9e06fdd96c1627a262c42b8405c1f128de972fa4/Resources/Private/JavaScript/Host/Redux/Sagas/Changes/index.js"&gt;Changes saga&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;It should go without saying that we must cover at least critical parts of our codebase with tests. In the given task we have to test reducers, selectors, component itself and probably sagas too. The most critical parts are reducers and selectors, and they are the easiest to test, after all, they are just a pure functions: pass some input and expect some output!To write assertions in a behavioural style we use chai. To run them in real browsers we use Karma. For acceptance tests we use Selenium.I have yet to finish writing acceptance tests for this feature, so I will update this article once I have some code to show.&lt;/p&gt;

&lt;p&gt;So I hope this gives you some insights into how we apply core React &amp;amp; Redux architecture principles to our app. Feel free to browse the rest of the codebase, I am sure you will find a lot of interesting stuff there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Neos Family
&lt;/h2&gt;

&lt;p&gt;If you stayed with me this far, you may be interested in the project itself, and not only the technology we use. As some very clever people &lt;a href="https://blog.engineyard.com/2014/open-source-software-contribution"&gt;put it to words&lt;/a&gt;, &lt;strong&gt;open source product is nothing without people behind it&lt;/strong&gt;. And we are truly blessed here: we are not just some nerds scattered all around the globe, neither are we employees paid by some businesses to do coding. We are a community of friends, almost a family. &lt;a href="http://dimaip.github.io/2014/10/05/the-code-sprint/"&gt;We organise code sprints&lt;/a&gt; regularly to not only code together but as well share all the good things we are given in this life, be it a walk across the Elba river in the night or a game of laser tag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So if you like our code, come join us!&lt;/strong&gt; We have a lot of code to write together, but, in the end, it does not have to stop there, let’s be friends!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.neos.io/join/contribute.html"&gt;Join the project!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please RT this stuff&lt;/strong&gt; , if you have friends who may be interested in this as well:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://twitter.com/neoscms"&gt;@neoscms&lt;/a&gt; goes for full &lt;a href="https://twitter.com/hashtag/reactjs?src=hash"&gt;#reactjs&lt;/a&gt;/&lt;a href="https://twitter.com/hashtag/redux?src=hash"&gt;#redux&lt;/a&gt; UI rewrite, and you may take something away from it too! &lt;a href="https://t.co/UiSEW7tH5e"&gt;https://t.co/UiSEW7tH5e&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/709265663328702464"&gt;March 14, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;And now for some tweet-media to prove all of this is real! =)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;React.js Workshop sponsored by &lt;a href="https://twitter.com/sitegeist_de"&gt;@sitegeist_de&lt;/a&gt; at the &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; code sprint. Go, &lt;a href="https://twitter.com/inkdpixels"&gt;@inkdpixels&lt;/a&gt;! &lt;a href="https://t.co/NSKWdu3BeD"&gt;pic.twitter.com/NSKWdu3BeD&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Wilhelm Behncke (@WilhelmBehncke) &lt;a href="https://twitter.com/WilhelmBehncke/status/706817768499318784"&gt;March 7, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Florian took the sprint participants on a cool tour around Dresden - coding and education go very well together! ~tg &lt;a href="https://t.co/rTyvlUu715"&gt;pic.twitter.com/rTyvlUu715&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— The Neos Project (@neoscms) &lt;a href="https://twitter.com/neoscms/status/707342534704558080"&gt;March 8, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;The early birds are already at work! It's a pleasure having all of you here in Dresden :D &lt;a href="https://twitter.com/hashtag/NeosCMS?src=hash"&gt;#NeosCMS&lt;/a&gt; &lt;a href="https://t.co/HAoq26GebQ"&gt;pic.twitter.com/HAoq26GebQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sandstorm (@sandstormmedia) &lt;a href="https://twitter.com/sandstormmedia/status/707484157056667648"&gt;March 9, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Day 3 on the &lt;a href="https://twitter.com/hashtag/NeosCMS?src=hash"&gt;#NeosCMS&lt;/a&gt; codesprint. Awesome stuff with awesome people! Let's get it on! :) &lt;a href="https://twitter.com/hashtag/oss?src=hash"&gt;#oss&lt;/a&gt; &lt;a href="https://t.co/r1erZYQsV8"&gt;pic.twitter.com/r1erZYQsV8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— inkdpixels (@inkdpixels) &lt;a href="https://twitter.com/inkdpixels/status/707506151030693888"&gt;March 9, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Exciting to see so many (new) developers working on the new &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; UI technology stack ... it's taking shape! &lt;a href="https://t.co/9POKUXrDPT"&gt;pic.twitter.com/9POKUXrDPT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Robert Lemke (@robertlemke) &lt;a href="https://twitter.com/robertlemke/status/707519911388643328"&gt;March 9, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Sunny hacking in a nice location sponsored and organized by &lt;a href="https://twitter.com/sandstormmedia"&gt;@sandstormmedia&lt;/a&gt; - thank you guys! &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; &lt;a href="https://t.co/1U16FlSqyi"&gt;pic.twitter.com/1U16FlSqyi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Robert Lemke (@robertlemke) &lt;a href="https://twitter.com/robertlemke/status/707570179564249088"&gt;March 9, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Had an awesome evening playing Lasertag with the team! This &lt;a href="https://twitter.com/hashtag/NeosCMS?src=hash"&gt;#NeosCMS&lt;/a&gt; sprint rocks! ~tg &lt;a href="https://t.co/0avtjTSx0z"&gt;pic.twitter.com/0avtjTSx0z&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— The Neos Project (@neoscms) &lt;a href="https://twitter.com/neoscms/status/707691612688814081"&gt;March 9, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;The retro is in full swing, with three team members joining remotely. Pretty inspiring for me so far. &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; &lt;a href="https://t.co/nHjOtrWgvW"&gt;pic.twitter.com/nHjOtrWgvW&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Robert Lemke (@robertlemke) &lt;a href="https://twitter.com/robertlemke/status/707870782651817984"&gt;March 10, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Lots of good things happened since our last retro &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; &lt;a href="https://t.co/GFPnVFPjZK"&gt;pic.twitter.com/GFPnVFPjZK&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Robert Lemke (@robertlemke) &lt;a href="https://twitter.com/robertlemke/status/707871782481567745"&gt;March 10, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Very nice &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; retrospective here - I like it a lot! &lt;a href="https://t.co/Nh4BRrA8lr"&gt;pic.twitter.com/Nh4BRrA8lr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sebastian Kurfürst (&lt;a class="comment-mentioned-user" href="https://dev.to/skurfuerst"&gt;@skurfuerst&lt;/a&gt;
) &lt;a href="https://twitter.com/skurfuerst/status/707880951083352064"&gt;March 10, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://twitter.com/sandstorm_tobi"&gt;@sandstorm_tobi&lt;/a&gt; sharing our thoughts on &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; vision! &lt;a href="https://twitter.com/hashtag/codesprint?src=hash"&gt;#codesprint&lt;/a&gt; &lt;a href="https://t.co/BhsUMIXtoU"&gt;pic.twitter.com/BhsUMIXtoU&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sebastian Kurfürst (&lt;a class="comment-mentioned-user" href="https://dev.to/skurfuerst"&gt;@skurfuerst&lt;/a&gt;
) &lt;a href="https://twitter.com/skurfuerst/status/707953618603941889"&gt;March 10, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks everybody who was at the &lt;a href="https://twitter.com/hashtag/neoscms?src=hash"&gt;#neoscms&lt;/a&gt; sprint, have a safe trip home. &lt;a href="https://twitter.com/hashtag/neosFamily?src=hash"&gt;#neosFamily&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/spirit?src=hash"&gt;#spirit&lt;/a&gt; &lt;a href="https://t.co/pWKp8OeHjV"&gt;pic.twitter.com/pWKp8OeHjV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sebastian Kurfürst (&lt;a class="comment-mentioned-user" href="https://dev.to/skurfuerst"&gt;@skurfuerst&lt;/a&gt;
) &lt;a href="https://twitter.com/skurfuerst/status/708277258042130432"&gt;March 11, 2016&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>neos</category>
      <category>react</category>
      <category>redux</category>
    </item>
    <item>
      <title>My adventures at Yandex Interface Development School</title>
      <dc:creator>Dmitri Pisarev 🇷🇺</dc:creator>
      <pubDate>Tue, 03 Nov 2015 00:00:00 +0000</pubDate>
      <link>https://dev.to/dimaip/my-adventures-at-yandex-interface-development-school-3fm0</link>
      <guid>https://dev.to/dimaip/my-adventures-at-yandex-interface-development-school-3fm0</guid>
      <description>&lt;p&gt;It’s been a while since I had last written to my blog, and there’s a good reason: for last three month I was caught in a crazy adventure. Just for fun I tried to enroll to &lt;a href="https://academy.yandex.ru/events/shri/"&gt;Yandex Interface Development School&lt;/a&gt;. To those who don’t know, Yandex is a Russian search engine giant, largest IT company in Europe, in other words these guys have quite some experience in frontend development. Every year they organize a free intensive frontend coding course with strong team work and practical side. Their motivation to host such coding schools is to improve quality of ecosystem of Russian internet by training better developers, which is a very noble goal indeed.&lt;/p&gt;

&lt;p&gt;I had heard about this school before, but didn’t seriously think I would be able to get through the competition. One evening I roughly sketched the solutions to three test tasks (&lt;a href="https://gist.github.com/dimaip/28e478f7c2783405d405"&gt;fix some broken JS code&lt;/a&gt;, &lt;a href="https://gist.github.com/dimaip/e8acf0b1b87c3083bbe9"&gt;write audio player with Web Audio API&lt;/a&gt; and make a &lt;a href="https://gist.github.com/dimaip/c1c4bbb99532fd6c1bce"&gt;CSS flight table&lt;/a&gt;) and a month later, to my utter surprise, I got a positive response. In total, there were 36 of us who made it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;начали лекции в &lt;a href="https://twitter.com/hashtag/%D0%A8%D0%A0%D0%98?src=hash"&gt;#ШРИ&lt;/a&gt; (@ 5. Синий кит (Яндекс)) &lt;a href="https://t.co/9huj5otKQ2"&gt;https://t.co/9huj5otKQ2&lt;/a&gt; &lt;a href="http://t.co/KlY55N6gyS"&gt;pic.twitter.com/KlY55N6gyS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Sergey Berezhnoy (@veged) &lt;a href="https://twitter.com/veged/status/641635953652723713"&gt;September 9, 2015&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The studying process consisted of two parts: lectures 2-3 times per week by top Yandex developers and practical team work on 3 hackathons.The first lecture was about CSS. We were asked to raise hands if we think we know CSS for 8 out of 10. Most of us did raise our hands, and then we were shown that in fact we don’t know nearly as much about CSS as we should. We were assigned to watch a lot of video lectures at home about CSS, and later got more lectures about how browser internals work and other in-depth stuff.Lectures were spot on and very up to date: we had lectures about CSS, SVG, WebGL, browsers, testing, React, Redux (!), nodejs, git, shell and a lot more stuff.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Team &lt;a href="https://twitter.com/hashtag/ShrimpJS?src=hash"&gt;#ShrimpJS&lt;/a&gt; in action &lt;a href="https://twitter.com/yandex"&gt;@yandex&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/%D0%A8%D0%A0%D0%98?src=hash"&gt;#ШРИ&lt;/a&gt; &lt;a href="http://t.co/gyk3GHAyqB"&gt;pic.twitter.com/gyk3GHAyqB&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/641706622016974850"&gt;September 9, 2015&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the biggest fun was team work. We split into 6 teams of 6 members, and each team had to create a chat app. Our team chose the most hipster stack we could lay our hands on (React, Redux, webpack, node, express, mongo) and we began to rock. We documented all of our project in English, so if you need an example of full-featured chat app written in Redux &lt;a href="https://github.com/shri-2015-org/shrimp"&gt;feel free to take a look&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was lucky to get on a very skilled team and learned a lot of new stuff during the project. The hard part was setting up proper workflow and branching strategy in a hackathon pace, but I think we managed just fine. Workflow and deployment lifecycle is probably the single thing that differentates small freelance work from stuff that is going on in Yandex: CI and CD look really scary and complex at their scale.&lt;br&gt;&lt;br&gt;
Each team could pick two mentors from among Yandex developers. We chose &lt;a href="https://twitter.com/Andre_487"&gt;Andre&lt;/a&gt; and &lt;a href="https://github.com/L0stSoul"&gt;Denis&lt;/a&gt;, two skilled developers doing search results page development at Yandex. Guys put a lot of energy into us, not really intervening our work, but guarding us from critical mistakes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Graduation day at Yandex interface developer school &lt;a href="https://twitter.com/hashtag/%D0%A8%D0%A0%D0%98?src=hash"&gt;#ШРИ&lt;/a&gt; &lt;a href="https://t.co/guvnl7p9wu"&gt;pic.twitter.com/guvnl7p9wu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/661686732551806976"&gt;November 3, 2015&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Participation in this coding school has definitely given me a lot more than I expected. I got inspired by working with Redux that I had &lt;a href="https://github.com/dimaip/encultN"&gt;rewritten one of my work projects with it&lt;/a&gt; – immediate gain. Also I hope to be now more prepared for contributing to frontend of &lt;a href="https://neos.io"&gt;Neos CMS&lt;/a&gt; project.But the most valuable takeaway of all is all those 36 amazing guys and girls that I met. Frankly I’d never met skilled developers from Russia before (I mostly work with developers from Europe), and here I met so many at once! This has resurrected hope in me to see a strong internet frontend community in Russia, and we are not that far from that day.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As an ending to our great adventure &lt;a href="https://twitter.com/yandex"&gt;@yandex&lt;/a&gt;, we were given a late night tour around all of their headquarters &lt;a href="https://twitter.com/hashtag/%D0%A8%D0%A0%D0%98?src=hash"&gt;#ШРИ&lt;/a&gt; &lt;a href="https://t.co/NQNJW0dDFZ"&gt;pic.twitter.com/NQNJW0dDFZ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Dmitri Pisarev (&lt;a class="comment-mentioned-user" href="https://dev.to/dimaip"&gt;@dimaip&lt;/a&gt;
) &lt;a href="https://twitter.com/dimaip/status/661813961243934721"&gt;November 4, 2015&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Late night after the graduation party we were offered a guided tour around all of the Yandex headquarters. They provide 4000 workplaces only in Moscow, so it took quite some time to walk around all the floors. These quys make quite some effort to make a developers feel like at home, and indeed many people stay working up until late night or even go to work on holidays… It would have been tempting for me to work at such place had not my heart already been taking by my current job at &lt;a href="http://sfi.ru"&gt;St Philaret’s Orthodox Christian Institute&lt;/a&gt;. We have very similar attitude towards work here, maybe that’s why I’m at my workplace right now on a national holiday :)&lt;/p&gt;

</description>
      <category>yandex</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
