<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gokula Krishna</title>
    <description>The latest articles on DEV Community by Gokula Krishna (@gokula_krishna_1f814922bb).</description>
    <link>https://dev.to/gokula_krishna_1f814922bb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2116961%2F8ad5f9c4-a393-43a1-b1da-b7ef60c64d8f.jpg</url>
      <title>DEV Community: Gokula Krishna</title>
      <link>https://dev.to/gokula_krishna_1f814922bb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gokula_krishna_1f814922bb"/>
    <language>en</language>
    <item>
      <title>From benchmarking to fine-tuning: what I learned about small decision models</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Wed, 30 Sep 2026 05:47:25 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/from-benchmarking-to-fine-tuning-what-i-learned-about-small-decision-models-2ln3</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/from-benchmarking-to-fine-tuning-what-i-learned-about-small-decision-models-2ln3</guid>
      <description>&lt;p&gt;I tested two small decision models. First I tested TypeSafe AI's hosted model, Jev. Then I tested Laya, an independent open-source alternative. I started out benchmarking and ended up fine-tuning.&lt;/p&gt;

&lt;p&gt;Here is what I found, in the order I found it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Benchmarking Jev
&lt;/h2&gt;

&lt;p&gt;TypeSafe AI &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;launched Jev on September 15, 2026&lt;/a&gt;. It does not write text. You give it a message and a list of possible answers. It tells you how confident it is in each one.&lt;/p&gt;

&lt;p&gt;I skipped the launch demos and ran my own evals on public Kaggle datasets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;31,369&lt;/strong&gt; test samples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12&lt;/strong&gt; classification tasks&lt;/li&gt;
&lt;li&gt;Three areas: anti-money laundering, customer support intent, and malicious network traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0.3 s&lt;/strong&gt; median response time, calling from Singapore to the US&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfdrosx9v6fycla1bbfb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfdrosx9v6fycla1bbfb.png" alt="Jev results across banking, network security and customer support." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main lesson: accuracy depends on the task, and even more on how you set the task up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Describe what the data shows, not what the criminal wants
&lt;/h3&gt;

&lt;p&gt;My first anti-money laundering pattern descriptions said what the criminal was trying to achieve. I rewrote them to describe what the transactions look like.&lt;/p&gt;

&lt;p&gt;Accuracy went from &lt;strong&gt;65% to 77%&lt;/strong&gt;. The model was the same. Only the descriptions changed.&lt;/p&gt;

&lt;p&gt;The model sees transactions, not motives. So describe the labels using what's in the input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove hidden bias from the setup
&lt;/h3&gt;

&lt;p&gt;In account-level laundering detection, I switched to using the same time window for every account. That removed a hidden bias in the setup. Accuracy went from &lt;strong&gt;63% to 75%&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some tasks have no signal
&lt;/h3&gt;

&lt;p&gt;Not everything worked. Classifying a &lt;strong&gt;single transaction&lt;/strong&gt; as laundering or not gave &lt;strong&gt;54%&lt;/strong&gt;, which is about chance. One transaction on its own doesn't carry enough signal. This is a limit of the task, not of the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confidence you can trust
&lt;/h3&gt;

&lt;p&gt;For customer support intent, Jev picked the right answer &lt;strong&gt;92.3%&lt;/strong&gt; of the time. On banking intents it was &lt;strong&gt;79.8%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The confidence score is honest. When Jev said it was 90% sure, it was right about 90% of the time.&lt;/p&gt;

&lt;p&gt;That makes thresholds useful. If you accept only predictions above 90% confidence, accuracy rises to &lt;strong&gt;97.6%&lt;/strong&gt;, and that still covers &lt;strong&gt;82%&lt;/strong&gt; of cases. The other 18% go to a fallback: a bigger model, a person, or a request for more information.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F58s4l2crt0gwwf0jqxll.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F58s4l2crt0gwwf0jqxll.png" alt="Customer support: 92% accuracy on all cases, 97.6% on the 82% of cases above the confidence threshold. The other 18% go to fallback." width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask the question directly
&lt;/h3&gt;

&lt;p&gt;To catch off-topic messages, I first used a low top score as the signal. That gave &lt;strong&gt;91.7%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then I added "is this off-topic?" as a separate yes/no question. That gave &lt;strong&gt;97.7%&lt;/strong&gt;. It costs no extra time, because everything is answered in one pass.&lt;/p&gt;

&lt;p&gt;If you care about a decision, ask for it directly. Don't infer it from another score.&lt;/p&gt;

&lt;h3&gt;
  
  
  The dataset can be wrong too
&lt;/h3&gt;

&lt;p&gt;On malicious network traffic, Jev scored &lt;strong&gt;78%&lt;/strong&gt; against the dataset labels as given.&lt;/p&gt;

&lt;p&gt;One capture had about &lt;strong&gt;400 windows labeled "benign"&lt;/strong&gt;. They were unanswered scans of thousands of hosts. Jev flagged them as malicious with 80% to 90% confidence. Without that capture, the score was &lt;strong&gt;98%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I report both numbers. The 98% is a filtered result. But the lesson holds: when the model disagrees with the ground truth, look at the data before you count it as a model error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Laya, the open-source alternative
&lt;/h2&gt;

&lt;p&gt;Less than a week later, an open-source alternative appeared. &lt;a href="https://github.com/NandhaKishorM/laya" rel="noopener noreferrer"&gt;Laya&lt;/a&gt; is a separate project, not a release of Jev's weights. It claims performance equal to Jev.&lt;/p&gt;

&lt;p&gt;I ran it on 11 of the same tasks, using the same harness I built for Jev.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8ehwy56jk540a2iordj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8ehwy56jk540a2iordj.png" alt="Jev vs untuned Laya: accuracy per task, confidence calibration and confidence filtering." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Jev&lt;/th&gt;
&lt;th&gt;Laya (untuned)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Help-desk requests&lt;/td&gt;
&lt;td&gt;92.3%&lt;/td&gt;
&lt;td&gt;49.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banking questions&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;td&gt;39.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money-laundering patterns&lt;/td&gt;
&lt;td&gt;77.3%&lt;/td&gt;
&lt;td&gt;13.5% (no better than guessing)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Across 11 tasks and 3 setups, Laya never beat Jev. On binary (yes/no) decisions it came much closer. On choosing between many answers it fell well behind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confidence was the real problem
&lt;/h3&gt;

&lt;p&gt;The accuracy gap was not the biggest issue. The confidence scores were.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Help-desk task:&lt;/strong&gt; 4,173 of 4,500 answers were at 99% confidence, and about half of them were right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banking task:&lt;/strong&gt; Laya claimed about 97% confidence on almost everything, and got more than half wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calibration error measures how far confidence is from actual accuracy, where 0 is perfect. Jev scored &lt;strong&gt;0.013&lt;/strong&gt;. Laya scored &lt;strong&gt;0.486&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That breaks confidence-based routing. With Jev, keeping only answers at 90%+ confidence raised accuracy to 97.6%. With Laya, the same filter dropped 7% of questions and gained only 2 points, to 51.7%.&lt;/p&gt;

&lt;p&gt;It also breaks refusals. Of the off-topic questions Laya should have refused, it still answered &lt;strong&gt;88%&lt;/strong&gt; with 90%+ confidence. Jev did that on &lt;strong&gt;14%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My conclusion then: I wouldn't use untuned Laya to route between many options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Fine-tuning changed my mind
&lt;/h2&gt;

&lt;p&gt;The next day I fine-tuned Laya on the banking task. It took about &lt;strong&gt;140 minutes&lt;/strong&gt; on my laptop.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad4xpmwsps8ld7go87p8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad4xpmwsps8ld7go87p8.png" alt="Banking accuracy: Jev 79.8%, untuned Laya 39.4%, fine-tuned Laya 78.3%. Random guessing is 1.3%." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy went from &lt;strong&gt;39.4% to 78.3%&lt;/strong&gt;, just 1.5 points below Jev's 79.8%. The task has 77 possible answers, so random guessing would get 1.3%.&lt;/p&gt;

