<?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>Forem: Justine</title>
    <description>The latest articles on Forem by Justine (@justinem).</description>
    <link>https://forem.com/justinem</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%2F1216432%2Fa79f96ae-c0d5-4052-b90b-5071c24f9e00.png</url>
      <title>Forem: Justine</title>
      <link>https://forem.com/justinem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/justinem"/>
    <language>en</language>
    <item>
      <title>Never worry again about your GraphQL API breaking changes</title>
      <dc:creator>Justine</dc:creator>
      <pubDate>Thu, 14 Mar 2024 09:38:17 +0000</pubDate>
      <link>https://forem.com/justinem/never-worry-again-about-your-graphql-api-breaking-changes-ie9</link>
      <guid>https://forem.com/justinem/never-worry-again-about-your-graphql-api-breaking-changes-ie9</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;We deploy our API and our frontend at the same time. It means our new frontend contains the necessary changes to match the new API ⇒ great ✅.&lt;br&gt;
🚨 But a user can keep their browser opened with the previous frontend code version, as a consequence the previous frontend version calls the new API, and if there are breaking changes in the modifications ⇒ it breaks 💥.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq859j9fj3pf7erhwx2i0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq859j9fj3pf7erhwx2i0.png" alt="Breaking change illustration" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our previous solution 
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;🚨 Spoiler: not the one we recommend now, scroll down to '&lt;strong&gt;Our new solution&lt;/strong&gt;' if you want to go straight to the point&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a long time, here is how we have handled our breaking changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;deprecate&lt;/strong&gt; the concerned field in the API instead of deleting/modifying it&lt;/li&gt;
&lt;li&gt;add the new one&lt;/li&gt;
&lt;li&gt;merge the pull request &lt;/li&gt;
&lt;li&gt;wait for 7 days: totally arbitrary timing, we considered that after 1 week, all our users would have refreshed their frontend&lt;/li&gt;
&lt;li&gt;remove the deprecated field&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros and cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🟢 no impact at all for most users&lt;/li&gt;
&lt;li&gt;🔴 not efficient for developers (2 pull requests, with a week delay between it)&lt;/li&gt;
&lt;li&gt;🔴 not perfect: the 7 days delay is arbitrary, some users can still face a bug&lt;/li&gt;
&lt;li&gt;🔴 we often forgot to clean the deprecated field: legacy code remaining in the codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wanted to improve that situation.&lt;br&gt;
Here are the solutions we considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;a href="https://blog.hubspot.com/website/api-versioning"&gt;API versioning&lt;/a&gt;: too costly to put in place versus the gain + GraphQL does not recommend it (see &lt;a href="https://graphql.org/learn/best-practices/#versioning"&gt;best practices&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://blog.logrocket.com/versioning-fields-graphql/"&gt;Fields versioning&lt;/a&gt;: forces you to maintain legacy code in the backend&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Automatically refresh the user frontend when they face an API breaking change&lt;/strong&gt;: this is the solution we chose and I will explain below how we implemented it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our new solution
&lt;/h2&gt;

&lt;p&gt;👉 Automatically refresh the user frontend when they face an API breaking change&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faouqncvqih9yk2xfxyxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faouqncvqih9yk2xfxyxm.png" alt="Automatic window reload" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to 
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In your &lt;strong&gt;ApolloClient&lt;/strong&gt;, if you do not have one already, you will need to add an &lt;strong&gt;errorLink&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2vh92w5mnwy0ar5h1kg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2vh92w5mnwy0ar5h1kg.png" alt="error link" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build this errorLink thanks to the given onError link furnished by &lt;a class="mentioned-user" href="https://dev.to/apollo"&gt;@apollo&lt;/a&gt; in &lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/apollo"&gt;@apollo&lt;/a&gt;/client/link/error&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/client/link/error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;graphQLErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graphQLErrors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* We are targeting here GraphQL API breaking changes 
    * due to schema changes which correspond to code 
    * 'GRAPHQL_VALIDATION_FAILED'
    */&lt;/span&gt; 
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphQLErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
          &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GRAPHQL_VALIDATION_FAILED&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;definitions&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;def&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DefinitionNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;OperationDefinitionNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;def&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OperationDefinition&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="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mutation&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMutation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* In case of a mutation being affected 
        * by the breaking change,
        * as the data they entered will not be saved
        * we decided to display a pop up to the user 
        * so they can collect their data before losing it 
        * to avoid frustration, if it was a long text for example.
        */&lt;/span&gt; 
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sorry, the page needs to refresh to work properly.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;** You'll need to enter your latest data again **&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Note that you can:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1. Click on 'Cancel'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;2. Copy your data&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3. Refresh the page&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;4. Paste your data back&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Sorry again for the inconvenience.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;This will lead to improvements for you down the road.&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros and cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🟢 no mental load for the developers (API fields can be modified/deleted with peace of mind)&lt;/li&gt;
&lt;li&gt;🟢 the legacy code is removed right  away&lt;/li&gt;
&lt;li&gt;🟠 small impact on user experience: users can face an auto reload or a pop up in the case of a mutation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  One last thing
&lt;/h3&gt;

&lt;p&gt;Do not forget to stop your error alerting on 'Validation errors' if you had some, because you won't need it anymore.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you have any feedback, please feel free to comment! Thanks 🙏&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building my first browser extension: should I use a framework?</title>
      <dc:creator>Justine</dc:creator>
      <pubDate>Wed, 22 Nov 2023 17:44:41 +0000</pubDate>
      <link>https://forem.com/justinem/building-my-first-browser-extension-should-i-use-a-framework-43e6</link>
      <guid>https://forem.com/justinem/building-my-first-browser-extension-should-i-use-a-framework-43e6</guid>
      <description>&lt;p&gt;A few months ago, the Product team I work with came up with this new project in mind: let’s create a Browser extension to help our users.&lt;br&gt;
All the tech team got excited about it as it was new to all of us.&lt;br&gt;
But then, during our first technical refinement, one of the developers raised a very good question: should we use a framework or not?&lt;/p&gt;

&lt;p&gt;So here is this article, to help you with this question if you face the same situation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS: This article will focus solely on Chrome, as it is currently the only browser we have experience with.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Spoiler ⚠️
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We decided to use a framework!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We chose &lt;a href="https://www.plasmo.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Plasmo&lt;/em&gt;&lt;/a&gt; because the other options available were not suitable for us:&lt;br&gt;
— &lt;a href="https://crxjs.dev/" rel="noopener noreferrer"&gt;&lt;em&gt;CRXJS&lt;/em&gt;&lt;/a&gt;: Chrome specific, we had the ambition to deploy to other browsers in the future;&lt;br&gt;
— &lt;a href="https://www.bedframe.dev/" rel="noopener noreferrer"&gt;&lt;em&gt;Bedframe&lt;/em&gt;&lt;/a&gt;: early access only.&lt;/p&gt;

&lt;p&gt;In this article, I will compare building an extension with &lt;em&gt;Plasmo&lt;/em&gt; and without it.&lt;br&gt;
&lt;em&gt;Please feel free to compare the same bullet points on Bedframe and CRXJS, and share them in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is why we chose to use a framework:&lt;/p&gt;

&lt;h3&gt;
  
  
  Better developer experience: live reload
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Without Plasmo framework:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuq4u6r2pog1ve620fak.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuq4u6r2pog1ve620fak.gif" alt="Without Plasmo framework"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With Plasmo framework:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuxq15ul8j9eqbw0galp.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuxq15ul8j9eqbw0galp.gif" alt="With Plasmo framework"&gt;&lt;/a&gt; &lt;br&gt;
With Plasmo, you have got live reloading.&lt;br&gt;
Whereas without it, you need to close and reopen the extension to see your changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Easier QA validation
&lt;/h3&gt;

&lt;p&gt;Chrome extensions are subject to a &lt;a href="https://developer.chrome.com/docs/webstore/review-process/" rel="noopener noreferrer"&gt;review&lt;/a&gt; to be published. It can take a few days to be reviewed (depending on the content of your extension).&lt;br&gt;
But then how do you do QA?&lt;/p&gt;

&lt;p&gt;Here are the 2 ways:&lt;br&gt;
&lt;strong&gt;Without &lt;em&gt;Plasmo&lt;/em&gt; framework:&lt;/strong&gt;&lt;br&gt;
— the developer bundles the extension =&amp;gt; got a zip file&lt;br&gt;
— the developer sends it to QA&lt;br&gt;
— QA manually uploads the new file in &lt;a href="https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked" rel="noopener noreferrer"&gt;Chrome extension developer mode&lt;/a&gt;&lt;br&gt;
✅ Free&lt;br&gt;
🚨 Error-prone and time-consuming for both dev and QA teams&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With &lt;em&gt;Plasmo&lt;/em&gt; framework:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Plasmo&lt;/em&gt; proposes a service (&lt;a href="https://docs.plasmo.com/itero" rel="noopener noreferrer"&gt;&lt;em&gt;Plasmo Itero&lt;/em&gt;&lt;/a&gt;) to have a staging environment for your extension.&lt;br&gt;
Process: at merge, a CD job is automatically run, it builds and deploys the browser extension on a private store. QA testers install the extension once, and then just need to update the extension on &lt;em&gt;chrome://extensions/&lt;/em&gt; to get the last update.&lt;br&gt;
✅ Fully automated process&lt;br&gt;
🟠 Paid service: $65/month&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic permissions management
&lt;/h3&gt;

&lt;p&gt;One of our learning on Chrome publication is “The more &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions" rel="noopener noreferrer"&gt;permissions&lt;/a&gt; your browser extension asks, the more time the Google review will take”&lt;br&gt;
(and from our experience it can be quite long, for us it was often 1 week)&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92p3ocaahq89vot6c2si.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%2F92p3ocaahq89vot6c2si.png" alt="Us waiting the Google review"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a consequence, you do not want to mess up by asking for permissions that are not required.&lt;br&gt;
It is yet another &lt;em&gt;Plasmo&lt;/em&gt; framework benefit: as the &lt;em&gt;manifest.json&lt;/em&gt; file (the file containing all your browser extension configuration, permissions included) is generated by &lt;em&gt;Plasmo&lt;/em&gt;, permissions asked by your extension reflect the permissions actually needed by the codebase, nothing more.&lt;/p&gt;

&lt;h3&gt;
  
  
  One codebase for all browsers
&lt;/h3&gt;

&lt;p&gt;One promise of &lt;em&gt;Plasmo&lt;/em&gt; is: “you can build an extension once and easily &lt;a href="https://docs.plasmo.com/framework/workflows/build#with-a-specific-target" rel="noopener noreferrer"&gt;target it to multiple browsers&lt;/a&gt;”.&lt;br&gt;
We did not experience it, so I can not give feedback on that point. But that was an additional point that made us choose to use &lt;em&gt;Plasmo&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easier communication with external services
&lt;/h3&gt;

&lt;p&gt;Finally, you will probably need your extension to communicate with external services (your API, external APIs, etc).&lt;br&gt;
If you call the external API naively from the current page, chances are you will run into a &lt;strong&gt;CORS issue&lt;/strong&gt;: your request origin is the current page and your API is expecting another origin. So to bypass this, you can use a &lt;strong&gt;background service worker&lt;/strong&gt; to proxy the communication with your API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Without framework:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhcsmj0ijs2w6d189brc.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%2Frhcsmj0ijs2w6d189brc.png" alt="Without framework"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With &lt;em&gt;Plasmo&lt;/em&gt;:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgceq6emqwp8k6t10z69g.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%2Fgceq6emqwp8k6t10z69g.png" alt="With Plasmo"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Plasmo&lt;/em&gt; defines listeners and does the cleanup for you.&lt;/li&gt;
&lt;li&gt;Again, &lt;em&gt;Plasmo&lt;/em&gt;‘s code is not Chrome specific&lt;/li&gt;
&lt;li&gt;Code organisation: your messages are split into different files, and &lt;em&gt;manifest.json&lt;/em&gt; background key is generated automatically for you&lt;/li&gt;
&lt;li&gt;Message name is type-safe, which can save you not pleasant debugging time:
&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%2Fy4ajug1friaw4qm4oclk.png" alt="type safety on message name"&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Before I leave you, one advice:
&lt;/h2&gt;

&lt;p&gt;I advise you to try a first publication to Chrome Web Store as soon as possible (you will be able to publish it as Private as a first step).&lt;br&gt;
It will allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the list of elements required by the store (description, category, images with precise sizes, etc) and ask the right team in your company to give you those elements on time for your expected release in production.&lt;/li&gt;
&lt;li&gt;Get a sense of the time a publication review can take.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you have any questions, please comment on this article, I hope we can have an answer to help you with our experience of building a browser extension.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>extensions</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
