<?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: Maxim Salnikov</title>
    <description>The latest articles on DEV Community by Maxim Salnikov (@webmaxru).</description>
    <link>https://dev.to/webmaxru</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%2F161711%2F961c53c5-fec1-41a7-aa4d-8555126efa23.jpg</url>
      <title>DEV Community: Maxim Salnikov</title>
      <link>https://dev.to/webmaxru</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/webmaxru"/>
    <language>en</language>
    <item>
      <title>Leveraging GitHub Copilot Chat syntax: chat participants, chat variables, slash commands</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Sun, 23 Jun 2024 14:44:42 +0000</pubDate>
      <link>https://dev.to/webmaxru/leveraging-github-copilot-chat-syntax-chat-participants-chat-variables-slash-commands-34c9</link>
      <guid>https://dev.to/webmaxru/leveraging-github-copilot-chat-syntax-chat-participants-chat-variables-slash-commands-34c9</guid>
      <description>&lt;p&gt;GitHub Copilot Chat is an incredibly powerful and useful feature that allows you to chat with or about your code. Even though it’s 100% natural language-friendly (i.e., you can send your messages without using any specific syntax), leveraging some special chat capabilities can unlock new AI-assisted development scenarios and significantly boost your productivity.&lt;/p&gt;

&lt;p&gt;These powerful features, which you can use by applying special syntax, include chat participants, slash commands, and context variables. Note that the described features are available in VS Code and might not be fully supported in other IDEs where GitHub Copilot Chat is available.&lt;/p&gt;

&lt;h1&gt;
  
  
  Target your question or request by messaging one of the available chat participants
&lt;/h1&gt;

&lt;p&gt;In GitHub Copilot Chat, you can reference one of the AI-powered “domain experts” using conventional chat syntax—by prepending @ to the participant name. The currently available chat participants are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;@workspace&lt;/code&gt;: Knows everything about the code in your currently open workspace. This is the chat participant you will most likely communicate with frequently.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@terminal&lt;/code&gt;: Knows all about the integrated terminal shell, its contents, and its buffer.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@vscode&lt;/code&gt;: Knows about the VS Code editor, its commands, and features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example: Let’s get information about the backend part of the project we’ve just been assigned to by asking the &lt;code&gt;@workspace&lt;/code&gt; chat participant right after we open the project folder in VS Code.&lt;/em&gt;&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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--Ww0R7c7a--%2Ff_auto%2Fv1719151849%2Fugc%2Fcontent_30347ab9-9a22-4f3f-b7a2-e8576f07d1ee" 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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--Ww0R7c7a--%2Ff_auto%2Fv1719151849%2Fugc%2Fcontent_30347ab9-9a22-4f3f-b7a2-e8576f07d1ee" alt="Picture1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this particular case, you don’t even need to have files open in your editor. Compare this with the response you get without tagging &lt;code&gt;@workspace&lt;/code&gt;:&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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--bnBy8tPh--%2Ff_auto%2Fv1719151864%2Fugc%2Fcontent_c8ea427d-0865-4215-b0c0-bf2de6c3d398" 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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--bnBy8tPh--%2Ff_auto%2Fv1719151864%2Fugc%2Fcontent_c8ea427d-0865-4215-b0c0-bf2de6c3d398" alt="Picture2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@workspace&lt;/code&gt; chat participant is instrumental for all solution-wide queries where you want all code to be considered for the chat response. However, this doesn't mean that all code will be used and sent as part of the prompt. The GitHub Copilot Chat extension in VS Code does its best to determine relevant files and parts of these files using local knowledge and intelligence first. You can check which files and code lines were used for the prompt by expanding the “Used references” line:&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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--jB5_6AUY--%2Ff_auto%2Fv1719151937%2Fugc%2Fcontent_063549a2-6e02-4eb4-a63b-85e92bf6b50a" 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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--jB5_6AUY--%2Ff_auto%2Fv1719151937%2Fugc%2Fcontent_063549a2-6e02-4eb4-a63b-85e92bf6b50a" alt="Picture3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Productivity hint: Use Ctrl-Enter (Cmd-Enter) instead of just Enter after typing your message, and the &lt;code&gt;@workspace&lt;/code&gt; string will be inserted into your message automatically before sending.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Be precise in setting the context using chat variables
&lt;/h1&gt;

&lt;p&gt;In many cases, considering the full solution as the context for your question or request (by using &lt;code&gt;@workspace&lt;/code&gt;) is overkill. You might want to point to specific files or even parts of the files in your message. Chat variables can help! Use # to call one from this list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;#file&lt;/code&gt;: Points to a specific file in your workspace.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#codebase&lt;/code&gt;: All content of the open workspace. It’s similar to using &lt;code&gt;@workspace&lt;/code&gt; and might be useful when you chat with another agent (like &lt;code&gt;@terminal&lt;/code&gt;) but still want to reference the full solution.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#editor&lt;/code&gt;: Source code in the editor’s viewport (visible part).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#git&lt;/code&gt;: Current git repository: branch, remotes, path, etc.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#selection&lt;/code&gt;: The currently selected code.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#terminalLastCommand&lt;/code&gt;: Last run command in the editor’s terminal.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;#terminalSelection&lt;/code&gt;: Selection in the editor's terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example: Let’s get help on improving method names in a specific file (and we want to ensure that the whole content of the file is taken into consideration).&lt;/em&gt;&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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--RUomZ8hU--%2Ff_auto%2Fv1719152018%2Fugc%2Fcontent_6bb7c1b7-e4e4-443e-93a5-55583186f5a3" 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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs--RUomZ8hU--%2Ff_auto%2Fv1719152018%2Fugc%2Fcontent_6bb7c1b7-e4e4-443e-93a5-55583186f5a3" alt="Picture4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Productivity hint: Use the up and down keyboard arrows to pick the chat variable you need after typing #. In the case of &lt;code&gt;#file&lt;/code&gt;, use keyboard navigation again to pick one of the suggested files.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Call the most often used actions quickly with slash commands
&lt;/h1&gt;

&lt;p&gt;Chatting with your code using natural language is fun, but having the option to call often-used actions using handy shortcuts is even better. Compare typing the full message “Explain how selected code works” versus typing “/”, then using keyboard arrows to pick &lt;code&gt;/explain&lt;/code&gt; from the popup overlay. Another benefit of using the predefined syntax for commands is the confidence that GitHub Copilot understands our intent 100% correctly (natural language might have some ambiguity). There are a bunch of slash commands available. You can use them in conjunction with referencing the chat participant to provide the desired scope. Some of the commands are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;/help&lt;/code&gt;: Help about available slash commands, chat participants, chat variables, and more.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;/doc&lt;/code&gt;: Generate documentation for the code.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;/explain&lt;/code&gt;: Explain how the code works (or get help with terminal commands if you prepend @terminal).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;/fix&lt;/code&gt;: Optimize and/or fix issues in the code.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;/tests&lt;/code&gt;: Create unit tests for the code.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;/new&lt;/code&gt;: Scaffold a new workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Example: Let’s get an explanation for one of the regular expressions in our code. Select the code line and use the slash command “&lt;br&gt;
&lt;code&gt;/explain&lt;/code&gt;.&lt;/em&gt;&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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs---AmsVwl9--%2Ff_auto%2Fv1719152091%2Fugc%2Fcontent_59927606-9031-4757-9abc-34a5eb609c70" 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%2Fres.cloudinary.com%2Fdaily-now%2Fimage%2Fupload%2Fs---AmsVwl9--%2Ff_auto%2Fv1719152091%2Fugc%2Fcontent_59927606-9031-4757-9abc-34a5eb609c70" alt="Picture5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Productivity hint: Try GitHub Copilot Chat in inline mode instead of having the chat always open in the side pane. Press Ctrl-I (Cmd-I) and type your message in the small overlay dialog that appears right above the line where your cursor is in the code window.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Use chat participants, chat variables, and slash commands to maintain full control over the conversation context, ensure correct and consistent understanding of your intentions, and ultimately chat and code faster!&lt;br&gt;
Start your free GitHub Copilot trial here: &lt;a href="https://aka.ms/try-github-copilot" rel="noopener noreferrer"&gt;https://aka.ms/try-github-copilot&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://github.blog/changelog/2024-01-30-code-faster-and-better-with-github-copilots-new-features-in-visual-studio/#slash-commands" rel="noopener noreferrer"&gt;https://github.blog/changelog/2024-01-30-code-faster-and-better-with-github-copilots-new-features-in-visual-studio/#slash-commands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.blog/changelog/2023-11-30-github-copilot-november-30th-update/" rel="noopener noreferrer"&gt;https://github.blog/changelog/2023-11-30-github-copilot-november-30th-update/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://code.visualstudio.com/docs/copilot/copilot-chat#_chat-participants" rel="noopener noreferrer"&gt;https://code.visualstudio.com/docs/copilot/copilot-chat#_chat-participants&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://devblogs.microsoft.com/visualstudio/copilot-chat-slash-commands-and-context-variables/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/visualstudio/copilot-chat-slash-commands-and-context-variables/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://code.visualstudio.com/updates/v1_85#_terminal-agent-and-command-suggestion-improvements" rel="noopener noreferrer"&gt;https://code.visualstudio.com/updates/v1_85#_terminal-agent-and-command-suggestion-improvements&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://code.visualstudio.com/updates/v1_84#_chat-agents" rel="noopener noreferrer"&gt;https://code.visualstudio.com/updates/v1_84#_chat-agents&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>aideveloper</category>
      <category>aiassistant</category>
    </item>
    <item>
      <title>Building oEmbeddr using Azure Static Web Apps with Svelte, Bootstrap 5, and TikTok!</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Tue, 03 Nov 2020 21:57:01 +0000</pubDate>
      <link>https://dev.to/azure/building-oembeddr-using-azure-static-web-apps-with-svelte-bootstrap-5-and-tiktok-384b</link>
      <guid>https://dev.to/azure/building-oembeddr-using-azure-static-web-apps-with-svelte-bootstrap-5-and-tiktok-384b</guid>
      <description>&lt;p&gt;Let's build a simple web application that uses posts embedding code generation API exposed by the social networks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TL;DR: In this chapter, you will learn about:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffolding a Svelte 3 app which performs a fetch request&lt;/li&gt;
&lt;li&gt;Adding Bootstrap 5 UI with using modern frontend techniques&lt;/li&gt;
&lt;li&gt;oEmbed format&lt;/li&gt;
&lt;li&gt;The very first step to build, deploy, and host your webapp on Azure Static Web Apps service&lt;/li&gt;
&lt;li&gt;The app will look like this:
&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%2Fi%2Fnm1rnja3nu2jcdglmd3k.png" alt="oEmbeddr v1"&gt;
&lt;a href="https://kind-sea-08852d11e.azurestaticapps.net/" rel="noopener noreferrer"&gt;oEmbeddr v1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To find the app idea, I explored a couple of quite extensive unofficial TikTok API projects but decided to keep things simple and go for the only web-focused official one dedicated to embedding videos using &lt;a href="https://oembed.com/" rel="noopener noreferrer"&gt;oEmbed&lt;/a&gt; format. I've never heard about oEmbed before but it seems like it's an industry-standard in sharing the content from social networks: there are &lt;a href="https://github.com/iamcal/oembed/tree/master/providers" rel="noopener noreferrer"&gt;248 providers&lt;/a&gt; available so far including all the major players.&lt;/p&gt;

&lt;p&gt;This format and corresponding API is too simple - a single GET endpoint. So I decided to add some extra complications to challenge myself a bit: for the frontend I will use the &lt;a href="https://svelte.dev" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; framework I've never tried before, and for the UI, I will use Bootstrap. Well, it's hard to find a frontender who built websites in the early 10s (pre-frameworks golden era) and never used Bootstrap. I personally actively used versions 2 and 3 of it before switching to framework-connected UI tools. But after a long break, I decided to check what's going on with this legendary UI library and discovered a &lt;a href="https://v5.getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap 5 Alpha 1&lt;/a&gt; version announced in mid-June 2020. As &lt;a href="https://blog.getbootstrap.com/2020/06/16/bootstrap-5-alpha/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; states, it's the first version without dependency on jQuery, including enhanced grid system, extensive usage of CSS variables, rearchitected form controls, and tons of other new features. It's hard to find a better opportunity to explore it and share my findings.&lt;/p&gt;

&lt;p&gt;Also, TikTok video embedding API will be just a starting point for us - the app I called oEmbeddr will eventually support all the providers mentioned in the &lt;strong&gt;oEmbed&lt;/strong&gt; repo. But let's keep this for the next articles. What do I want to achieve today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A SPA with only one section (so far) where we have the input for the social network post URL to share&lt;/li&gt;
&lt;li&gt;Sending a request to the corresponding API endpoint &lt;/li&gt;
&lt;li&gt;Displaying an HTML code we received, having "Copy to clipboard" feature, and a preview&lt;/li&gt;
&lt;li&gt;Responsive, accessible, satisfactory looking UI&lt;/li&gt;
&lt;li&gt;Zero configuration CI/CD workflow&lt;/li&gt;
&lt;li&gt;Globally distributed hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's go!&lt;/p&gt;

&lt;h1&gt;
  
  
  What is oEmbed and how to use it in TikTok
&lt;/h1&gt;

&lt;p&gt;Many if not all social networks have the functionality to get an HTML code to embed a particular post in some external resource like a blogpost. And all of them build this feature using oEmbed format co-created by &lt;a href="https://twitter.com/iamcal" rel="noopener noreferrer"&gt;Cal Henderson&lt;/a&gt;, current Slack CTO, and co-founder.&lt;/p&gt;