&lt;p&gt;The confidence fix mattered more. After fine-tuning, when Laya said it was 97% confident, it was right &lt;strong&gt;96%&lt;/strong&gt; of the time.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsuonkerwys2hfc60tvz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsuonkerwys2hfc60tvz.png" alt="After fine-tuning, Laya predictions at 97% confidence were right 96% of the time." width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is one confidence group, not a full calibration curve. But it's a big change from "99% sure, right half the time."&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;Jev&lt;/th&gt;
&lt;th&gt;Laya (fine-tuned)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Banking accuracy&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;td&gt;78.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;~309 ms (hosted)&lt;/td&gt;
&lt;td&gt;~90 ms (local)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inference cost&lt;/td&gt;
&lt;td&gt;~$0.07 per 1,000 requests&lt;/td&gt;
&lt;td&gt;$0 (runs on my laptop)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;fine-tune per task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The catch is that you need to fine-tune for each task, and that takes task-specific data. I have not yet tested off-topic detection on the fine-tuned model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Laya experts
&lt;/h2&gt;

&lt;p&gt;So I built &lt;strong&gt;Laya experts&lt;/strong&gt;: Laya models fine-tuned for specific tasks. Each one is lightweight, runs on a single machine, and makes a decision in about &lt;strong&gt;80 ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One expert detects personally identifiable information (PII). On entity F1, the untuned Laya scored &lt;strong&gt;0.156&lt;/strong&gt;, Jev &lt;strong&gt;0.736&lt;/strong&gt;, and the Laya PII expert &lt;strong&gt;0.967&lt;/strong&gt;. A check like this could run at each stage of a data pipeline to support compliance.&lt;/p&gt;

&lt;p&gt;Laya experts is published here: &lt;a href="https://huggingface.co/goku-san/laya-experts" rel="noopener noreferrer"&gt;Laya experts&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone trying these models
&lt;/h2&gt;

&lt;p&gt;These models look like a real "System 1" decision layer. They handle fast classification, routing and other bounded decisions, so an LLM or agent doesn't have to call a generative model for everything.&lt;/p&gt;

&lt;p&gt;To get good results, spend less time on the model and more on the setup around it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Descriptions.&lt;/strong&gt; Describe labels using what's visible in the input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask directly.&lt;/strong&gt; Make important decisions their own question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check confidence.&lt;/strong&gt; Test whether "90% sure" really means right 90% of the time. If it doesn't, thresholds won't work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set thresholds and a fallback.&lt;/strong&gt; Decide what happens to the cases the model isn't sure about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect disagreements.&lt;/strong&gt; Sometimes the label is wrong, not the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tune when needed.&lt;/strong&gt; An untuned open-source model can be far off. A short fine-tune on task data can close most of the gap.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Benchmark note: all figures are my own results on public benchmarks, with thresholds tuned per task. They are not vendor claims. Jev 1.13.0 (hosted). Laya 421M via laya-mlx, run locally on an Apple M3. The untuned Laya evaluation covered 23 runs and 65,078 requests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://gokulakrishna.co/2026/09/30/benchmarking-to-fine-tuning-decision-models/" rel="noopener noreferrer"&gt;gokulakrishna.co&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>benchmarking</category>
      <category>finetuning</category>
      <category>modelevaluation</category>
    </item>
    <item>
      <title>I built a Digital Human you can talk to</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Mon, 03 Aug 2026 09:07:58 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/i-built-a-digital-human-you-can-talk-to-4o39</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/i-built-a-digital-human-you-can-talk-to-4o39</guid>
      <description>&lt;p&gt;Most digital-human demos start with a prepared script or audio file. I wanted a live conversation.&lt;/p&gt;

&lt;p&gt;A user had to speak through the browser. The system had to understand the question, retrieve relevant information, generate a response, synthesize speech, animate a face, and stream the result back as video.&lt;/p&gt;

&lt;p&gt;The notes from our 21 June 2024 demo recorded about five seconds to stream video for a short, 20-token response. We ran that demo on an AWS G5.2xlarge instance using Groq, Chroma, AWS Polly, GeneFace++, FastAPI, and WebRTC.&lt;/p&gt;

&lt;p&gt;A separate end-to-end test on the AWS A10G measured an 18-second minimum and a 25-second average for a ten-word response. The experiment log does not contain enough detail to reconcile the difference. I treat the five-second figure as a demo observation and the end-to-end table as the repeatable benchmark.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The five-second result came from a pipeline of separate systems. Each system had its own latency, output format, failure modes, and hardware requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Defining the conversation
&lt;/h2&gt;

&lt;p&gt;The proof of concept supported a turn-based, two-way loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user spoke to the avatar through a browser.&lt;/li&gt;
&lt;li&gt;The browser sent the audio to the backend.&lt;/li&gt;
&lt;li&gt;The backend transcribed the speech and generated an answer.&lt;/li&gt;
&lt;li&gt;The avatar spoke the answer and streamed video back to the browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full-duplex barge-in remained outside the PoC. The notes do not show a user interrupting the avatar mid-response and forcing the system to cancel queued speech and video frames. That requires a different session state machine and cancellation path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Browser microphone&lt;/strong&gt; → &lt;strong&gt;FastAPI session&lt;/strong&gt; → &lt;strong&gt;Deepgram VAD + STT&lt;/strong&gt; → &lt;strong&gt;Groq + Chroma&lt;/strong&gt; → &lt;strong&gt;AWS Polly&lt;/strong&gt; → &lt;strong&gt;GeneFace++&lt;/strong&gt; → &lt;strong&gt;WebRTC video&lt;/strong&gt; → &lt;strong&gt;Browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The system processed one turn through eight stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Capture:&lt;/strong&gt; React captured the user’s microphone input in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session handling:&lt;/strong&gt; FastAPI accepted the audio stream and maintained the conversation session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn detection:&lt;/strong&gt; Deepgram’s voice activity detection identified speech boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transcription:&lt;/strong&gt; Deepgram converted the recorded turn into text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval and generation:&lt;/strong&gt; Chroma retrieved relevant document chunks, and Groq generated the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunking:&lt;/strong&gt; The backend split the response into units that the speech and animation stages could process without waiting for the full answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speech and animation:&lt;/strong&gt; AWS Polly produced audio. GeneFace++ used that audio and the trained avatar data to generate video frames.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery:&lt;/strong&gt; &lt;code&gt;aiortc&lt;/code&gt; sent the frames to the browser over WebRTC.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chroma held indexed reference material for retrieval. It did not train the language model. That distinction matters because teams often label any data connected to an LLM as training data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The latency budget
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Tvisible ≈ Tendpoint + Tretrieval + Tfirst token + Tspeech chunk + Tavatar chunk + Ttransport&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The individual benchmark values below cannot be added into one exact end-to-end total. We tested some components with different input sizes, GPUs, cloud regions, and levels of overlap. The equation shows where the delay came from and where we had to measure it.&lt;/p&gt;

&lt;p&gt;The avatar renderer consumed much of the budget. GeneFace++ generated ten seconds of video in about five seconds on an RTX 4070. A one-second LLM response still felt slow if the system waited for a complete audio track and complete video before sending the first frame.&lt;/p&gt;

&lt;p&gt;Text chunking gave the pipeline a way to overlap work. The speech and animation stages could process the first chunk while the LLM produced later chunks. Chunk size created a trade-off: small chunks reduced the wait but could damage prosody and create visible transitions; large chunks produced smoother output at the cost of a longer pause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical specification
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;PoC choice&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;AWS G5.2xlarge with NVIDIA A10G&lt;/td&gt;
&lt;td&gt;Hosted the API and GPU avatar workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Python 3.8 and FastAPI&lt;/td&gt;
&lt;td&gt;Managed sessions and pipeline orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice activity and STT&lt;/td&gt;
&lt;td&gt;Deepgram&lt;/td&gt;
&lt;td&gt;Detected turns and converted speech to text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;Groq, with Mixtral 8x7B in the RAG tests&lt;/td&gt;
&lt;td&gt;Generated the response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval&lt;/td&gt;
&lt;td&gt;Local Chroma DB&lt;/td&gt;
&lt;td&gt;Retrieved document context without a managed search service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text-to-speech&lt;/td&gt;
&lt;td&gt;AWS Polly&lt;/td&gt;
&lt;td&gt;Generated the response audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avatar renderer&lt;/td&gt;
&lt;td&gt;GeneFace++&lt;/td&gt;
&lt;td&gt;Generated lip-synced video frames for the demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video transport&lt;/td&gt;
&lt;td&gt;WebRTC through Python &lt;code&gt;aiortc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Streamed low-latency video to the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;TypeScript and React with chat UI components&lt;/td&gt;
&lt;td&gt;Captured audio and displayed the avatar conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The proposed architecture kept the avatar engine replaceable. We also evaluated SyncTalk and several other lip-sync systems. GeneFace++ powered the measured demo stack.&lt;/p&gt;

