<?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: Oles Maiboroda</title>
    <description>The latest articles on DEV Community by Oles Maiboroda (@omaiboroda).</description>
    <link>https://dev.to/omaiboroda</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%2F125985%2Fa09257b3-2c7b-4e50-8f10-2c8df922d7cd.jpeg</url>
      <title>DEV Community: Oles Maiboroda</title>
      <link>https://dev.to/omaiboroda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omaiboroda"/>
    <language>en</language>
    <item>
      <title>Stale-while-revalidate and it's usage with Next.js</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Thu, 22 Aug 2024 16:18:36 +0000</pubDate>
      <link>https://dev.to/omaiboroda/stale-while-revalidate-and-its-usage-with-nextjs-55c7</link>
      <guid>https://dev.to/omaiboroda/stale-while-revalidate-and-its-usage-with-nextjs-55c7</guid>
      <description>&lt;p&gt;Stale while revalidate (SWR) is an established pattern for quick data display while refreshing data in the background. It is popularised by SWR library &lt;a href="https://www.npmjs.com/package/swr" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/swr&lt;/a&gt;, but it roots comes from &lt;a href="https://datatracker.ietf.org/doc/html/rfc5861" rel="noopener noreferrer"&gt;HTTP spec&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache-Control: max-age=600, stale-while-revalidate=30, stale-while-error=30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern fits well for content that doesn't update often like blogs, static pages, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  SWR in Next.js
&lt;/h2&gt;

&lt;p&gt;Next.js promotes ISR, which is an alternative to SWR. SWR is not supported in Vercel CDN, and this is the reason you might want to &lt;a href="https://dev.to/omaiboroda/vercel-doesnt-want-you-to-pull-out-2047"&gt;pull out from Vercel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next.js itself requires some configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  SWR delta
&lt;/h2&gt;

&lt;p&gt;To make SWR work in Next.js, you'd need to specify &lt;code&gt;swrDelta&lt;/code&gt; in next.config.js, which is available from next@14&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;swrDelta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31536000&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;This is the value, for how long does server considers the content &lt;em&gt;stale&lt;/em&gt; and responds immediately to it. It is a common pattern to have this value high (ie 1 year), so at any point, we can react quickly and prefetch the latest content in the background.&lt;/p&gt;

&lt;p&gt;It's essentially our &lt;code&gt;stale-while-revalidate&lt;/code&gt; value in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Max-age
&lt;/h2&gt;

&lt;p&gt;This value specifies how long the server would respond without even checking the origin server.&lt;/p&gt;

&lt;p&gt;In next.js, it is set via the &lt;code&gt;revalidate&lt;/code&gt; constant, as with ISR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's essentially our &lt;code&gt;max-age&lt;/code&gt; value in seconds.&lt;/p&gt;

&lt;p&gt;As you can't modify &lt;code&gt;cache-control&lt;/code&gt; in next.js, this is the only way to set the value.&lt;/p&gt;

&lt;p&gt;This value is relatively short 1 hour to 1 day, depending on how active development is - in case of new features or bugfixes you still want to deliver quickly and propagate the changes.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;stale-while-revalidate&lt;/code&gt; you can afford not to invalidate the CDN cache on redeploy! After redeploying, your changes will propagate in &lt;code&gt;max-age&lt;/code&gt; time (ie 1hr).&lt;/p&gt;

&lt;h2&gt;
  
  
  Boosting with CDN
&lt;/h2&gt;

&lt;p&gt;As abovementioned, Vercel doesn't support SWR, so there are some popular alternatives:&lt;/p&gt;

&lt;p&gt;Fastly: supports SWR ✅ &lt;a href="https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today/" rel="noopener noreferrer"&gt;https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare doesn't support SWR ❌ &lt;a href="https://community.cloudflare.com/t/support-for-stale-while-revalidate/496788" rel="noopener noreferrer"&gt;https://community.cloudflare.com/t/support-for-stale-while-revalidate/496788&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudfront: supports since 2023 ✅ &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/05/amazon-cloudfront-stale-while-revalidate-stale-if-error-cache-control-directives/" rel="noopener noreferrer"&gt;https://aws.amazon.com/about-aws/whats-new/2023/05/amazon-cloudfront-stale-while-revalidate-stale-if-error-cache-control-directives/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example on Cloudfront
&lt;/h2&gt;

&lt;p&gt;No setup is required, as CDN either recognizes headers from origin or not and just respects the instructions in cache control.&lt;/p&gt;

