<?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>A practitioner's guide to getting more value out of AI coding: agent quality &amp; token optimization</title>
      <dc:creator>Maxim Salnikov</dc:creator>
      <pubDate>Mon, 25 May 2026 20:15:13 +0000</pubDate>
      <link>https://dev.to/webmaxru/a-practitioners-guide-to-getting-more-value-out-of-ai-coding-agent-quality-token-optimization-3n7j</link>
      <guid>https://dev.to/webmaxru/a-practitioners-guide-to-getting-more-value-out-of-ai-coding-agent-quality-token-optimization-3n7j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: The Wrong Question
&lt;/h2&gt;

&lt;p&gt;GitHub's shift from premium requests to usage-based billing has triggered a wave of anxiety across engineering teams. The question echoing through Slack channels and leadership meetings is some variation of: &lt;em&gt;"How do we reduce our token spend?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's the wrong question.&lt;/p&gt;

&lt;p&gt;Focusing purely on cost diminishes the value you get from agents. A better framing is: &lt;strong&gt;"How do we get the most out of the tokens we spend?"&lt;/strong&gt; That subtle reframing changes everything — from how you write prompts, to which model you reach for, to how you architect your codebase, to how you organize your team's workflows.&lt;/p&gt;

&lt;p&gt;This article walks through the full case for quality-first token optimization, the foundational mental models you need to reason about it, and the concrete controls and techniques that move the needle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Why Agent Quality Is the Better Lens
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Agent Gambling Is No Longer Sustainable
&lt;/h3&gt;

&lt;p&gt;When tokens were effectively free, agent accuracy didn't really matter. The dominant pattern became what's best described as "agent gambling": throw together a lazy prompt with minimal context, fire off an agent, and if it fails, fire off another one. Think of it as the NASA Artemis problem in reverse — if rockets were cheap, you'd send 20 in the general direction of the moon and hope one lands.&lt;/p&gt;

&lt;p&gt;That worked when each developer ran a handful of agents per day. It stops working the moment developers — and especially AI engineers orchestrating fleets — are running dozens or hundreds of agents per day. The economics invert. The cost of misfires dwarfs the cost of doing the work properly.&lt;/p&gt;

&lt;p&gt;The fix isn't to send fewer rockets blindly. It's to make sure each rocket actually lands. Higher per-agent quality means fewer retries, fewer wasted tokens, and better ROI on every dollar of usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ROI Mental Model
&lt;/h3&gt;

&lt;p&gt;The guiding equation for thinking about agent economics:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Agent ROI = (Value of Agent Output − Token Cost) / Token Cost × 100%&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can't calculate this precisely, but it's a directionally useful lens. Two things follow immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Optimizing cost when value is zero is meaningless.&lt;/strong&gt; Cutting your spend by 50% on outputs that don't ship anything useful is just losing money more slowly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increasing value often means decreasing tokens.&lt;/strong&gt; Developers routinely stuff irrelevant text into prompts, let conversations compound with stale context, and pile on documentation the model doesn't need. Trimming that context usually improves both quality &lt;em&gt;and&lt;/em&gt; cost simultaneously. They're the same lever.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Compound Error Problem
&lt;/h3&gt;

&lt;p&gt;Here's the math that should haunt anyone running multi-step agent workflows: &lt;strong&gt;errors compound multiplicatively.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At &lt;strong&gt;99% accuracy per step&lt;/strong&gt;, a 50-step workflow lands at &lt;strong&gt;~60% overall success.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;At &lt;strong&gt;95% accuracy per step&lt;/strong&gt;, the same workflow drops to &lt;strong&gt;~8%.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LLMs are non-deterministic. Every step in an inner agent loop, every hop in an orchestrated workflow, every tool call — they all multiply against each other. This means every percentage point of per-step quality buys you a disproportionate improvement in overall reliability. And every miss isn't just a wasted token call — it triggers fix cycles, review overhead, reruns, debugging sessions, and burned human attention.&lt;/p&gt;