&lt;p&gt;The source notes did not record the microphone codec, sample rate, chunk duration, VAD thresholds, WebRTC topology, or concurrent-session capacity. Those values belong in a production specification before another team tries to reproduce the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLM and retrieval benchmarks
&lt;/h2&gt;

&lt;p&gt;We averaged each result across ten runs. These numbers describe this PoC environment rather than a general ranking of the services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Without a knowledge base
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Throughput&lt;/th&gt;
&lt;th&gt;Average latency&lt;/th&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Groq&lt;/td&gt;
&lt;td&gt;107 tokens/s&lt;/td&gt;
&lt;td&gt;1.77 s&lt;/td&gt;
&lt;td&gt;Best response time in this test; API variance remained a dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Bedrock with Titan Express in Singapore&lt;/td&gt;
&lt;td&gt;9.2 tokens/s&lt;/td&gt;
&lt;td&gt;2.12 s&lt;/td&gt;
&lt;td&gt;Easier managed setup; answer quality failed some basic test questions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  With retrieval
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Throughput&lt;/th&gt;
&lt;th&gt;Average latency&lt;/th&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Groq, Mixtral 8x7B, local Chroma&lt;/td&gt;
&lt;td&gt;168.1 tokens/s&lt;/td&gt;
&lt;td&gt;1.00 s&lt;/td&gt;
&lt;td&gt;Fastest RAG configuration in the test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Groq, Mixtral 8x7B, AWS OpenSearch&lt;/td&gt;
&lt;td&gt;9 tokens/s&lt;/td&gt;
&lt;td&gt;11.00 s&lt;/td&gt;
&lt;td&gt;Managed retrieval added a large delay in this setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Bedrock Claude Haiku and AWS OpenSearch in Oregon&lt;/td&gt;
&lt;td&gt;10.46 tokens/s&lt;/td&gt;
&lt;td&gt;10.22 s&lt;/td&gt;
&lt;td&gt;Slowest managed configuration tested&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Chroma and OpenSearch rows include more than database time. Region, model endpoint, networking, and implementation choices also changed between configurations. The results support a decision about this PoC stack; they do not isolate the retrieval engine as the sole cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text-to-speech benchmarks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Generation latency&lt;/th&gt;
&lt;th&gt;Test input&lt;/th&gt;
&lt;th&gt;Generated audio&lt;/th&gt;
&lt;th&gt;Notes recorded during the evaluation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Polly&lt;/td&gt;
&lt;td&gt;1.25 s&lt;/td&gt;
&lt;td&gt;337 characters&lt;/td&gt;
&lt;td&gt;17.1 s&lt;/td&gt;
&lt;td&gt;Fast managed API; limited custom-voice options for our use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElevenLabs&lt;/td&gt;
&lt;td&gt;4.73 s&lt;/td&gt;
&lt;td&gt;337 characters&lt;/td&gt;
&lt;td&gt;17.1 s&lt;/td&gt;
&lt;td&gt;Custom voice support; slower in this test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Picovoice&lt;/td&gt;
&lt;td&gt;2.00 s&lt;/td&gt;
&lt;td&gt;337 characters&lt;/td&gt;
&lt;td&gt;22 s&lt;/td&gt;
&lt;td&gt;Strong inference speed; custom voice required vendor coordination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speechify&lt;/td&gt;
&lt;td&gt;1.52 s&lt;/td&gt;
&lt;td&gt;337 characters&lt;/td&gt;
&lt;td&gt;26 s&lt;/td&gt;
&lt;td&gt;Custom voice and good perceived quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deepgram&lt;/td&gt;
&lt;td&gt;1.41 s&lt;/td&gt;
&lt;td&gt;337 characters&lt;/td&gt;
&lt;td&gt;17.1 s&lt;/td&gt;
&lt;td&gt;Custom voice options, pause control, and local-hosting potential&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS Polly gave us the clearest path to the demo because it generated a 17.1-second clip in 1.25 seconds and required no voice-model training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lip-sync benchmarks
&lt;/h2&gt;

&lt;p&gt;We ran these tests on an RTX 4070 and measured the time required to generate ten seconds of video.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Generation time&lt;/th&gt;
&lt;th&gt;Evaluation note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wav2Lip&lt;/td&gt;
&lt;td&gt;4 s&lt;/td&gt;
&lt;td&gt;Fast setup, lower resolution, and no head movement in our test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneFace++&lt;/td&gt;
&lt;td&gt;5 s&lt;/td&gt;
&lt;td&gt;Fast inference and better lip movement; model training and environment setup took more work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SyncTalk&lt;/td&gt;
&lt;td&gt;10 s&lt;/td&gt;
&lt;td&gt;Best lip movement and image quality in our evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real3DPortrait&lt;/td&gt;
&lt;td&gt;29 s&lt;/td&gt;
&lt;td&gt;Incorrect head, lip, and torso movement in our test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SadTalker&lt;/td&gt;
&lt;td&gt;45 s&lt;/td&gt;
&lt;td&gt;Slow inference and weak lip sync for this use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hallo&lt;/td&gt;
&lt;td&gt;More than 10 min&lt;/td&gt;
&lt;td&gt;Natural body movement, but the inference time ruled it out for conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The evaluation notes also recorded commercial-use concerns for some frameworks. Licences and repository terms can change, so any production team should verify the current terms before selecting an engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  End-to-end GPU results
&lt;/h2&gt;

&lt;p&gt;We used Groq, AWS Polly, and GeneFace++ for the end-to-end GPU comparison. Prices show the hourly figures recorded during the 2024 evaluation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider and GPU&lt;/th&gt;
&lt;th&gt;Recorded price/hour&lt;/th&gt;
&lt;th&gt;Minimum response time for 10 words&lt;/th&gt;
&lt;th&gt;Inference speed&lt;/th&gt;
&lt;th&gt;Average response time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS G5.2xlarge, A10G, Sydney&lt;/td&gt;
&lt;td&gt;$1.212&lt;/td&gt;
&lt;td&gt;18 s&lt;/td&gt;
&lt;td&gt;45 it/s&lt;/td&gt;
&lt;td&gt;25 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runpod, RTX 4090, Slovakia&lt;/td&gt;
&lt;td&gt;$0.74&lt;/td&gt;
&lt;td&gt;4.6 s&lt;/td&gt;
&lt;td&gt;130 it/s&lt;/td&gt;
&lt;td&gt;11.26 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runpod, RTX 4090, Romania&lt;/td&gt;
&lt;td&gt;$0.74&lt;/td&gt;
&lt;td&gt;5.6 s&lt;/td&gt;
&lt;td&gt;123 it/s&lt;/td&gt;
&lt;td&gt;11.44 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runpod, H100, Netherlands&lt;/td&gt;
&lt;td&gt;$4.96&lt;/td&gt;
&lt;td&gt;6 s&lt;/td&gt;
&lt;td&gt;86 it/s&lt;/td&gt;
&lt;td&gt;9.14 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The RTX 4090 produced the shortest minimum response and the highest iteration rate in this workload. The H100 delivered the lowest average response time. Its recorded hourly price exceeded the 4090 instances by more than six times. Model architecture, clock speed, software optimisation, and provider variance affected the outcome. The GPU name alone did not predict the best user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why WebRTC
&lt;/h2&gt;

&lt;p&gt;The avatar needed a browser-native path with low delay. HLS introduced segmenting and caching delays. RTMP lacked direct browser playback. Downloading complete files produced broken transitions between responses.&lt;/p&gt;

&lt;p&gt;WebRTC handled media delivery in the browser and supported a future path toward two-way media. Python’s &lt;code&gt;aiortc&lt;/code&gt; let us keep the media server in the same language as the AI pipeline, though it added setup work around tracks, timing, and connection state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production changes I would make
&lt;/h2&gt;