&lt;p&gt;Let's check now the first visit and response from Cloudfront:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x-cache: Miss from cloudfront
ttfb: 0.332219
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subsequent visit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;age: 642
x-cache: Hit from cloudfront
ttfb: 0.066109
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit when content is considered stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;age: 4001
x-cache: RefreshHit from cloudfront
ttfb: 0.024514
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;RefreshHit&lt;/code&gt; Cloudfront responds immediately and makes call to the origin server asynchronously.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>cdn</category>
      <category>cache</category>
    </item>
    <item>
      <title>Vercel Doesn’t Want You to Pull Out</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Tue, 06 Aug 2024 17:31:14 +0000</pubDate>
      <link>https://dev.to/omaiboroda/vercel-doesnt-want-you-to-pull-out-2047</link>
      <guid>https://dev.to/omaiboroda/vercel-doesnt-want-you-to-pull-out-2047</guid>
      <description>&lt;p&gt;I have been working with Vercel for years and love it, although it's started to frustrate me over the last few months. Here are some points that made us decide to pull out, even though Vercel doesn't want you to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Struggles with Vercel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weird CDN
&lt;/h3&gt;

&lt;p&gt;The TTFB occasionally hiccups to 250ms+. These requests were responding &lt;code&gt;x-vercel-cache: HIT&lt;/code&gt;, and it's the same page. This slow, which is quite weird.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F53c3kLs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F53c3kLs.png" alt="CDN quirks"&gt;&lt;/a&gt;&lt;/p&gt;
Looks like CDN goes to origin, yet still responds with HIT



&lt;h3&gt;
  
  
  Overwhelming of pay-per-request
&lt;/h3&gt;

&lt;p&gt;I know it's the essence of SaaS, but it's overwhelming how many items you can be billed for, including &lt;a href="https://x.com/shoeboxdnb/status/1643639119824801793" rel="noopener noreferrer"&gt;horror stories&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As for the latest changes, Vercel is rolling out Data Cache (which is a great feature), starting to bill it, and &lt;a href="https://nextjs.org/blog/next-15-rc#caching-updates" rel="noopener noreferrer"&gt;changing the default data cache policy from cache-all to no-cache&lt;/a&gt; between Next.js 14 and Next.js 15. This doesn't contribute to my confidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FhFmtwHk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FhFmtwHk.png" alt="Vercel pay-per-request"&gt;&lt;/a&gt;&lt;/p&gt;
Overwheling amount of billing parameters at vercel.com



&lt;h3&gt;
  
  
  Support has not been great
&lt;/h3&gt;

&lt;p&gt;I have submitted three support requests over the last few months (a bug in log drain exports, elaboration on data/deployment cache), and none of them were satisfactory. After message ping-pong on every ticket, being responded to by multiple support engineers, I just gave up on my hopes of being helped. At some point, I see that further discussion is pointless and close the ticket. I hope this is not a support strategy!&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs filtering DX
&lt;/h3&gt;

&lt;p&gt;Personally, I find the Logs tab in Vercel confusing, with limited capabilities to filter and occasional very similar records going side-by-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if we self-host?
&lt;/h2&gt;

&lt;p&gt;All these points led us to pull out from Vercel and self-host Next.js. But it's not amazing - &lt;code&gt;Cache-Control&lt;/code&gt; header is optimized only for Vercel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache-Control headers are locked-in
&lt;/h3&gt;

&lt;p&gt;You simply &lt;a href="https://github.com/vercel/next.js/issues/22319" rel="noopener noreferrer"&gt;cannot set &lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt; header on pages. Next.js locks them in for you, saying they know what's best—optimizing for deployment on Vercel:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  For ISR, default max-age is one year
&lt;/h3&gt;

&lt;p&gt;Next.js casually &lt;a href="https://github.com/vercel/next.js/issues/22319" rel="noopener noreferrer"&gt;sets a one-year(!) cache&lt;/a&gt; for your HTML if you don't explicitly specify the &lt;code&gt;revalidate&lt;/code&gt; value.&lt;/p&gt;

&lt;h3&gt;
  
  
  stale-while-revalidate was introduced not per spec until Next.js 14
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; directive was provided &lt;a href="https://github.com/vercel/next.js/issues/51823" rel="noopener noreferrer"&gt;without a value&lt;/a&gt;. This led to incompatibility with other CDNs—only Vercel could understand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we?
&lt;/h2&gt;

&lt;p&gt;We have managed to overcome this and moved to self-hosting, SWR, and Cloudfront - a setup which I will share soon.&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Guide to Side effects in Hasura</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Tue, 14 Mar 2023 08:04:37 +0000</pubDate>
      <link>https://dev.to/omaiboroda/guide-to-side-effects-in-hasura-5a1g</link>
      <guid>https://dev.to/omaiboroda/guide-to-side-effects-in-hasura-5a1g</guid>
      <description>&lt;p&gt;When dealing with Hasura, it can quickly come to the side-effects of doing something less trivial than using only predefined upserts/inserts.&lt;/p&gt;

&lt;p&gt;Most often, this comes to &lt;em&gt;operations with databases, calling external API, or doing hefty validations&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Hasura offers multiple ways of achieving it, though these methods have their own advantages and use cases, here are them:&lt;/p&gt;

&lt;h2&gt;
  
  
  Postgres triggers
&lt;/h2&gt;