&lt;p&gt;From the UX perspective, some of the networks (like TikTok) have the corresponding button explicitly visible and getting the code is just a click away, other (like Facebook) redirect you to the developer portal and you have to learn about SDK, configuration, etc. before getting the code, thirds (like Twitter) just hide this feature from UI but support it.&lt;/p&gt;

&lt;p&gt;You can read the full oEmbed format specification &lt;a href="https://oembed.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt; but for our project, we'll use the simplest scenario: we call an API endpoint (GET method) where we pass a required parameter &lt;code&gt;url&lt;/code&gt;. In return, we receive a JSON with the HTML code in the &lt;code&gt;html&lt;/code&gt; field to embed. A page called &lt;a href="https://developers.tiktok.com/doc/Embed" rel="noopener noreferrer"&gt;Embed videos&lt;/a&gt; of the TikTok Developer portal informs us about supporting exactly this option. &lt;/p&gt;

&lt;p&gt;And... we are lucky with selecting TikTok as a very first provider for our demo: it's the only service from the social networks I tried which has the Cross-origin resource sharing (CORS) configured the way we can call this API endpoint straight from the browser. So we can keep building backend of oEmbeddr for the next articles.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scaffolding a Svelte app
&lt;/h1&gt;

&lt;p&gt;Why did I ever decide to use Svelte? First, I wanted to try it for a long time. And second, I'm not a big fan of the direct DOM manipulation. So I'm fine with having some code overhead and getting a nice developer experience in return.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you use the VS Code I &lt;strong&gt;strongly recommend&lt;/strong&gt; installing &lt;a href="https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode" rel="noopener noreferrer"&gt;Svelte extension&lt;/a&gt;. It provides syntax highlighting and rich intellisense for Svelte components in VS Code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After reading the &lt;a href="https://svelte.dev/blog/the-easiest-way-to-get-started" rel="noopener noreferrer"&gt;"Getting started" page&lt;/a&gt; I created a "hello world" template using &lt;strong&gt;degit&lt;/strong&gt; - a utility for the straightforward project scaffolding, also by &lt;a href="https://twitter.com/Rich_Harris" rel="noopener noreferrer"&gt;Rich Harris&lt;/a&gt;, creator of Svelte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx degit sveltejs/template oembeddr
cd oembeddr
npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Just a few moments and a ready-to-go dev environment created. Opening &lt;code&gt;http://localhost:5000/&lt;/code&gt; in the browser works fine. Running &lt;code&gt;npm run build&lt;/code&gt; generates a production code to be deployed in the &lt;em&gt;public&lt;/em&gt; folder (let's remember this name for later).&lt;/p&gt;

&lt;p&gt;After quick learning on how to &lt;a href="https://svelte.dev/tutorial/text-inputs" rel="noopener noreferrer"&gt;bind data&lt;/a&gt; from the input value (to pass the URL user provides to my fetch method) and &lt;a href="https://svelte.dev/tutorial/dom-events" rel="noopener noreferrer"&gt;handling DOM events&lt;/a&gt; (form submission in my case), I was ready to fetch the data from TikTok API.&lt;/p&gt;

&lt;p&gt;So, we have to send an HTTP request and update UI based on the response. This is where Svelte's reactivity and DX shine! I just followed this &lt;a href="https://svelte.dev/tutorial/await-blocks" rel="noopener noreferrer"&gt;async/await example&lt;/a&gt; from the tutorial to have a resulting code and markup in the core (and only for now) app component in the file &lt;em&gt;App.svelte&lt;/em&gt; like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And it works! Hereafter, I will use &lt;a href="https://www.tiktok.com/@webmaxru/video/6858249402683886854" rel="noopener noreferrer"&gt;my first ever TikTok video&lt;/a&gt; as an example. Please feel free to watch the magic transition of my cat to Octocat, and follow me :)&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%2Fi%2Fqzhzc1mlebtcnv1nimhq.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%2Fi%2Fqzhzc1mlebtcnv1nimhq.png" alt="Initial scaffold with fetch"&gt;&lt;/a&gt;&lt;br&gt;
I send a request, get results, display it in UI. All reactively, declaratively, without manual DOM manipulation. Svelte just works and the &lt;em&gt;bundle.js&lt;/em&gt; size after I run &lt;code&gt;npm run build&lt;/code&gt; is 6K. I find it acceptable.&lt;/p&gt;

&lt;p&gt;But we can't deploy a page with such a minimalistic design and poor UX. Let's use a UI library!&lt;/p&gt;
&lt;h1&gt;
  
  
  Adding Bootstrap 5
&lt;/h1&gt;

&lt;p&gt;Let's live on the edge and install a very first (and only to the moment) Alpha of this version: &lt;code&gt;npm install bootstrap@5.0.0-alpha1&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer. I'm aware of the awesome &lt;a href="https://sveltestrap.js.org/" rel="noopener noreferrer"&gt;Sveltestrap&lt;/a&gt; library with some really "Svelte-native" components based on Bootstrap. It might be a better technical decision for real-life projects. But my strong intention was to try Bootstrap 5 (Sveltestrap uses v4) in a "modern frontend" mode: with compiling Sass for theming, with importing only JS components we use, with outsourcing the build to Svelte dev environment. Also, I understand that by direct usage of Bootstrap JS components I partially lost the app code's reactivity and declarativity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Styles
&lt;/h2&gt;

&lt;p&gt;The package contains both built/complied assets and source code. I want to have full control over resulting CSS both in terms of components I include and theme I use, so let's find a way to compile Sass during the Svelte app build.&lt;/p&gt;

&lt;p&gt;Bootstrap uses PostCSS and Autoprefixer for Sass compilation and we can have these tools injected into the overall build flow by the magic of &lt;a href="https://github.com/sveltejs/svelte-preprocess" rel="noopener noreferrer"&gt;svelte-preprocess&lt;/a&gt; package that "acts as a facilitator to use other languages with Svelte, providing multiple features, sensible defaults, and less noisy development experience". To get this configured I follow &lt;a href="https://github.com/sveltejs/sapper/issues/474#issuecomment-521482255" rel="noopener noreferrer"&gt;this thread&lt;/a&gt;:&lt;br&gt;
1) Installing the packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D svelte-preprocess node-sass autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;2) Adding to the Rollup configuration file &lt;em&gt;rollup.config.js&lt;/em&gt; following lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import autoPreprocess from 'svelte-preprocess';

const preprocessOptions = {
    scss: {
      includePaths: [
        'node_modules',
        'src'
      ]
    },
    postcss: {
      plugins: [
        require('autoprefixer'),
      ]
    }
  }

export default {
    ...
    plugins: [
        svelte({
            ...
            },
            preprocess: autoPreprocess(preprocessOptions)
        }),
        ...
    ],
    ...
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It gives us possibility to use Sass syntax in the source code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;style type="text/scss"&amp;gt;
  $body-bg: #eeeeee;
  @import "bootstrap/scss/bootstrap";
  // and other Sass awesome features
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;I also recommend to &lt;a href="https://github.com/sveltejs/language-tools/blob/master/docs/preprocessors/scss-less.md#2-setting-up-a-svelte-configjs" rel="noopener noreferrer"&gt;set up Svelte for VS Code extension&lt;/a&gt; to have better Sass support in IDE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's good to know that specifying &lt;code&gt;node_modules&lt;/code&gt; in &lt;code&gt;includePaths&lt;/code&gt; gives us the possibility to skip adding &lt;em&gt;../node_modules/&lt;/em&gt; part of the import path which makes our source code look way cleaner.&lt;/p&gt;

&lt;p&gt;Then, I create &lt;em&gt;src/styles.scss&lt;/em&gt; file and follow the &lt;a href="https://v5.getbootstrap.com/docs/5.0/customize/sass/#importing" rel="noopener noreferrer"&gt;customization guide&lt;/a&gt; of Bootstrap. I import only the core configuration files and the files with the styles of components I will use: Nav, Navbar, Toast, Alert, etc. On top of the same file, I will override some color variables to give my app a unique style. The result (shortened a bit):&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;I reference this file in &lt;em&gt;App.svelte&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;style type="text/scss"&amp;gt;
  @import 'styles';
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, after the build, I see some Bootstrap classes in &lt;em&gt;bundle.css&lt;/em&gt; - a good sign! But the file size is too good to be true - 2 to 7K (depending on the amount of HTML markup you have so far). Bootstrap CSS with the number of components we specified can't be so lean. When we try to apply some Bootstrap classes to HTML markup the CSS bundle file size grows but the result looks somehow broken. What's wrong? Nothing. In addition to styles scoping, Svelte's compiler is removing unused (in this particular component) classes from the resulting CSS. But in case of the UI library (like Bootstrap or others), we have some classes applied to outer parts of this particular component even if it's the root one (for example, for &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag) and we want all classes to be available for all other components without scoping. I.e. we take a technical decision to plug in the whole UI library CSS file to Svelte's root component (&lt;em&gt;App.svelte&lt;/em&gt; in our example) "as is".&lt;/p&gt;

&lt;p&gt;And there is a solution for that! We can make the styles in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block fully or partially untouched by Svelte's scoping and optimizing engines by using a &lt;code&gt;global&lt;/code&gt; modifier like explained &lt;a href="https://github.com/sveltejs/svelte-preprocess#global-style" rel="noopener noreferrer"&gt;here&lt;/a&gt;. So I added it to the code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;style global type="text/scss"&amp;gt;
  @import 'styles';
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and everything related to Bootstrap styling started to work as intended.&lt;/p&gt;

&lt;p&gt;The obvious downsides of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Svelte can't optimize CSS by removing unused styles. We take care of it ourselves by importing only the parts of the UI library we need.&lt;/li&gt;
&lt;li&gt;Svelte can't prevent styles "leakage" from this component to all others. But in for the UI library, this behavior is exactly what we want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All clear with the styles now, we are ready to use Bootstrap classes in our markup!&lt;/p&gt;

&lt;p&gt;Adding some layout helpers, classes, extra attributes to the form...&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
...and the look &amp;amp; feel is completely different:&lt;br&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%2Fi%2F307ee7l396akwze2cp6f.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%2Fi%2F307ee7l396akwze2cp6f.png" alt="Bootstrap styles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript plugins
&lt;/h2&gt;

&lt;p&gt;Let's stop with the styles for now and have a look at Bootstrap's JavaScript plugins. For oEmbeddr, I decided to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tabbed content component (In the Bootstrap, it's called "Nav" in the component library, plus "Tab" in JavaScript plugins) to separate UI of the embed code (we'll place this code into an &lt;code&gt;textarea&lt;/code&gt;) and preview (we'll load this code into an &lt;code&gt;iframe&lt;/code&gt; using &lt;code&gt;srcdoc&lt;/code&gt; attribute)&lt;/li&gt;
&lt;li&gt;Floating message box ("Toast" in the Bootstrap) to show the success message after clicking the "Copy code to clipboard" button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setting up Nav/Tab is straightforward: just use the &lt;a href="https://v5.getbootstrap.com/docs/5.0/components/navs/#using-data-attributes" rel="noopener noreferrer"&gt;provided HTML code sample&lt;/a&gt; from the documentation with correct data attributes binding the "tab" and content it shows. For our scenario, there is no need in any JavaScript code except the module import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Tab from 'bootstrap/js/src/tab';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the modern browsers, we can use this import without any extra processing in &lt;code&gt;&amp;lt;script type="module"&amp;gt;&lt;/code&gt; tag but let's ask Svelte to do bundling for us, just put this import into &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of &lt;em&gt;App.svelte&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The component works fine:&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%2Fi%2F5nlsty9xrprz75fygmlw.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%2Fi%2F5nlsty9xrprz75fygmlw.png" alt="Tabs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's cook a toast now. First, import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Toast from 'bootstrap/js/src/toast';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, adding HTML (I simplified this sample):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="toast" role="alert" id="copySuccessToast" data-delay="2000"&amp;gt;
      &amp;lt;div class="toast-header"&amp;gt;
        &amp;lt;strong&amp;gt;oEmbeddr&amp;lt;/strong&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="toast-body"&amp;gt;The code was copied to the clipboard&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we initialize and show (&lt;a href="https://v5.getbootstrap.com/docs/5.0/components/toasts/#javascript-behavior" rel="noopener noreferrer"&gt;here is the API docs&lt;/a&gt;) this message box (after successful copying code from textarea to the clipboard):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let copySuccessToast = new Toast(document.getElementById("copySuccessToast"));
copySuccessToast.show();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's give it a try:&lt;br&gt;
1) Open &lt;a href="https://kind-sea-08852d11e.azurestaticapps.net/" rel="noopener noreferrer"&gt;https://kind-sea-08852d11e.azurestaticapps.net/&lt;/a&gt;&lt;br&gt;
2) Paste &lt;em&gt;&lt;a href="https://www.tiktok.com/@webmaxru/video/6858249402683886854" rel="noopener noreferrer"&gt;https://www.tiktok.com/@webmaxru/video/6858249402683886854&lt;/a&gt;&lt;/em&gt; into the input, click "Get code"&lt;br&gt;
3) Click "Copy to clipboard" and you will see this toast:&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%2Fi%2F55xdkf8tz3c9hnxm4pdq.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%2Fi%2F55xdkf8tz3c9hnxm4pdq.png" alt="Toast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That was a journey! But now we know how to plug in Bootstrap 5 in its the most natural and flexible form to Svelte application.&lt;/p&gt;