&lt;p&gt;The PoC proved the conversation loop. A production version needs more control around cancellation, load, and measurement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a session state machine
&lt;/h3&gt;

&lt;p&gt;Each session should move through &lt;code&gt;LISTENING&lt;/code&gt;, &lt;code&gt;TRANSCRIBING&lt;/code&gt;, &lt;code&gt;THINKING&lt;/code&gt;, and &lt;code&gt;SPEAKING&lt;/code&gt;. A new user turn should invalidate stale LLM, TTS, and frame-generation jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduce bounded queues and backpressure
&lt;/h3&gt;

&lt;p&gt;The LLM can generate text faster than the avatar can render it. Unbounded audio and frame queues increase memory use and make interruptions feel broken.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure first-frame latency
&lt;/h3&gt;

&lt;p&gt;Total response time hides the pause the user feels. I would record speech-end to transcript, first token, first audio chunk, first video frame, and playback start, with P50 and P95 values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate GPU workers from the API
&lt;/h3&gt;

&lt;p&gt;FastAPI should manage sessions while dedicated workers handle avatar inference. That separation makes GPU scheduling and horizontal scaling easier to control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design for barge-in
&lt;/h3&gt;

&lt;p&gt;Full-duplex conversation needs interruption detection, job cancellation, audio ducking, and a clean return to the listening state. The session controller must drop stale frames from the previous response after the user starts a new turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Synchronise with timestamps
&lt;/h3&gt;

&lt;p&gt;The media layer should align audio samples and video presentation timestamps. Queue length alone cannot preserve lip sync under network jitter or variable inference time.&lt;/p&gt;

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

&lt;p&gt;End-to-end latency followed the slowest media stage. The one-second RAG result helped, while the user still waited for speech generation, avatar inference, and the first playable WebRTC frame.&lt;/p&gt;

&lt;p&gt;Streaming changed the useful metric. The system did not need to finish the whole response before it could begin playback. It needed to deliver the first coherent audio-and-video chunk, then keep producing chunks faster than the browser consumed them.&lt;/p&gt;

&lt;p&gt;The hardware tests also challenged a common assumption. The most expensive GPU did not produce the shortest first response in our stack. Testing the complete pipeline gave us a better answer than choosing hardware from model specifications.&lt;/p&gt;

&lt;p&gt;The PoC gave us a working digital-human conversation and a clear list of the remaining engineering work. The next version would focus on barge-in, cancellation, percentile latency, and concurrent sessions.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/N0_U4HKpAkg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;June 2024 digital-human proof of concept&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Benchmark figures in this post come from the PoC tests recorded in June 2024. Cloud pricing, model endpoints, framework licences, and service performance may have changed since the evaluation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webrtc</category>
      <category>generativeai</category>
      <category>voice</category>
    </item>
    <item>
      <title>How I Migrated a 350,000-Line Java/JSP Application to TypeScript in Five Days</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Fri, 31 Jul 2026 06:30:06 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/how-i-migrated-a-350000-line-javajsp-application-to-typescript-in-five-days-2364</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/how-i-migrated-a-350000-line-javajsp-application-to-typescript-in-five-days-2364</guid>
      <description>&lt;p&gt;I recently migrated a 350,000-line Java/JSP application to TypeScript using AI agents.&lt;/p&gt;

&lt;p&gt;Our original estimate for a conventional migration was three engineers and close to a year. Instead, one engineer orchestrating pools of up to 20 sub-agents at each stage got the application running in a development environment in five days.&lt;/p&gt;

&lt;p&gt;The headline sounds like a story about faster code generation. It is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The decisive factor was verification.&lt;/strong&gt; Once the agents had a reliable way to compare the old and new systems, parallelism became useful rather than chaotic.&lt;/p&gt;

&lt;h3&gt;
  
  
  350,000
&lt;/h3&gt;

&lt;p&gt;lines of Java/JSP&lt;/p&gt;

&lt;h3&gt;
  
  
  5 days
&lt;/h3&gt;

&lt;p&gt;to a running development environment&lt;/p&gt;

&lt;h3&gt;
  
  
  20
&lt;/h3&gt;

&lt;p&gt;sub-agents in each stage&lt;/p&gt;

&lt;h2&gt;
  
  
  Why parallel agents were only half the solution
&lt;/h2&gt;

&lt;p&gt;Jarred Sumner’s account of the Bun rewrite gave me useful ideas for running agents in parallel. Bun had an important advantage: a language-independent TypeScript test suite. The implementation could change while the expected behaviour remained measurable.&lt;/p&gt;

&lt;p&gt;Our application had no equivalent safety net.&lt;/p&gt;

&lt;p&gt;Only about half of the existing tests worked. Many had weak or missing assertions, and others depended directly on infrastructure. A large agent pool could generate code quickly, but without an independent oracle it could also produce 20 plausible versions of the wrong system.&lt;/p&gt;

&lt;p&gt;We needed a way to answer a simple question at every stage: &lt;em&gt;does the new application behave like the one already running?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The migration sequence
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;UI&lt;/li&gt;
&lt;li&gt;Business logic&lt;/li&gt;
&lt;li&gt;Improvements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That order was deliberate. Each stage created a more stable foundation for the next, and it limited the number of variables that could change at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Reconstructing the database contract
&lt;/h3&gt;

&lt;p&gt;The legacy application did not have a reliable database schema we could treat as authoritative.&lt;/p&gt;

&lt;p&gt;We reconstructed the application-facing Oracle schema from the SQL queries embedded throughout the codebase. The agents identified tables, columns, joins, constraints and data assumptions from the queries, then we validated the reconstructed contract against the running system.&lt;/p&gt;

&lt;p&gt;This was more than a database exercise. The schema defined the vocabulary shared by every API, page and business rule that followed. Starting anywhere else would have forced the agents to build on guesses.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Rebuilding the APIs
&lt;/h3&gt;

&lt;p&gt;Once the data contract was stable, agents could migrate API components in parallel. Each work item was narrow: rebuild a component, exercise it against both systems, compare the result and resolve the discrepancy.&lt;/p&gt;

&lt;p&gt;This gave the UI migration a dependable backend target instead of asking agents to infer behaviour simultaneously at every layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Migrating the UI
&lt;/h3&gt;

&lt;p&gt;The UI required more than checking HTTP status codes. We needed to know whether the same data appeared, whether the same elements were present and whether the same interactions produced the same outcomes.&lt;/p&gt;

&lt;p&gt;This is where the parity harness became the centre of the migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a parity harness where tests were missing
&lt;/h2&gt;

&lt;p&gt;The parity harness ran the legacy and TypeScript applications side by side and compared them at three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API responses and resulting database state&lt;/li&gt;
&lt;li&gt;Page elements and screenshots&lt;/li&gt;
&lt;li&gt;User interactions in both applications&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v5fauumsx4h5v1sjur8.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v5fauumsx4h5v1sjur8.jpg" alt="A symmetrical amber and teal visualization of a parity harness comparing legacy and TypeScript application stacks." width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The parity harness compared observable behaviour across the legacy and TypeScript systems.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The harness did not need to understand the intended design of the whole system. It needed to expose observable differences.&lt;/p&gt;

&lt;p&gt;That distinction mattered. It turned the running legacy application into an executable specification, including behaviours that were not documented and edge cases that the surviving tests did not cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parity before correctness
&lt;/h2&gt;

&lt;p&gt;During the migration, we treated existing bugs as expected behaviour.&lt;/p&gt;

&lt;p&gt;That may sound counterintuitive, but fixing bugs while changing languages, frameworks and architecture would have made failures ambiguous. When a result differed, we would not know whether we had introduced a regression or intentionally changed behaviour.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First reproduce the system. Then improve it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We recorded known defects and fixed them only after achieving parity. This separated migration risk from product change and made discrepancies much easier to diagnose.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the agent pools worked
&lt;/h2&gt;

&lt;p&gt;At each migration stage, a pool of up to 20 sub-agents handled bounded pieces of work. Their jobs generally fell into three categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rebuild a component in TypeScript.&lt;/li&gt;
&lt;li&gt;Compare the old and new implementations through the parity harness.&lt;/li&gt;
&lt;li&gt;Investigate and fix the differences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The primary engineering task shifted from writing every line to designing work packets, maintaining dependency order, interpreting failures and improving the verification loop.&lt;/p&gt;