&lt;p&gt;Hasura heavily relies on the database functionality, and in the case of Postgres, there is a possibility to achieve desired functionality with &lt;a href="https://hasura.io/docs/latest/schema/postgres/postgres-guides/functions/" rel="noopener noreferrer"&gt;Postgres functions and triggers&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;insert_initial_album&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;PLPGSQL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt;
      &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;artist_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'first album'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;artist_after_insert&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt;
  &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;PROCEDURE&lt;/span&gt; &lt;span class="n"&gt;insert_initial_album&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to write &lt;a href="https://hasura.io/docs/latest/schema/postgres/data-validations/#pg-data-validations-pg-triggers" rel="noopener noreferrer"&gt;validations&lt;/a&gt; on a Database-level&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; powerful flexibility, database-native functionality.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; calling a 3rd-party API is impossible. Maintainance and visibility of Functions and Triggers can be a hassle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; you are good with SQL, a believer in "you don't need an ORM".&lt;br&gt;
&lt;strong&gt;When to avoid:&lt;/strong&gt; at large projects where observability of SQL can be an issue. SQL is not your strength.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multiple Mutations in a Request
&lt;/h2&gt;

&lt;p&gt;Given there's a relationship established in Hasura, there's a possibility to &lt;a href="https://hasura.io/docs/latest/mutations/postgres/multiple-mutations/" rel="noopener noreferrer"&gt;modify nested objects&lt;/a&gt;, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;insert_album&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"First album"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bono"&lt;/span&gt;&lt;span class="p"&gt;}}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;returning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, Hasura would create Album and Artist, and assign artist_id to the newly created Artist&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; out-of-the-box functionality, single transaction.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; lacking flexibility, if required to do more business logic on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; fits your simple use-case.&lt;br&gt;
&lt;strong&gt;When to avoid:&lt;/strong&gt; rarely, if it does solve the problem - use it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Triggers
&lt;/h2&gt;

&lt;p&gt;Another option is an &lt;a href="https://hasura.io/docs/latest/event-triggers/index/" rel="noopener noreferrer"&gt;Event trigger&lt;/a&gt; - webhook reaction on a database change&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flebpox1dw6skh06cptv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flebpox1dw6skh06cptv3.png" alt="Creating an Event Trigger"&gt;&lt;/a&gt;&lt;/p&gt;
Creating an Event Trigger



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; out-of-the-box functionality, ability to subscribe for particular CRUD events.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; it's not a transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; effects, that doesn't require integrity, for instance: analytics update, sending emails, notifications.&lt;br&gt;
&lt;strong&gt;When to avoid:&lt;/strong&gt; no atomicity - operations are executed separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hasura Actions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hasura.io/docs/latest/actions/index/" rel="noopener noreferrer"&gt;Actions&lt;/a&gt; is a mechanism for customizations, where custom queries or mutations can be implemented in webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8nc8czbes85sb65g6dm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8nc8czbes85sb65g6dm.png" alt="Creating new Hasura Action"&gt;&lt;/a&gt;&lt;/p&gt;
Creating new Hasura Action



&lt;p&gt;This action would instruct Hasura to invoke handler: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81g5nx2c8opczmpumebz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81g5nx2c8opczmpumebz.png" alt="Example of webhook handler"&gt;&lt;/a&gt;&lt;/p&gt;
Example of webhook handler



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; programming language runtime, flexibility, ability to do 3rd-party calls, and access to Database.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; cumbersome to set up handlers, types for every action needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; you have to do a few manual operations, and you have a server in place (ie next) that can handle the requests.&lt;br&gt;
&lt;strong&gt;When to avoid:&lt;/strong&gt; you need lots of side effects (then add Remote schema) or when it's possible to use the abovementioned solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote schema
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hasura.io/docs/latest/remote-schemas/index/" rel="noopener noreferrer"&gt;Remote schema&lt;/a&gt; is a variation on Actions, but you bring your own additional Graphql Schema. Adding it to Hasura, will extend existing Schema with Queries/Mutations where you have full control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn4fw4k1f8oph4a72zad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn4fw4k1f8oph4a72zad.png" alt="Creating new Remote Schema"&gt;&lt;/a&gt;&lt;/p&gt;
Creating new Remote Schema



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; easy integration with Hasura.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; you'd need to maintain your own GraphQL Server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; you already have a GQL server, or the application demands lots of non-trivial code.&lt;br&gt;
&lt;strong&gt;When to avoid:&lt;/strong&gt; your app is small and doesn't require a standalone GQL server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;These were 5 ways of achieving similar results in Hasura:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postgres triggers&lt;/li&gt;
&lt;li&gt;Multiple Mutations in a Request&lt;/li&gt;
&lt;li&gt;Hasura Actions&lt;/li&gt;
&lt;li&gt;Event Triggers&lt;/li&gt;
&lt;li&gt;Remote schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of them has its strengths and weaknesses, and you can use them successfully in combination.&lt;br&gt;
Hope this was helpful to pick the ones that suit you the best.&lt;/p&gt;