&lt;p&gt;Time to push our project to GitHub - &lt;a href="https://github.com/webmaxru/oembeddr/" rel="noopener noreferrer"&gt;here is my repo with the full source code&lt;/a&gt;, please, feel free to clone it - and proceed to the next step.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hosting
&lt;/h1&gt;

&lt;p&gt;Let's present our application to the whole world! With the global distribution and dynamic scale provided by &lt;a href="https://azure.microsoft.com/en-us/services/app-service/static/#features" rel="noopener noreferrer"&gt;Azure Static Web Apps&lt;/a&gt; service, we are fully covered!&lt;/p&gt;

&lt;p&gt;This will be the shortest section of our story because setting up a frontend application on Azure Static Web Apps takes only a few clicks and less than 5 minutes.&lt;/p&gt;

&lt;p&gt;Log in to Azure cloud portal (if you don't have an account please register a &lt;a href="https://aka.ms/free-azure-pass" rel="noopener noreferrer"&gt;free trial&lt;/a&gt;), search for &lt;em&gt;Static Web Apps&lt;/em&gt; in the top search field, and click "Add".&lt;/p&gt;

&lt;p&gt;On the first screen, you enter your app name, resource group (or create new), and region. Also, you will be asked to log in to your GitHub account and to select an organization/repo/branch where the app located: &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%2Fi%2Fse9czfwx963t7rllujlk.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%2Fi%2Fse9czfwx963t7rllujlk.png" alt="Step 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, you need to specify some details about your project. In our case, we only need to type &lt;em&gt;public&lt;/em&gt; in the &lt;strong&gt;App artifact location&lt;/strong&gt; field (do you remember I asked you to remember where Svelte outputs the build artifacts?)&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%2Fi%2Fz566me9dz5iv60k77n6d.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%2Fi%2Fz566me9dz5iv60k77n6d.png" alt="Step 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking on "Create" and a few seconds, you will get a test public URL of your application available (for my project - &lt;a href="https://kind-sea-08852d11e.azurestaticapps.net/" rel="noopener noreferrer"&gt;https://kind-sea-08852d11e.azurestaticapps.net/&lt;/a&gt;). But while Azure Static Web Apps fetches your source code from GitHub, builds &amp;amp; deploys it (all automatically) you might want to have a look at the workflow for GitHub Actions file (&lt;a href="https://github.com/webmaxru/oembeddr/blob/master/.github/workflows/azure-static-web-apps-kind-sea-08852d11e.yml" rel="noopener noreferrer"&gt;my one&lt;/a&gt;) which was just created and added to your repo.&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%2Fi%2Fwai53v7dvrmvzji1koi1.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%2Fi%2Fwai53v7dvrmvzji1koi1.png" alt="Step 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are not going into the details of the Workflow file format, I just want to mention that since now, every push/merge to your &lt;em&gt;master&lt;/em&gt; branch will invoke the CI/CD flow and you will be able to see an updated version of your app in a few moments (on the same URL). You can track the build progress in the corresponding &lt;a href="https://github.com/webmaxru/oembeddr/runs/961323346?check_suite_focus=true#step:4:1" rel="noopener noreferrer"&gt;GitHub Actions section&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary and what's next
&lt;/h1&gt;

&lt;p&gt;We covered the application scaffolding using Svelte, adding Bootstrap 5 library, and hosted this simple page on the Azure Static Web Apps service. In the next article we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check how the staging works in Azure Static Web Apps when we want to preview a next version of the application without touching the "production URL"&lt;/li&gt;
&lt;li&gt;Add more social network providers. To do it we'll build a backend using API part of the Azure Static Web Apps solution. Serverless for the win!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's stay connected on Twitter &lt;a href="https://twitter.com/webmaxru" rel="noopener noreferrer"&gt;@webmaxru&lt;/a&gt;. And I'll be happy to see your feedback in the comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maxim Salnikov&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Developer Engagement Lead in Microsoft Norway&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>svelte</category>
      <category>bootstrap</category>
      <category>staticwebapps</category>
    </item>
    <item>
      <title>You are an Azure Hero. What's next?</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Fri, 09 Oct 2020 16:19:43 +0000</pubDate>
      <link>https://dev.to/azure_heroes/you-are-an-azure-hero-what-s-next-374f</link>
      <guid>https://dev.to/azure_heroes/you-are-an-azure-hero-what-s-next-374f</guid>
      <description>&lt;blockquote&gt;
&lt;h3&gt;
  
  
  Disclaimer: if you are not a lucky owner of an Azure Hero badge yet, please, &lt;a href="https://aka.ms/azure-heroes" rel="noopener noreferrer"&gt;learn more about this program&lt;/a&gt; and how to join.
&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;Congratulations! You are one of the Azure Heroes. That means you have a mobile app called Enjin Wallet, created by our partners, which contains one or multiple badges. What's next? You decide! You could just secretly keep this collection for your eyes only and grow it. Or you could explore some interesting opportunities for you to contribute back to the tech community using your Azure Heroes status.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Share your award with the developer community
&lt;/h1&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%2Fi%2F66ac1b3zpcbngxhf88jl.jpg" 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%2Fi%2F66ac1b3zpcbngxhf88jl.jpg" alt="Azure Heroes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every badge you have in your Enjin Wallet has a convenient way to publish it on social media. Just use the well-known "Share" icon on the badge details screen or use the "Share" menu item (click the three dots menu to get to it). You will get a quick post template for sharing on Twitter, LinkedIn, and others.  Or you can just copy-paste the link to the badge from this template and write your own text. As a result, you will receive a nicely formatted social media post.&lt;/p&gt;

&lt;p&gt;You are welcome to share every new badge you receive this way!&lt;/p&gt;

&lt;p&gt;Also, on LinkedIn, there is a cool way to have the badges in your profile like this:&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%2Fi%2Fd3nuw0h2osedvn0ui4td.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%2Fi%2Fd3nuw0h2osedvn0ui4td.png" alt="Featured"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just go to Edit profile -&amp;gt; Featured -&amp;gt; Add link&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Reserve your spot on the Azure Heroes Community map
&lt;/h1&gt;

&lt;p&gt;By default, your badge is completely anonymous and it's up to you to publicize it to the global developer community. And we have a solution for this!&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%2Fi%2Fsyr3z3ppp3k4xnyqmlrv.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%2Fi%2Fsyr3z3ppp3k4xnyqmlrv.png" alt="Azure Heroes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just go to the &lt;a href="https://azureheroes.community/" rel="noopener noreferrer"&gt;Azure Heroes Community&lt;/a&gt; website and associate your Twitter or LinkedIn profile with your badges (these is a simple and quick process for it). Once you've completed this, your name will appear on this website, linked to your country of residence.&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%2Fi%2Fxtai0vr4rh14eo593745.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%2Fi%2Fxtai0vr4rh14eo593745.png" alt="Azure Heroes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, you can remove this connection, and thus remove your profile from the Azure Community website, at any time.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Ask your employer if your company participates in the "Azure Heroes for Enterprise" program
&lt;/h1&gt;

&lt;p&gt;For company-internal skilling and educational events related to Azure, we have a special offering called "Azure Heroes for Enterprise" with a special Azure Polymath badger. Do you want to delight your colleagues by providing them with Azure Heroes badges? Just ask the person who drives this internal motion to contact us at &lt;a href="//mailto:ahhelp@microsoft.com"&gt;ahhelp@microsoft.com&lt;/a&gt;, and we'll come to your company's internal events to share the badges.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Collect more badges &amp;amp; badges of different types...
&lt;/h1&gt;

&lt;p&gt;...and follow the updates on the &lt;a href="https://twitter.com/azureheroes" rel="noopener noreferrer"&gt;Azure Heroes Twitter account&lt;/a&gt; or updates from your employer if your company is a part of "Azure Heroes for Enterprise". Soon we will start sharing some cool giveaways that you can get in exchange for some of your digital badges (exact rules are defined on a case-by-case basis). It's always great to have something "real" in addition to the digital badge, isn't it?&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%2Fi%2Fq4vc9qlkevy6y6uxvuu1.jpg" 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%2Fi%2Fq4vc9qlkevy6y6uxvuu1.jpg" alt="Azure Heroes plushie"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Received a badge as a student and your university has some courses about cloud technology?
&lt;/h1&gt;

&lt;p&gt;We have a program called "Azure Heroes for University" where we distribute digital badges for the students attending these courses. Please, introduce your teacher to us by sending an email to &lt;a href="//mailto:ahhelp@microsoft.com"&gt;ahhelp@microsoft.com&lt;/a&gt;, and we'll pick up the conversation.&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%2Fi%2F4mi6o56lqxmbmzgf7j24.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%2Fi%2F4mi6o56lqxmbmzgf7j24.png" alt="Azure Heroes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Do you know more ways to benefit from Azure Heroes badges?
&lt;/h1&gt;

&lt;p&gt;Please, share your ideas with us! We are eager to hear from you and reward the best suggestions :)&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
Yours, Azure Heroes team (Sherry, Maxim, Nick)&lt;/p&gt;

</description>
      <category>azureheroes</category>
      <category>azure</category>
    </item>
    <item>
      <title>We celebrate Hacktoberfest by introducing OpenSourcer badge of Azure Heroes program</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Thu, 01 Oct 2020 14:44:52 +0000</pubDate>
      <link>https://dev.to/azure_heroes/we-celebrate-hacktoberfest-by-introducing-opensourcer-badge-of-azure-heroes-program-1gjn</link>
      <guid>https://dev.to/azure_heroes/we-celebrate-hacktoberfest-by-introducing-opensourcer-badge-of-azure-heroes-program-1gjn</guid>
      <description>&lt;p&gt;Today, at &lt;a href="https://contributing.today" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt;, we announced the new addition to our #azureheroes family: The OpenSourcer badge!&lt;/p&gt;

&lt;p&gt;👉 OpenSourcer badgers recognize those who help build a robust OSS environment, with impactful technology and a collaborative, engaged community. OpenSourcers build, maintain, and make meaningful contributions to open source projects, while inspiring others to be part of the OSS movement.&lt;/p&gt;

&lt;p&gt;To qualify, you must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be a creator, maintainer or contributor to an impactful (in your own definition) open-source software project.&lt;/li&gt;
&lt;li&gt;And react to contributors' questions and requests in Issues and Pull Requests, in a way that people with all levels of the technical experise feel welcomed in your code repository.&lt;/li&gt;
&lt;li&gt;Optionally, introduce OSS by speaking, blogging and other public activities.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffsw6ucdfyermiy4viqvw.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%2Fi%2Ffsw6ucdfyermiy4viqvw.png" alt="OpenSourcer "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 Are you all about contributing to #OSS success? This is the right badge for you then! &lt;a href="https://aka.ms/we.nominate" rel="noopener noreferrer"&gt;Nominate yourself&lt;/a&gt; or your colleague or friend!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azureheroes</category>
      <category>opensource</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Meet a new Green Contributor badge!</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Wed, 30 Sep 2020 20:30:16 +0000</pubDate>
      <link>https://dev.to/azure_heroes/meet-a-new-green-contributor-badge-47d9</link>
      <guid>https://dev.to/azure_heroes/meet-a-new-green-contributor-badge-47d9</guid>
      <description>&lt;p&gt;Today, at &lt;a href="https://greenconf.io" rel="noopener noreferrer"&gt;GreenConf&lt;/a&gt; 💚, we announced the new addition to our #azureheroes family: The Green Contributor 🌱&lt;/p&gt;

&lt;p&gt;👉 Green developers contribute to the growth of the sustainable software engineering field. Recipients of this badge drive innovation by building sustainable software and help educate the community by creating content and resources related to sustainable software engineering.&lt;/p&gt;

&lt;p&gt;To qualify, you must verify at least one of the impacts in any of the bullets below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creator or maintainer or core contributor to an open-source software project with a focus on sustainable software.&lt;/li&gt;
&lt;li&gt;Created several pieces of content for blogs, webinars, podcasts, or talks related to sustainable software.&lt;/li&gt;
&lt;li&gt;Created tutorials, how-to guides, or sample code related to the field of sustainable software engineering.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe5zencfrbc1qnxqw4fhd.jpg" 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%2Fi%2Fe5zencfrbc1qnxqw4fhd.jpg" alt="Green Contributor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 Are you all about #sustainable software engineering? This is the right badge for you then! &lt;a href="https://aka.ms/we.nominate" rel="noopener noreferrer"&gt;Nominate yourself&lt;/a&gt; or your colleague or friend!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azureheroes</category>
      <category>greencontributor</category>
    </item>
    <item>
      <title>My developer journey, and today's aspirations. Interview for Kode24.</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Thu, 25 Jun 2020 07:51:09 +0000</pubDate>
      <link>https://dev.to/webmaxru/my-developer-journey-and-today-s-aspirations-interview-for-kode24-468l</link>
      <guid>https://dev.to/webmaxru/my-developer-journey-and-today-s-aspirations-interview-for-kode24-468l</guid>
      <description>&lt;p&gt;I'm proud to be featured as a Week's Coder of the Norwegian online-magazine for the developers &lt;a href="https://www.kode24.no/ukas-koder/vanskelig-a-finne-norske-talere/72602900"&gt;Kode24&lt;/a&gt;. Here is the English version of the interview I gave to Ole Petter Baugerød Stokke (Editor at Kode24) for the publication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;strong&gt;Maxim Salnikov&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Age: &lt;strong&gt;40&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Education: &lt;strong&gt;Master's degree in Software of computing technique and automated systems at Volga State University of Telecommunications and Informatics&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Location: &lt;strong&gt;Oslo, Norway&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Job title and employer: &lt;strong&gt;Developer Engagement Lead at Microsoft Norway&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Experience as a professional developer: &lt;strong&gt;20+ years&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your setup (what kind of machine, OS, editor, etc. you are using): &lt;strong&gt;Microsoft Surface Book 2 + VS Code + Visual Studio Codespaces&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Favorite music right now: &lt;strong&gt;Røyksopp&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Favorite TV-series right now: &lt;strong&gt;Discovery Channel&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Social media
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Twitter: &lt;a href="https://twitter.com/webmaxru"&gt;https://twitter.com/webmaxru&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/webmax"&gt;https://linkedin.com/in/webmax&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Speaker profile: &lt;a href="https://sessionize.com/maxim-salnikov"&gt;https://sessionize.com/maxim-salnikov&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FAkeAjif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7b84bs6h2e7f74fr0tdv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FAkeAjif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7b84bs6h2e7f74fr0tdv.jpg" alt="Maxim Salnikov"&gt;&lt;/a&gt;&lt;em&gt;Me attending CES expo in Las Vegas&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How did you get started with coding? (for example, what is the first thing you remember developing, and where did the road go?)
&lt;/h2&gt;

&lt;p&gt;I believe it's a classic story of the computer geek. I understood that programming is "my thing" during the first lessons of Basic in the school - I was especially excited by the programmatic drawing of the objects. Then I was deeply engaged with Pascal/Delphi (C++ to a lesser extent) in the University (again, in different v-teams I was always responsible for the visual and UI parts of the project). So it was natural for me to immediately become a "webmaster" in the late 90s when I discovered Internet: HTML, CSS, and obligatory JS-powered animated mouse pointer tail :) Approximately at the same time, I built my first commercial projects driven by Apache+PHP+MySQL (LAMP stack).&lt;/p&gt;

&lt;p&gt;My &lt;a href="https://www.linkedin.com/in/webmax/"&gt;developer career&lt;/a&gt; was always connected with the Internet and UI: I spent many years by developing SaaS e-commerce solutions as a Frontend Lead, then migrated legacy Win32 solution to the web stack using Angular as a Senior Developer, and ended up as a Fullstack Developer for the digital identity company by building end-to-end PoCs where IoT sensors sent data to the web UIs in the realtime.&lt;/p&gt;

&lt;p&gt;After moving to Norway in 2011, I discovered a meetups scene. I started to visit the events, then to help organizers, then to build userg roups and run events myself. A bit later, I started my technical speaker journey and now I deliver around 30 technical sessions per year. Then, based on the experience I gathered by running meetups and attending events as a speaker, after finding soulmates to build the teams, I co-founded two full-scale developer conferences: &lt;a href="https://mobileera.rocks"&gt;Mobile Era&lt;/a&gt; (HQ: Norway, topics: mobile development in the broadest sense) and &lt;a href="https://ngvikings.org"&gt;ngVikings&lt;/a&gt; (HQ: Nordics, topics: Angular framework). With all these community-focused activities gradually appeared in my life...&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you working on normally, and what are you working on right now? (what product with which technologies, etc.)
&lt;/h2&gt;

&lt;p&gt;...In March 2019, I naturally shifted my career gearbox to the Developer Relations with the focus on Azure cloud (before that official move, running developer communities and conferences, technical speaking and blogging were my fulltime hobby). So when the time comes to write the code, I mainly do it for the different web and cloud use cases to support my technical demos, talks, training.&lt;/p&gt;

&lt;p&gt;For example, an Angular app to showcase &lt;a href="https://docs.microsoft.com/en-us/azure/static-web-apps/getting-started"&gt;Azure Static Web App&lt;/a&gt; service during my live demo at NDC Oslo 2020. Or &lt;a href="https://github.com/webmaxru/webmax.ru"&gt;my blog&lt;/a&gt; to show cloud deployment of a statically generated Angular/Scully app. By the way, the English version of this interview is hosted on this blog and there is a new automatic build on every push of the corresponding &lt;a href="https://github.com/webmaxru/webmax.ru/edit/master/blog/kode24.md"&gt;markdown file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, I traditionally build and support the websites for the conferences I mentioned: Mobile Era 2016-2020, ngVikings 2017-2020. For the recent editions of these, I use &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; static site generator. Another recent mini-project: I built an Azure Function using NodeJS to generate a &lt;a href="https://raw.githubusercontent.com/ngVikings/ngvikings-2020/master/static/images/team/badge-maxim_salnikov.jpg"&gt;digital badge&lt;/a&gt; for every attendee of ngVikings 2020 (I believe I suddenly invented such a thing as badges for the online conferences!) and put it on the webhook of our ticketing system. Folks loved to get a badge right after the ticket registration! On the day of the event, Twitter was full of these pictures shared :)&lt;/p&gt;