&lt;p&gt;Parallelism helped because the work was decomposable. The harness helped because every agent could check its own result against the same external standard.&lt;/p&gt;

&lt;p&gt;Without that feedback loop, adding more agents would have increased review load. With it, discrepancies became actionable inputs for the next iteration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost
&lt;/h2&gt;

&lt;p&gt;The migration consumed approximately &lt;strong&gt;100 million output tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At Fable 5 output-token pricing, that was roughly &lt;strong&gt;US$5,000&lt;/strong&gt; , excluding input tokens and other usage costs.&lt;/p&gt;

&lt;p&gt;That number needs context. It is not the total cost of productionizing a modernized core system, and getting the application running in development is not the same as completing security review, performance testing, deployment and operational handover.&lt;/p&gt;

&lt;p&gt;But it changes the economics of the first major migration milestone. A project estimated at three engineers for close to a year reached a working development environment with one engineer in five days.&lt;/p&gt;

&lt;h2&gt;
  
  
  What made the five-day migration possible
&lt;/h2&gt;

&lt;p&gt;The result did not come from asking a model to “rewrite the application in TypeScript.”&lt;/p&gt;

&lt;p&gt;It came from combining five practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Establish an observable source of truth before scaling code generation.&lt;/li&gt;
&lt;li&gt;Migrate in dependency order so every stage has a stable foundation.&lt;/li&gt;
&lt;li&gt;Give agents bounded work with clear inputs and outputs.&lt;/li&gt;
&lt;li&gt;Preserve legacy behaviour until parity removes ambiguity.&lt;/li&gt;
&lt;li&gt;Make comparison and repair part of every agent’s loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The parity harness was the force multiplier. It reduced the amount of trust required, made parallel work measurable and let agents detect many of their own mistakes before human review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agents supplied the throughput. Verification converted that throughput into progress.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for legacy modernization
&lt;/h2&gt;

&lt;p&gt;The biggest shift may not be that AI writes code faster. It is that migration work can be reorganized around machine-executable feedback.&lt;/p&gt;

&lt;p&gt;Systems once considered too large, too poorly documented or too risky to touch may become practical candidates when their observable behaviour can be captured and compared incrementally.&lt;/p&gt;

&lt;p&gt;That does not remove the need for engineering judgment. It makes judgment more leveraged. The engineer still decides the sequence, defines the boundaries, builds the oracle and determines when parity is good enough to move forward.&lt;/p&gt;

&lt;p&gt;As migration time and cost continue to fall, the question for many organizations may change from “Can we afford to modernize?” to “What would we need to verify it safely?”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Would you consider modernizing your core systems?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gokulakrishna.co/contact/" rel="noopener noreferrer"&gt;Get in touch&lt;/a&gt; if you are exploring an AI-assisted migration or need help designing a verification strategy.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiagents</category>
      <category>typescript</category>
      <category>java</category>
    </item>
    <item>
      <title>Accelerate product development and marketing using these AI Agents</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Thu, 16 Jan 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/accelerate-product-development-and-marketing-using-these-ai-agents-46ce</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/accelerate-product-development-and-marketing-using-these-ai-agents-46ce</guid>
      <description>&lt;p&gt;2024 was the year of AI Chatbots, and 2025 will be the year of AI Agents. AI agents can act on your behalf and not just generate content. Here is the list of tools you can implement in your company to reap the benefits of AI agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create a full-stack prototype using Replit Agent
&lt;/h3&gt;

&lt;p&gt;Replit Agent is a great way to create full-stack applications using prompts. It helps with both the development and deployment of your complete application in the cloud with a predictable cost.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9just1n6u5qi8u797v83.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9just1n6u5qi8u797v83.png" alt="Replit Agent" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Rapid front-end prototyping with Locofy
&lt;/h3&gt;

&lt;p&gt;Locofy is a Figma plugin that helps convert Figma designs into production-ready front-end code using its proprietary Large Design Model. The designs can be exported to front-end frameworks like NextJS, Vue, or Angular, and they directly help deploy to hosting providers such as Vercel.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Locofy expects the Figma prototype in a particular format; you might want to change some processes in your design.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Automate report generation using Vanna.AI
&lt;/h3&gt;

&lt;p&gt;Vanna.ai helps generate reports using natural language. It provides an interface to extract your DB schema and train the LLM. Once you have done that, it becomes easier to integrate their agent into Slack, have them start asking questions regarding your company data, and generate relevant reports.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmbiv0vfokr6agw0kxugn.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmbiv0vfokr6agw0kxugn.jpg" alt="Vanna.AI" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Personalized marketing using Sync
&lt;/h3&gt;

&lt;p&gt;Digital avatars allow you to hyper-personalize the message for users. For example, a company’s CEO can send personalized videos to millions of customers using the LipSync framework. This can be done with an open-source lip sync generator like SyncLabs.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvss9z2tvulwdoexqiv2p.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvss9z2tvulwdoexqiv2p.png" alt="SyncLabs" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Establish brand presence using ComfyUI
&lt;/h3&gt;

&lt;p&gt;Tools like ComfyUI will help you create a content-generation pipeline using no-code tools. Here is an example pipeline to extract an anime character and place it on another background.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz85m2z9zqpuc39x87iu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz85m2z9zqpuc39x87iu.png" alt="ComfyUI pipeline" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ComfyUI plugins can automatically generate content and post it on your social media.&lt;/p&gt;

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

&lt;p&gt;As AI agents become more widespread, it is crucial to adopt these tools to stay competitive. It is also vital to have systems to verify the output generated by these tools that cause undesirable results.&lt;/p&gt;

&lt;p&gt;Are you interested in integrating AI agents into your company? Feel free to &lt;a href="https://gokulakrishna.co/contact/" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiagents</category>
      <category>productivity</category>
      <category>comfyui</category>
    </item>
    <item>
      <title>Top 5 Startup Security Checklist</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Sun, 29 Dec 2024 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/top-5-startup-security-checklist-46mb</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/top-5-startup-security-checklist-46mb</guid>
      <description>&lt;p&gt;In 2024, there were over 2,741 publicly disclosed data breaches in the U.S. alone, affecting 1.35 billion individuals. Small businesses were especially vulnerable, with 43% of cyberattacks targeting small businesses, yet only 14% are prepared to defend against them.&lt;/p&gt;

&lt;p&gt;If you are a startup founder, the security of your application should not be an afterthought. In this post, I will share the top 5 essential security measures that every startup should implement to protect their application and their users. These recommendations are based on my experience as a CTO scaling an insurance tech platform across 10+ markets.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtdnmy5z8tsgodxpuie5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtdnmy5z8tsgodxpuie5.png" alt="Security checklist" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Conduct Penetration Testing
&lt;/h2&gt;

&lt;p&gt;Penetration testing simulates real-world attacks to find vulnerabilities before malicious actors do. You don’t need an expensive security firm to get started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools to get started:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OWASP ZAP&lt;/strong&gt; — Free, open-source web app security scanner. Great for automated and manual testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Burp Suite Community Edition&lt;/strong&gt; — Industry-standard tool for web vulnerability assessment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nmap&lt;/strong&gt; — Network discovery and security auditing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to do it:&lt;/strong&gt; Before every major release and at least quarterly. Consider hiring a professional penetration testing firm annually.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Enable Web Application Firewall (WAF)
&lt;/h2&gt;

&lt;p&gt;A WAF protects your application from common web exploits like SQL injection, XSS, and DDoS attacks by filtering and monitoring HTTP traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended:&lt;/strong&gt; Cloudflare WAF — offers a generous free tier with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DDoS protection&lt;/li&gt;
&lt;li&gt;Bot management&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;IP reputation filtering&lt;/li&gt;
&lt;li&gt;Custom firewall rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuifj5nhf54q9eupwsyya.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuifj5nhf54q9eupwsyya.webp" alt="Cloudflare WAF" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick setup:&lt;/strong&gt; Point your DNS to Cloudflare, enable WAF rules, and configure rate limiting for your API endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Implement Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Rate limiting prevents abuse by restricting the number of requests a user can make in a given time period. This protects against brute force attacks, credential stuffing, and API abuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation approaches:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application-level:&lt;/strong&gt; Use middleware like express-rate-limit (Node.js) or django-ratelimit (Python)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure-level:&lt;/strong&gt; Configure rate limits in your CDN/WAF (Cloudflare, AWS WAF)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway:&lt;/strong&gt; Use built-in rate limiting in API gateways&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1ivzyqvp64s3m2jtn5u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1ivzyqvp64s3m2jtn5u.png" alt="Rate limiting configuration" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Block High-Risk Countries
&lt;/h2&gt;