</description>
      <category>hasura</category>
      <category>postgres</category>
      <category>grahpql</category>
      <category>mutations</category>
    </item>
    <item>
      <title>Shorten your feedback loop</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Fri, 18 Dec 2020 17:08:44 +0000</pubDate>
      <link>https://dev.to/omaiboroda/shorten-your-feedback-loop-53f2</link>
      <guid>https://dev.to/omaiboroda/shorten-your-feedback-loop-53f2</guid>
      <description>&lt;p&gt;One of the key satisfiers in the development experience is knowing the results of your actions immediately.&lt;br&gt;
  That's why apart from the constant aim of simplifying everything as a software developer, there can be an aim of a constant shortening the feedback loop.&lt;/p&gt;

&lt;p&gt;A short feedback loop is a fundamental idea in some core programming practices, such as TDD.&lt;/p&gt;

&lt;p&gt;In the perspective of frontend development, here's what you can consider:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Shorten UI feedback during development.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pay attention to the time it takes between the UI change in development mode and its reflection in the browser.&lt;br&gt;
  Ensure you're leveraging &lt;a href="https://www.npmjs.com/package/react-refresh"&gt;react-refresh&lt;/a&gt; and use proper &lt;a href="https://webpack.js.org/configuration/devtool/"&gt;webpack devtool config&lt;/a&gt; if you're on this ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Know how to simulate the state of the app, i.e. to stay on the same page on refresh during development.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the growing complexity of the application, the number of states, in which this application can is growing as well.&lt;br&gt;
  It is worth to consider a mechanism for state simulation, like &lt;a href="https://kentcdodds.com/blog/make-your-own-dev-tools/"&gt;make your own dev tools&lt;/a&gt;&lt;br&gt;
  so you can quickly jump to a certain app state, and/or stay in it during development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Know how to Deploy the application on the test environment from your local machine.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depending on your setup, this can save you from running build or test steps on CI just to quickly check some ideas.&lt;br&gt;
  Even better: know how to simulate the test environment locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Know how to run and scope tests locally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than waiting on CI to check if the tests are green, know how to run your unit/integration tests and scope them to certain files/suits for faster outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Know how to test things without redeploying the app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, to validate ideas, there's no need to deploy the app on a test environment - leverage browsers' devtools features like blocking API requests or local overrides. This way you got some answers to your questions immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Ensure IDE assists you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seeing fast lint-check or type-check errors right in IDE gives you great (probably the fastest) feedback about potential problems. Make sure your lint &amp;amp; type-check solutions are integrated tightly with the IDE.&lt;/p&gt;

&lt;p&gt;For a delightful developer experience, speed of ideas validation is a key player for your mental health and sometimes sets the mood for the whole day. Make "shorten your feedback loop" a motto for your setups, don't be lazy to think about how can you validate ideas faster and enjoy your work even more.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>feedbackloop</category>
    </item>
    <item>
      <title>Try to update your dependencies with Renovate or Dependabot</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Tue, 08 Dec 2020 17:06:32 +0000</pubDate>
      <link>https://dev.to/omaiboroda/try-to-update-your-dependencies-once-a-month-5dm5</link>
      <guid>https://dev.to/omaiboroda/try-to-update-your-dependencies-once-a-month-5dm5</guid>
      <description>&lt;p&gt;Keeping dependencies up-to-date is an important part of healthy projects, but often overlooked in Frontend (due to a lower security risk?) compared to backend or infrastructure.&lt;/p&gt;

&lt;p&gt;Having the latest versions of libraries gives you and your teammates&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security confidence&lt;/li&gt;
&lt;li&gt;Performance optimizations&lt;/li&gt;
&lt;li&gt;Bugfixes&lt;/li&gt;
&lt;li&gt;Access to latest features &amp;amp; new APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Often though, its importance is overlooked, which may lead up to critical level problems, when the project cannot work in the required environment (new browsers, or new requirements from app stores in case of mobile application).&lt;/p&gt;

&lt;p&gt;Dependency update strategy though may vary according to the size of your team, kind of the product you're working on, etc. In some larger companies, there can be also special departments and private repositories for dependencies.&lt;/p&gt;

&lt;p&gt;Working in a middle-sized team of developers (and non-health/wealth risk projects) we have tried 2 approaches for the updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Per library, live updates
&lt;/h3&gt;

&lt;p&gt;First what comes to mind is Dependabot, now a "native" solution from Github. Dependabot will open PRs once a new version of the library is in.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How to enable:&lt;/em&gt; visit &lt;a href="//app.dependabot.com"&gt;app.dependabot.com&lt;/a&gt; and select repositories you want to add.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pricing:&lt;/em&gt; free of charge.&lt;/p&gt;

&lt;p&gt;Once enabled, Dependabot will start to open PRs for new versions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fc9hBaTD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fc9hBaTD.png" alt="Imgur"&gt;&lt;/a&gt;&lt;/p&gt;
dependabot in Pull requests tab