&lt;p&gt;Right now, I work on the demo of the background services of Progressive Web Apps for my next technical talk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4oEW85Fh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fhp6pgufq2d0idc5hkok.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4oEW85Fh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fhp6pgufq2d0idc5hkok.jpg" alt="My workplace"&gt;&lt;/a&gt;&lt;em&gt;Delivering a technical session from home&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does a typical workday look like to you “these days”? (are you at the home office, how do regular meetings, work tasks, etc. work)
&lt;/h2&gt;

&lt;p&gt;My work is all about building the strategy and communication. So only the target channel and media changed but not the core. Now, I have meetings with my colleagues, with the local and global community organizers, with the developers who want to learn more about the cloud - from my kitchen or terrace. From the same places, I speak at the conferences worldwide these days - I think the number of my speaker appearances &lt;a href="https://twitter.com/webmaxru/status/1273566526940033024"&gt;has only increased&lt;/a&gt; last months!&lt;/p&gt;

&lt;h2&gt;
  
  
  What would you like to learn more about in the future? (for example, new technologies you are curious about, languages you want to try, new disciplines you want to test, etc.)
&lt;/h2&gt;

&lt;p&gt;My developer's passion is Web and Progressive Web Apps in particular. I scrupulously follow all the updates in this area, and there are always many things I want to learn and experiment with, for example, early drafts and Trial Origins of &lt;a href="https://goo.gle/fugu-api-tracker"&gt;Web Capabilities APIs&lt;/a&gt;. Also, after &lt;a href="https://flutter.dev/web"&gt;Flutter for Web&lt;/a&gt; release, I want to try this framework and Dart language which drives it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What specific tools can't you do without? (for example, specific programs and editors, editor plugins, build tools, database solutions, hosting services, etc.)
&lt;/h2&gt;

&lt;p&gt;VS Code + Azure cloud extensions + "must-have" developer extensions like Prettier, Live Server, Import Cost, Peacock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mu2juyx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ovm77iwqzejhf46z2mk0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mu2juyx7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ovm77iwqzejhf46z2mk0.jpg" alt="My job"&gt;&lt;/a&gt;&lt;em&gt;In the studio: recording videoshow for the developers&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the worst thing you can be asked about at work? (for example, tasks you think are tedious, systems you don't like to work on, technologies you think are bad / difficult / tedious, etc.)
&lt;/h2&gt;

&lt;p&gt;Not at work, but from my conference co-organizers: "the website shows older version, we need to fix it asap!" (that means I made something wrong again in my "genius" but quite an experimental Service Worker and have to revert it back, which is a challenge in this scenario :)&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you think Norwegian developers should be better at?
&lt;/h2&gt;

&lt;p&gt;Developers in Norway are very strong professionals, and I'd like them to share their knowledge and experience more often at the community events. As a meetup organizer, I always look for the speakers, and sometimes it's a challenge to find local folks. Now, with all the gatherings went online, it became simpler to invite speakers from other countries but I want to grow vivid technical speaker community in Norway.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you like to do when you're not working? (do you develop something in your spare time, also? hobbies?)
&lt;/h2&gt;

&lt;p&gt;Besides running community meetups and developer conferences in my free time, I love traveling (now within Norway), alpine skiing, and producing music using my synth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's stay connected
&lt;/h2&gt;

&lt;p&gt;I'll be happy to hear from you. Feel free to send me a couple of lines if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want to know more about the developer community in Norway&lt;/li&gt;
&lt;li&gt;Need advice on your technical speaker or community organizer journey&lt;/li&gt;
&lt;li&gt;Have technical questions about PWA, Angular, Azure&lt;/li&gt;
&lt;li&gt;Want to invite me to &lt;a href="http://bit.ly/maxim-salnikov-speaker-request"&gt;speak at your technical event&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wish to learn more about cloud technologies from Microsoft&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The easiest way to reach out to me is to send a direct message on &lt;a href="https://twitter.com/webmaxru"&gt;Twitter&lt;/a&gt; or &lt;a href="https://linkedin.com/in/webmax"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>interview</category>
      <category>personal</category>
    </item>
    <item>
      <title>Inspiring Stories: Benedicte Raae</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Wed, 15 Apr 2020 13:12:54 +0000</pubDate>
      <link>https://dev.to/azure_heroes/inspiring-stories-benedicte-raae-34jh</link>
      <guid>https://dev.to/azure_heroes/inspiring-stories-benedicte-raae-34jh</guid>
      <description>&lt;p&gt;As a society, we tend to focus on titles and roles, and we forget that behind each title there is a person who has a story to tell. And truly every person’s story is unique.&lt;br&gt;
In honor of International Women's day, we interview inspiring women from the community on the story of how they got into Tech, and where they are today.&lt;/p&gt;

&lt;p&gt;In this post, I interview &lt;a href="https://twitter.com/raae" rel="noopener noreferrer"&gt;Benedicte Raae&lt;/a&gt;, who is based in Oslo, Norway.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Benedicte
&lt;/h1&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%2Fi%2Fsui1axrwddsop2i8dxuo.jpeg" 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%2Fi%2Fsui1axrwddsop2i8dxuo.jpeg" alt="Benedicte Raae"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m Benedicte of Lilly Labs. I’m a computer scientist living in Oslo, Norway and have been an independent software engineer for almost a decade. I have clients in both the private and public sector in Norway and also a few international clients.&lt;/p&gt;

&lt;p&gt;I love to create robust, user-friendly tools using web technology. My current favorite is Gatsby, a React framework. &lt;/p&gt;

&lt;p&gt;Right now, I’m developing a &lt;a href="http://usepow.app" rel="noopener noreferrer"&gt;privacy-first period tracker called POW&lt;/a&gt;! launched March 8th. The level of detail I found in existing apps made me less inclined to use them; it felt creepy, a little invasive even. And last year, the feeling was backed up by reports that several of the most popular tracker apps were &lt;a href="https://www.theguardian.com/world/commentisfree/2019/sep/14/your-period-tracking-app-could-be-sharing-intimate-details-with-all-of-facebook" rel="noopener noreferrer"&gt;selling or sharing data they collected with third parties&lt;/a&gt; such as Facebook.&lt;/p&gt;

&lt;p&gt;So I spent 2019 answering the question: “A privacy first-period tracker? Is it even possible?” It led me to learn about encryption in web applications and to share my new-found knowledge at &lt;a href="https://javazone.no" rel="noopener noreferrer"&gt;JavaZone&lt;/a&gt;, &lt;a href="https://mobileera.rocks.no" rel="noopener noreferrer"&gt;Mobile Era&lt;/a&gt;, &lt;a href="https://jsconfbp.com/" rel="noopener noreferrer"&gt;JSConf Budapest&lt;/a&gt;, and &lt;a href="https://ruhrjs.de/" rel="noopener noreferrer"&gt;RuhrJS&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I also realized I just had to make a pass at making one. In addition to keeping your privacy safe, I hope to also give the control back to the user. POW! leverages hashtags to let you track the things you are interested in using a language you are familiar with.  &lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/raae" rel="noopener noreferrer"&gt;@raae &lt;/a&gt; &lt;br&gt;
Instagram: &lt;a href="https://instagram.com/raae.codes/" rel="noopener noreferrer"&gt;@raae.codes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you first become interested in technology and what sparked this interest?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My mother is a mechanical engineer but worked as a developer for most of her career, before transitioning to a managerial role. This gave me early access to computers. I remember us connecting to the Internet over a noisy modem, while I was still in elementary school, only to check vg.no and waiting forever for it to appear. I was not very impressed.&lt;/p&gt;

&lt;p&gt;However, at 13, my Norwegian teacher introduced me to HTML and CSS. Also, the Internet had become a lot more fun. At 16, I was responsible for Foss revyen’s first website and needed JavaScript to create an image carousel. I have never spelled height wrong again after spending hours debugging the code. &lt;/p&gt;

&lt;p&gt;I am more interested in what we can create with technology than the technology itself. And lately I've been even more interested in what we probably should not make, even though we could. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What education do you have?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Master of Technology, Computer Science, from NTNU (Norwegian University of Science and Technology).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describe your way towards your first job in tech; how did you land this job?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was a summer intern at Iterate and after finishing that and graduating in 2009, they offered me a job. At that time, this situation was typical for most of my class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have any role models that influenced you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My mother, and the other ladies working with her. They normalized the whole “women in tech thing” to such a degree that it took me years to realize others had questioned their right to be here much more than I've ever had.&lt;/p&gt;