&lt;p&gt;The takeaway: apply the same "shift-left" mindset to agents that you apply to quality, testing, and security in traditional engineering.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mantra
&lt;/h3&gt;

&lt;p&gt;The whole philosophy collapses into one line worth pinning to your monitor:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Instead of counting tokens, make every token count.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reduce token usage as a &lt;em&gt;consequence&lt;/em&gt; of pursuing quality — not as a goal in itself. Send fewer, better-targeted rockets. The fuel savings follow automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Foundations — LLMs, Agents, and Context Windows
&lt;/h2&gt;

&lt;p&gt;Before you can optimize anything, you need to internalize a few mechanical truths about how this technology actually works.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLMs Are Pure Word Probability Machines
&lt;/h3&gt;

&lt;p&gt;Strip away the marketing and what you have is a text-in, text-out system that predicts the next word given an input plus the patterns from its training data. When you type "GitHub Copilot is the world's most widely…" the model assigns probabilities to candidate next words — &lt;em&gt;used&lt;/em&gt;, &lt;em&gt;adopted&lt;/em&gt;, &lt;em&gt;deployed&lt;/em&gt;, and so on — and picks one. In a coding context, it's predicting the next instruction.&lt;/p&gt;

&lt;p&gt;Models have gotten dramatically better, but the underlying mechanism hasn't changed. This matters because &lt;strong&gt;the math doesn't distinguish hallucination from fact.&lt;/strong&gt; A made-up function name and a real one occupy the same probability space. The model isn't "lying" when it hallucinates — it's just doing what it always does with insufficient signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Principle of Context
&lt;/h3&gt;

&lt;p&gt;Which leads to the single most important principle in this entire discipline:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Provide as little context as possible, but as much as required.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two failure modes flank this principle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too much context&lt;/strong&gt; biases the model toward irrelevant patterns and dilutes the signal. Stuffing in five files when one is relevant makes the model worse, not better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too little context&lt;/strong&gt; forces the model to hallucinate to fill gaps. It has to predict &lt;em&gt;something&lt;/em&gt;, and without grounding, it guesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context engineering — the discipline of finding that sweet spot — is the fundamental skill of working with agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  What an Agent Actually Is
&lt;/h3&gt;

&lt;p&gt;An agent is not magic. It's an app — code that sits between you and the LLM. The architecture is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You and your project ↔ The agent (harness) ↔ The LLM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Harnesses are things like VS Code Chat, Copilot CLI, Copilot Cloud Agent, Claude Code, OpenAI Codex. Models are things like GPT 5.5, Claude Opus 4.7, Gemini Pro. The harness is the orchestrator; the model is the inference engine.&lt;/p&gt;

&lt;p&gt;Two things are crucial to understand here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The LLM is stateless.&lt;/strong&gt; What feels like a "conversation" is actually the harness re-sending every prior input and output on every turn. There's no memory inside the model — only an ever-growing transcript being shipped back and forth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens compound.&lt;/strong&gt; Every loop drags the previous loops along with it. Your levers are the things that &lt;em&gt;go into&lt;/em&gt; the context: your prompt, the files you reference, and the agent configs (instructions, skills, MCPs) the harness injects.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Context Window Mechanics
&lt;/h3&gt;

&lt;p&gt;A token is roughly ¾ of an English word. Smaller models offer 50K–200K token windows; larger ones like Opus and GPT-5.5 push toward 1M tokens. For scale: 1M tokens is roughly the entire &lt;em&gt;Lord of the Rings&lt;/em&gt; trilogy plus &lt;em&gt;The Hobbit&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Don't obsess over token counting at the character level. Think at the level of prompts, files, and responses — those are the units that compound on each loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Rot: The Hidden Failure Mode
&lt;/h3&gt;