&lt;p&gt;Dependabot will open PRs with the following contents&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FlG2FlCd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FlG2FlCd.png" alt="Imgur"&gt;&lt;/a&gt;&lt;/p&gt;
dependabot's Pull request description



&lt;h3&gt;
  
  
  Batch update, once a month
&lt;/h3&gt;

&lt;p&gt;Soon we've realized live updates per library are too verbose.&lt;br&gt;
In the case of Dependabot there was no chance to set up PRs to be open only for major updates, therefore another attempt with Renovate:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How to enable:&lt;/em&gt; visit &lt;a href="//renovatebot.com"&gt;renovatebot.com&lt;/a&gt; and select repositories you want to add.&lt;br&gt;
&lt;em&gt;Pricing:&lt;/em&gt; free of charge.&lt;/p&gt;

&lt;p&gt;It is possible to ask Renovatebot to open batch PR once a month&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fw2R9gJg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fw2R9gJg.png" alt="Imgur"&gt;&lt;/a&gt;&lt;/p&gt;
renovate in Pull requests tab



&lt;p&gt;With the following configuration, Renovate will open monthly PR with the following contents&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F9vTRp2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F9vTRp2m.png" alt="Imgur"&gt;&lt;/a&gt;&lt;/p&gt;
renovate's Pull request description



&lt;p&gt;Here's renovate.json configuration we settled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;extends&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config:base&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group:all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Regular updates&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;schedule:monthly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// Random assignee of PR&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assigneesFromCodeOwners&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assigneesSampleSize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;separateMinorPatch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packageRules&lt;/span&gt;&lt;span class="dl"&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="c1"&gt;// Only major and minor updates&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updateTypes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// Encrypted NPM token for private packages&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encrypted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npmToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w/0FYXCXO1aYkUeivEFbB7yG/CPM4xC2ksGVLLua0rNMYyEF8bxlrYqMtJqriuG53oecATlrSzgh06/U3sYkA2ZNgHabyoinZWc+fI0rchCCgSH0EXIeRifZH+nRNY69e5EleMOT0wNrkYlSuWN0U0uOU53ZZB+bojKPEpWhULZTOC6nXsN5WOrYdP0T1Tw==&lt;/span&gt;&lt;span class="dl"&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;Some other recommendations for the deps updates duties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find a balance so updates are regular, but not too verbose.&lt;/li&gt;
&lt;li&gt;Random assign update PR to one of the contributors.&lt;/li&gt;
&lt;li&gt;Check libraries that got a major update for new APIs and features.&lt;/li&gt;
&lt;li&gt;Write update log, if some libraries were impossible to update.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>dependencies</category>
      <category>dependabot</category>
      <category>renovate</category>
    </item>
    <item>
      <title>Frontend 360</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Sun, 28 Jul 2019 13:42:59 +0000</pubDate>
      <link>https://dev.to/omaiboroda/frontend-360-35no</link>
      <guid>https://dev.to/omaiboroda/frontend-360-35no</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://oles.dev/posts/frontend-360" rel="noopener noreferrer"&gt;https://oles.dev/posts/frontend-360&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I needed to organize my thoughts on Frontend so I've ended up with a mindmap that I've ambitiously named as Frontend 360.&lt;br&gt;
The system can help you to evaluate the state of the projects and point to the strengths and weaknesses.&lt;/p&gt;

&lt;p&gt;Mindmap can be categorized into 3 subcategories - Peers, Self and Users&lt;/p&gt;

&lt;h2&gt;
  
  
  Peers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbVGauNK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbVGauNK.png" alt="Peers"&gt;&lt;/a&gt;&lt;/p&gt;
Areas of Frontend Peers



&lt;p&gt;Frontend cannot live in the vacuum itself. As a touchpoint with the customers, it has interdependencies with other domains.&lt;br&gt;
The Peers category relates to the domains relative to the Frontend - &lt;strong&gt;Testing, DevOps, Backend, Design, Content, Marketing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How fast the project builds and how easy are deploys?&lt;/li&gt;
&lt;li&gt;Does the application has enough test coverage to prevent bugs?&lt;/li&gt;
&lt;li&gt;Is the bad API decisions leak into Frontend?&lt;/li&gt;
&lt;li&gt;Is the UI/UX you have in the application is consistent?&lt;/li&gt;
&lt;li&gt;Are the translation/content mechanisms pleasure to work with?&lt;/li&gt;
&lt;li&gt;Are the user tracking reports error-free?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Self
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FLqoYd3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FLqoYd3h.png" alt="Self"&gt;&lt;/a&gt;&lt;/p&gt;
Areas of Frontend itself



&lt;p&gt;The second section relates to ourselves, Frontend code maintainers with covering following domains - &lt;strong&gt;Code, Documentation, Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does codebase has decent documentation?&lt;/li&gt;
&lt;li&gt;Are there any JS errors on production?&lt;/li&gt;
&lt;li&gt;How do you react on performance regressions?&lt;/li&gt;
&lt;li&gt;How big is the technical debt?&lt;/li&gt;
&lt;li&gt;Are all our dependencies up-to-date?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Users
&lt;/h3&gt;