&lt;p&gt;My grandmother, co-founder of Kronborg Leverpostei and women’s rightist activist who was a force to be reckoned with until the end. Both my daughter (Lillian) and my company (Lilly Labs) are named after her. &lt;/p&gt;

&lt;p&gt;David Heinemeier Hansson, founder of Basecamp, who has created a thriving tech business focusing on paying customers and is taking a stand against surveillance marketing. &lt;/p&gt;

&lt;p&gt;And many, many more. &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%2Fi%2F2lwih6m01fbufb650i0z.jpeg" 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%2Fi%2F2lwih6m01fbufb650i0z.jpeg" alt="Benedicte Raae"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who were/are the biggest supporters in your career?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My partner Ola and my good friend and cheerleader Marit Letnes! &lt;/p&gt;

&lt;p&gt;I also find support in the Slow Business Community (led by Torill Bye Wilhelmsen), where the focus is on building businesses we enjoy, while running them. This as opposed to the hustle, hustle, hustle, and then flip mentality prevalent in tech. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell us more about your current job – e.g. what do you like most about your role?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I really enjoy and need the freedom of being an independent developer! &lt;/p&gt;

&lt;p&gt;At the moment, I split my time between POW! and an engagement creating a web application, in React, for a Scandinavian defense force.  &lt;/p&gt;

&lt;p&gt;I often work long term, but not full time for my clients. It’s a win-win; I keep the outside perspective that clients often need, while being a stable resource on their projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your typical day look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I take our daughter to kindergarten, then head to a coffee shop to work remotely or go to the city center to work at my current client’s office. I try to, and most often do, get several hours of uninterrupted time to code, giving me that sweet flow feeling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you do in your free time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being independent, there is not as hard a line between work and free time.  &lt;/p&gt;

&lt;p&gt;I love to code, and you can find me spending an evening at the local café coding on something fun, like POW! while enjoying a glass of wine. But you can also find me mid-week taking a break from everything just reading a book.&lt;/p&gt;

&lt;p&gt;However, I have started horseback riding again after 20 years, and it’s a blast: “Hest er fortsatt best.”  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What advice will you give to women and girls who dream about a career in tech?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do it, join me, you belong!   &lt;/p&gt;

</description>
      <category>azureheroes</category>
      <category>iwd2020</category>
      <category>womenintech</category>
      <category>surfacethewomen</category>
    </item>
    <item>
      <title>Inspiring Stories: Monica Beate Tvedt</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Sun, 15 Mar 2020 10:56:29 +0000</pubDate>
      <link>https://dev.to/azure_heroes/inspiring-stories-monica-beate-tvedt-5bj8</link>
      <guid>https://dev.to/azure_heroes/inspiring-stories-monica-beate-tvedt-5bj8</guid>
      <description>&lt;p&gt;As a society, we tend to focus on titles and roles, and we forget that behind each title there is a person who has a story to tell. And truly every person’s story is unique.&lt;br&gt;
In honor of International Women's day, we interview inspiring women from the community on the story of how they got into Tech, and where they are today.&lt;/p&gt;

&lt;p&gt;In this post, I interview &lt;a href="https://twitter.com/monicatvedt"&gt;Monica Beate Tvedt&lt;/a&gt;, who is based in Oslo, Norway.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Monica
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kpBho3NQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/is8uobs6xrg8mnevx01b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kpBho3NQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/is8uobs6xrg8mnevx01b.png" alt="Monica Beate Tvedt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My name is Monica Beate Tvedt, Head of .NET Development at Sopra Steria, entering a new role as Partner &amp;amp; Director of Technology. I'm also the owner of the Scandinavian Microsoft Developer Community at Sopra Steria and organizer of Oslo Xamarin Meetup.  &lt;/p&gt;

&lt;p&gt;I was a keynote speaker at this year's Women in Tech Summit, where I talked about my journey from being a single mom with no education, to where I am today, a hands-on technology director, doing what I love - trying to encourage more women into tech. &lt;/p&gt;

&lt;p&gt;As programming is my greatest passion, I strive to contribute back to the development community by giving tech talks and hands-on workshops. Next up is the Global AI on Tour 2020 the 4th of April at Microsoft Oslo, where we will teach you more about Azure's AI and Machine learning services. &lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/monicatvedt"&gt;@monicatvedt &lt;/a&gt; &lt;br&gt;
Facebook: &lt;a href="https://facebook.com/mtvedt/"&gt;Monica Beate Tvedt&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://linkedin.com/in/monicatvedt"&gt;Monica Beate Tvedt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you first become interested in technology and what sparked this interest?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve always been very visual. Growing up, I loved solving number-, pattern- and logical problems, at the same time spending a lot of time drawing cartoon characters, as I was fascinated by Walt Disney and how they drew each frame to animate a given gesture.&lt;/p&gt;

&lt;p&gt;Being a kid from the 80s, I also play a lot of video games (Atari, Nintendo), again fascinated by the animation, the logic, and graphics used. When we then got our first computer it was only a matter of time before I created my first website showcasing my drawing and animation skills. Programming enabled me to do what I loved the most, solving both logical problems and being creative in designing user interfaces. There’s really nothing quite like it... &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What education do you have?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Computer Engineering - Software development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describe your way towards your first job in tech; how did you land this job?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started out as a freelancer in 2001 before I took my engineering degree. I created websites for family and friends, and asked local companies if they needed help going digital. It was literally a door-to-door job, often being rejected, but every time I didn't, I made sure my work was added to my online portfolio, building my brand.&lt;/p&gt;

&lt;p&gt;I then started volunteering as a teacher assistant in multiple engineering subjects and one day I was asked to do an interview for a school paper where I talked about my passion for software development. I did the interview and later I got a phone call from a company who had read my story, asking if I would come work for them. Which I did. &lt;/p&gt;

&lt;p&gt;This is why I cannot stress enough the importance of showcasing your work and your passions, as this has been the recipe for almost every one of my job offers ever since. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have any role models that influenced you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bill Gates and Warren Buffet. I loved them when I was growing up - their mindset, their business acumen and what they achieved. I was, and still am, in absolute awe of their accomplishments... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aCT-NdPH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a1162pmujmqsr0r4yaf2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aCT-NdPH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a1162pmujmqsr0r4yaf2.gif" alt="Monica Beate Tvedt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who were/are the biggest supporters in your career?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It has been a somewhat lonely journey, being a woman in the tech industry. But time has changed and today I’m very thankful for the enormous support I get from the Oslo tech community, and the people I work with day to day.  &lt;/p&gt;

&lt;p&gt;I would especially like to mention Eirik Lie, Kjetil Kværne, and Hilde Solberg Holm, who have all made their impact and let their support be known, each in their own way. They're hard-working and talented individuals, bringing out the best in others - it really is all about having the right mindset! &lt;/p&gt;

&lt;p&gt;My two beautiful children have also been a huge motivation, making me work even harder trying to achieve my goals. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your typical day look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lately, I’ve been working on a project for Kværner, where we developed a new mobile app at the same time as running our .NET departments at Sopra Steria.  &lt;/p&gt;

&lt;p&gt;A typical day running our .NET departments will start with me checking our department's current productive billable rate, noting any planned deviations preparing for next month's business review, going through our department's profit and loss. &lt;/p&gt;

&lt;p&gt;Then off to meetings about the relocation of resources, bids, and new leads. Throughout the day I follow up with my consultants, making sure they thrive at work, answering any questions they may have. Depending on my main focus of the day, I’m spending the rest of the day either planning our next community meetup with our community leads, preparing slides for a department meeting, taking actions on my department's current strategy, interviewing .NET developers or advising our customers on what to do next.  &lt;/p&gt;

&lt;p&gt;When working as a developer at Kværner, the day is quite different, starting with us fetching a cup of coffee or opening a can of Monster Energy, catching up during our daily stand-up and then just enjoying ourselves coding all day, making awesome apps. Working Lean in autonomous teams using Azure DevOps, building our apps using .NET Core, Xamarin and Azure, all while getting continuous feedback from our pilot users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you do in your free time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ll proudly admit I’m mostly at my computer: programming, learning new things or playing chess at chess.com, often simultaneously while watching a movie. There is no shortcut in this business, so you need to put in the hours and most importantly not feel guilty for doing so or feel the need to apologize for it. Do what you love and do more of it. When my laptop finally runs out of battery - it may happen - I enjoy playing tennis, eating out or going skiing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What advice will you give to women and girls who dream about a career in tech?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start building your brand today. Document your journey both for your own motivation and for future employers to see. Do more volunteering work for the Tech community and say yes more often, but only if it will add real value to your CV. If you get an opportunity but you’re not sure how or if you can do it, say yes. You will learn as you go, but the opportunity may not come again. &lt;/p&gt;

&lt;p&gt;Remember that you have all the cards, so try to play them well. There are too few software developers out there so the world is yours if you take the time and effort to become really good at it.  &lt;/p&gt;

&lt;p&gt;There are not that many women in Tech, so if you are ever in the need of advice, are stuck programming a school assignment or at work, just give me a shout on SoMe and we’ll try to solve it together. I would love to help in any way I can, so do not hesitate to reach out!  &lt;/p&gt;

</description>
      <category>azureheroes</category>
      <category>iwd2020</category>
      <category>womenintech</category>
      <category>surfacethewomen</category>
    </item>
    <item>
      <title>Inspiring Stories: Kristina Simakova</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Tue, 10 Mar 2020 10:03:21 +0000</pubDate>
      <link>https://dev.to/azure_heroes/inspiring-stories-kristina-simakova-eoc</link>
      <guid>https://dev.to/azure_heroes/inspiring-stories-kristina-simakova-eoc</guid>
      <description>&lt;p&gt;As a society we tend to focus on titles and roles, and we forget that behind each title there is a person who has a story to tell. And truly every person’s story is unique.&lt;br&gt;
In honor of International Women's day, we interview inspiring women from the community on the story of how they got into Tech, and where they are today.&lt;/p&gt;

&lt;p&gt;In this post, I interview &lt;a href="https://twitter.com/KristiSimakova"&gt;Kristina Simakova&lt;/a&gt; who is based in Oslo, Norway.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Kristina
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Mmo3dLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n3sxzcwbfmb7ctudbop0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Mmo3dLC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n3sxzcwbfmb7ctudbop0.jpeg" alt="Kristina Simakova"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kristina is an independent IT consultant based in Norway. She is a Google Developer Expert for Android &amp;amp; Maps and has a keen interest in AR, ML and computer graphics. By day, she writes Android native code in Kotlin and by night, she blogs at &lt;a href="https://creativetech.blog"&gt;creativetech.blog&lt;/a&gt; about AR and helps organise &lt;a href="https://www.meetup.com/GDGOslo/"&gt;GDG Oslo&lt;/a&gt; events and support the &lt;a href="https://facebook.com/WomenITech/"&gt;Women in Tech Oslo&lt;/a&gt; community. &lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/KristiSimakova"&gt;@KristiSimakova&lt;/a&gt; &lt;br&gt;
Facebook: &lt;a href="https://facebook.com/ksimakova/"&gt;Kristina Simakova&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you first become interested in technology and what sparked this interest?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was always interested in technology in general and I got interested in programming during my bachelor study in a computer graphics course.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What education do you have?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Master of Science - Computer Engineering&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describe your way towards your first job in tech; how did you land this job?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have a major in Computer Graphics and after finishing my masters I was looking forward to working in this field. I applied for a few positions and there was one that I really liked where I would work with creating ship and crane simulators for training. Fortunately, the company gave me the offer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xztcq8Kh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7audrcqs1b0bx4yfdp9d.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xztcq8Kh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7audrcqs1b0bx4yfdp9d.jpeg" alt="Kristina Simakova"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have any role models that influenced you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of people that influenced me throughout my life: from my math teacher to the founder of my previous company. From the people everyone knows, I want to mention Richard Branson. His spirit and adventures inspire me.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who were/are your biggest supporters in your career?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My mother is my biggest supporter of all. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell us more about your current job– e.g. what do you like most about your role?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am an independent consultant focusing mainly on Android development. I usually join an existing team of developers for 6 months or a longer period of time. I am currently working for a Norwegian mobile payment company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your typical day look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On a typical day I would be at my customer's office. As a part of the project, I help develop Android applications. In the morning, we have a short team meeting  and occasionally other meetings during the day. I use most of my time writing code in Kotlin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you do in your free time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I enjoy traveling and walking around in a new city. I like cooking Asian cuisine and making gluten-free desserts, which helps me clear my thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What advice will you give to women and girls who dream about a career in tech?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start with building small projects that you would use yourself and keep learning.&lt;/p&gt;

</description>
      <category>azureheroes</category>
      <category>iwd2020</category>
      <category>womenintech</category>
      <category>surfacethewomen</category>
    </item>
    <item>
      <title>Inspiring Stories: Victoria Bergquist</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Sun, 08 Mar 2020 13:31:39 +0000</pubDate>
      <link>https://dev.to/azure_heroes/inspiring-stories-victoria-bergquist-3ae4</link>
      <guid>https://dev.to/azure_heroes/inspiring-stories-victoria-bergquist-3ae4</guid>
      <description>&lt;p&gt;As a society we tend to focus on titles and roles, and we forget that behind each title there is a person who has a story to tell. And truly every person’s story is unique.&lt;br&gt;
In honor of International Women's day, we interview inspiring women from the community on the story of how they got into Tech, and where they are today.&lt;/p&gt;