&lt;p&gt;Even with a huge window, models don't treat all positions equally. Two well-documented effects govern how attention is distributed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lost in the Middle (below ~50% window fill):&lt;/strong&gt; Models bias toward content at the &lt;em&gt;beginning&lt;/em&gt; and the &lt;em&gt;end&lt;/em&gt; of the context. Middle content gets less weight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recency Bias (above ~50% window fill):&lt;/strong&gt; As the window fills up, attention skews heavily toward the &lt;em&gt;end&lt;/em&gt;. System prompts and custom instructions sitting at the beginning start getting effectively ignored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical implications are significant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The beginning of context is prime real estate for instructions and goals.&lt;/li&gt;
&lt;li&gt;The end is where current work lives.&lt;/li&gt;
&lt;li&gt;The middle is where past work decays in influence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Just because you can fill the context window doesn't mean you should.&lt;/strong&gt; Try to keep it under 60–70%.&lt;/li&gt;
&lt;li&gt;If you switch tasks mid-session, the model may revert to the original task, because that's where the strongest signal still lives.&lt;/li&gt;
&lt;li&gt;Above 50% fill, you start losing your own guardrails to recency bias.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix isn't compaction (which trades tokens for potential information loss). It's &lt;strong&gt;a new context window per task&lt;/strong&gt; — &lt;code&gt;/clear&lt;/code&gt; liberally, divide work into discrete sessions, and don't let conversations sprawl.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Quality and Token Controls — The Practical Playbook
&lt;/h2&gt;

&lt;p&gt;Now to the controls themselves, ordered roughly by leverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where You Are on the Maturity Curve Matters
&lt;/h3&gt;

&lt;p&gt;Two archetypes exist on the agent maturity spectrum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI-assisted engineers&lt;/strong&gt; work mostly synchronously with one agent at a time. If you're sending ten agents per day and spending $20/month, saving 50% on tokens just gets you to $10. The juice isn't worth the squeeze.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI engineers&lt;/strong&gt; orchestrate fleets of asynchronous agents. Every percentage point compounds across hundreds of runs. The compound error problem hits hardest here, and optimization pays back enormously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calibrate effort accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Two Biggest Levers
&lt;/h3&gt;

&lt;p&gt;Two controls vastly outweigh everything else: &lt;strong&gt;model choice&lt;/strong&gt; and &lt;strong&gt;relevant context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model choice&lt;/strong&gt; is the single highest-leverage decision. The cost gap between top-tier reasoning models (Claude Opus 4.7) and small models (GPT-5.4 mini) is roughly &lt;strong&gt;24x.&lt;/strong&gt; Match the model to the task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning models&lt;/strong&gt; (Opus, GPT-5.5) for synchronous planning, architecture, debugging, and any work involving large context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-tier models&lt;/strong&gt; (Sonnet, GPT-5.4) for asynchronous implementation work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-tier models&lt;/strong&gt; (Haiku, GPT-mini) for small refactors, repetitive tasks, and documentation updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A reasoning model on a trivial task isn't just expensive — it can actively make things worse, second-guessing tight specifications and "going rogue." Conversely, a small model on a planning task will produce shallow, brittle output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto Mode&lt;/strong&gt; (rolling out from June) detects task intent and selects the model for you. It's the lazy default for anyone who doesn't want to think about it — and it's usually right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relevant context&lt;/strong&gt; is the other half of the equation. Don't stuff prompts with "might need" information. Let the agent discover what it needs. Compacting sessions trades tokens for potential info loss — use it cautiously. And use &lt;code&gt;/clear&lt;/code&gt; often — tokens don't carry across sessions, so a clean slate is free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your Prompt
&lt;/h3&gt;

&lt;p&gt;The prompt is always-on. It sits at the beginning of the context window and has outsized influence due to lost-in-middle effects.&lt;/p&gt;