&lt;p&gt;The last, but not least - Frontend from user's perspective - &lt;strong&gt;Accessibility, Performance, Security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the service you provide is inclusive?&lt;/li&gt;
&lt;li&gt;Is the speed in your app is a UX bottleneck?&lt;/li&gt;
&lt;li&gt;Is our users' data and your application secure?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fb5R3jPJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fb5R3jPJ.png" alt="Peer"&gt;&lt;/a&gt;&lt;/p&gt;
Areas of users



&lt;h3&gt;
  
  
  All together
&lt;/h3&gt;

&lt;p&gt;These questions and sections can build a good picture of the state of Frontend&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FUWCuhtt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FUWCuhtt.png" alt="Frontend 360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All these aspects are important to keep a healthy project that will bring the joy to work with for you, your peers and your users.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>overview</category>
      <category>mindmap</category>
    </item>
    <item>
      <title>During PR review - do you check in into the branch?</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Sat, 27 Jul 2019 16:02:16 +0000</pubDate>
      <link>https://dev.to/omaiboroda/during-pr-review-do-you-check-in-into-the-branch-d4j</link>
      <guid>https://dev.to/omaiboroda/during-pr-review-do-you-check-in-into-the-branch-d4j</guid>
      <description>&lt;p&gt;Have found a very fundamental post on Code review &lt;a href="https://charcoalbin.com/posts/code-review.html"&gt;https://charcoalbin.com/posts/code-review.html&lt;/a&gt; where Authour states that it is needed to check the functionality of PR.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Usability Test&lt;br&gt;
Confirm if the PR achieves its goal, visually and functionally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have thought about this idea, but actually never tried, mostly because of time. &lt;br&gt;
Do some of you follow it, what's your perspective?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>codereview</category>
      <category>pr</category>
    </item>
    <item>
      <title>Why I love Frontend: two triangles</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Tue, 19 Feb 2019 21:41:20 +0000</pubDate>
      <link>https://dev.to/omaiboroda/why-i-love-frontend-two-triangles-4pm5</link>
      <guid>https://dev.to/omaiboroda/why-i-love-frontend-two-triangles-4pm5</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nBNNN1-X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/JtbFPNA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nBNNN1-X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/JtbFPNA.png" alt="The beauty of Frontend" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;
The beauty of Frontend



&lt;p&gt;Not much to add: the chance to work Product ⇆ Frontend ⇆ Design and Backend ⇆ Frontend ⇆ DevOps - that's what make our position unique.&lt;/p&gt;

</description>
      <category>frontend</category>
    </item>
    <item>
      <title>Control the colors in your project</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Fri, 08 Feb 2019 12:56:55 +0000</pubDate>
      <link>https://dev.to/omaiboroda/control-the-colors-in-your-project-5gf4</link>
      <guid>https://dev.to/omaiboroda/control-the-colors-in-your-project-5gf4</guid>
      <description>&lt;p&gt;Sometimes you may face this kinds of projects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wGqIh2t0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/667goev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wGqIh2t0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/667goev.png" alt="Example"&gt;&lt;/a&gt;&lt;/p&gt;
Hard case, huh?



&lt;p&gt;which bring you a little discomfort in the stomach. Today we'll talk about colors in particular and how they can be organized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit
&lt;/h2&gt;

&lt;p&gt;One of the great options is to use online tools, like cssstats.com&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CltUZJ1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/mNhdfLX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CltUZJ1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/mNhdfLX.png" alt="Example"&gt;&lt;/a&gt;&lt;/p&gt;
dev.to on cssstats.com



&lt;p&gt;The issue with it that it shows only the colors that appear on the single page, so if you have code-splitting, some colors might be missing.&lt;br&gt;
    I had a need something that would just show the colors used in the source files, but there wasn't anything, so I had to write a small utility &lt;a href="https://github.com/omaiboroda/colors-in"&gt;colors-in&lt;/a&gt;. It traverses the folder end extracts used colors:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MLvNdmbi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/X7ayPGN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MLvNdmbi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/X7ayPGN.png" alt="colors-in"&gt;&lt;/a&gt;&lt;/p&gt;
Colors in src folder



&lt;h2&gt;
  
  
  Fix
&lt;/h2&gt;

&lt;p&gt;Having this you can look at your the designs UI Kit or talk to your design to define the colors you're gonna use across the project.&lt;/p&gt;

&lt;p&gt;At that point, we'd a need to "merge" similar colors and there's one approach you can do it. Since all colors are living in 3-dimensional &lt;code&gt;Red, Green, Blue&lt;/code&gt; space, we can calculate the distance between them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iSeZaN4P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://wikimedia.org/api/rest_v1/media/math/render/svg/06cdd86ced397bbf6fad505b4c4d91fa2438b567" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iSeZaN4P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://wikimedia.org/api/rest_v1/media/math/render/svg/06cdd86ced397bbf6fad505b4c4d91fa2438b567" alt="colors-in"&gt;&lt;/a&gt;&lt;/p&gt;
Euclidean distance