&lt;p&gt;In this post, I interview &lt;a href="https://twitter.com/vicbergquist"&gt;Victoria Bergquist&lt;/a&gt; who is based in Oslo, Norway.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Victoria
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LJDLB70M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4ckyk6devpvdzvxfccz5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LJDLB70M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4ckyk6devpvdzvxfccz5.jpg" alt="Victoria Bergquist"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My name is Victoria and I'm a Frontend Engineer at Sanity.io. I'm from Oslo, Norway, but only recently moved back to Norway after living abroad for almost 8 years. Alongside work I enjoy being a part of the developer community through Twitter, meetups, events, and conferences. While I'm actually an introvert and often prefer to spend time at home, I find it very rewarding to organise events and spend time helping others around me. That is why I started two meetups in Frankfurt, organise and speak at events and conferences, and volunteer as a Chapter Leader for Vue Vixens, an organisation dedicated to helping women and those who identify as women learn Vue.js. &lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vicbergquist"&gt;@vicbergquist&lt;/a&gt; &lt;br&gt;
Twitter: &lt;a href="https://twitter.com/vicbergquist"&gt;@vicbergquist&lt;/a&gt; &lt;br&gt;
CodePen: &lt;a href="https://codepen.io/vicbergquist/"&gt;@vicbergquist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When did you first become interested in technology and what sparked this interest?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In high school I chose to do vocational studies in media and communication, with classes in design history, advertisement, journalism, photography, and more. We had to keep a blog/portfolio for a lot of our assignments and homework, and that's how I was first introduced to web technology. I struggled a lot to find a theme for my portfolio and blog, so I started looking into the code just for fun. HTML and CSS weren’t completely new to me, because my friend was taking a web design class and had been showing me what they were doing. However, I thought it sounded kind of boring, and instead learned a little about HTML and CSS on my own to customise the theme for my blog/portfolio. I had so much fun doing this and was at it for hours several times a week, but after spending a year in Japan and switching to general studies when I was back in high school in Norway, I went on to study languages and linguistics at university in Australia. I completely stopped coding and didn't touch or think about it for almost 7 years, before I got back to it after finishing university and needed to figure out what to do next! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What education do you have?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have a Bachelor of Arts with majors in Japanese and German, and electives in linguistics, neuroscience and psychology from The University of Queensland in Brisbane, Australia. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describe your way towards your first job in tech; how did you land this job?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was very happy with my studies and had a great time at university, but had no idea what to do after I graduated, so I tried a few different things. I went to Design School, but dropped out because it was too expensive. Then, because of my interest in coding from high school and languages, I started a Master's Degree in Linguistics and Web Technology. However, I dropped out before the first semester ended. The university just wasn't the right fit for me, so instead I decided to continue learning how to code on my own. I started spending all my time learning web development by completing various projects on freeCodeCamp and getting really into CSS. Eventually I also joined Twitter and began the #100DaysOfCode challenge to accelerate my progress, and began to code every day. &lt;/p&gt;

&lt;p&gt;During this time, I was putting in a lot of hard work and applied to various junior roles, but kept getting rejected because of my lack of experience and untraditional background. This was very discouraging, but then out of the blue a company contacted me about a frontend developer role they were hiring for. Apparently they had found me on Stack Overflow and had seen my CSS work on CodePen! I managed to find the job ad they contacted me about and it said that they were looking for a JavaScript developer, which was disappointing. I didn’t want to do JavaScript, so I emailed them back and said I wasn’t interested. I was having so much fun with CSS and instead wanted a job where I could do that. Lucky for me, they told me to disregard the job ad and simply wanted to talk to get to know me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJkVztq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bkm2w9lj2tb8b4tq5ri6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJkVztq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bkm2w9lj2tb8b4tq5ri6.jpg" alt="Victoria Bergquist"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ended up meeting them a week or two later, and the interview lasted quite a while. They wanted me to share my story and what I was passionate about, and said they wanted to hire their first frontend developer with a passion for the craft. Despite my lack of experience, they admired the progress I had made in such a short amount of time and how excited I got when talking about CSS. The day after, on day 87 of #100DaysOfCode, they called me back and offered me the job. Despite having no professional experience, they said they knew I would learn what I needed to learn on the job and thought I'd be a great addition to the team! So I become their first frontend developer, a junior and a lead! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell us more about your current job– e.g. what do you like most about your role?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where to start! I'm really excited about all the things I get to do at Sanity, like building APIs, tooling and working on Sanity Studio, an open-source content editing environment. I also love the fact that I’m able to continuously learn and explore new technologies. Sanity is used as a content backend for so many fun and interesting projects both by us internally and the community, like real-time quizzes and voice assistants, so seeing these things that people make with the product that I'm working on everyday is incredibly motivating and inspiring. It makes it so much fun to go to work!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your typical day look like?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My cat usually wakes me up by walking all over my face between 4 and 7 AM, and if that doesn't happen, my other cat wakes me up around the same time singing the song of her people, until someone goes to cuddle her. I quite enjoy being at the office around 8, but I often only go in around lunch in order to spend time with my cats before a day at the office. When at work I enjoy spending time talking with my colleagues. Everyone enjoys sharing what they are working on and the challenges they are facing, which almost makes you feel a part of all the projects at once, and that inspires me every day. In between work, we also play foosball to relax and have fun. I'm currently losing a lot, so I'm playing less than normal these days! &lt;/p&gt;

&lt;p&gt;On a typical day I also do a lot of pair-programming with my project partners. Parts of our code base are still very unfamiliar to me, so working closely with someone who knows it better may be the favourite part of my day. Other than that, I also have a few meetings, often with people in San Francisco, before I head home for the day around 4 or 5 PM. But I also often forget to go home when I should, because I'm having such a good time at the office! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you do in your free time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I code more than I should! I enjoy my job, so I struggle to put it away. To distract myself I like to watch TV shows, cook or bake, hang out with my cats, and play board games. Right now I'm doing clicker training with one of my cats, and try my best to stay away from coding and social media for my health and to do things I enjoy. I also organise meetups, however due to moving and health reasons, I haven't been able to do it as much as I'd like, but I'm really excited about starting it again soon! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What advice will you give to women and girls who dream about a career in tech?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are so many ways into this industry. Don't let anyone tell you that you need to follow one specific one, or that you can't do it with the background you have. Your are what the industry needs in order to be the best it can be, whether that's with a CS degree, or not. &lt;br&gt;
There's no doubt that being a woman in tech is hard, but women from your area and all around the world are there to help you on your journey. To find them, join communities for women in tech, local ones or online. I've met so many incredible women this way, and they have helped me get to where I am today. &lt;/p&gt;

</description>
      <category>azureheroes</category>
      <category>iwd2020</category>
      <category>womenintech</category>
      <category>surfacethewomen</category>
    </item>
    <item>
      <title>Workbox 4: Implementing refresh-to-update-version flow using the workbox-window module</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Thu, 28 Feb 2019 01:21:48 +0000</pubDate>
      <link>https://dev.to/webmaxru/workbox-4-implementing-refresh-to-update-version-flow-using-the-workbox-window-module-4e3c</link>
      <guid>https://dev.to/webmaxru/workbox-4-implementing-refresh-to-update-version-flow-using-the-workbox-window-module-4e3c</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fOOVBgMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL0WSeQSNj3Q6kzNv5rN4bg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fOOVBgMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AL0WSeQSNj3Q6kzNv5rN4bg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next major version of the very popular PWA helper library was just released. Workbox 4 brings &lt;a href="https://github.com/GoogleChrome/workbox/releases/tag/v4.0.0"&gt;many interesting additions&lt;/a&gt; to the existing modules and only a &lt;a href="https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-v3#breaking_changes"&gt;few minor breaking changes&lt;/a&gt;. Also, it ships one totally new module called &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-window"&gt;&lt;em&gt;workbox-window&lt;/em&gt;&lt;/a&gt;, to fulfil the need of developers in a simple and powerful way to register the service worker, to hook into its lifecycle, and to provide a bi-directional communication channel with the app. This is the first module of Workbox to be used in the &lt;em&gt;window context&lt;/em&gt;, i.e. in our application’s (not service worker’s) code.&lt;/p&gt;

&lt;p&gt;Let’s explore this new module to check what will it take to build the well-known “refresh-to-update-version” technique — one of the UX best practice for PWA. As we use this flow often while building our applications, and Workbox exposes the corresponding tooling now, we just need to find a simple and robust code to build that flow. This article is my try to find that code: minimal and stable. But first, what is this flow I’m talking about?&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh-to-update-version 101
&lt;/h3&gt;

&lt;p&gt;You open some website. And after a couple of seconds, it shows you some prompt/dialogue saying “A new version of this website is available. [Refresh the page to get it]”. In most cases that means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This is a service worker-driven origin (if it also has Web App Manifest we can call it PWA), and the UI you see was not fetched from the network but was taken from the Cache Storage of your browser&lt;/li&gt;
&lt;li&gt;You visited this website using this browser some time ago&lt;/li&gt;
&lt;li&gt;Between the previous and current visit, the deployed version was updated&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dialogue you see is a sort of trade-off between the possibility to show app UI immediately, without fetching it from the network (using precached version) and the need to deliver the actual version of the app to the user after all. Where is the compromise then? It’s in the fact that we still load the precached (“old”) version of the app from Cache Storage, but if the service worker knows that there is a new version available, it fetches the updated resources, updates cache and sends a message (using Broadcast Channel API or postMessage) to the app. Where we catch this message and show the notorious “The app was updated. Click refresh to upload” message to the user. Next page load — and we serve the “new” version from Cache Storage (of course, if our service worker performed all the listed above operations in a proper way).&lt;/p&gt;

&lt;p&gt;Another variation of this technique — we do not send any signal from service worker but listen to the changes of its lifecycle in our app. For our case, the combination of onupdatefound and onstatechange events caused by the fetching of byte-different service worker could mean the change of hash sums of the resource(s) mentioned in “to precache” list injected in service worker. Which, in its turn, means that the new version of the app was built — so we can safely show “The app was updated” message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workbox v3 options to have refresh-to-update-version flow
&lt;/h3&gt;

&lt;p&gt;First, let’s say thanks to the Workbox maintainers for the magic precacheAndRoute() method we could use in our own service worker. This method takes care of all the complexity of precaching, version maintaining, fetching updated resources, updating the cache etc. We just pass the object with resources and their hash sums (built by another helper from Workbox family — workbox-build module) and it works. Furthermore, another line of code in the service worker:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;…and we can receive the signals about the precached resources were updated in our app code — exactly what we need to show the message to our user:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We can even add the same plugin to the resources we cache during runtime to follow their updates if needed.&lt;/p&gt;

&lt;p&gt;Why do I tell about the option we use in Workbox 3 in the article about Workbox 4? Because it still works fine — your code from v3 related to this flow will not break.&lt;/p&gt;

&lt;p&gt;What about the second option — when we rely on the service worker lifecycle events? In v3 we don’t have any helpers to actually register our Workbox-driven service worker in our app code and subscribe to its events. Of course, we always can write this ourselves or use the really nice &lt;a href="https://github.com/yyx990803/register-service-worker"&gt;register-service-worker&lt;/a&gt; library by Evan You, then the code in our app could look like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;But now we have way more powerful, flexible and truly Workbox-native way to achieve it: &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-window"&gt;workbox-window module&lt;/a&gt;. As stated in the documentation, The key features/goals of this module are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To simplify the process of service worker registration and updates by helping developers identify the most critical moments in the service worker lifecycle, and making it easier to respond to those moments.&lt;/p&gt;

&lt;p&gt;To help prevent developers from making the most common mistakes.&lt;/p&gt;

&lt;p&gt;To enable easier communication between code running in the service worker and code running in the window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s implement the above UX trick using this module.&lt;/p&gt;

&lt;h3&gt;
  
  
  The refresh-to-update-version flow powered by workbox-build
&lt;/h3&gt;

&lt;p&gt;Let’s start at the very beginning. To demo the flow, we need to implement a service worker with precaching and serving the resources forming our application shell.&lt;/p&gt;

&lt;p&gt;The minimalistic version of the Workbox-powered service worker source file could look like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Lines 8 and 9 are important in the context of this article. You will read later why do we need them&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why is this “source file”? Because we have to process it after every build of our application. To be precise — we have to inject the list of resources to precache and their hash sums as a parameter for precacheAndRoute() method (instead of this empty array). To save us from this boring task Workbox has 3 options to choose from: Workbox CLI, Webpack plugin, and Node module. The last one is my choice: it needs neither globally installed CLI nor Webpack configuration file exposed. Installing the &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-build"&gt;&lt;em&gt;workbox-build&lt;/em&gt;&lt;/a&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install workbox-build --save-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now the service worker build script:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And the final part — is to add the npm run script combining the build of our app and service worker, one after another:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you might notice, I use an &lt;a href="https://github.com/webmaxru/angular-pwa/tree/workbox-v4"&gt;Angular app&lt;/a&gt; in my example (ng build --prod is a build command for it) but everything I describe in that article about Workbox modules and PWA techniques is applicable to any JavaScript application.&lt;/p&gt;