&lt;p&gt;A few rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't optimize prompts for fewer tokens.&lt;/strong&gt; Optimize them to steer correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be precise and descriptive.&lt;/strong&gt; "Fix the bug" is useless. "Issue #45 describes a bug where X happens — fix it" actually goes somewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add stop signals.&lt;/strong&gt; Phrases like "Stop after you've written the fix. Do not commit or push." prevent agents from running past your intent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add known context upfront.&lt;/strong&gt; Relevant file paths, doc URLs, skills to invoke. Don't make the agent rediscover what you already know.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Divide and Conquer: Research → Plan → Implement
&lt;/h3&gt;

&lt;p&gt;A single context window doing research, planning, and implementation drags irrelevant files and stale reasoning through every phase. Quality degrades.&lt;/p&gt;

&lt;p&gt;The pattern that works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt; (e.g., Gemini 2.5 Pro): "I want to change X. What files are relevant?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt; (e.g., Opus 4.7): Take the research output and produce a precise specification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement&lt;/strong&gt; (e.g., GPT-5.4, often in parallel): Multiple agents split by architecture layer (frontend, backend, database) with clearly defined contracts between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each phase gets a fresh context window. The spec is the artifact that carries information across the boundary — clean, distilled, free of noise. This saves both time and tokens, and produces far higher-quality output than one monolithic session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deterministic Controls: The Compound Error Antidote
&lt;/h3&gt;

&lt;p&gt;Tests, linters, security scanners, type checkers — anything code-enforced and deterministic — are essential context engineering tools. A test either fails or passes. There's no probability. &lt;strong&gt;Every passing test resets the compounding error rate to zero for the property it covers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The contrast is stark:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;With tests:&lt;/strong&gt; buggy change → failing test → correction → passing test. Done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without tests:&lt;/strong&gt; buggy change → buggy change on top of it → another one → incident → debugging session → burned CI/CD minutes, review cycles, human time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Copilot CLI team ships roughly 500 PRs per week. Roughly 53% of their codebase is tests. That's not overhead — that's the moat that lets them move that fast without burning down the production system.&lt;/p&gt;

&lt;p&gt;Cheap in the short term means expensive in the medium term. Guardrails pay back many times over.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Configs: The Context Engineering Surface
&lt;/h3&gt;