&lt;p&gt;If your startup operates in specific regions, consider geo-blocking traffic from countries where you don’t have users. This significantly reduces your attack surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to implement:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Cloudflare’s IP Geolocation to identify visitor countries&lt;/li&gt;
&lt;li&gt;Create firewall rules to block or challenge traffic from high-risk regions&lt;/li&gt;
&lt;li&gt;Whitelist countries where your legitimate users are located&lt;/li&gt;
&lt;li&gt;Consider using CAPTCHA challenges instead of outright blocks for borderline regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Always ensure geo-blocking doesn’t impact legitimate users, especially if you have a distributed team or use VPNs.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Secure Your Dependencies
&lt;/h2&gt;

&lt;p&gt;Supply chain attacks through compromised dependencies are increasingly common. Regularly audit and update your dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools and practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm audit / yarn audit&lt;/strong&gt; — Built-in vulnerability scanning for Node.js projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependabot / Renovate&lt;/strong&gt; — Automated dependency update PRs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snyk&lt;/strong&gt; — Comprehensive vulnerability database and monitoring&lt;/li&gt;
&lt;li&gt;Pin dependency versions in production&lt;/li&gt;
&lt;li&gt;Review changelogs before updating major versions&lt;/li&gt;
&lt;li&gt;Use lock files (package-lock.json, yarn.lock) and commit them&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Security is not a one-time setup but an ongoing process. Start with these five fundamentals, and as your startup grows, invest in more comprehensive security measures like SOC 2 compliance, bug bounty programs, and dedicated security teams.&lt;/p&gt;

&lt;p&gt;The cost of implementing these measures early is a fraction of the cost of a data breach. Protect your users, protect your business.&lt;/p&gt;

</description>
      <category>security</category>
      <category>startup</category>
      <category>cloudflare</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Delete Background from Videos and Add a New Background via CLI</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Mon, 23 Sep 2024 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/how-to-delete-background-from-videos-and-add-a-new-background-via-cli-1oo6</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/how-to-delete-background-from-videos-and-add-a-new-background-via-cli-1oo6</guid>
      <description>&lt;p&gt;Recently, I was looking for tools to delete the background from a video I needed for a presentation. Unfortunately, the quality of the free online tools I tried wasn’t up to par. After some exploration, I found some open-source alternatives that delivered much better results. In this post, I’ll walk you through the process I followed and what I learned along the way.&lt;/p&gt;

&lt;p&gt;I’ll introduce an open-source library called Rembg and demonstrate how to delete the background from a single image. Next, I’ll explain how to extract frames from a video and apply Rembg for background removal. Finally, I’ll show you how to add a new background to those images and combine them back into a video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, let’s make sure we have the following tools installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; : For running AI models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FFmpeg&lt;/strong&gt; : A powerful tool for video processing. We will use it to extract video frames and combine images back to video.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ImageMagick&lt;/strong&gt; : Tool for manipulating images. We will use it to overlay the foreground on a new background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rembg&lt;/strong&gt; : An AI-powered tool to remove image backgrounds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ubuntu installation:&lt;/strong&gt;&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;# Ubuntu procedure&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install Python&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3

&lt;span class="c"&gt;# Install FFmpeg&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ffmpeg

&lt;span class="c"&gt;# Install rembg Python library&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;rembg[gpu,cli]

&lt;span class="c"&gt;# Install ImageMagick&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;imagemagick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mac installation:&lt;/strong&gt;&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;# Mac procedure&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;python3
brew &lt;span class="nb"&gt;install &lt;/span&gt;ffmpeg
pip &lt;span class="nb"&gt;install &lt;/span&gt;rembg[gpu,cli]
brew &lt;span class="nb"&gt;install &lt;/span&gt;imagemagick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Background Removal from a Single Image
&lt;/h2&gt;

&lt;p&gt;Let’s start with removing the background from a single image using Rembg:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rembg i input.png output.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Febcva9ybrboatpi1rekj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Febcva9ybrboatpi1rekj.png" alt="Single image background removal" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwf9bip1j6xppma01o0ar.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwf9bip1j6xppma01o0ar.png" alt="Background removal result" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also choose different AI models. I prefer the &lt;code&gt;isnet-anime&lt;/code&gt; model for anime-style images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rembg i &lt;span class="nt"&gt;-m&lt;/span&gt; isnet-anime anime.png output.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Extracting Frames from Video
&lt;/h2&gt;

&lt;p&gt;To process a video, we first need to extract individual frames using FFmpeg:&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="nb"&gt;mkdir &lt;/span&gt;frames
ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; input_video.mp4 frames/frame_%04d.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnfb8tu8opx3p6nqollr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnfb8tu8opx3p6nqollr.png" alt="Extracted video frames" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Remove Background from All Frames
&lt;/h2&gt;

&lt;p&gt;Now apply Rembg to all extracted frames:&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="nb"&gt;mkdir &lt;/span&gt;frames_no_bg
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in &lt;/span&gt;frames/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;rembg i &lt;span class="nt"&gt;-m&lt;/span&gt; u2net &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"frames_no_bg/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcn1dogc5vjcbicqzwndz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcn1dogc5vjcbicqzwndz.png" alt="Frames with background removed" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU processing: ~12.7 seconds per frame&lt;/li&gt;
&lt;li&gt;GPU processing: ~3.6 seconds per frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a GPU dramatically speeds up the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add New Background and Combine
&lt;/h2&gt;

&lt;p&gt;Use ImageMagick to overlay each frame on a new background:&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="nb"&gt;mkdir &lt;/span&gt;frames_final
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in &lt;/span&gt;frames_no_bg/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;convert background.png &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gravity&lt;/span&gt; center &lt;span class="nt"&gt;-composite&lt;/span&gt; &lt;span class="s2"&gt;"frames_final/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ytqp3y3clfy8m0dq95m.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ytqp3y3clfy8m0dq95m.png" alt="Frames with new background" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, combine the frames back into a video:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-framerate&lt;/span&gt; 30 &lt;span class="nt"&gt;-i&lt;/span&gt; frames_final/frame_%04d.png &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx264 &lt;span class="nt"&gt;-pix_fmt&lt;/span&gt; yuv420p output_video.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Founpzqosqioqe5iiygte.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Founpzqosqioqe5iiygte.png" alt="Final result" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73dvsd9fb2g3ixq47v58.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73dvsd9fb2g3ixq47v58.png" alt="Comparison" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhp4p0qxpl5tg3jhc8hi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwhp4p0qxpl5tg3jhc8hi.png" alt="Output video" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Using open-source tools like Rembg, FFmpeg, and ImageMagick, you can achieve professional-quality background removal from videos entirely via the command line. The GPU acceleration makes it practical even for longer videos.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>ffmpeg</category>
      <category>cli</category>
    </item>
    <item>
      <title>My journey and insights in building a chatbot for eCommerce claims handling</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Tue, 23 Apr 2024 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/my-journey-and-insights-in-building-a-chatbot-for-ecommerce-claims-handling-3n79</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/my-journey-and-insights-in-building-a-chatbot-for-ecommerce-claims-handling-3n79</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I will share my journey and insights while building a chatbot for automating eCommerce claims handling. The chatbot was designed to handle customer claims, guide users through the claims process, and integrate with existing systems for seamless resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In eCommerce, handling customer claims (returns, refunds, damaged goods) is a significant operational burden. Most companies rely on manual processing, which is slow, inconsistent, and expensive to scale. The goal was to automate this process using a conversational AI chatbot that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand customer claims in natural language&lt;/li&gt;