&lt;p&gt;From my empirical feeling, those colors that are having less than 10% color difference can be deprecated in favor of more often used in a pair.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ATEt0B2H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/eQ7DHi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ATEt0B2H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/eQ7DHi7.png" alt="colors-in"&gt;&lt;/a&gt;&lt;/p&gt;
Difference between two shades of gray with npmjs.com/package/color-difference



&lt;p&gt;Other things you can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unify color format to hex;&lt;/li&gt;
&lt;li&gt;Agree on 6 digits hex format;&lt;/li&gt;
&lt;li&gt;Remove unnecessary opacity layer;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case we were able to reduce the number of unique color definitions from 29 to 14:&lt;br&gt;
Before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NJRD0Say--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/zmwhwPP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NJRD0Say--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/zmwhwPP.png" alt="colors-in"&gt;&lt;/a&gt;&lt;/p&gt;
29 color definitions



&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6RDxljn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/9BDCvPg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6RDxljn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/9BDCvPg.png" alt="colors-in"&gt;&lt;/a&gt;&lt;/p&gt;
14 color definitions



&lt;p&gt;So if you have that "zoo" - make an Audit. Or if you keep your colors in an organized way - check if you forgot to use it somewhere else with &lt;code&gt;&amp;gt;colors-in src&lt;/code&gt;;&lt;/p&gt;

</description>
      <category>colors</category>
      <category>extract</category>
      <category>hex</category>
      <category>rgb</category>
    </item>
    <item>
      <title>Editing web translations right in your browser</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Wed, 09 Jan 2019 20:28:12 +0000</pubDate>
      <link>https://dev.to/omaiboroda/editing-web-translations-right-in-your-browser-1nhp</link>
      <guid>https://dev.to/omaiboroda/editing-web-translations-right-in-your-browser-1nhp</guid>
      <description>&lt;p&gt;Sometimes GIF replaces 1000 words:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPPDZrBn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FPPDZrBn.gif" alt="In-Context Editor"&gt;&lt;/a&gt;&lt;/p&gt;
looks interesting, right?



&lt;p&gt;&lt;a href="https://demo.phraseapp.com/" rel="noopener noreferrer"&gt;DEMO&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having this, you give your translator 100% understanding of the translation context.&lt;/p&gt;