&lt;p&gt;Modern agent harnesses pick up a stack of markdown files automatically. These are the surface you work with as a context engineer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent instructions&lt;/strong&gt; — &lt;code&gt;copilot-instructions.md&lt;/code&gt;. Always loaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom agents&lt;/strong&gt; — &lt;code&gt;./github/agents/*.agent.md&lt;/code&gt;. Role-based, manually invoked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; — &lt;code&gt;./github/skills/*/skill.md&lt;/code&gt;. Conditionally loaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCPs&lt;/strong&gt; — external tool integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents&lt;/strong&gt; — separate context windows spawned by the main session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped instructions&lt;/strong&gt; — &lt;code&gt;./github/instructions/*.instructions.md&lt;/code&gt;. Path-pattern based.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt files&lt;/strong&gt; — &lt;code&gt;./.github/prompts/*.prompt.md&lt;/code&gt;. Manual starting points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copilot Memory&lt;/strong&gt; — small always-on instructions learned from behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each has a place. Let's go through the high-leverage ones.&lt;/p&gt;

&lt;h4&gt;
  
  
  Persistent Instructions
&lt;/h4&gt;

&lt;p&gt;These are your always-on guidance, the proactive human-in-the-loop signal. Three things belong in them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project non-negotiables&lt;/strong&gt; (architecture rules, conventions that can't be inferred).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A log of recurring agent misses&lt;/strong&gt; (wrong test framework? wrong build command? Write it down.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output-trimming statements&lt;/strong&gt; ("be concise"). Output tokens are the most expensive — trim them aggressively.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Critical rules: &lt;strong&gt;keep them small, don't use AI to generate them, and recreate them often.&lt;/strong&gt; Research shows that "be concise" performs nearly as well as a 50-line "caveman" skill. AI-generated instructions bloat. Write them yourself, iterate, throw them away. The Copilot CLI team rewrites their entire instructions file every three months as a living document.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Agents
&lt;/h4&gt;

&lt;p&gt;A custom agent forces the model into a specific role or workflow — for example, a &lt;code&gt;/tdd-red&lt;/code&gt; agent that only writes failing tests. The harness retrieves the agent file, injects the definition, restricts the available tools, and appends your prompt.&lt;/p&gt;

&lt;p&gt;The token savings are modest (input is cached). The real win is &lt;strong&gt;preventing wrong paths.&lt;/strong&gt; Restricting an agent to read-only access on GitHub issues, for instance, eliminates an entire class of mistakes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Skills
&lt;/h4&gt;

&lt;p&gt;Skills are conditionally loaded markdown. The harness puts the &lt;em&gt;description&lt;/em&gt; of every skill into context; the LLM tells the harness when it needs the full skill loaded.&lt;/p&gt;

&lt;p&gt;Two pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't overdo it.&lt;/strong&gt; Hundreds of skill descriptions bloat context for marginal benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid redundant skills.&lt;/strong&gt; A "React skill" is wasted if the model already knows React fluently. Skills should add capabilities the agent wouldn't otherwise have. And maintain them as models evolve — what was needed last year may be built-in now.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  MCPs
&lt;/h4&gt;

&lt;p&gt;MCPs add external tools and API calls. The harness offers tool descriptions to the LLM, which invokes them when needed.&lt;/p&gt;

&lt;p&gt;Be rigorous. MCPs bloat tool descriptions and can lead to undesired tool calls. &lt;strong&gt;Deactivate MCPs you don't always need&lt;/strong&gt;, or wrap them inside custom agents that scope when they're active.&lt;/p&gt;

&lt;p&gt;The Playwright MCP is the canonical example: powerful for frontend work, but expensive (screenshots, page reads, full DOM parsing). If always-on, it triggers unnecessary work for trivial CSS changes. Pair it with a custom agent that only activates it when you're doing real UI work.&lt;/p&gt;

&lt;h4&gt;
  
  
  Subagents
&lt;/h4&gt;

&lt;p&gt;A subagent opens a second context window for a specific task — research, document summarization, etc. — and returns a compact summary to the main session. This keeps the main context clean.&lt;/p&gt;

&lt;p&gt;The trade-off: more tokens are spent inside the subagent. It's a conditional optimization. Use it when the alternative is polluting your main session with hundreds of irrelevant files.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Rest
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scoped instructions&lt;/strong&gt; are useful in monoliths with distinct sections (e.g., one set of rules for the auth module, another for billing). Start with static persistent instructions first; reach for scoped only when needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt files&lt;/strong&gt; are manually invoked, can trim the toolset, and serve as good standardized starting points. (Not supported in Copilot CLI at the moment.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copilot Memory&lt;/strong&gt; learns from your behavior automatically. Check it periodically to make sure it's learned the right things.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Power User Techniques
&lt;/h3&gt;

&lt;p&gt;For orchestrators running hundreds or thousands of agents, additional levers exist — though they trade quality for token savings and require careful testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Think in code.&lt;/strong&gt; Prefer scripts to analyze files over feeding them to the LLM. A 200-line file analyzed by a Python script consumes near-zero tokens versus thousands in context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI over MCP.&lt;/strong&gt; Models already know how to use tools like &lt;code&gt;gh&lt;/code&gt;. A CLI invocation can be leaner than the equivalent MCP, because the model doesn't need static tool descriptions injected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trim shell outputs.&lt;/strong&gt; Tools like &lt;a href="https://github.com/rtk-ai/rtk" rel="noopener noreferrer"&gt;rtk&lt;/a&gt; strip CLI output down to agent-relevant information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run &lt;code&gt;/chronicle tip&lt;/code&gt; regularly in Copilot CLI&lt;/strong&gt; to surface optimization opportunities from your actual session logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collapse tool calls.&lt;/strong&gt; Plugins like &lt;a href="https://github.com/jsturtevant/copilot-codeact-plugin" rel="noopener noreferrer"&gt;copilot-codeact-plugin&lt;/a&gt; batch multiple calls into single operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model-specific context tweaks.&lt;/strong&gt; Only worth it for fleet orchestrators with thousands of runs. Risky given how fast models change.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 4: Long-Term Guidance — The Skills That Will Matter
&lt;/h2&gt;

&lt;p&gt;Zooming out from the tactical playbook, three durable traits separate developers who'll thrive in the agent era from those who won't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Analytical Skills
&lt;/h3&gt;

&lt;p&gt;Coding itself was never the true source of developer value. Analytical thinking and deep domain proficiency were. Agents can write code; they can't decide &lt;em&gt;what&lt;/em&gt; should be built, in &lt;em&gt;what&lt;/em&gt; domain language, with &lt;em&gt;what&lt;/em&gt; trade-offs. &lt;strong&gt;The ability to tell an agent precisely what to do, in the language of the domain, is the most valuable skill.&lt;/strong&gt; Invest there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apply Good Architecture
&lt;/h3&gt;

&lt;p&gt;Domain-Driven Design, Hexagonal Architecture, CQRS, Event-Driven Design — these matter more now, not less. Good architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes agent discovery faster (clear file organization, predictable patterns).&lt;/li&gt;
&lt;li&gt;Provides guardrails that prevent agents from putting code in the wrong place.&lt;/li&gt;
&lt;li&gt;Reduces the fix/debug cycles that come from architectural drift.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The old debates — five-line functions versus ten, semicolons, comment style — are noise. Architecture is signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterate on Prompts and Agent Configs
&lt;/h3&gt;

&lt;p&gt;Treat this with an engineering mindset. Keep configs fresh. &lt;strong&gt;Treat every agent miss like an incident&lt;/strong&gt; — log it, fix the underlying instruction or skill, prevent recurrence. Use &lt;code&gt;/chronicle&lt;/code&gt; regularly in the CLI to surface patterns. This is continuous engineering work, not a one-time setup.&lt;/p&gt;

&lt;p&gt;You are now a context engineer. That's the job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Five Things to Start Doing Today
&lt;/h2&gt;

&lt;p&gt;If you take nothing else from this, take these five:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose the right model for the right task.&lt;/strong&gt; Reasoning models for planning and debugging; mid-tier for implementation; small models for trivial work. Let Auto Mode pick when in doubt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide clear guidance in your prompts.&lt;/strong&gt; Be precise. Add stop signals. Provide known context upfront. Don't be terse for the sake of saving tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research → Plan → Implement.&lt;/strong&gt; Separate context windows per phase. Distill a precise spec between them. Parallelize implementation across architecture layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide deterministic guardrails.&lt;/strong&gt; Tests, linters, security scans — anything code-enforced. These reset the compound error rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain a concise, human-written &lt;code&gt;copilot-instructions.md&lt;/code&gt;.&lt;/strong&gt; Use it as an agent-miss log and to trim outputs. Keep it small. Rewrite it often. Don't let AI generate it.&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;The whole discipline reduces to one principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write as little context as required, and as much as necessary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Token cost optimization isn't really about tokens. It's about quality, precision, and engineering rigor applied to a new substrate. The teams that internalize this — that stop counting tokens and start making every token count — will out-ship, out-quality, and out-economize everyone still gambling with cheap agents.&lt;/p&gt;

&lt;p&gt;I'm happy to answer your questions, and to help your team or organization with agent quality and token optimizations techniques - &lt;a href="https://www.linkedin.com/in/webmax/" rel="noopener noreferrer"&gt;send me a message on LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <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.&amp;nbsp;&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.&amp;nbsp;&amp;nbsp;&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.&amp;nbsp;&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&amp;nbsp; 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;/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;

&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;/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;

&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>
  </channel>
</rss>