&lt;li&gt;Guide customers through the required steps&lt;/li&gt;
&lt;li&gt;Collect necessary information (order details, photos, descriptions)&lt;/li&gt;
&lt;li&gt;Make decisions based on company policies&lt;/li&gt;
&lt;li&gt;Escalate complex cases to human agents&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;p&gt;The chatbot needed to handle multiple claim types with different workflows. I broke the problem down into:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Intent recognition — Understanding what the customer wants&lt;/li&gt;
&lt;li&gt;Entity extraction — Pulling out order IDs, product names, issue descriptions&lt;/li&gt;
&lt;li&gt;Dialog management — Managing multi-turn conversations&lt;/li&gt;
&lt;li&gt;Policy engine — Applying business rules for auto-resolution&lt;/li&gt;
&lt;li&gt;Handoff — Smooth transition to human agents when needed&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Evaluating the Tools
&lt;/h2&gt;

&lt;p&gt;I evaluated several tools and frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Lex&lt;/strong&gt; — AWS’s conversational AI service. Good integration with AWS ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialogflow&lt;/strong&gt; — Google’s NLU platform. Strong entity recognition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rasa&lt;/strong&gt; — Open-source, highly customizable. Steeper learning curve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom LLM-based&lt;/strong&gt; — Using GPT/Claude for more flexible conversations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose Amazon Lex for its integration with our existing AWS infrastructure and its built-in support for multi-turn conversations.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuanqgqd3bpqsaynerinx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuanqgqd3bpqsaynerinx.png" alt="Chatbot architecture" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Development Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Lex&lt;/strong&gt; for conversational AI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; for fulfillment logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; for session and claims data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lex Web UI&lt;/strong&gt; (&lt;a href="https://github.com/aws-samples/aws-lex-web-ui" rel="noopener noreferrer"&gt;github.com/aws-samples/aws-lex-web-ui&lt;/a&gt;) for the frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; for storing claim attachments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Operations
&lt;/h2&gt;

&lt;p&gt;Running the chatbot in production taught me several lessons about operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor confidence scores — Low confidence often indicates new intent patterns&lt;/li&gt;
&lt;li&gt;Log everything — Every conversation turn should be logged for training data&lt;/li&gt;
&lt;li&gt;A/B test responses — Small wording changes can dramatically affect resolution rates&lt;/li&gt;
&lt;li&gt;Build escalation paths early — Not every case can be automated&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Insights
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Start with the most common claim types first (80/20 rule)&lt;/li&gt;
&lt;li&gt;Users prefer guided flows over open-ended conversation for claims&lt;/li&gt;
&lt;li&gt;Photo upload capability dramatically reduces back-and-forth&lt;/li&gt;
&lt;li&gt;Clear expectations about what the bot can/cannot do reduces frustration&lt;/li&gt;
&lt;li&gt;Regular retraining with real conversation data is essential&lt;/li&gt;
&lt;li&gt;The biggest ROI came from reducing first-response time, not full automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building a claims chatbot is as much about understanding business processes as it is about AI technology. The key is starting simple, measuring everything, and iterating based on real user interactions.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>chatbot</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Build and deploy your own Shopify checkout extension: A step-by-step guide</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Wed, 01 Mar 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/build-and-deploy-your-own-shopify-checkout-extension-a-step-by-step-guide-2pm6</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/build-and-deploy-your-own-shopify-checkout-extension-a-step-by-step-guide-2pm6</guid>
      <description>&lt;p&gt;I came across the Shopify checkout extension and decided to do a proof of concept to demo it to a client. Since this is a new feature with limited preview to only Shopify Plus store merchants, I couldn’t find any straightforward tutorial on deployment. There are good tutorials on YouTube on how to build it in development, but I couldn’t find solutions for deploying it on a live server.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will assume that you are new to Shopify development and break down the concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with React development using TypeScript&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.shopify.com/sg/partners" rel="noopener noreferrer"&gt;Shopify Partner&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;Familiar with a reverse proxy such as Nginx&lt;/li&gt;
&lt;li&gt;Ngrok setup&lt;/li&gt;
&lt;li&gt;A new Shopify demo store&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initializing the Project
&lt;/h2&gt;

&lt;p&gt;Create a new Shopify app with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init @shopify/app@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6zalbinnfqdn5byghx8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft6zalbinnfqdn5byghx8.png" alt="Shopify app configuration" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm43rdkf9gfvjo6z8s1i8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm43rdkf9gfvjo6z8s1i8.png" alt="Shopify app prompts" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, change to the checkout-demo directory and run &lt;code&gt;npm run dev&lt;/code&gt;. You will be prompted to log in to Shopify.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5budiwsw7acxm8qsrd80.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5budiwsw7acxm8qsrd80.png" alt="Shopify login" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once logged in via browser, go back to the terminal and choose your organization, app name, and the development store. A ngrok URL will be generated. Use it to install your app.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazihduelt395lgmvys08.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fazihduelt395lgmvys08.png" alt="Ngrok URL" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fioh0hdowkgzez3np7opa.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fioh0hdowkgzez3np7opa.png" alt="App installation" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Checkout Extension
&lt;/h2&gt;

&lt;p&gt;To add a new extension to this app, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run shopify app generate extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the wizard, choose checkout UI, give it a suitable name, and select TypeScript React. Then run &lt;code&gt;npm run dev&lt;/code&gt; again. You will see a link to the dev console.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F55mxu5om3l2opcg3xhem.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F55mxu5om3l2opcg3xhem.png" alt="Dev console" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j7cdj3see76ofmop2u1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j7cdj3see76ofmop2u1.png" alt="Checkout UI extension" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Recommendation
&lt;/h2&gt;

&lt;p&gt;Now I’ll use a fake API to get a product recommendation. Change the directory to &lt;code&gt;extensions/checkout-demo-ext/src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add network permission by opening &lt;code&gt;shopify.ui.extension.toml&lt;/code&gt; and adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[capabilities]&lt;/span&gt;
&lt;span class="py"&gt;network_access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy and paste the following code in &lt;code&gt;extensions/checkout-demo-ext/src/index.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Banner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shopify/checkout-ui-extensions-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout::Dynamic::Render&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;productPlacement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProductPlacement&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading product recommendation...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="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="s1"&gt;https://fakestoreapi.com/products/9&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setProductPlacement&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&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="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Banner&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkout-demo-ext"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productPlacement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Banner&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;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffhozs0vu5taq100eu5e0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffhozs0vu5taq100eu5e0.png" alt="Product recommendation in checkout" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing for Deployment
&lt;/h2&gt;

&lt;p&gt;Copy the below environment variables to a new file and get the relevant keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;SHOPIFY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Client ID from Shopify Partner &amp;gt; Apps &amp;gt; Client Credentials&amp;gt;&lt;/span&gt;
&lt;span class="py"&gt;SHOPIFY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Client Secret&amp;gt;&lt;/span&gt;
&lt;span class="py"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://your-domain.com&lt;/span&gt;
&lt;span class="py"&gt;SCOPES&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;write_products&lt;/span&gt;
&lt;span class="py"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8081&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the below nginx config to &lt;code&gt;/etc/nginx/sites-enabled/your-domain.com&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;your-domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8081/&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;
  
  
  Web Setup
&lt;/h2&gt;

&lt;p&gt;We need a CLI token for production deployment. Go to &lt;a href="https://partners.shopify.com/current/settings/" rel="noopener noreferrer"&gt;Shopify Partners Settings&lt;/a&gt; &amp;gt; CLI Token &amp;gt; Manage Token. Generate and copy the &lt;code&gt;SHOPIFY_CLI_PARTNERS_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go to the &lt;code&gt;web/&lt;/code&gt; directory and install dependencies:&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="nb"&gt;cd &lt;/span&gt;web/
npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pm2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file under web and place all the environment variables. Then open &lt;code&gt;web/shopify.js&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the web server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 start index.js &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s1"&gt;'Checkout UI'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Build the app:&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="nv"&gt;SHOPIFY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Client ID&amp;gt; &lt;span class="nv"&gt;SHOPIFY_API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Client Secret&amp;gt; &lt;span class="nv"&gt;SCOPES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;write_products &lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-domain.com &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8081 npx shopify app build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy:&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="nv"&gt;SHOPIFY_CLI_PARTNERS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;CLI token&amp;gt; &lt;span class="nv"&gt;SHOPIFY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Client ID&amp;gt; &lt;span class="nv"&gt;SHOPIFY_API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Client Secret&amp;gt; &lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://your-domain.com &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8081 npx shopify app deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your previous development deployment will still be on the website and may throw errors during testing. I suggest creating different stores for dev/prod testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activating the Plugin
&lt;/h2&gt;