&lt;p&gt;After I do npm run build-pwa I see something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generated dist/angular-pwa/service-worker.js, which will precache 6 files, totaling 735289 bytes.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the service worker in the distribution folder now contains all the info Workbox needs to know about our app:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;It would be the same in Workbox 3. But now the difference starts: let’s register this service worker in our app using &lt;em&gt;workbox-window&lt;/em&gt;. Installing the module first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install workbox-window
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Hint: there are &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-window#importing_and_using_workbox-window"&gt;different scenarios&lt;/a&gt; of importing/using/bundling this module available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now in our application code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Some important things to notice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is no service worker feature detection in register() method, so don’t forget to have this in your code (line 3). At the same time, the delaying of the registration until the window.onload event is included, so we don’t have to add this extra wrapper.&lt;/li&gt;
&lt;li&gt;Where exactly to put this code in your app? The later — the better. Have it after your app was fully rendered for service worker not to compete with the main thread for the network and CPU resources. For Angular apps, the best place will be in then() block after bootstrapModule() call in main.ts file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Time to run our app in any static http server. I use &lt;a href="https://www.npmjs.com/package/serve"&gt;serve&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3WRp70XJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AYD12Vh1zTvbki3UEMXlmKA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3WRp70XJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AYD12Vh1zTvbki3UEMXlmKA.png" alt=""&gt;&lt;/a&gt;Running the PWA&lt;/p&gt;

&lt;p&gt;This is exactly what we expect: the service worker was registered, some files were precached. Now if you shut down the server or check &lt;em&gt;Offline&lt;/em&gt; checkbox in DevTools — the app will still be available. Thanks to our Workbox-powered service worker serving the resources from the Cache Storage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hint: to have much more detailed log just set the corresponding logging level in DevTools — see the &lt;em&gt;Default levels&lt;/em&gt; dropdown in the right bottom corner of the screenshot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s time to update something in our app. Let’s change the title to &lt;em&gt;Angular PWA 6&lt;/em&gt;. Build/deploy/serve/refresh the page: you still see &lt;em&gt;Angular PWA 5&lt;/em&gt;. Hit browser’s refresh button once again — now you see the new title. This was expected and our goal is to give the user a hint that the app was actually updated while they see the older version. One of the listeners exposed by workbox-window called installed will help!&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now on every application update, we’ll see the prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9X8ADsi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_jfyZdcctc2NqCR32_hBtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9X8ADsi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_jfyZdcctc2NqCR32_hBtg.png" alt=""&gt;&lt;/a&gt;Refresh-to-update-version&lt;/p&gt;

&lt;p&gt;Some notices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We have an extra condition in the code — event.isUpdate. It’s there because we don’t want to show this message on the very first service worker installation, only on the updated. The inversion of this condition is a good option to show a message like “This app is now ready for the offline usage”&lt;/li&gt;
&lt;li&gt;The described approach works ONLY if we are good with modifying the service worker lifecycle by skipping the waiting phase (see lines 8 and 9 in our service worker code). Without this modification the new service worker will not be activated until the user closes all the open tabs with this app — as a result, they will see the old version until that. If for some reason skipping activation step is not an option for your project — please use more general (and more complex) &lt;a href="https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users"&gt;option mentioned in the documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Please, don’t use JavaScript’s confirm() method in production :) It’s in the sample code only for simplicity. Use the less obtrusive and non-blocking toast/snackbar from the UI library you use for your application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Awesome! Now we have pretty laconic code helping us to register service worker and catch the proper moment when to show the message about a new version is available.&lt;/p&gt;

&lt;p&gt;Of course, workbox-window has &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-window#important_service_worker_lifecycle_moments"&gt;lots of other useful events&lt;/a&gt; helping us to have full control over the service workers: both “internal” (registered by workbox-window) and “external” — all others, for example from some 3rd party services providing Push-notifications. Also, it gives a convenient option to &lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-window#window_to_service_worker_communication"&gt;communicate with the service worker&lt;/a&gt; which will be extended and standardized by the release of the &lt;a href="https://github.com/GoogleChrome/workbox/issues/1848"&gt;workbox-messages&lt;/a&gt; module.&lt;/p&gt;

&lt;p&gt;To sum up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Workbox 4 was released — production-ready library for the PWA’s main network tasks automation&lt;/li&gt;
&lt;li&gt;The new module workbox-window gives developers a convenient way to register service worker and listen to the events of its lifecycle&lt;/li&gt;
&lt;li&gt;We discovered the proper event to react on for showing the message to the user for the refresh-to-update-version flow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, let’s try the brand new &lt;a href="https://developers.google.com/web/tools/workbox/"&gt;Workbox 4&lt;/a&gt; and its workbox-window module! If you find any issues, &lt;a href="https://github.com/GoogleChrome/workbox/issues"&gt;please report&lt;/a&gt;. If you wish to know all the latest news about Workbox and PWA in general, please follow me on Twitter &lt;a href="https://twitter.com/webmaxru"&gt;webmaxru&lt;/a&gt; and join &lt;a href="https://bit.ly/go-pwa-slack"&gt;PWA Slack&lt;/a&gt; team. My direct messages are always open for your technical questions and invitations to speak about PWA/Workbox at your conference or hold a PWA workshop for your conference/company.&lt;/p&gt;




</description>
      <category>workbox</category>
      <category>javascript</category>
      <category>serviceworker</category>
      <category>pwa</category>
    </item>
    <item>
      <title>Background Fetch API: Get Ready To Use It!</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Fri, 30 Nov 2018 08:49:15 +0000</pubDate>
      <link>https://dev.to/webmaxru/background-fetch-api-get-ready-to-use-it-314n</link>
      <guid>https://dev.to/webmaxru/background-fetch-api-get-ready-to-use-it-314n</guid>
      <description>&lt;p&gt;I had a section with the explainer of Background Fetch API in many of my PWA slidedecks I presented during the last 12 months. It’s in “Upcoming Features” chapter of my “Advanced Service Worker” session, and I only introduced the basic concept and had some code on the slides. The late evening before the day of my talk at &lt;a href="https://holyjs-moscow.ru/talks/6ebx7vkuxg8kyksmkii2wo/" rel="noopener noreferrer"&gt;HolyJS&lt;/a&gt; conference I decided to check the state of this promising API at &lt;a href="https://www.chromestatus.com/features/5712608971718656" rel="noopener noreferrer"&gt;ChromeStatus.com&lt;/a&gt; and in Chrome Canary (I love to report all the latest statuses during my sessions) and… I didn’t sleep much that night — I found the &lt;a href="https://github.com/WICG/background-fetch" rel="noopener noreferrer"&gt;latest API spec&lt;/a&gt; in the almost working state and dedicated some hours to the experiments. This article is about the idea of background fetch in service workers and about how to start using it now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Background Fetch API is still very experimental. It has only the rough specification proposal and partially working implementation in Chrome Canary (M72) with &lt;a href="https://dev.tochrome://flags/#enable-experimental-web-platform-features"&gt;Experimental Web Platform features&lt;/a&gt; flag enabled. All the concepts/interfaces/methods/events might change in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is Background Fetch?
&lt;/h3&gt;

&lt;p&gt;If we look at the Progressive Web Apps concept from “filling the gap between web and native apps functionality” point of view, this API brings manageable and predictable downloads and uploads of the large assets (sets of assets to be precise): files, resources, you name it. Large enough to start worrying about the possible network interruptions, and/or script timeouts. For example, storing locally MP3-files of your music streaming service, or podcast episodes, or maybe even some videos, or large PDF-files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You might say that we do it for years without any service workers — just click a link to the file and the browser will do the rest: with the possibility to pause, cancel and restore the download. You are right, but you download this resource to the &lt;em&gt;operating system’s&lt;/em&gt; context (file system), not your web application. Everything we get from the network using Background Fetch API we can store in the context of the corresponding app for later use without any extra hassle.&lt;/li&gt;
&lt;li&gt;Now you say that we can achieve the same by just using the &lt;em&gt;fetch&lt;/em&gt; event of the service worker. Yes, we can download some file and put it into app origin’s Cache Storage (and this is what we normally do for the app shell architecture and runtime API endpoints caching). The differentiator here, again, is the size of this resource — is there a chance that the browser will kill the service worker during the download because of the timeout. Also, from the UX perspective, it really makes sense to notify the user about the download fact and its progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Background Fetch is here to solve the large assets download/upload issues in both developers and users friendly way. Let’s have a look at the features of this API listed at the WICG’s (Web Incubator Community Group) &lt;a href="https://github.com/WICG/background-fetch" rel="noopener noreferrer"&gt;repo&lt;/a&gt; playing the role of specification proposal:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Allow fetches (requests &amp;amp; responses) to continue even if the user closes all windows &amp;amp; workers to the origin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow a single job to involve many requests, as defined by the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow the browser/OS to show UI to indicate the progress of that job, and allow the user to pause/abort.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow the browser/OS to deal with poor connectivity by pausing/resuming the download.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow the app to react to success/failure of the job, perhaps by caching the results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow access to background-fetched resources as they fetch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simple words: after we registered a background fetch and initiated the transfer by this fact (it could be done both in our main thread and in the service worker) we are fully covered by the browser (or the platform) possibilities. During the transfer, we can close the tab with application — the transfer will not stop. We can close the browser — the transfer will continue after the browser restart. The network may go down — the browser will continue the transfer automatically after it’s up. The results of the operation will be sent to the service worker (as the application tab might be closed by that time). And despite this API’s name, it’s not 100% “background” operation as it gives to the user the clear understanding about something is going on.&lt;/p&gt;

&lt;p&gt;The feature set looks really interesting, let’s start coding to see how that works and how that looks like!&lt;/p&gt;

&lt;h3&gt;
  
  
  Background Fetch MVP
&lt;/h3&gt;

&lt;p&gt;We start from the very minimal solution, just to check if that works at all. The plan is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We register a service worker&lt;/li&gt;
&lt;li&gt;We register a background fetch in our main app by clicking the button&lt;/li&gt;
&lt;li&gt;We listen to the background fetch events in the service worker. On success — we put the asset(s) into Cache Storage. On fail — we still try to put into the storage all we might get&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: to focus on our today’s topic we will not create a PWA in its full sense (there will be neither app shell nor web app manifest), so we can formally call our project a “service worker-driven website/webapp”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Minimal code to see the download starts
&lt;/h4&gt;

&lt;p&gt;The code for Step 1 is below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;On the app start, we register a service worker file service-worker.js. Please note that even for this demo I use the best practices for the registration to just emphasize the importance of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature detection — to not break the app in older browsers. Good manners for most of the PWA features.&lt;/li&gt;
&lt;li&gt;Registration postponing until window.onload. Well, in our case we don’t have any other resources to load except index.html itself, but let this proper registration snippet be a reminder about the fact that service worker &lt;strong&gt;cannot&lt;/strong&gt; improve the &lt;em&gt;first-load experience&lt;/em&gt;, but it can easily make it worse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On “Store assets locally” (“&lt;em&gt;Download&lt;/em&gt;” term does not 100% correctly reflect the action we perform) button click we call backgroundFetch.fetch() of the active service worker registration. It registers a background fetch and immediately initiates the transfer.&lt;/p&gt;

&lt;p&gt;In our service worker, in addition to utility install and activate events (to simpler track the lifecycle stage), we have only one useful event handler at the moment: backgroundfetchsuccess where we output the registration object of the event. Let’s see it in action! For our example, I download 1,39 GB movie file (to have some seconds to watch the flow) from the same origin using http-server static webserver on Mac OS. &lt;em&gt;(A reminder: we have to use Chrome Canary M72 to test Background Fetch API)&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Abqi7gem3j5e5OlfMtht0PQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Abqi7gem3j5e5OlfMtht0PQ.gif"&gt;&lt;/a&gt;We download the file using Background Fetch API&lt;/p&gt;

&lt;p&gt;Yes! It works! We see our file downloading in the browser’s bottom bar (where we normally see the regular downloads). So what happens if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;You close the tab with the app&lt;/em&gt;: download will continue, you see it in the bottom bar.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;You close the browser&lt;/em&gt; (by clicking x button): the closing confirmation will appear. If we confirm — the download will still continue, now the status is on the browser’s icon:&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%2Fcdn-images-1.medium.com%2Fmax%2F140%2F1%2AE8YUcIMi4aQ6MM3ZR7P4dw.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%2Fcdn-images-1.medium.com%2Fmax%2F140%2F1%2AE8YUcIMi4aQ6MM3ZR7P4dw.png"&gt;&lt;/a&gt;Chrome is downloading the asset without the window open&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;You unload the browser from the memory&lt;/em&gt; (by choosing Quit from the right-click menu): download will stop but will continue after you launch the browser again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awesome! We have a download mechanism which is persistent across the app status and browser restarts. We can even Pause/Cancel this transfer:&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%2Fcdn-images-1.medium.com%2Fmax%2F626%2F1%2AfvIWzpTlayP1vcp-zS64Qg.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%2Fcdn-images-1.medium.com%2Fmax%2F626%2F1%2AfvIWzpTlayP1vcp-zS64Qg.png"&gt;&lt;/a&gt;Using standard browser download UI to manage background fetches&lt;/p&gt;

&lt;p&gt;There is only one issue: we download this asset to… nowhere! Because it’s up to us — what do we want to do with these bytes received. Time to implement it!&lt;/p&gt;

&lt;h4&gt;
  
  
  The code to store the received data in the Cache Storage
&lt;/h4&gt;

