<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Nishu Goel on Medium]]></title>
        <description><![CDATA[Stories by Nishu Goel on Medium]]></description>
        <link>https://medium.com/@nishugoel?source=rss-68bccf9d3a63------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*aOUpe3AFoaTcWWuHNMp1nw.png</url>
            <title>Stories by Nishu Goel on Medium</title>
            <link>https://medium.com/@nishugoel?source=rss-68bccf9d3a63------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 13:59:54 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@nishugoel/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[The ü/ü Conundrum]]></title>
            <link>https://nishugoel.medium.com/the-%C3%BC-%C3%BC-conundrum-1b628e8cf36a?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/1b628e8cf36a</guid>
            <category><![CDATA[chrome]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[encoding]]></category>
            <category><![CDATA[life]]></category>
            <category><![CDATA[diacritics]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Sun, 11 Feb 2024 21:35:26 GMT</pubDate>
            <atom:updated>2024-02-12T00:43:52.712Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sfnYHneEMLv7VUk9" /></figure><p>I implemented search and filtering for entities on our product at epilot. The users were heavily using the feature, however, a unique issue surfaced: difficulties in filtering file names containing diacritical marks like umlauts (Äpfel, über, schön, etc.).</p><p>Intrigued, I delved into the logic to investigate. Initially, everything seemed in order. For instance, a file named “blöb” was saved precisely as “blöb” — no bizarre encoding alterations were apparent.</p><p>Can you spot any difference between “blöb” and “blöb”?</p><p>This time instead of typing the word in the search bar, I copied it from the uploaded and saved filename, and and voilà, the search functioned perfectly! 🤯. This definitely meant the file name was subtly altered upon upload, hinting at a background encoding transformation.</p><p>So, as anyone would, I employed the power of encodeURIComponent to see for myself what really was different.</p><p>There it was! The different types of encoding forms that led to this. Now you might be wondering (<em>I was wondering too!</em>):</p><blockquote>As if worrying about encoding wasn’t enough, now we’ve got to stress over its different types too 🤦‍♀ and wait, its 2024, and we are still grappling with Unicode character encoding problems?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/536/1*NViLUmi4Sx6mPrMygS1zuQ.png" /></figure><p>The culprits in this case were these two contrasting encoding types: <strong>%C3%BC</strong> and <strong>%CC%88</strong>, leading to the search malfunction. This is a well-known issue with NFD/NFC encoding forms.</p><p>There is a detailed explanation <a href="https://unicode.org/reports/tr15/#Norm_Forms">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/974/1*0kVUt4jpUmk_aS0qk0riPA.png" /></figure><p><em>Now imagine explaining this technical jargon that to a user! </em>😃</p><p>To fix it, I aligned the encoding type of the searched filename with that of the saved filename. <br>On MacOS, the encoding was NFD (for ö: 111 + 776), whereas on Linux, it switched to NFC (ö: 246).</p><p>The solution?</p><ul><li>Post-upload, before saving the file as an entity, I applied .normalize() to the filename, ensuring it was uniformly searchable.</li><li>Additionally, as a cleanup step — to write a migration script to normalize the names of all existing files with non-ASCII characters.</li></ul><p>A simple string normalise proved effective in this case. ✅</p><p>Read more about String.prototype.normalize() <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#canonical_equivalence_normalization">here</a>. There is also specific normalization option to pass to ensure canonically equivalent normalization — for example: string.normalize(&quot;NFC&quot;)</p><p>Here’s a visual representation of the problem with the solution output:</p><p><a href="https://drive.google.com/file/d/1Jkp8ye47iRfDoeTaMQkkskY_BTHegj_D/view?usp=sharing">files umlaut.mov</a></p><p>Hope it helps!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1b628e8cf36a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How using Server-Timing API helped bring > 70% perf improvement]]></title>
            <link>https://nishugoel.medium.com/how-using-server-timing-api-helped-bring-70-perf-improvement-f468d6fe88ed?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/f468d6fe88ed</guid>
            <category><![CDATA[browsers]]></category>
            <category><![CDATA[servers]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[serverless]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Thu, 28 Dec 2023 11:08:42 GMT</pubDate>
            <atom:updated>2023-12-28T11:08:42.639Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6VIMwcjKv0gtDEZWRq9HvQ.jpeg" /></figure><p>When working on the web, we all end up coming across situations where the experience for the user seems a bit janky. This happens either when you are dogfooding, or the users actually highlight facing that slowness, or you see that for yourself in your performance metrics or tools.</p><p>What one’d do in such a situation is look at the obvious things first: are the images loaded lazily? Is the bundle size too big? Is the main thread blocked? How is the performance panel recording looking? Is the data coming from the backend taking more time than it should?</p><p>If its one of the first problems, we’d fire up our favourite devtools and try to find out the root cause. If its the data being slow, we could see how long that is taking in the network requests duration. And, if it does end up being slow from the backend, the next step would be to dive into some logs and checking what piece of code in the request endpoint takes the time, or embedding some performance information in the response, or using other performance tools etc.</p><p>We had a similar situation at <a href="https://epilot.cloud/">epilot</a> where we use <a href="https://www.elastic.co/">Elasticsearch</a> to look for all related products of an order for example, and also all the other entities the order itself might be a relation to. <br>This meant, looking for direct relations of the order but also the reverse relations of that order.</p><p>Now in the Elasticsearch world, this meant:</p><ol><li>Getting the list of related products of the order.</li><li>Searching through the documents in elasticsearch for all those related IDs.</li><li>Also, searching all other docs for having this one order as a relation to this (the reverse relation example, I shared above).</li><li>Finally returning all the found relations.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*llIX5jCvzrFjkHsoVv0q6g.png" /><figcaption>showcasing two products as relations of an order</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ygt1PAJjbOB0woZehxuWlg.png" /><figcaption>Entities and relationships</figcaption></figure><p>This worked great for the first few years and got us exactly what we needed quickly. However, as we scaled, the amount of documents indexed in ES increased too, and that meant, the <em>step 3</em> above started getting expensive and slow! The logic was to look for an entity ID in all the indexed documents and find if it is a relation or not. For orders with a lot of products, the endpoint would respond in seconds.</p><p>We had to fix the logic/ update the design asap.</p><p>The first solution was to update the ES query to make it more specific about what is being looked up, and then check if it improved with that query change and how the results looked for the entities with many relations. This meant looking into a crazy amount of <a href="https://aws.amazon.com/cloudwatch/">Cloudwatch</a> logs and looking at the total time taken and if it was any better.<br>We made a change to <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html">search query to be an IDs specific query</a> which would look through only the IDs in the indexed docs, and not through all the fields in those ES documents.</p><pre>GET /_search<br>{<br>  &quot;query&quot;: {<br>    &quot;ids&quot; : {<br>      &quot;values&quot; : [&quot;1&quot;, &quot;4&quot;, &quot;100&quot;]<br>    }<br>  }<br>}</pre><p>The expectation was this would completely solve the problem and be super quick. This wasn’t the case unfortunately.<br>What we were missing was to see which part of the code was really adding to the time taken by the endpoint. We needed to observe the different steps in the process and see all the sections where the time taken could be reduced. We needed to see if it was really ES that was the culprit.</p><p>This is where <strong>Server-Timing API </strong>came into picture. Something that would break down the request into pieces and give us an idea about what’s expensive. The API allowed to pass the timings specific to a request from the backend to the browser. This meant that we could now see in the network requests, how the response times looked like, which part of the function took more time and so on.</p><p>To support this, we need to send a Server-Timing response header from the backend and time the methods that we needed. Something like this:</p><pre>HTTP/1.1 200 OK</pre><pre>Server-Timing: db;dur=53, app;dur=47.2</pre><p>The Server-Timing header can send three bits of information -<br>- <em>metric name</em><br>- <em>duration</em><br>- <em>description of the metric</em></p><p>In the above example:<br>db — metric name<br>dur=53 — the time in milliseconds thedb metric took (This is for instance, the time to fetch some data from the Database)<br>app — another metric name and dur=47.2 being the time taken for the same.<br>The header can take multiple metrics separated by commas delivering great information and is super lightweight. Although it is recommended to keep the metric names as small as possible.</p><p>An example of what the returned Server-Timing header looks like:</p><pre>db; dur=142.715967; desc=&quot;getRelationsForEntity&quot;,flt; dur=36.777609; desc=&quot;filterEntityListByAccess&quot;,pgn; dur=22.96549; desc=&quot;getPaginatedRelations&quot;,hyd; dur=0.64605; desc=&quot;hydrateRelations&quot;,ddp; dur=0.13311699999999999; desc=&quot;dedupeRelations&quot;,total; dur=721.388338</pre><p>And this information then translates into a more visual and helpful view as a part of the network timings tab of the request OR available via the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming">PerformanceServerTiming</a> interface.</p><p>To access the three available properties, they correspond in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming">PerformanceServerTiming</a> as:</p><ul><li><em>“name”</em> -&gt; <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming/name">PerformanceServerTiming.name</a></li><li><em>“dur”</em> -&gt; <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming/duration">PerformanceServerTiming.duration</a></li><li><em>“desc”</em> -&gt; <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceServerTiming/description">PerformanceServerTiming.description</a></li></ul><p>Here’s the <a href="https://www.w3.org/TR/server-timing/"><strong>working draft</strong></a><strong> </strong>with more information.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/996/1*yS0EFvP8J7mK-3sWXngx4A.png" /><figcaption>Timings tab to see server response timings</figcaption></figure><p><em>These are not available as a series of timings as such and just simple metrics and therefore, does not show in a waterfall pattern.</em></p><p>This means we can now tackle both the frontend performance problems and the backend slowness problems all in one place. No jumping to the logs to find a response was slow or heading backing again to the devtools to find the frontend problem.</p><p>We could also read the data now available using the navigation (<a href="https://www.w3.org/TR/navigation-timing-2/#dom-performancenavigationtiming">PerformanceNavigationTiming</a>) and resource timing (<a href="https://www.w3.org/TR/resource-timing/#dom-performanceresourcetiming">PerformanceResourceTiming</a>) APIs and send it our analytic tools to create metrics/monitors.</p><pre>const performanceObserver = new PerformanceObserver((list) =&gt; {<br>  for (const entry of list.getEntries()) {<br>    log(&#39;Server Timing&#39;, entry.serverTiming);<br>  }<br>});</pre><pre>performanceObserver.observe({type: &#39;navigation&#39;, buffered: true});</pre><p>Below is one of the examples from a method taking different amounts of time at different steps in the request. We will set up together the whole setting up and measuring the server response times later in this article.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*ZqnmaYer1puCDxR6BvXPhQ.png" /><figcaption>Server response timings for a request endpoint in Browser devtools</figcaption></figure><p>This was especially helpful for the Elasticsearch example that I shared earlier as we needed to know, which change was improving what part of the implementation and if it was moving us closer to improved response times. The Server-Timing header does not directly improve the timings but helps find out what could be worked towards.</p><p>The uniqueness of the API is that its super flexible and allows the server to communicate important information to the browser, even other than the server timings. It would be amazing to have this supported on Safari as well, which currently lacks the API support.</p><h3>Coming back to how we came down to the API response in 400ms seconds as compared to 1.8s earlier</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5w2jc5GPO973pc8rOgYYrA.png" /></figure><p>These are the two versions of the API. When we measured the performance with server-timings on the ES implementation after switching to the IDs search query on Elasticsearch, the performance seemed to be taking all the time especially on this function searchRelationsForEntity .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NY2FyhJ_IW04T1ZqateiJQ.png" /><figcaption>Server timing indicating huge time taken by method `searchRelationsForEntity`.</figcaption></figure><p>Now we knew this was to do with ES but where exactly. We timed specifics inside this method.<br>On measuring further, it was entitySearch.searchEntities that was taking up the most time so we needed to find a solution that would not go through all the ES documents at all to get the relation entities.</p><p>This is where changing the design made sense, and we moved to a graph table design with Dynamo, which would store all the relationships as mappings, and return all the entity IDs whether as direct relations or reverse. <br>Now we could choose if we wanted to search ES with all the IDs that we have or just batch get the entities from the db, and support pagination (this was an added advantage with ES) etc. separately.</p><p>Now, this complete dynamo approach saved us looking through all the indexed documents for the IDs that we need, and also cut some slack on Elasticsearch which would have this unnecessary load, eventually affecting other straightforward searches as well.</p><p>This is how the server timings looked with the DynamoDB approach:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CcH2JLiYOAG5pxVkYDYK6g.png" /><figcaption>Server timing indicating reduced times after switching to Dynamo to fetch entity relations</figcaption></figure><p>This was an amazing improvement, clearly visible with this significant performance information right in the browser devtools for us. This also ended up helping improve the code in general, to gain some more milliseconds by refactoring, and using performant ways to filter/hydrate/paginate things etc.</p><blockquote>We love to see the output of what’s in the works, and this was a great example to look at and feel a sense of achievement for.</blockquote><h3>Implementation of Server-Timing as a middleware for AWS Lambda 💪</h3><p>Now if you look up the existence of libraries supporting server-timing header, there are so many but most of them for <em>Express</em> and <em>NodeJS</em>.</p><ul><li><a href="https://www.npmjs.com/package/server-timing">server-timing</a></li><li><a href="https://www.npmjs.com/package/server-timings">server-timings</a> etc.</li></ul><p>We needed to create one for the serverless use case which could be used for AWS Lambda, lets say to use with <a href="https://github.com/middyjs/middy"><strong><em>middy</em></strong></a> for example.</p><h4>Step 1. Create a withServerTimings() middleware to use for AWS lambda</h4><pre>import middy from &#39;@middy/core&#39;;<br>import * as Lambda from &#39;aws-lambda&#39;;</pre><pre>export const withServerTimings = () =&gt; {<br>    return {<br>      // add Server-Timing header to response<br>      after: (handler: middy.Request) =&gt; {<br>        const response = handler.response as Lambda.APIGatewayProxyStructuredResultV2;</pre><pre>        try {<br>          const headers: unknown[] = [];<br>          /** <br>              This is where the headers with different metrics are be saved <br>              and retrieved from<br>           */<br>          const timings = getServerTimingHeader(headers); </pre><pre>          response.headers = {<br>            ...response.headers,<br>            &#39;Server-Timing&#39;: timings,<br>          };<br>        } catch (e) {<br>          Log.debug(`Middleware Error: Could not record server timings`, e);<br>        }<br>      },<br>    };<br>};</pre><p>This creates a usable withServerTimings()<strong> </strong>middleware to then be used as:</p><h4>Step 2. Apply the middleware to the request handler</h4><pre>return middy(handler)<br>    .use(...)<br>    .use(withCors())<br>    .use(withServerTimings())<br>    .use(...)</pre><p>This already ensures that your backend will now be sending a Server-Timing header in the request endpoints.</p><p>Next step would be to actually time our operations and store them as a common timing variable which can then be sent to the getServerTimingHeader method for example.</p><h4>Step 3. Implement timing for your methods</h4><pre>const times = new Map&lt;string, Record&lt;string, unknown&gt;&gt;();</pre><pre>// starting the timer on a method<br>const startTimer = (name: string, description?: string) =&gt; {<br>  times.set(name, {<br>    name,<br>    description: description || &#39;&#39;,<br>    startTime: process.hrtime(),<br>  });<br>};</pre><pre>// ending the timer and recording the duration of the method<br>const endTimer = (name: string, description?: string) =&gt; {<br>  const timeObj = times.get(name);<br>  if (!timeObj) {<br>    return console.warn(`No such name ${name}`);<br>  }<br>  const duration = process.hrtime(timeObj.startTime as [number, number]);<br>  const value = duration[0] * 1e3 + duration[1] * 1e-6;<br>  timeObj.value = value;<br>  times.delete(name);</pre><pre>  return timeObj;<br>};</pre><pre>// finally setting the metric value received from endTimer object</pre><pre>const metric =<br>    typeof description !== &#39;string&#39; || !description<br>      ? `${name}; dur=${dur ?? 0}`<br>      : `${name}; dur=${dur}; desc=&quot;${description}&quot;`;</pre><p>This would then be used as:</p><h4>Step 4. Usage of timers of the methods</h4><pre>startTime(&#39;db&#39;, &#39;getRelationsForEntity&#39;);<br>  const relations = await getRelationsForEntity({<br>    ...<br>  });<br>  endTime(&#39;db&#39;, &#39;getRelationsForEntity&#39;);</pre><pre>  .<br>  .<br>  .</pre><pre>  startTime(&#39;pgn&#39;, &#39;getPaginatedRelations&#39;);<br>  const { relations: paginatedRelations, hits } = getPaginatedRelations({<br>    ...<br>  });<br>  endTime(&#39;pgn&#39;, &#39;getPaginatedRelations&#39;);</pre><p>And there we go, we will have the timed methods and the server headers looking like:</p><pre>db; dur=139.99; desc=&quot;getRelationsForEntity&quot;,flt; dur=36.68; desc=&quot;filterEntityListByAccess&quot;,<br>pgn; dur=39.37; desc=&quot;getPaginatedRelations&quot;,hyd; dur=0.61; <br>desc=&quot;hydrateRelations&quot;,ddp; dur=0.130; desc=&quot;dedupeRelations&quot;,<br>total; dur=741.53</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y9Olm6Xx5QX0J0aVZ6guSQ.png" /><figcaption>visual representation of the server-timing headers</figcaption></figure><p>All the timing implementation and setting up the middleware can be a hassle, and should be a one-time job. I created a package out of this implementation called lambda-server-timing which we can simply use by importing the things we need, i.e.,</p><ul><li><a href="https://www.npmjs.com/package/lambda-server-timing">lambda-server-timing</a></li><li><a href="https://github.com/NishuGoel/lambda-server-timing">GitHub - NishuGoel/lambda-server-timing: Enables Lambdas to return responses with Server-Timing Header allowing to to pass request-specific timings from the backend to the browser.</a></li></ul><ul><li>withServerTimings to use a middleware</li><li>startTime to start measuring a function/set of functions</li><li>endTime to end the timing of the function/set of functions and get the metric set.</li></ul><pre>import {withServerTimings, startTime, endTime } from &#39;lambda-server-timing&#39;;</pre><p>Lets time our functions down! 💪</p><p>References</p><ul><li><a href="https://www.w3.org/TR/server-timing/">Working draft</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing">MDN Server Timing</a></li><li><a href="https://www.w3.org/TR/navigation-timing/">Navigation Timing</a></li><li><a href="https://www.w3.org/TR/resource-timing/">Resource Timing</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f468d6fe88ed" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spin up a new Elasticsearch cluster?]]></title>
            <link>https://medium.com/epilot/spin-up-a-new-elasticsearch-cluster-341916d7b821?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/341916d7b821</guid>
            <category><![CDATA[elasticsearch]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[architecture]]></category>
            <category><![CDATA[elastic-cloud]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Tue, 08 Aug 2023 14:01:48 GMT</pubDate>
            <atom:updated>2023-08-09T13:22:02.936Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*2OVJiifga0L5tpUn8PzdDQ.png" /><figcaption>communicating between remote elasticsearch clusters</figcaption></figure><p><em>This post explains how to setup a new elasticsearch cluster from scratch and make it ready to be used for indexing data into, communicating to/from as a remote cluster, along with setting up monitoring for the new cluster.</em></p><p><strong>Problem 1: Create a new elasticsearch cluster on elastic.co based on the required configuration for your use case.</strong></p><p>To create a new cluster from scratch:<br>Create a main.tf file with following resources:</p><p>a. ec_deployment - The main cluster deployment resource<br>b. aws_secretsmanager_secret- The cluster secrets<br>c. aws_secretsmanager_secret_version - The cluster secret version needed for the secrets<br>d. elasticstack_elasticsearch_cluster_settings - To add default settings to the cluster like cluster nodes etc.</p><p><em>Example: ec_deployment</em></p><pre>resource &quot;ec_deployment&quot; &quot;&lt;cluster-name&gt;&quot; {<br>  name = &quot;&lt;cluster-name&gt;&quot;<br><br>  region                 = &quot;aws-eu-central-1&quot;<br>  version                = var.elasticsearch_version<br>  deployment_template_id = standard/arm-based (decide based on the requirement)<br><br>  elasticsearch {<br>    autoscale = &quot;false&quot;<br>    topology {<br>      id   = &quot;hot_content&quot;<br>      size = &quot;15g&quot; // size of the cluster<br>      zone_count = 3 // number of nodes <br>    }<br>  }<br>  kibana {}<br><br>  // important to include this in the cluster config to ensure to NOT override any existing remote cluster related config<br>  # ignore changes to remote cluster config<br>  lifecycle{<br>    ignore_changes = [<br>      elasticsearch[0].remote_cluster<br>    ]<br>  }<br>}</pre><p>Few important bits to note:<br>Ensure the region and ES version are consistent for your cluster.</p><p>By default, deployment_template_id should be defined as standard aws-storage-optimized-v3 but if your are creating the cluster for memory-intensive work, consider creating it as arm-based aws-general-purpose-arm-v6</p><p>The size and number of nodes vary based on the need, so decide it accordingly from the Elastic Cloud UI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*9qEyrU_19DCthOecSTrSow.png" /></figure><p>An important bit to always add in your cluster deployment is to ignore changes to the remote cluster config. This ensures the remote communication that is configured among clusters doesn’t get overriden by this update.</p><pre>ignore_changes = [<br>      elasticsearch[0].remote_cluster<br>    ]</pre><p><em>Example: Cluster Secrets</em></p><pre># Create the AWS Secret Manager secret<br>resource &quot;aws_secretsmanager_secret&quot; &quot;&lt;cluster-secret-name&gt;&quot; {<br>  name = &quot;/elasticsearch/&lt;cluster-name&gt;/credentials&quot;<br>  description = &quot;Elasticsearch credentials for &lt;cluster-name&gt; cluster&quot;<br>}<br><br># Create the AWS Secret Manager secret version with JSON value<br>resource &quot;aws_secretsmanager_secret_version&quot; &quot;&lt;cluster-secret-name&gt;&quot; {<br>  secret_id     = aws_secretsmanager_secret.&lt;cluster-secret-name&gt;.id<br>  secret_string = &lt;&lt;JSON<br>{<br>  &quot;endpoint&quot;: &quot;${ec_deployment.&lt;cluster-name&gt;.elasticsearch.0.https_endpoint}&quot;,<br>  &quot;username&quot;: &quot;${ec_deployment.&lt;cluster-name&gt;.elasticsearch_username}&quot;,<br>  &quot;password&quot;: &quot;${ec_deployment.&lt;cluster-name&gt;.elasticsearch_password}&quot;<br>}<br>JSON<br>}</pre><p><em>Example: cluster_settings</em></p><pre>resource &quot;elasticstack_elasticsearch_cluster_settings&quot; &quot;&lt;cluster-settings&gt;&quot; {<br>  elasticsearch_connection {<br>    username = ec_deployment.&lt;cluster-name&gt;.elasticsearch_username<br>    password = ec_deployment.&lt;cluster-name&gt;.elasticsearch_password<br>    endpoints = [ec_deployment.&lt;cluster-name&gt;.elasticsearch.0.https_endpoint]<br>  }<br><br>  persistent {<br>    setting {<br>      name  = &quot;cluster.max_shards_per_node&quot;<br>      value = &quot;2500&quot;<br>    }<br>    setting {<br>      name  = &quot;cluster.routing.allocation.cluster_concurrent_rebalance&quot;<br>      value = &quot;30&quot;<br>    }<br>    setting {<br>      name  = &quot;cluster.routing.allocation.node_concurrent_recoveries&quot;<br>      value = &quot;30&quot;<br>    }<br>  }<br>}</pre><p>Three important settings that would make sense to add as default 👆</p><ul><li>max_shards_per_node the value would vary based on the cluster size and requirement</li><li>cluster_concurrent_rebalance To allow correct rebalancing of shards to all available nodes (20-30 is a good value for this)</li><li>node_concurrent_recoveries To allow correct rebalancing of shards to all available nodes (20-30 is a good value for this)</li></ul><p>Above steps will create a cluster with the defined config. ✅</p><p>Always good to ensure and check what our changes are adding/updating/deleting, to be extra cautious to NOT end up deleting/updating some existing resource. 😨</p><p><strong>Problem 2: Add the newly created elastic cluster to the POOL of clusters in the DynamoDB Cluster assignment table</strong></p><p>Now that the new cluster is and available to view on elasticsearch as a healthy deployment, we can make the next move to include it in the list of available clusters, to be able to then use it for our ES needs.</p><p><em>example:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*ZldNpWi1K7tgiS8st40zUg.png" /></figure><p>To add it to the pool, you can assign it to the table where cluster assignment is defined, in our case: ClusterAssignmentTable</p><p>To add your new cluster to this pool, use:</p><pre>json=$(cat &lt;&lt;EOF<br>{<br>  &quot;pk&quot;: {&quot;S&quot;: &quot;POOL&quot;},<br>  &quot;sk&quot;: {&quot;S&quot;: &quot;CLUSTER#${cluster_name}&quot;},<br>  &quot;cluster&quot;: {&quot;S&quot;: &quot;${cluster_name}&quot;},<br>  &quot;endpoint&quot;: {&quot;S&quot;: &quot;${endpoint}&quot;},<br>  &quot;username&quot;: {&quot;S&quot;: &quot;${username}&quot;},<br>  &quot;password&quot;: {&quot;S&quot;: &quot;${password}&quot;}<br>}<br>EOF<br>)<br><br>echo &quot;===&gt; Storing cluster $cluster_name in table $table_name...&quot;<br>aws dynamodb put-item \<br>    --table-name $table_name \<br>    --item &quot;${json}&quot;</pre><p>As a result of this, the new cluster should be found in the cluster assignment table.</p><p><strong>Problem 3: Allow it the access to communicate to/from other existing ES clusters.</strong><br>When dealing with multiple ES clusters, there is the requirement to query data from one cluster to another. In the current implementation, we already have use cases of doing multi-org queries where those orgs could belong to different clusters.</p><p><em>Usage looks like:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*2OVJiifga0L5tpUn8PzdDQ.png" /></figure><p>To allow this, we need to ensure, every time a new cluster is created, its also set up for the remote cluster communication.</p><p>To do this, you could do something like for all your clusters in the pool:</p><pre>payload=$(cat &lt;&lt;EOF<br>{<br>  &quot;persistent&quot;: {<br>    &quot;cluster&quot;: {<br>      &quot;remote&quot;: {<br>        &quot;${remote_cluster}&quot;: {<br>          &quot;mode&quot;:&quot;proxy&quot;,<br>          &quot;server_name&quot;: &quot;${remote_server_name}&quot;,<br>          &quot;proxy_address&quot;: &quot;${remote_proxy_address}&quot;,<br>          &quot;skip_unavailable&quot;: true<br>        }<br>      }<br>    }<br>  }<br>}<br>EOF<br>)<br><br>curl -sS -X PUT &quot;$endpoint/_cluster/settings&quot; \<br>  -u &quot;$username:$password&quot; \<br>  -H &#39;Content-Type: application/json&#39; \<br>  -d &quot;$payload&quot; | jq</pre><p>This will ensure all the clusters for that environment are setup for remote communication to one another.</p><p>Also, remember we had that extra step to ignore changes to the remote cluster config? This is why! We DO NOT want to override any remote config changes when updating clusters.</p><p>At this point, you have your cluster ready on elasticsearch, cluster allocation table, and also able to talk to other clusters 💪</p><p>And then there are the Not-so-good times when clusters are not doing that great and healthy.</p><p>With these many clusters (even more probably in the future), we want to know the whereabouts of each of them, to be better at managing these.<br>So a cluster specific monitoring is a good idea. We do that using Datadog. Datadog provides a Elastic Cloud integration that can be used for the same.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*M3-tKtNU7yUdn52l0Z2aXQ.png" /></figure><p>Something like above 👆</p><p>Hope this helps!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=341916d7b821" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epilot/spin-up-a-new-elasticsearch-cluster-341916d7b821">Spin up a new Elasticsearch cluster?</a> was originally published in <a href="https://medium.com/epilot">epilot</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[the “fancy” year]]></title>
            <link>https://nishugoel.medium.com/year-in-review-dc32071fe3e2?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/dc32071fe3e2</guid>
            <category><![CDATA[code]]></category>
            <category><![CDATA[conference]]></category>
            <category><![CDATA[travel]]></category>
            <category><![CDATA[moving]]></category>
            <category><![CDATA[tech]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Thu, 29 Dec 2022 15:04:20 GMT</pubDate>
            <atom:updated>2023-01-03T00:36:41.234Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>I have come to realise a pattern in my past few years.</blockquote><p>It happens to be a series of one type of things that I do, and I do a lot of that for the year. For example, 2018 was “writing blog posts” year, wrote more than 50 blog posts. 2019, “the reading books” years, read 36 or so books that year. 2021, again a lot of writing that year, 2022, “insane traveling” year.</p><h3>TL;DR</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/910/1*p0DkR2KAEnRQiCrmN3xkXQ.jpeg" /><figcaption>Courtesy: Revolut</figcaption></figure><p>When I started this year, I was aware of one travel that was going to happen, for sure. It was the one that involved me moving to a different country to work. However, there was so much more in store than what I expected. <br>It felt like telling Covid: Enough of your shit!<br>The point is, I ended up traveling to all the countries that I would think about traveling to, one day. And I wouldn’t have been writing this proudly, if it was not work-related. All of the travel was tech related. And what can be better than that?</p><p>Started the year with speaking at my favourite conference — <a href="https://www.ng-ind.com/">ngIndia</a>, and the energy was crazy since it was happening now after covid. Knowledgeable speakers, getting to know people.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QPnJJHLMpKtob78mU2zSrA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GgU93C3xPoO_iHY8Y2QNvA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X6xMa6DgeXbJRzoTe4fU-Q.jpeg" /></figure><p>I joined <a href="https://epilot.cloud/">epilot</a> beginning of this year which involved lots of learning and working with amazing people and in the best work culture one could have. <br>One important thing I realised was:</p><blockquote>“A company is really doing something right, if even its engineers talk about the product (bigger picture) with as wide eyes and great passion as the founders.”</blockquote><p>And I see this at epilot everyday and this inspires me so much to have such a culture at my own company, someday. <br>Like I mentioned in the beginning, this year was so much traveling and I am going to write small interesting instances from all my trips here, so I can come back later to this and smile.</p><p>Started with me traveling to the company summit in Germany which involved a week of fun with colleagues. This was when I got to know that a beer CAN cost less than water sometimes if you are in Germany :) Also saw, <em>the team that parties crazy also achieves crazy</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2fT6wrnjmDriTi0dsNC9CA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mtuRRNShhbCnZzoroy142w.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fLZ8jrcHXIegwv1bIs8Ojg.jpeg" /></figure><p>And before it starts sounding like an epilot appreciation post, I should also mention the amazing time I had at the GDE summit in Leh Ladakh. All the Google developer experts coming together after covid, meeting in person, doing activities, playing games, was exactly what was needed after those two dreadful years.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*14BqG5gDduPOEBS5fZC1lg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Yqz0mH_dLYX3CjO9MP8_wQ.jpeg" /></figure><p>September brought the wave of travel and never stopped until December. I moved to a new place here in Köln, and the next day I was traveling to Romania already for a web talk at Devfest Craiova about JavaScript Surprises. It was a one day event meeting amazing developers followed by a wonderful day out in the city.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*Z4pM6Sapdb2QnHqrikSPwA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y0vgNoc16lFkjLo9u2KsfQ.jpeg" /></figure><p>Visited the <a href="https://en.wikipedia.org/wiki/Nicolae_Romanescu_Park">Nicolae Romanescu Park</a>, the largest natural park in Eastern Europe, apparently. I think the highlight of the Romania trip would be the Papanași that I had and the <a href="https://en.wikipedia.org/wiki/S%C3%A2rb%C4%83">Sârbă</a> dance we did at a get together dinner event.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1NsG95e_gsseT-9AIaUOlw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T4NlGhNivdrIzPeoxgza5w.jpeg" /></figure><p>This was followed by a surprise opportunity to visit the Firebase Summit in New York as an Angular GDE. And New York is a dreamy city until you have visited it at least once, and after that one decides for himself, if NYC is still as exciting for them. To each their own. <br>This was great fun as well with fellow GDEs exploring all the touristy stuff — Times Square, Wall street, Helicopter View of the city, Brooklyn bridge, Statue of Liberty, Cruise tour, Summit One Vanderbilt etc. The highlight has to be the event venue which surrounded The Little Island.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xk-eX5UKyms3YhDhesGJsg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7FNVOuFWH-GJKxEvgMCUJA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GgiwIKoCso4xQEflF_dBig.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C1Zeyd77nxkjCzok-4Ysdg.jpeg" /></figure><p>Next was a talk at JavaScript Poland in Warsaw, which was a trip full of learning. Attending workshop on <a href="https://qwik.builder.io/">Qwik</a>, learning about <a href="https://partytown.builder.io/">Partytown</a>, <a href="https://www.solidjs.com/guides/getting-started">Solid.js</a> and reactivity conversation with Ryan, Streaming concept and Out of Order streaming, etc. The highlight would be meeting <a href="https://twitter.com/ngKalbarczyk">Dariusz</a> after 3 years of meeting him for the first time in <a href="https://www.ng-ind.com/">ngIndia</a> and the beautiful JS Poland stage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3ZsR2-9HcH6RtVYq9LKmpg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E_AbSvnf3-DAs4gTHwJCSg.jpeg" /></figure><p>Had a work vacation planned in Vienna, Austria in November and boy, the city is beautiful. We stayed at a hostel called Jo and Joe which had a creative element to it. We walked around the city and I got to see the first snow of the season there. The highlight of the trip was really just enjoying, playing with colleagues, winning the Dart game, but cluelessly :)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YFFQEppPTMP6V71M3dOtjA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*wk8DJQ0q5Pihh431PWcOag.gif" /></figure><p>Last but not the least was the trip to London for another Devfest event. This was quite unexpected as I had visa issues until two days before the travel, but ended up working out a day before, and how could I miss it then. Shared about JavaScript Memory Usage at Devfest London, again followed by a day out in the city, again some touristy stuff, but also great conversation with fellow speakers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l_VApAKmQndpEBt4k8XSWw.jpeg" /></figure><p>I happened to be in London during Christmas and that in itself is the best highlight, but there were so many other. It snowed there making it even more beautiful, sitting in those London special red buses. Walking around the city and having conversation with a friend was what made it all worth.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BoCBbs6Cc-D_mwf0Cerg2g.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nlcB4t8UTnU8AmFHwyAsgg.jpeg" /></figure><p>You know how when we realise a pattern, it is no more fun to still let it going. We, at least I, like to see if the pattern can be broken and can something good come out of it. I think I would really not want this year to be a series of one kind of thing. <br>Would really like it to be a mix of all kinds of things I like to, need to do, with the right amount of balance, consistency, etc. Looking forward to writing, reading, creating more value this year. :)</p><p>Also, moving is quite an adventure. We introduce new music, food, culture, and new habits in general to our lives, sometimes language as well. Example being, :D</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cbKLHbvwRXfpPaSUbjunQQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QjtnrhaU4G6CUJnt2sG8Ww.jpeg" /></figure><p>Some talks:<br>- <a href="https://gdg.community.dev/events/details/google-gdg-london-presents-gdg-london-devfest-2022-1/">Devfest London</a><br>- <a href="https://jspoland.pl/speaker/nishu-goel">JS Poland</a><br>- <a href="https://youtu.be/_DY4sTCugkU">Devfest Craiova</a> (<em>recording</em>)<br>- <a href="https://gdg.community.dev/events/details/google-gdg-bangalore-presents-devfest-bangalore-2022/">Devfest Bangalore</a><br>- <a href="https://youtu.be/ydCCndGRo-g">ngIndia</a> (<em>recording</em>)<br>- <a href="https://webdirections.org/globalscope/">Web Directions — Global Scope</a></p><p>Some slides:<br>- <a href="https://docs.google.com/presentation/d/1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8/edit?usp=sharing">microFrontends</a><br>- <a href="https://docs.google.com/presentation/d/1gHd_vTsA_PnSC76zR3W37kRYMmEHNE4U/edit?usp=sharing&amp;ouid=110120705238669915810&amp;rtpof=true&amp;sd=true">JavaScript Surprises</a><br>- <a href="https://docs.google.com/presentation/d/1CVNdNywodV5VAm0N1MYKWI3WMR709Rvy/edit?usp=sharing&amp;ouid=110120705238669915810&amp;rtpof=true&amp;sd=true">Understanding JS memory Usage</a></p><p>Some writings:<br>- <a href="https://calendar.perfplanet.com/2022/breaking-up-long-tasks/">Breaking up long tasks — Perf Calender</a><br>- <a href="https://unravelweb.dev/2022/03/20/dabbling-at-epilot-with/">Dabbling at epilot</a><br>- <a href="https://medium.com/@nishugoel/unraveling-the-web-w-nishu-goel-70227097d8f">Unraveling the Web</a><br>- <a href="https://medium.com/epilot/svelte-i18next-e0c6591a8fcc">Svelte-i18next</a></p><p>Some books read:<br>- The midnight library<br>- The T-shirts I love<br>- Thinking fast and Slow<br>- AI 2041<br>- The Bitcoin Standard<br>etc.</p><p>Prost! 🍻</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dc32071fe3e2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Breaking up long tasks]]></title>
            <link>https://nishugoel.medium.com/breaking-up-long-tasks-ed8de2d6a3e7?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/ed8de2d6a3e7</guid>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-performance]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[code]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Sun, 25 Dec 2022 19:10:59 GMT</pubDate>
            <atom:updated>2022-12-25T19:10:59.153Z</atom:updated>
            <content:encoded><![CDATA[<p>Imagine the scenario where you click on an element and expect a response but the element doesn’t seem like it was clicked and doesn’t respond, so you click again. Those two clicks however, when the response completes, lead to undoing the action you wanted. Ever happened with you? Sounds frustrating, doesn’t it?</p><p>This is the kind of experience, we as web engineers, cannot afford to give to the users. And there can be multiple reasons that could lead to this. If you regularly check the performance of your apps, you would have come across suggestions to code-split your application and reduce shipped JavaScript. In this post, we look at the causes of slow responsiveness of an application, how we could measure the slowness, and the approaches to improve it.</p><blockquote><strong><em>TL;DR</em></strong><em><br>Certain tasks on a user interaction can be blocking, giving the user a broken experience, and resulting in a low INP metric score.</em></blockquote><blockquote><em>Some ways to optimise it could be:</em></blockquote><ul><li>keep tasks small and dedicated to what they should do.</li><li>use Chrome’s scheduler API to schedule tasks for browser</li><li>yield important tasks to the main thread</li></ul><p>Let us first understand why a user interaction could be slow. When a user interacts with an element on the browser, the user agent takes that as one <em>task</em> and queues it in the list of tasks for the browser. This one task could take more time or less depending on the type of operation needed to be performed. Some tasks take longer resulting in the main thread being busy performing that and leading to the the other tasks being blocked in the meantime.</p><h3>Long tasks</h3><p>A task is considered a long one if it goes beyond <strong>50ms</strong> to execute and <strong>50ms</strong> to respond, basically, more than <strong>100ms</strong>. A website which doesn’t have long tasks, is expected to respond within <strong>100ms</strong> with both the execution and response time on a user input, according to <a href="https://developers.google.com/web/fundamentals/performance/rail"><strong>RAIL</strong></a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/712/0*hOLXTkWRBuFRavs5.png" /></figure><p>As per <a href="https://almanac.httparchive.org/en/2022/javascript#fig-34">data from Web Almanac</a>, it’s interesting to note that a median mobile page tends to have 19 long tasks.</p><p>This is where <a href="http://web.dev/inp"><strong>INP</strong></a> comes into picture. INP is a responsiveness metric, stands for Interaction to next paint, which measures the time from when the user interacted with the page until when the next frame was actually visible on the page. A good INP measure is when the time between interaction and the feedback as a result of that interaction is the least.<br>An interaction could mean events like tap, click, keyUp, pointerdown etc.</p><p>An interesting read <a href="https://web.dev/inp/#:~:text=A%20note%20on%20how%20INP%20is%20calculated"><strong>here</strong></a> on how’s INP actually measured</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/64/0*r5CaoKyxOdFdyJlI.png" /></figure><p>.</p><blockquote><em>INP is calculated by observing all the interactions made with a page. The chosen value is then a </em><a href="https://en.wikipedia.org/wiki/Percentile"><em>percentile</em></a><em> of those interactions. A formula is then used to choose a high percentile value of those interactions.</em></blockquote><p>The way we put web vital measures in a Good, Needs Improvement, Bad buckets, here is what a good and poor looks like for the INP metric.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*HTIq_h21NsP8nzsj.png" /></figure><p>To calculate the overall page responsiveness, an INP measure is calculated when the user navigates away from a page, and this comprises of all interactions like mouse events, keyboard events, touch/tap on a touchscreen device.</p><h3>FID / INP?</h3><p>I remember analysing my websites, and always being satisfied with at least one metric of the three core web vitals 😀 — the First Input Delay. Would up my ego a bit that I am doing something good for sure. But if we look at the bigger picture, it is noticed that most websites have a good FID value, even when the responsiveness experience isn’t that great.<br>And to address this, INP with a better measurement approach has been introduced. <em>INP doesn’t just measure the first interaction but all interactions on a page, unlike FID.</em> <em>Also, as the name says, INP measures the time until the next paint happens, unlike FID, that measures the time only until the processing happens.</em></p><p>If we look at the INP scores on mobile devices, <strong>55%</strong> of websites have a good INP score, and <strong>36%</strong> need improvement. However, in case of FID, <strong>92%</strong> of websites having “good” FID. So, a better responsiveness measure with INP already brought down the “<em>good</em>” score.</p><p>It is also interesting to see that INP score tends to be inversely proportional to the popularity of a website, as <a href="https://almanac.httparchive.org/en/2022/performance#inp-by-rank">per data from the Performance chapter in web almanac</a>.</p><p>With a better approach to measure the responsiveness of a website with INP, it would make sense for developers to really start considering this metric as an important performance check, also with Google considering to replace FID with INP as one of the core web vitals.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dxIp42tzRIE5ufJp.png" /></figure><p>When looking at the mobile experiences by site rank, <strong>52%</strong> of top 1K websites fall in the <em>good</em> category with FID metric, however only <strong>20%</strong> pass with good status with INP as a metric, and this is a huge difference hinting towards the need to really improve the overall responsiveness situation, <em>long tasks</em> being one of the things to solve.</p><h3>Can I track my website’s INP value?</h3><p>One cool way to track our website’s interaction to next paint measure with real data from users is using the web-vitals library.</p><p>The library offers the <em>onINP</em> method that returns the type of event along with the INP value.</p><p>We could record this data and then receive it via some API or analytics provider to be able to understand the reasons behind the INP measure based on the real usage from field.</p><p>Something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/64/0*wdRVfNruTIbIl5vD.png" /></figure><pre>import {onINP} from &#39;web-vitals&#39;;<br>onINP(({name, value, attribution}) =&gt; {<br>  console.log(name)<br>  console.log(value)<br>  console.log(attribution.eventType)<br>});</pre><p>This gives us the measure of our INP and helps us understand further how we this interaction delay be improved.</p><p>This is how the general INP score looked like on websites as per the <a href="https://almanac.httparchive.org/en/2022/javascript#fig-31">data from Web almanac</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9Y3UNA40PMg0a8Pk.png" /></figure><h3>How do you find a long task in your application?</h3><p>By now, we would know how the INP value is doing, and the next step right away would be to profile our app and see the improvement areas. To do this, we go straight to the performance profiler and identify the long tasks (ones exceeding the threshold of <strong>50ms</strong>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Mc99gtz4ex3TeLH4.png" /></figure><p>As we can see, there are many long tasks leading to slower input response to the user, one such having a delay of <strong>676ms</strong>.</p><p>And as we dive deeper, it gives us some idea about the operations causing this to go up to <strong>600ms</strong> here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LwScIM_y9K2HHhHZ.png" /></figure><p>Let us try to optimise one such long task now. There can be different approaches to improve long tasks, with one being breaking up your long tasks into smaller dedicated tasks.</p><p>For example, the long task from above which took <strong>600ms</strong>, was a blocking task for UI related updates, and thus would result in a broken experience for the users since they’d see a delayed response. What if the task still does exactly what it does but it is broken into multiple tasks with small operations, meaning:</p><ul><li>Task 1 — Only for querySelector step</li><li>Task 2 — parse</li><li>Task 3 — getOperations</li><li>Task 4 — removeChild<br>and so on.</li></ul><p>Read more here 👇🏻</p><p><a href="https://calendar.perfplanet.com/2022/breaking-up-long-tasks/">Breaking up long tasks</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ed8de2d6a3e7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ng-India: microfrontends]]></title>
            <link>https://nishugoel.medium.com/ng-india-microfrontends-a4c486aba6bc?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/a4c486aba6bc</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Sun, 09 Oct 2022 10:22:48 GMT</pubDate>
            <atom:updated>2022-10-09T10:22:48.012Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P6LvHkcVT5MakO7HK9iAQQ.png" /></figure><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fdocs.google.com%2Fpresentation%2Fembed%3Fid%3D1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8%26size%3Dl&amp;display_name=Google+Docs&amp;url=https%3A%2F%2Fdocs.google.com%2Fpresentation%2Fd%2F1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8%2Fedit%3Fusp%3Dsharing&amp;image=https%3A%2F%2Flh5.googleusercontent.com%2FL5FgMrzd3OKFwN6vjqhnQ5Zhne9ZPbf3luYlemZ8KDerWch3UwMRS5wwBoAmGpu9vi-mhrjHySIYgQ%3Dw1200-h630-p&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=google" width="700" height="559" frameborder="0" scrolling="no"><a href="https://medium.com/media/69e9290161974e872ee9b7edb7ed22c7/href">https://medium.com/media/69e9290161974e872ee9b7edb7ed22c7/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a4c486aba6bc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[3 times Microsoft MVP]]></title>
            <link>https://nishugoel.medium.com/3-times-microsoft-mvp-e682566b41bf?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/e682566b41bf</guid>
            <category><![CDATA[mvpbuzz]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[microsoft]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Tue, 05 Jul 2022 19:19:50 GMT</pubDate>
            <atom:updated>2022-07-05T19:19:50.587Z</atom:updated>
            <content:encoded><![CDATA[<p>Today was the renewal day for Microsoft MVPs, and I am honoured to be awarded the prestigious <a href="https://mvp.microsoft.com/en-US/Overview">Microsoft Most Valuable Professional award</a> for the 3rd year in a row. I was first awarded in the year 2020.</p><p>Blessed to have been surrounded by the best people in the field. <a href="https://www.linkedin.com/in/dhananjaykumar07/"><strong>Dhananjay</strong></a>, who introduced me to the JavaScript community, himself a 12-times MVP. <a href="https://www.linkedin.com/in/deepakrajendran/"><strong>Deepak Rajendran</strong></a> who is a constant motivation to share more of what we do. The rest of the community who gives the opportunities to learn and grow together with others.</p><p><a href="https://mvp.microsoft.com/en-us/PublicProfile/5003753"><strong><em>Here is my MVP profile</em></strong></a><strong><em>.</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/462/1*vz07OPLx4lvuGCTZ3o_hSQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hlASaFTOEm2QKhNIHl1uYQ.png" /><figcaption>MVP award announcement</figcaption></figure><p>Hope to continue to keep learning, sharing, growing. Listing some of my contributions from the past year:</p><ul><li><a href="https://github.com/NishuGoel/svelte-i18next"><strong>OSS — Created a svelte wrapper for using i18next</strong></a></li><li><a href="https://www.ng-ind.com/#speakers"><strong>Speaker — Microfrontends at ngIndia 2022</strong></a></li><li><a href="https://medium.com/epilot/dabbling-with-epilot-dev-4165a58b13f1"><strong>Blog — Work/Life update</strong></a></li><li><a href="https://meetup.com/microsoft-reactor-bengaluru/events/285817329/"><strong>Meetup Session — How the web/JavaScript has evolved over the years</strong></a></li><li><a href="https://web.dev/gde-focus-nishu-goel/"><strong>GDE Community Highlight</strong></a></li><li><a href="https://dev.to/nishugoel/svelte-i18next-1108"><strong>Blog — How to: Svelte i18next</strong></a></li><li><a href="https://almanac.httparchive.org/en/2021/javascript"><strong>Author — JavaScript 2021 Web Almanac</strong></a></li><li><a href="https://open.spotify.com/episode/2C4KYDkRroA6Uc2QevdhLt"><strong>Podcast (Spotify, Apple, Google) — Unraveling the Web with me</strong></a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ZMM8hscM0btD3r9N.gif" /><figcaption>thank you image</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e682566b41bf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Matching faces to Twitter DPs #ngIndia]]></title>
            <link>https://medium.com/epilot/matching-faces-to-twitter-dps-ngindia-522c279c4af2?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/522c279c4af2</guid>
            <category><![CDATA[code]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Thu, 26 May 2022 23:09:11 GMT</pubDate>
            <atom:updated>2022-10-07T16:16:25.000Z</atom:updated>
            <content:encoded><![CDATA[<p>Guess what I am talking about?</p><p>Year 2022, this was after more than 2 years that we got to be at an in-person conference, and gosh, the experience! I am forced to accept that we were simply compromising when being a part of the online conferences.</p><p>I think conferences were anyway never about only consuming the content delivered from the sessions. It was majorly about the networking, the warmth from meeting people. So when we went to ngIndia 2022 event this May, it was about meeting those people from online bonds from the past two years and connecting NOT from behind the screens.</p><p>So, 21st May, I got the opportunity to speak at #ngIndia on the topic ‘<em>Microfrontends’. </em>Now I chose this topic because I am currently working with micro-frontends at <a href="https://epilot.cloud/"><strong>@epilotGmbH</strong>,</a> and what could be better than talking about the success and challenges of using micro-frontends for large-scale apps than this.</p><p>So my preparation did include solving issues related to, for example, cross-micro-frontend communication AT WORK and coming back home and trying to explain the problem and its solution from the day through my slides.</p><p>Finally I decided to do my talk in the form of a role-play with <a href="http://twitter.com/srashtisj"><strong>Srashti Jain</strong></a> to help audience understand better.</p><blockquote>Me — I wish I could work for this company but I haven’t used X framework for a while now.</blockquote><blockquote><a href="https://medium.com/u/8b5edc9f57e5">Srashti Jain</a> (as a recruiter) — oh that’s not a problem at all! Our product is based on a <a href="https://twitter.com/hashtag/microfrontend?src=hashtag_click">micro-frontend</a> architecture. We’d love to hire you to work on your choice of framework and build our awesome product.</blockquote><blockquote>Me — Wow that sounds interesting! So I don’t need to skill up in another framework (X framework) now to contribute to this product. Awesome!</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O6b1VdM_WD0yFNbKc4N2PQ.jpeg" /><figcaption>microfrontends role-play</figcaption></figure><p>So this is how I began my talk to explain how micro-frontends made scaling up teams and projects framework-agnostic. But thats not it, I am sharing my slides here to share a sense of what was discussed, however, we will soon have the recorded talk up live and ready to be shared here.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fdocs.google.com%2Fpresentation%2Fembed%3Fid%3D1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8%26size%3Dl&amp;display_name=Google+Docs&amp;url=https%3A%2F%2Fdocs.google.com%2Fpresentation%2Fd%2F1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8%2Fedit&amp;image=https%3A%2F%2Flh5.googleusercontent.com%2FL5FgMrzd3OKFwN6vjqhnQ5Zhne9ZPbf3luYlemZ8KDerWch3UwMRS5wwBoAmGpu9vi-mhrjHySIYgQ%3Dw1200-h630-p&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=google" width="700" height="559" frameborder="0" scrolling="no"><a href="https://medium.com/media/e3e01749e80be58c586ac6cbb058c055/href">https://medium.com/media/e3e01749e80be58c586ac6cbb058c055/href</a></iframe><p>My experience speaking about micro-frontends was beyond my expectations, and I had such important conversations with many engineers from the audience. Our discussions revolved around how developers could use single-spa framework to deal with their micro-frontend architecture as the top level router, around how developers faced routing issues in their current single-spa MFEs, how some developers desired to switch to micro-frontends to migrate from legacy codebases like that of AngularJS etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nSnfM2BJxyVplGVnTqlBNA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lsZhYbrdC1T8QEH96QFStQ.jpeg" /><figcaption>Discussion &amp; networking sessions</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4GBbJVjPJm4sHtNVWdZ1hw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HQ1sKiFRRbR-4GTAqpbQ7w.jpeg" /><figcaption>Discussion &amp; networking sessions</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vO0Lw66uIzLix_lA_YEsNw.jpeg" /><figcaption>a happy picture with the volunteers and the attendees</figcaption></figure><p>The conference had more than 500 people joining in the fun, with the right element of over-coffee networking, over-lunch chit-chat, after-talk questions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s3AfiVvB8S4-S0N8-HKbMw.jpeg" /><figcaption>500 attendees with the organiser</figcaption></figure><p>Speakers from all over the world shared interesting findings during their talks. The conference was followed by an after party where the speakers and the volunteers partied together with food, drinks, music, dance and what not!<br>During the pre-conference dinner, the speakers were felicitated with gifts and swags from ngIndia organiser, <a href="http://twitter.com/debug_mode">Dhananjay Kumar</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*chlDq2wqnk6LyDvv" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8NEKSMhHjZfdSAN5dcbHyg.jpeg" /><figcaption>Speakers felicitation</figcaption></figure><p>I always love to speak at ngIndia and looking at the love from people who attend this event yearly, I am proud to be a home grown speaker of ngIndia, saying this since this is where my foray into speaking at events to share my learning started.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AxviKZXCCbDAQ9j9JPw84Q.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0naRxhW_ns99Uk6XmYRxEg.jpeg" /><figcaption>My speaking moments at ngIndia</figcaption></figure><blockquote><strong>Here are some of the captured moments from the conference.</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lGI6xrTw_ZRRxe7YLgZEeg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y7mpchYvF-HiSKPepEqGRQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hoJQ7zR1I0f8KJTuBdbH9A.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T5sHugae03j6deN2g89grA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c1CX8uLzH8mAr669u-nyrA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GjP1PCbNhW-SQKFPhtPfUg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DYNzx0khrcRhatMAUttlAQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H4AnmXQk0sEU1J_YBSQHhA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M4YMxn2fV7x0ffE-xLVHcw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ytaZ2yzXGSmsp0sXS27SIQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-opQVvxvwZmDb0eWMhHidA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3O27AQ3k0Pjbxe_52-dDXQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7weW5utlbgru9h2IXPfFIQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dNPaG3YhQ48Rdoxa_0bnDg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PWAp9BvMgfFsPavNMLO24g.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Nn4cI8_PFy9NTGC8_lUaNA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FI5lXWd_DL49ZMjYvF0VqQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Lwn5AX51dg30TrM3NjHCOA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w8KdEiPOFvaaUENLuUW-PQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vWqR1RdfoKQxBSKf4HO2hA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qfBQLggOlYSe2nbgZ_BuQQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gruzvOEtNmx8Q1_O8mMH1Q.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9vhn0id_ijcfkyMp2zQqew.jpeg" /></figure><blockquote>Thank you!!!</blockquote><blockquote>Stay tuned for the talk recordings :)</blockquote><blockquote><a href="https://www.ng-ind.com/"><strong>https://www.ng-ind.com/</strong></a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=522c279c4af2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epilot/matching-faces-to-twitter-dps-ngindia-522c279c4af2">Matching faces to Twitter DPs #ngIndia</a> was originally published in <a href="https://medium.com/epilot">epilot</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[svelte-i18next]]></title>
            <link>https://medium.com/epilot/svelte-i18next-e0c6591a8fcc?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/e0c6591a8fcc</guid>
            <category><![CDATA[svelte]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[sveltejs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Thu, 07 Apr 2022 18:36:59 GMT</pubDate>
            <atom:updated>2022-04-07T18:36:59.280Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KFYA-Kbaq2TsHcQNsiQr7g.png" /><figcaption>svelte-i18next</figcaption></figure><p>Presenting the svelte wrapper for i18next.</p><p>This library, based on i18next, wraps an i18next instance inside a svelte store and observes the i18next events like languageChanged, resource added, etc. to be able to render our svelte components.</p><p><strong>Package:-</strong></p><p><a href="https://www.npmjs.com/package/svelte-i18next">svelte-i18next</a></p><p><strong>GitHub:-</strong></p><p><a href="https://github.com/NishuGoel/svelte-i18next">GitHub - NishuGoel/svelte-i18next: Internationalization for svelte framework. Based on i18next ecosystem</a></p><p>The implementation looks like this:</p><p>The package creates a Svelte writable of i18next and listens to the events triggered by any updates on the i18next instance.<br>For example, if an application loads a new namespace for their translations, the svelte_i18next package will trigger the <br>i18next.on(‘loaded’, function(loaded) {}) event.</p><p>Check the <a href="https://www.i18next.com/overview/api#events"><strong>i18next events</strong></a> here.</p><p><strong>Usage:</strong></p><p><em>i18n.js</em></p><pre>import i18next from &quot;i18next&quot;;<br>import { createI18nStore } from &quot;svelte-i18next&quot;;<br><br>i18next.init({<br>   lng: &#39;en&#39;,<br>   resources: {<br>     en: {<br>       translation: {<br>         &quot;key&quot;: &quot;hello world&quot;<br>       }<br>     }<br>   }<br>});<br><br>export const i18n = createI18nStore(i18next);</pre><p><em>App.svelte</em></p><pre>&lt;script&gt;<br>  import i18n from &#39;./i18n.js&#39;;<br>&lt;/script&gt;<br><br>&lt;div&gt;<br>    {$i18n.t(&#39;key&#39;)}}<br>&lt;/div&gt;</pre><p>See a example usage here: <a href="https://github.com/NishuGoel/svelte-i18next/tree/main/example"><strong>Svelte example</strong></a></p><p>You could also use namespaces as you would using i18next and the svelte wrapper will re-render the translations based on the namespace provided.</p><pre>import { createI18nStore } from &#39;svelte-i18next&#39;</pre><pre>const i18n = createI18nStore(i18n_instance) <br>i18n.loadNamespaces([&#39;example&#39;]) // this triggers the re-render and loads translations from the provided namespace.</pre><pre>&lt;div&gt;<br>    {$i18n.t(key)}<br>&lt;/div&gt;</pre><p>Into svelte? Dealing with localisation?</p><blockquote><a href="https://twitter.com/intent/tweet?text=%40sveltejs%20wrapper%20for%20i18next%20%0Ahttps%3A%2F%2Fgithub.com%2FNishuGoel%2Fsvelte-i18next%0Avia%20%40TheNishuGoel%20%20&amp;hashtags=i18next,sveltejs,svelte,javascript,webdev">Show some love @svelte-i18next</a> ❤</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e0c6591a8fcc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epilot/svelte-i18next-e0c6591a8fcc">svelte-i18next</a> was originally published in <a href="https://medium.com/epilot">epilot</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[dabbling with ____ @epilot-dev]]></title>
            <link>https://medium.com/epilot/dabbling-with-epilot-dev-4165a58b13f1?source=rss-68bccf9d3a63------2</link>
            <guid isPermaLink="false">https://medium.com/p/4165a58b13f1</guid>
            <category><![CDATA[web]]></category>
            <category><![CDATA[careers]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[germany]]></category>
            <dc:creator><![CDATA[Nishu Goel]]></dc:creator>
            <pubDate>Sun, 20 Mar 2022 11:04:36 GMT</pubDate>
            <atom:updated>2022-03-21T10:16:40.558Z</atom:updated>
            <content:encoded><![CDATA[<h3>dabbling @epilot-dev with ______</h3><p>Year 2022 marked an amazing start for me with a new stint with <a href="https://epilot.cloud/"><strong>epilot GmbH</strong></a> based out of Cologne, Germany. I joined the team to be working on their micro-frontend architecture, with my primary focus going to be on the performance aspects of the applications. <br>Like the first recent addition we did after I started was to add <a href="https://github.com/google/brotli">brotli compression</a> to each of our micro-frontend (MFEs) bundles.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CvujCz8dT83A8mMzbBNhEA.png" /></figure><p>Let’s look at the tech stack involved.</p><h4>Micro services</h4><p>The main epilot app is built using microfrontends with each entity as its own application. There is a login application that takes care of the user login state (built using <em>React + TypeScript</em>), a topbar application that has features like Global search, notifications (<em>React + TypeScript</em>), a sidebar application that navigates the user to different entities based on user permissions (<em>Svelte + TypeScript + Tailwind</em>), and so on.</p><p>The component library of the product is based on material UI, built with React.</p><p><a href="https://www.npmjs.com/package/@epilot/base-elements">@epilot/base-elements</a></p><p>Due to the open source experience of many engineers in the team, most of the non-confidential stuff is kept public on GitHub and npm.<br>One of the very interesting things I got to do to ramp up was to build a part of the product as a Svelte MFE. With most of the existing MFEs built using react, this was quite interesting and challenging to do.</p><p>We use <a href="http://single-spa.js.org/"><strong>single-spa</strong></a> framework to build our microfrontends and use different frameworks per the ease and knowledge of an engineer. To make the development journey easier for a new developer starting up with microfrontends, we have a mfe-app-generator package that creates a boilerplate application with the required config.</p><p><a href="https://www.npmjs.com/package/@epilot360/mfe-app-generator">@epilot360/mfe-app-generator</a></p><p>Due to performance reasons, one of the goals was to switch to rollup for bundling all our applications, however, we use webpack due to <a href="https://github.com/aws/aws-sdk-js/issues/1769">rollup’s known issues with </a><a href="https://github.com/aws/aws-sdk-js/issues/1769">aws-s</a>dk.</p><p>We use serverless microservices architecture exposed via APIs. Most of the backend services are written in TypeScript using AWS lambda functions, APIs, Cloudfront distribution etc. More on that <a href="https://docs.epilot.io/docs/architecture/overview">here</a>.</p><p>With Svelte added to our microfrontend stack, we plan to further diversify the frameworks to be used, ensuring to not lose focus on the performance aspect of the application. <a href="https://single-spa.js.org/docs/ecosystem">Here’s a list of support frameworks by single-spa.</a></p><p>I also plan to share our journey from a high <a href="https://web.dev/lcp/">LCP (largest contentful paint)</a> to a much reduced one, but I’d keep it for another blog post.</p><p>When working with custom implementation and bundling, one of the challenging tasks is to configure your bundler and ensure it is consistent throughout all the applications (MFEs in this case). We have defined a @epilot360/webpack-config-epilot360 to base one’s webpack config on top of the existing basic config defined.</p><p><a href="https://www.npmjs.com/package/@epilot360/webpack-config-epilot360">@epilot360/webpack-config-epilot360</a></p><p>I will share more on the webpack configuration for microfrontends and cache invalidation strategies in another post but <em>cache invalidation</em> is definitely my major ongoing focus area.</p><h4>Epilot SDK</h4><p>All public epilot services are available through an epilot-sdk (written in JS/TS) that allows developers to interact with epilot APIs. Internally as well, the recommended way to use the services or APIs using directly the SDK instead of going the granular way.</p><p>For e.g.,</p><pre>import { getClient } from &#39;epilot-sdk/entity-client&#39;</pre><p>Want to scratch your itch? Find it here:</p><p><a href="https://www.npmjs.com/package/@epilot/epilot-sdk">@epilot/epilot-sdk</a></p><h4>Communicating between microfrontends</h4><p>One of the interesting challenges I recently faced was to communicate between two applications without any shared library. One was the sidebar built using Svelte and another the topbar built with React. Now we usually tend to avoid any such communication between our MFEs but this was a state clear event to happen before a routing change in any of the two MFEs.</p><p>I used single-spa’s custom events to do this. The idea was to clear the selected state of an icon in the topbar when an icon in the sidebar was selected, and vice versa.<br>To do this, I used the single-spa:before-routing-event custom event to check if the active URL in the current application was undefined, we could clear the state, if however, the URL existed, that becomes the active state. <br>Checking the URL history without communicating between two applications was not possible due to two separate routers for each application. Svelte app using the svelte-routing package where with React, the react-router .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tbIakQdOsZdWqMkEuAr3TA.png" /></figure><p><a href="https://single-spa.js.org/docs/api/#custom-events">Read more about the single spa custom events here</a>.</p><p>Here’s a detailed view of the tech stack used at epilot:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V7VGeO-iDvUbnYFgC577mQ.png" /><figcaption><a href="https://docs.epilot.io/techradar/">https://docs.epilot.io/techradar/</a></figcaption></figure><p><a href="https://docs.epilot.io/techradar/">https://docs.epilot.io/techradar</a></p><p>I have shared more info on my journey to epilot as a full-stack developer in my Spotify Podcast here:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fopen.spotify.com%2Fembed%2Fepisode%2F2C4KYDkRroA6Uc2QevdhLt%3Futm_source%3Doembed&amp;display_name=Spotify&amp;url=https%3A%2F%2Fopen.spotify.com%2Fepisode%2F2C4KYDkRroA6Uc2QevdhLt&amp;image=https%3A%2F%2Fi.scdn.co%2Fimage%2Fab67656300005f1f7a4e3efc454293a4e51f85f1&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=spotify" width="456" height="152" frameborder="0" scrolling="no"><a href="https://medium.com/media/409b43bc664626fc7ab18bcfce3dd3cc/href">https://medium.com/media/409b43bc664626fc7ab18bcfce3dd3cc/href</a></iframe><p>Interested to know more about how we do things at epilot? Reach out to me here: <a href="http://twitter.com/@TheNishuGoel">twitter.com/@TheNishuGoel</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4165a58b13f1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/epilot/dabbling-with-epilot-dev-4165a58b13f1">dabbling with ____ @epilot-dev</a> was originally published in <a href="https://medium.com/epilot">epilot</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>