&lt;p&gt;Log in to the development store. Go to the checkout editor.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxi93t103grqon9yyu83b.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxi93t103grqon9yyu83b.png" alt="Checkout editor" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the bottom left, check the “Add an app” folder and you should see your plugin there.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsv4dkwg0bk26hzur1dle.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsv4dkwg0bk26hzur1dle.png" alt="Plugin in checkout editor" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>react</category>
      <category>typescript</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Encrypting traffic between Cloudflare and Kubernetes cluster</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Sat, 10 Jul 2021 14:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/encrypting-traffic-between-cloudflare-and-kubernetes-cluster-5eem</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/encrypting-traffic-between-cloudflare-and-kubernetes-cluster-5eem</guid>
      <description>&lt;h2&gt;
  
  
  Objective
&lt;/h2&gt;

&lt;p&gt;In this post, I’ll explain how to encrypt the traffic between Cloudflare and your Kubernetes cluster using TLS certificates. This ensures end-to-end encryption from the user’s browser to your application pods.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uqdhswc79jlfqambzz6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uqdhswc79jlfqambzz6.png" alt="Architecture diagram" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;The architecture involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare as the CDN and DNS provider (handles browser-to-Cloudflare TLS)&lt;/li&gt;
&lt;li&gt;Traefik as the Kubernetes ingress controller&lt;/li&gt;
&lt;li&gt;TLS certificates for Cloudflare-to-cluster encryption&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwsao13com34u1e8sb8c.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwsao13com34u1e8sb8c.png" alt="Cloudflare SSL settings" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; In your Cloudflare dashboard, go to SSL/TLS and set the encryption mode to &lt;strong&gt;Full (Strict)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Generate an Origin Certificate in Cloudflare. Go to SSL/TLS &amp;gt; Origin Server &amp;gt; Create Certificate. Download both the certificate and private key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Create a Kubernetes TLS secret with the Cloudflare origin certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret tls cloudflare-origin-cert &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;origin-cert.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;origin-key.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Configure Traefik IngressRoute to use the TLS certificate:&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IngressRoute&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;site-a-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;websecure&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Host(`your-domain.com`)&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rule&lt;/span&gt;
      &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;site-a-service&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloudflare-origin-cert&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbzlv5tzg6qu65fukwjaj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbzlv5tzg6qu65fukwjaj.png" alt="Traefik configuration" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending to Multiple Domains
&lt;/h2&gt;

&lt;p&gt;To add another domain, repeat the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a new origin certificate for the additional domain in Cloudflare&lt;/li&gt;
&lt;li&gt;Create a new TLS secret in Kubernetes&lt;/li&gt;
&lt;li&gt;Add a new IngressRoute pointing to the appropriate service&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbs9qhi5367suca11cq18.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbs9qhi5367suca11cq18.png" alt="Multi-domain setup" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this setup, all traffic between Cloudflare and your Kubernetes cluster is encrypted, ensuring end-to-end security for your applications.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloudflare</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Caesar’s Encryption in C++</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Sat, 10 Jul 2021 13:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/caesars-encryption-in-c-37o0</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/caesars-encryption-in-c-37o0</guid>
      <description>&lt;p&gt;The Caesar cipher (also known as ROT13 for a shift of 13) is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher where each letter is replaced by a letter a fixed number of positions down the alphabet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;In ROT13, each letter is shifted by 13 positions. Since the English alphabet has 26 letters, applying ROT13 twice returns the original text. This makes it both an encryption and decryption function.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2hwqtvp14vf9wrt8vm6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2hwqtvp14vf9wrt8vm6.png" alt="Substitution table" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the letter A becomes N, B becomes O, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The C++ Implementation
&lt;/h2&gt;

&lt;p&gt;Here is the complete C++ program for ROT13 encryption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;rot13&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&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="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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;output&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="o"&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;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="sc"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="sc"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;output&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sc"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="sc"&gt;'Z'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;output&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Enter message: "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;getline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rot13&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Encrypted: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;decrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rot13&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Decrypted: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;decrypted&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pause"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&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;h2&gt;
  
  
  Testing It Out
&lt;/h2&gt;

&lt;p&gt;When we encode a message:&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fscekozcwm2w221ujawul.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fscekozcwm2w221ujawul.png" alt="Encoding example" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And when we decode it back:&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiilk25sdt08fppkvmm0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiilk25sdt08fppkvmm0.png" alt="Decoding example" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since ROT13 shifts by exactly half the alphabet, the same function works for both encryption and decryption. This makes it elegantly simple, though not suitable for real security purposes.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>cryptography</category>
      <category>encryption</category>
      <category>security</category>
    </item>
    <item>
      <title>Computing Prime numbers faster with C++</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Sat, 10 Jul 2021 12:00:00 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/computing-prime-numbers-faster-with-c-ba5</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/computing-prime-numbers-faster-with-c-ba5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;“Prime numbers are numbers which are divisible by 1 and itself” by definition. So why do we need to know about prime numbers? How do we find them using a computer program? How do we generate them? If generated, is there a faster method? This post answers these questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application of Prime Numbers
&lt;/h2&gt;

&lt;p&gt;Prime numbers have a wide range of applications in cryptography and pseudo random number generators. In cryptography, prime numbers are used to generate keys for public key techniques. The &lt;code&gt;rand()&lt;/code&gt; function in C++ is also based on prime number generation. Let us create the best algorithm to generate prime numbers from scratch, analyze and improvise them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use of Modulo Operation
&lt;/h2&gt;

&lt;p&gt;The modulo operator &lt;code&gt;%&lt;/code&gt; gives the remainder when two numbers are divided:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2 % 2 = 0 // No remainder
5 % 4 = 1 // Remainder is 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 is divisible by 2 hence there is no remainder, but when 5 is divided by 4 the remainder is 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Code It!
&lt;/h2&gt;

&lt;p&gt;The definition says “numbers which are divisible by 1 and itself.” So when a number is given, we check by dividing all numbers from 1 up to the given number. If we find that it has only 2 divisors, then it is prime.&lt;/p&gt;

&lt;p&gt;On a computer, we count the number of modulo operations that result in zero:&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2nhu906nfj7uy3rc93j1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2nhu906nfj7uy3rc93j1.png" alt="Modulo operation table" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above gives us an idea of finding a pattern. See the zero counts of prime numbers and non-prime numbers. For example, 4 has 3 zeros as a result of modulo operation.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzhmz1mvrlqlqfg2lq6h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzhmz1mvrlqlqfg2lq6h.png" alt="Code progression" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The left operand is constant (our variable &lt;code&gt;n&lt;/code&gt;) and the right operand increments by one (variable &lt;code&gt;i&lt;/code&gt;), so we use a loop. A counter variable increments when the modulus equals zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&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;int&lt;/span&gt; &lt;span class="n"&gt;count&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="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="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="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&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;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"%"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"= "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&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="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&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="n"&gt;count&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="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;count&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Prime"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&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="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Not Prime"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pause"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&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;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzqs6ji2wwd3cdcwa3jt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzqs6ji2wwd3cdcwa3jt.png" alt="Program output" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the algorithm is made interactive by getting user input and checking whether it is prime.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flntftjh0lxikwnfml75p.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flntftjh0lxikwnfml75p.png" alt="Interactive program" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization: Checking up to n/2
&lt;/h2&gt;

&lt;p&gt;As a programmer, we should improve the algorithm to take fewer steps. Basic algebra shows that it is enough to check from 1 to half of the number. Since 4×2 = 8, no factors will be greater than n/2. This saves computation time.&lt;/p&gt;

&lt;p&gt;Here is a program demonstrating the performance difference when generating primes from 1 to 100 using both approaches (1 to n/2 vs. 1 to n):&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsninnpv0851efr6y3u82.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsninnpv0851efr6y3u82.png" alt="Performance comparison" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The n/2 version requires significantly fewer operations than the traditional approach. This optimization becomes more impactful as the numbers get larger.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>algorithms</category>
      <category>optimization</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