&lt;p&gt;The feature is usually called &lt;code&gt;In-Context Editor&lt;/code&gt; (for instance in &lt;a href="https://docs.locize.com/more/incontext-editor" rel="noopener noreferrer"&gt;Locize&lt;/a&gt; or &lt;a href="https://help.phraseapp.com/translate-website-and-app-content/use-in-context-editor-to-translate/translate-directly-on-your-website" rel="noopener noreferrer"&gt;Phraseapp&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It's worth to say you need PhraseApp Pro subscription for it. In pair to that, we also use &lt;code&gt;lingui-js&lt;/code&gt; as a translation library.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to enable
&lt;/h2&gt;

&lt;p&gt;All you need to is&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add PhraseApp In-Context Editor snippet to your page&lt;/li&gt;
&lt;li&gt;Format messages into the proper format.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're in the Javascript world, I have wrote a &lt;a href="https://www.npmjs.com/package/lingui-phraseapp" rel="noopener noreferrer"&gt;lingui-phraseapp&lt;/a&gt;, which helps you to do these two steps, so your integration may look alike&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { initializePhraseAppEditor, initializePhraseAppEditor } from "lingui-phraseapp";
import catalog from './locales/de/messages.js';

const configInContextEditor = {
  projectId: REACT_APP_PHRASEAPP_PROJECT_ID,
  autoLowercase: false,
  prefix: "--__",
  suffix: "__--",
  phraseEnabled: !!localStorage.getItem("inContextEditor");
};

...
&amp;lt;!-- Add snippet --&amp;gt;
initializePhraseAppEditor(configInContextEditor);

&amp;lt;!-- Format messages --&amp;gt;
const catalogFormatted = configInContextEditor.phraseEnabled
  ? transformCatalog(catalog, configInContextEditor)
  : catalog;

const i18n = setupI18n({ catalogs: { de: catalogFormatted });
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see from above to enable Editor set &lt;code&gt;inContextEditor: true&lt;/code&gt; in the LocalStorage. After that, the modal for username and password will appear.&lt;/p&gt;

&lt;p&gt;There are also implementations for other popular translation libraries &lt;a href="https://github.com/phrase/react-intl-phraseapp" rel="noopener noreferrer"&gt;react-intl-phraseapp&lt;/a&gt;, &lt;a href="https://github.com/phrase/react-i18next-phraseapp" rel="noopener noreferrer"&gt;react-i18next-phraseapp&lt;/a&gt;, although integration itself is pretty straightforward and simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most important question
&lt;/h2&gt;

&lt;p&gt;Now to the things that really matter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this brings value at all?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The translator we're working says she wanted it for 2 years, although we haven't started active translation phase yet. And you can't say for sure until you'll work with it on daily basis.&lt;/p&gt;

&lt;p&gt;I will give an update on this feature, stay tuned!&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>phraseapp</category>
      <category>incontexteditor</category>
      <category>translations</category>
    </item>
    <item>
      <title>Three ways to name i18n translation keys</title>
      <dc:creator>Oles Maiboroda</dc:creator>
      <pubDate>Fri, 04 Jan 2019 16:44:55 +0000</pubDate>
      <link>https://dev.to/omaiboroda/three-ways-to-name-i18n-translation-keys-2fed</link>
      <guid>https://dev.to/omaiboroda/three-ways-to-name-i18n-translation-keys-2fed</guid>
      <description>&lt;p&gt;Teams do internationalization of applications either from the very beginning or after delivering a primary language. Usually, the translation flow looks similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6lprarxv5bskyj2uf1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6lprarxv5bskyj2uf1h.png" alt="Translation flow"&gt;&lt;/a&gt;&lt;/p&gt;
Translation flow



&lt;p&gt;And from a developer perspective, it contains 3 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive content from designs, create keys and populate initial "en" translation;&lt;/li&gt;
&lt;li&gt;Announce keys to translators;&lt;/li&gt;
&lt;li&gt;Occasionally fetch latest translations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is pretty easy to create translation keys, but hard to do it in a way that it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes clear for translators what do they mean;&lt;/li&gt;
&lt;li&gt;Makes clear for developers what do they mean;&lt;/li&gt;
&lt;li&gt;Is looking good in source files;&lt;/li&gt;
&lt;li&gt;Avoids often context switching between translation files and sources;&lt;/li&gt;
&lt;li&gt;Helps developers to map code blocks with "physical" representations on the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are already good i18n guidelines written by &lt;a href="https://phraseapp.com/blog/posts/ruby-lessons-learned-naming-and-managing-rails-i18n-keys/" rel="noopener noreferrer"&gt;Phraseapp&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_content_best_practices" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;, but they do not answer how should we name the translation keys themselves.&lt;/p&gt;

&lt;p&gt;To abstract from implementation, we'll agree on translation function &lt;code&gt;t&lt;/code&gt;, which takes a translation key and returns message:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t('things') // =&amp;gt; "How things work"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Option 1  - Interesting
&lt;/h3&gt;

&lt;p&gt;You might have only custom defined keys, without defaults. For instance:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t('landing_page.how_things_work') // =&amp;gt; "How things work"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As for the initial translation, there will be a need to add initial messages to a translation file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't take much space in source files&lt;/li&gt;
&lt;li&gt;Gives enough context to a translator&lt;/li&gt;
&lt;li&gt;Ready to change over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In case if the key is too generic hard to track the relationship between the source and actual place of display on the page&lt;/li&gt;
&lt;li&gt;To set the initial message, you would need to switch from sources to the translation file&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 2  -  Bulletproof
&lt;/h3&gt;

&lt;p&gt;You can specify both - translation key and default message:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t({
  key: 'landing_page.how_things_work', 
  defaultMessage: "Things explained"
}) // =&amp;gt; "How things work"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexible - you can change your default message without being afraid it will cause issues;&lt;/li&gt;
&lt;li&gt;You have a connection between sources and actual visual representation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;May face duplication in short keys, for example &lt;code&gt;t({ key: 'general.errors.empty', defaultMessage: 'empty'})&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Bloats sources size with the id and message.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 3 - Straightforward.
&lt;/h3&gt;

&lt;p&gt;You can use the same text as your message in English:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t('How things work') // =&amp;gt; "How things work"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This way we're eliminating the necessity to maintain default messages and create keys for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers maintain single entity;&lt;/li&gt;
&lt;li&gt;Gives translator instant clarity what should be translated;&lt;/li&gt;
&lt;li&gt;You can use them as default messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It worth to add more context for a translator. This can be hints to the source code's filename (as in .po format) or in-context-editor;&lt;/li&gt;
&lt;li&gt;Sometimes you have to translate entire paragraphs, which make key too long;&lt;/li&gt;
&lt;li&gt;Requires extra solution for those English words, which have multiple meanings i.e. &lt;em&gt;to cry&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you might already understand, there's no 100% correct to define the translation keys and the decision should be made based on your project size, how often you change translations and team preference.&lt;/p&gt;

&lt;p&gt;At the current project, we've decided to give a shot for "#3 Straightforward" approach for sake of simplicity, ease of maintenance and less context switching.&lt;/p&gt;

&lt;p&gt;But how do you cook i18n keys?&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>translation</category>
      <category>localization</category>
      <category>namingconventions</category>
    </item>
  </channel>
</rss>