&lt;p&gt;We take the sample code from &lt;a href="https://github.com/WICG/background-fetch" rel="noopener noreferrer"&gt;Background Fetch API proposal repo&lt;/a&gt; in the simplified form for now. At the same time in our main app let’s add the second asset to download to the same registration of the background fetch — it’s possible according to the spec.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let’s split it into steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Button click in index.html&lt;/em&gt;: we register a background fetch with the ID my-fetch and the array with two files as a target. We can pass the array of URLs (this is what we do) or Requests (useful for &lt;em&gt;upload&lt;/em&gt; usecase) there. Right after the browser starts executing this request (&lt;em&gt;download&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Download ready in service-worker.js&lt;/em&gt;: service worker gets backgroundfetchsuccess event where we:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Open (or create) the cache with the name equals background fetch registration.id. The cache name could be any string actually.&lt;/li&gt;
&lt;li&gt;Get all the records of this background fetch using registration.matchAll(). Each record = URL/Request we listed during background fetch registration.&lt;/li&gt;
&lt;li&gt;Build the array of promises by iterating through the records, waiting until record’s responseReady resolved and trying to put this record’s response into the Cache Storage.&lt;/li&gt;
&lt;li&gt;Execute the promises&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Alz4s8_nwI1wjeaihfrMvZg.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Alz4s8_nwI1wjeaihfrMvZg.gif"&gt;&lt;/a&gt;Downloading two large files&lt;/p&gt;

&lt;p&gt;We see how the downloading is beginning and “Usage” chart is updating. During the download time we have our data in “Other” category, then (on cache.put()) it’s moving to the cache. If we open “Cache Storage” section we’ll find these two large files (1,39 GB and 140 MB) there — now we can intercept the requests to these and serve them from the cache!&lt;/p&gt;

&lt;p&gt;There is another thing. According to the specification, the browser should show the “Download multiple files” confirmation dialogue like this:&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%2Fcdn-images-1.medium.com%2Fmax%2F626%2F1%2AygTj-yRzpOuqzjX5V75Fgg.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%2Fcdn-images-1.medium.com%2Fmax%2F626%2F1%2AygTj-yRzpOuqzjX5V75Fgg.png"&gt;&lt;/a&gt;Native confirmation dialogue&lt;/p&gt;

&lt;p&gt;Sometimes it shows up but most of the times it’s not there. I tried to change the origins, reset cache, use different assets in the background fetch registration but was still unable to reproduce the sustainable popping up of this window. Most likely it’s the bug of the current Chrome Canary build. Anyway, I can explain how that works: if you click “Block” you change the setting for “Automatic downloads” for this origin and you see the corresponding icon in the address bar:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AtsdEZ2glcpn53pk2jgAuWA.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AtsdEZ2glcpn53pk2jgAuWA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why we have the alert() with the explanation of how to fix this in the catch block of background fetch registration. Most likely the possible error’s cause is exactly this setting.&lt;/p&gt;

&lt;p&gt;The last comment related to the code. Why do we disable the “Store assets locally” button if the background fetch registration was successful? To avoid another possible error: double registrations with the same ID. If the first registration is active (performing the network transfers) the second registration with the same ID &lt;em&gt;will be rejected&lt;/em&gt;. After the transfer completion, the corresponding background fetch registration to be removed and we can enable the button (not implemented at this moment) if we wish to give the possibility to perform exactly the same operation like the just completed one.&lt;/p&gt;

&lt;p&gt;Some observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The transfer appears neither in &lt;em&gt;DevTools -&amp;gt; Network&lt;/em&gt; tab nor &lt;em&gt;Downloads&lt;/em&gt; section of Chrome Canary. In that sense, it’s 100% true background fetch.&lt;/li&gt;
&lt;li&gt;Turning on “Offline” checkbox in &lt;em&gt;DevTools -&amp;gt; Network&lt;/em&gt; tab doesn’t affect the transfer.&lt;/li&gt;
&lt;li&gt;Resuming of the download after unloading the browser from the memory works weird. The transfer starts again but the flow stops after every status change: after downloading the first file you have to restart the browser to start downloading the second. After downloading the second file you have to restart the browser to call backgroundfetchsuccess event. I hope this will be fixed in the release version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;em&gt;almost&lt;/em&gt; all works good but looks not very user-friendly: not very informative to be precise. Let’s use the possibility to provide some meta-information about assets and to track the progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  Organizing a better UX
&lt;/h4&gt;

&lt;p&gt;Let’s externalize the JSON with meta-information about the asset set we want to register and add some extra features to our main app and service worker:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;What was added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON file with some settings (meta-information about this asset set). We fetch this data both in the main app and service worker. In real life, this could be a call to your backend API — to get the settings of this specific asset set based on its ID (hardcoded as 'series’ in our code).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;index.html&lt;/em&gt;: we now pass the options optional parameter to the background fetch registration containing thetitle, icons, downloadTotal — pretty much self-explanatory properties. Also, we attach progress event listener to the background fetch registration where we have an access to downloaded property which is changing with some periodicity.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;service-worker.js&lt;/em&gt;: both on success and failure of the main operation we update the download indicator with the current status and the &lt;em&gt;title&lt;/em&gt; of the background fetch we specified in JSON settings file. Of course, instead of this optional title property we could use background fetch registration ID both for the main app and service worker messages, but this ID is not for the UI (it can be a long auto-generated unique hash-string for example), so it’s a really good idea to have the human-friendly title provided.&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQiiKy6y6RBYeO4X9YXkztQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQiiKy6y6RBYeO4X9YXkztQ.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have a full control over the texts and user informing. Some notices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I was unable to make the custom icons showing up. Maybe this feature was not implemented yet or I use the wrong object format.&lt;/li&gt;
&lt;li&gt;The downloadTotal property we pass during the background fetch registration is the total number of bytes of the whole set. It’s also optional. If passed, it works as a guard — we’ll get backgroundfetcherror event in the service worker if the total size of the resources is larger. Also, it’s a good helper for our UI to show the correct download percentage if needed. Unfortunately, there is another bug: at least on Mac OS this number can’t be larger than 2147483647 (2 GB) which is the maximum size of &lt;em&gt;one&lt;/em&gt; file in the file system but it’s intended to be the maximum total size of the &lt;em&gt;whole set&lt;/em&gt;. I’m going to submit a bug to the &lt;a href="https://bugs.chromium.org/p/chromium/issues/list?q=component:Blink%3EBackgroundFetch" rel="noopener noreferrer"&gt;Chromium Bug Tracker&lt;/a&gt; about this. Meanwhile, if the total set size doesn’t bypass 2GB (like in our case) our calculations are correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let’s add some more useful handlers to our service worker and experiment with some non-standard cases to call some errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Handling errors
&lt;/h4&gt;

&lt;p&gt;We had a look only at best scenarios so far when everything works as planned which is not always the case when we deal with network connections. Let’s add more handlers to our service worker to be ready to the variety of possible errors. Again, we’ll use the code from &lt;a href="https://github.com/WICG/background-fetch" rel="noopener noreferrer"&gt;Background Fetch API proposal repo&lt;/a&gt; as a foundation.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;What do we expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;On any kind of failure&lt;/em&gt; — let’s try to save to the cache at least what we already downloaded (and let1s tey to continue with the next asset if any)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;If the user aborts the transfer&lt;/em&gt; — we do not store what was potentially downloaded&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Not related to the errors bu useful handler &lt;/em&gt;— if user click on this transfer infobar we just open the new tab with URL we specified in our JSON file with the asset settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Demo time! Let’s specify the wrong URL for one of the assets in our set: assets/s01e01.avi (not .mpg):&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVE-o1EzCCB5xp2iqvh6RBg.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVE-o1EzCCB5xp2iqvh6RBg.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The background fetch ends up by thebackgroundfetchfail event with the registration.failureReason equal to “&lt;em&gt;bad-status&lt;/em&gt;”. It’s important that despite the error with a particular asset, we continue our batch with the next asset and have the possibility to cache all our previous successful responses.&lt;/p&gt;

&lt;p&gt;My other tries to call different types of errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloading asset from another origin. It works normally if CORS set up. If not — we end up with backgroundfetchfail event with “&lt;em&gt;fetch-error&lt;/em&gt;” as registration.failureReason. You can only specify HTTPS-driven origins (or localhost).&lt;/li&gt;
&lt;li&gt;Shutting down the server and waiting until the network timeout— “&lt;em&gt;bad-status&lt;/em&gt;”&lt;/li&gt;
&lt;li&gt;Shutting server down and starting up again (to mimic the temporarily out-of-service) — doesn’t work well. It resumes the transfer after server goes online but sends backgroundfetchfail with “&lt;em&gt;bad-status&lt;/em&gt;” very soon. Also, we have non-consistent quota usage numbers:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AEYoDOamIjretIZ_SVylAZA.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AEYoDOamIjretIZ_SVylAZA.png"&gt;&lt;/a&gt;Three different values for what has been downloaded&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two more issues in that scenario. First, the downloading of the second file doesn’t even start (despite the server is alive). And second, “&lt;em&gt;Content-Length&lt;/em&gt;” shown in Cache explorer confusingly equals to the full file size despite we were able to download this asset only partially:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Au7xnX7TfQ95z11FHpwB51g.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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Au7xnX7TfQ95z11FHpwB51g.png"&gt;&lt;/a&gt;And value number four…&lt;/p&gt;

&lt;p&gt;The above errors give us a chance to cache what could be downloaded successfully. According to the &lt;a href="https://github.com/WICG/background-fetch" rel="noopener noreferrer"&gt;specification proposal&lt;/a&gt;, the behavior is different for the errors related to the downloadTotal property. Let’s set it equal to &lt;em&gt;140015339&lt;/em&gt; — exact size in bytes of our “small” asset (&lt;em&gt;s01e02.mpg&lt;/em&gt;). After the transfer start, we receive backgroundfetchfail with “&lt;em&gt;total-download-exceeded&lt;/em&gt;”. This is expected — the &lt;em&gt;s01e01.mpg&lt;/em&gt; is larger than this limit. What we don’t expect (but it’s correct according to the spec) — is that even &lt;em&gt;s01e02.mpg&lt;/em&gt; will not be cached in that case — the transfer operation aborts immediately on “&lt;em&gt;total-download-exceeded&lt;/em&gt;” error. We can’t put into the cache even properly sized assets. So if we remove this “incorrect size”-file from the URL array to fetch, &lt;em&gt;s01e02.mpg&lt;/em&gt; will land in Cache Storage.&lt;/p&gt;

&lt;p&gt;The last handler in our service worker — backgroundfetchclick — is not related to the error handling. This is what happens when the user clicks on this download infobar. In our example, we open a new tab with the URL we provided in the configuration.&lt;/p&gt;

&lt;p&gt;The full code of the demo (with downloading images instead of large movie files) is available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;a href="https://github.com/webmaxru/background-fetch" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On &lt;a href="https://background-fetch.glitch.me/" rel="noopener noreferrer"&gt;Glitch&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In my opinion, Background Fetch API could be the “next big thing” for the PWA concept. There are plenty of interesting usecases making the web apps as powerful as the native ones in terms of large app assets network manipulations. But there are some risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Browser vendors adoption&lt;/em&gt;. Yes, we all know that everything in PWA is based on “&lt;em&gt;progressive enhancement&lt;/em&gt;” idea and we HAVE TO architect our apps in that way and build our apps actively using feature detection. But I really don’t want to have some APIs specific to this or that vendor.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;User protection.&lt;/em&gt; Wasteful network traffic consumption, littering storage by abandoned resources, breaking the privacy by exposing the list of downloaded URLs are my main concerns. Downloading and storing large resources should be taken very seriously both by specification authors and developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t want to finish this article by the “risks” statement so let me give you an idea of the module/library/package for the automation of background fetches flows. Let’s assume that we want to give the possibility to our users to store locally some set) of the static files (MP3s?) we have on our server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using some kind of build helper we generate background fetch manifest (looking like the JSON setting file we used for the demo). It could be the script iterating through the folders.&lt;/li&gt;
&lt;li&gt;In our app UI, we can now generate the lists of assets. Like the filename + “Store locally” button.&lt;/li&gt;
&lt;li&gt;We agree on some kind of API — like “on click &lt;em&gt;Store locally&lt;/em&gt; button we POST the filename to &lt;em&gt;/storage-manager&lt;/em&gt; endpoint”. Another endpoint for removal.&lt;/li&gt;
&lt;li&gt;Also during this list generation, it makes sense to check the status of this asset in Cache Storage. If it’s there — we have “Remove from device” button instead of “Store”.&lt;/li&gt;
&lt;li&gt;In our service worker, based on the background fetch manifest and API we’ve just architected above, we generate the routing for “fetch” event. Now we can register background fetches in our service worker after the request from app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you wish to implement it as a standalone helper or as a plugin to &lt;a href="https://developers.google.com/web/tools/workbox/" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt; — ping me, I’ll give you more details about this idea.&lt;/p&gt;

&lt;p&gt;And let’s start our experiments with Background Fetch API!&lt;/p&gt;

&lt;h3&gt;
  
  
  About me
&lt;/h3&gt;

&lt;p&gt;I’m deep into PWA topic. And go deeper day-by-day :) I call myself PWAdvocate because I support the developers (as a developer myself) by providing the latest updates on this topic, by introducing and sharing developer-friendly and user-friendly ways to build the apps, by answering the questions. You can see me speaking and training at many conferences (I have 100+ flights each year) — please come to chat with me about PWA, Web Platform, developer communities!&lt;/p&gt;

&lt;p&gt;Also, I’m open for the invitations to speak/train at your conference or private corporate meeting/bootcamp/hackaton in 2019— just DM me on &lt;a href="https://twitter.com/webmaxru" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>webplatform</category>
      <category>serviceworker</category>
      <category>pwa</category>
      <category>backgroundfetch</category>
    </item>
  </channel>
</rss>
