<?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: Phantasm0009</title>
    <description>The latest articles on DEV Community by Phantasm0009 (@phantasm0009).</description>
    <link>https://dev.to/phantasm0009</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F984858%2F3540feae-c12a-412e-bcca-494f7e14c8e3.png</url>
      <title>DEV Community: Phantasm0009</title>
      <link>https://dev.to/phantasm0009</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phantasm0009"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Fri, 27 Mar 2026 16:52:42 +0000</pubDate>
      <link>https://dev.to/phantasm0009/-10g4</link>
      <guid>https://dev.to/phantasm0009/-10g4</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81" class="crayons-story__hidden-navigation-link"&gt;How I used Next.js and Claude 3.5 to stop my PM from nagging me about Jira&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/phantasm0009" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F984858%2F3540feae-c12a-412e-bcca-494f7e14c8e3.png" alt="phantasm0009 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/phantasm0009" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Phantasm0009
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Phantasm0009
                
              
              &lt;div id="story-author-preview-content-3411293" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/phantasm0009" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F984858%2F3540feae-c12a-412e-bcca-494f7e14c8e3.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Phantasm0009&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81" id="article-link-3411293"&gt;
          How I used Next.js and Claude 3.5 to stop my PM from nagging me about Jira
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/productivity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;productivity&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nextjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nextjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>nextjs</category>
      <category>ai</category>
    </item>
    <item>
      <title>How I used Next.js and Claude 3.5 to stop my PM from nagging me about Jira</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Thu, 26 Mar 2026 23:17:08 +0000</pubDate>
      <link>https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81</link>
      <guid>https://dev.to/phantasm0009/how-i-used-nextjs-and-claude-35-to-stop-my-pm-from-nagging-me-about-jira-1d81</guid>
      <description>&lt;p&gt;We’ve all been there. &lt;/p&gt;

&lt;p&gt;It’s 10:30 AM. You drop your update in the &lt;code&gt;#daily-standup&lt;/code&gt; Slack channel: &lt;em&gt;"Finally squashed that weird auth bug on the login portal, moving on to the database migration."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two hours later, your Product Manager DMs you: &lt;em&gt;"Hey, awesome job on the auth bug! Did you remember to move the ticket to Done in Jira?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Context switching is the absolute worst part of being a developer. We are already communicating our status naturally in Slack—why do we have to open a new tab, wait for Jira or Linear to load, find the right sprint, and click a button just to say the exact same thing?&lt;/p&gt;

&lt;p&gt;Last weekend, I decided I’d had enough. I built a Slack bot that reads my daily updates, figures out which ticket I’m talking about, and auto-closes it for me. &lt;/p&gt;

&lt;p&gt;Here is how I built it using Next.js, the Slack Events API, and Claude 3.5 Sonnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;

&lt;p&gt;The flow is surprisingly simple, but the execution requires some strict timing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developer posts a message in a monitored Slack channel.&lt;/li&gt;
&lt;li&gt;Slack fires a webhook to my Next.js API route.&lt;/li&gt;
&lt;li&gt;The API fetches the user's currently open tasks from Jira/Linear.&lt;/li&gt;
&lt;li&gt;Claude 3.5 evaluates the Slack message against the open tasks to see if there's a match.&lt;/li&gt;
&lt;li&gt;If confidence is &amp;gt; 95%, the bot hits the PM tool's API to mark it "Done" and replies in Slack.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Challenge 1: Beating the Slack 3-Second Timeout
&lt;/h3&gt;

&lt;p&gt;Slack's Events API is notoriously strict. When they send a webhook payload, you have exactly 3 seconds to respond with a &lt;code&gt;200 OK&lt;/code&gt;, or Slack assumes your app is dead and retries the event (which leads to duplicate AI calls and chaos). &lt;/p&gt;

&lt;p&gt;Because calling the Anthropic API and the Jira API takes way longer than 3 seconds, you cannot &lt;code&gt;await&lt;/code&gt; the AI processing in the main thread. &lt;/p&gt;

&lt;p&gt;To solve this in Next.js (deployed on Vercel), I used the &lt;code&gt;waitUntil&lt;/code&gt; function from &lt;code&gt;@vercel/functions&lt;/code&gt;. This allows the serverless function to return the &lt;code&gt;200 OK&lt;/code&gt; immediately to Slack, while keeping the execution environment alive to finish the AI pipeline in the background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/webhooks/slack/route.ts&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;waitUntil&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;@vercel/functions&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;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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;next/server&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;processSlackMessage&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;@/lib/ai/pipeline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;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="c1"&gt;// ... verify signatures and parse payload ...&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&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="c1"&gt;// 1. Queue the heavy AI processing in the background&lt;/span&gt;
    &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;processSlackMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;teamId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;team_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;channelId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messageText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messageTs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Immediately return 200 OK to satisfy Slack's 3-second rule&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 2: Stopping the AI from Hallucinating Tasks
&lt;/h3&gt;

&lt;p&gt;The scariest part of this build was the idea of an AI accidentally closing the wrong ticket. If a developer says &lt;em&gt;"I'm going to start working on the header tomorrow"&lt;/em&gt;, the AI shouldn't close the "Header Redesign" ticket just because the words match.&lt;/p&gt;

&lt;p&gt;To fix this, I engineered a highly specific system prompt for Claude 3.5. Instead of just asking "does this match?", I force the LLM to return a strict JSON object with a calculated &lt;code&gt;confidence_score&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I gave Claude very specific scoring guidelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/ai/prompts.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are NudgeBot's task-matching engine. Given a Slack message and a list of the sender's open tasks, determine whether the message indicates that one or more tasks have been completed.

Return ONLY valid JSON in this exact shape:
{
  "matches": [
    {
      "task_id": "&amp;lt;uuid&amp;gt;",
      "confidence_score": &amp;lt;0.0–1.0&amp;gt;,
      "reasoning": "&amp;lt;1–2 sentences&amp;gt;"
    }
  ]
}

Scoring guidelines:
  0.95–1.00  Explicit: "finished X", "deployed X", PR merged for X
  0.80–0.94  Strong: past-tense action verbs ("shipped", "pushed") + context
  0.65–0.79  Moderate: implicit completion language, partial name overlap
  0.50–0.64  Weak: tangentially related, ambiguous phrasing

Rules:
- Past tense, "just", "finally", deployment language are positive signals.
- Questions, future tense ("will", "going to"), and negations ("not done") are NOT completions.
- Return empty matches if nothing matches above 0.50.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the backend pipeline, if Claude returns a score of &lt;code&gt;0.95&lt;/code&gt; or higher, the app automatically closes the ticket. If it's between &lt;code&gt;0.70&lt;/code&gt; and &lt;code&gt;0.94&lt;/code&gt;, the bot sends an ephemeral (private) message to the user in Slack with a button asking: &lt;em&gt;"Looks like you finished the Auth Bug. Should I close the ticket for you?"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Trust" Feature (Audit Trails)
&lt;/h3&gt;

&lt;p&gt;If you automate project management, your PMs will eventually get paranoid about &lt;em&gt;why&lt;/em&gt; things are moving. &lt;/p&gt;

&lt;p&gt;Whenever the bot closes a ticket, it makes a secondary API call to Jira/Linear to leave a comment on the ticket: &lt;code&gt;✅ Marked as done by NudgeBot based on a Slack update from @Aditya: "Finally squashed that weird auth bug."&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Complete transparency, zero extra clicks. &lt;/p&gt;

&lt;h3&gt;
  
  
  Don't want to build it yourself?
&lt;/h3&gt;

&lt;p&gt;Building this was a super fun weekend project, but managing OAuth flows for Slack, Jira, Linear, Asana, and Trello is a massive headache. &lt;/p&gt;

&lt;p&gt;Because we are developers and we appreciate not reinventing the wheel, I actually packaged this exact codebase into a 1-click installable SaaS. &lt;/p&gt;

&lt;p&gt;If you want to stop your PMs from nagging you, you can invite the bot to your workspace and use it for free here: &lt;strong&gt;&lt;a href="https://nudgebot.xyz" rel="noopener noreferrer"&gt;NudgeBot.xyz&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let me know in the comments if you'd trust an AI to manage your project board!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>nextjs</category>
      <category>ai</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 16 Mar 2026 12:36:36 +0000</pubDate>
      <link>https://dev.to/phantasm0009/-2jgn</link>
      <guid>https://dev.to/phantasm0009/-2jgn</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/phantasm0009" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F984858%2F3540feae-c12a-412e-bcca-494f7e14c8e3.png" alt="phantasm0009"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/phantasm0009/i-wasted-200-hours-parsing-client-csvs-so-i-built-a-library-that-does-it-in-one-line-1pgp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;I wasted 200+ hours parsing client CSVs. So I built a library that does it in one line.&lt;/h2&gt;
      &lt;h3&gt;Phantasm0009 ・ Mar 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#datascience&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>python</category>
      <category>datascience</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I wasted 200+ hours parsing client CSVs. So I built a library that does it in one line.</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 16 Mar 2026 12:34:34 +0000</pubDate>
      <link>https://dev.to/phantasm0009/i-wasted-200-hours-parsing-client-csvs-so-i-built-a-library-that-does-it-in-one-line-1pgp</link>
      <guid>https://dev.to/phantasm0009/i-wasted-200-hours-parsing-client-csvs-so-i-built-a-library-that-does-it-in-one-line-1pgp</guid>
      <description>&lt;p&gt;Every data person has the same nightmare.&lt;/p&gt;

&lt;p&gt;German client sends a CSV with semicolons, &lt;code&gt;DD.MM.YYYY&lt;/code&gt; dates, and European number formatting. French client sends commas with &lt;code&gt;DD/MM/YYYY&lt;/code&gt;. US client sends &lt;code&gt;MM/DD/YYYY&lt;/code&gt;. You open each one with &lt;code&gt;pandas.read_csv()&lt;/code&gt; and it silently corrupts all three.&lt;/p&gt;

&lt;p&gt;I spent two years writing the same "detect encoding, guess delimiter, figure out date format" code for every new client. Last month I finally snapped and built a library.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Here's a typical European export file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;Kunden&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;Nr&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Name&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Datum&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Umsatz&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Aktiv&lt;/span&gt;
&lt;span class="mf"&gt;00742&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;M&lt;/span&gt;&lt;span class="err"&gt;ü&lt;/span&gt;&lt;span class="k"&gt;ller&lt;/span&gt; &lt;span class="k"&gt;GmbH&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;01.03.2025&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;1.234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;56&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Ja&lt;/span&gt;
&lt;span class="mf"&gt;00123&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Sch&lt;/span&gt;&lt;span class="err"&gt;ä&lt;/span&gt;&lt;span class="k"&gt;fer&lt;/span&gt; &lt;span class="k"&gt;AG&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;15.07.2024&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;789&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Nein&lt;/span&gt;
&lt;span class="mf"&gt;00456&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;B&lt;/span&gt;&lt;span class="err"&gt;ö&lt;/span&gt;&lt;span class="k"&gt;hm&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;Co&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;25.12.2024&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;12.345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;67&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="k"&gt;Ja&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Semicolons as delimiters. Dots in dates. Commas as decimal separators. Leading zeros in IDs. &lt;code&gt;Ja/Nein&lt;/code&gt; instead of &lt;code&gt;True/False&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Watch what pandas does to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;german_export.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="n"&gt;Kunden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Nr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Datum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Umsatz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Aktiv&lt;/span&gt;
&lt;span class="mi"&gt;00742&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Müller&lt;/span&gt; &lt;span class="n"&gt;GmbH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;01.03&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;1.234&lt;/span&gt;                         &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Ja&lt;/span&gt;
&lt;span class="mi"&gt;00123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Schäfer&lt;/span&gt; &lt;span class="n"&gt;AG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;15.07&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mi"&gt;789&lt;/span&gt;                          &lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Nein&lt;/span&gt;
&lt;span class="mi"&gt;00456&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Böhm&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Co&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;25.12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mf"&gt;12.345&lt;/span&gt;                          &lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Ja&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One giant mangled column. Pandas assumed commas, so the semicolons became part of the values. The European decimal comma in &lt;code&gt;1.234,56&lt;/code&gt; split the number across columns. Every single row is corrupted.&lt;/p&gt;

&lt;p&gt;You can fix this. Pass &lt;code&gt;sep=";"&lt;/code&gt;. Pass &lt;code&gt;dtype=str&lt;/code&gt; for the ID column. Write a custom date parser. Replace commas in numbers. Map &lt;code&gt;Ja&lt;/code&gt; to &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's 15 lines of boilerplate. Per file. Per client. Per locale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;german_export.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;Kunden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Nr&lt;/span&gt;         &lt;span class="n"&gt;Name&lt;/span&gt;      &lt;span class="n"&gt;Datum&lt;/span&gt;    &lt;span class="n"&gt;Umsatz&lt;/span&gt;  &lt;span class="n"&gt;Aktiv&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;     &lt;span class="mi"&gt;00742&lt;/span&gt;  &lt;span class="n"&gt;Müller&lt;/span&gt; &lt;span class="n"&gt;GmbH&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;   &lt;span class="mf"&gt;1234.56&lt;/span&gt;   &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;     &lt;span class="mi"&gt;00123&lt;/span&gt;   &lt;span class="n"&gt;Schäfer&lt;/span&gt; &lt;span class="n"&gt;AG&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;    &lt;span class="mf"&gt;789.00&lt;/span&gt;  &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;     &lt;span class="mi"&gt;00456&lt;/span&gt;    &lt;span class="n"&gt;Böhm&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Co&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;  &lt;span class="mf"&gt;12345.67&lt;/span&gt;   &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line. csvmedic auto-detected the semicolon delimiter, parsed &lt;code&gt;DD.MM.YYYY&lt;/code&gt; dates correctly, normalized European numbers, preserved leading zeros as strings, and converted &lt;code&gt;Ja/Nein&lt;/code&gt; to booleans.&lt;/p&gt;

&lt;p&gt;Every decision is logged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diagnosis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;csvmedic&lt;/span&gt; &lt;span class="nc"&gt;Diagnosis &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.17&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;utf_8 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;Delimiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
  &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;

  &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;Kunden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Nr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preserved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;leading_zeros_detected&lt;/span&gt;
  &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skipped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="n"&gt;Datum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;date &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;format_detected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;
      &lt;span class="n"&gt;dayfirst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
      &lt;span class="n"&gt;ambiguous_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;unambiguous_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="n"&gt;Umsatz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;locale_detected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;de_DE&lt;/span&gt;
      &lt;span class="n"&gt;decimal_separator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;thousands_separator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="n"&gt;Aktiv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;boolean &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;true_variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ja&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;false_variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nein&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No silent corruption. Every transformation visible and auditable. The &lt;code&gt;—&lt;/code&gt; means preserved as string (leading zeros detected), &lt;code&gt;·&lt;/code&gt; means skipped (already a clean string), and &lt;code&gt;✓&lt;/code&gt; means successfully converted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part: DD/MM vs MM/DD
&lt;/h2&gt;

&lt;p&gt;Encoding and delimiter detection are solved problems. The genuinely hard part — and the reason I built this — is &lt;strong&gt;date disambiguation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you see &lt;code&gt;03/04/2025&lt;/code&gt;, is that March 4 or April 3? There's no way to know from a single value. And pandas just guesses (or gives up and leaves it as a string).&lt;/p&gt;

&lt;p&gt;csvmedic doesn't guess. It uses the &lt;strong&gt;data itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The core insight: you don't need every value to be unambiguous. You need &lt;strong&gt;one&lt;/strong&gt;. If a column contains &lt;code&gt;03/04/2025&lt;/code&gt;, &lt;code&gt;05/06/2025&lt;/code&gt;, and &lt;code&gt;25/03/2025&lt;/code&gt;, that last value has a first component of 25. Since 25 &amp;gt; 12, it can't be a month. So the first component must be the day. One unambiguous value resolves the entire column.&lt;/p&gt;

&lt;p&gt;You can see this in the diagnosis output above: &lt;code&gt;ambiguous_count: 1&lt;/code&gt; (the value &lt;code&gt;01.03.2025&lt;/code&gt; where both 01 and 03 could be day or month) and &lt;code&gt;unambiguous_count: 2&lt;/code&gt; (the values where the day component was &amp;gt; 12, which locked in the format for the whole column).&lt;/p&gt;

&lt;p&gt;When there's no single unambiguous value (every date has both components ≤ 12), csvmedic falls through a chain of strategies: cross-column inference (if another date column in the same file was already resolved, assume the same locale), separator hints (dot separator like &lt;code&gt;01.03.2025&lt;/code&gt; is strongly associated with European day-first formats), and sequential monotonicity (try both orderings and see which produces sorted dates).&lt;/p&gt;

&lt;p&gt;If none of these produce a confident answer, csvmedic &lt;strong&gt;doesn't guess&lt;/strong&gt;. It leaves the column as a string and flags it as ambiguous in the diagnosis. Silent corruption is worse than no conversion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving it: csvmedic.diff()
&lt;/h2&gt;

&lt;p&gt;If you're skeptical (you should be), csvmedic has a built-in proof tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;leading_zeros.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;leading_zeros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;
  &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;pandas &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="nf"&gt;csvmedic &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;Columns&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;differences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;zip_code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;Sample&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="nf"&gt;differences &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;742&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00742&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00123&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;456&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00456&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;zip_code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;01234&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;zip_code&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90210&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;90210&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pandas converted &lt;code&gt;00742&lt;/code&gt; to &lt;code&gt;742&lt;/code&gt;, &lt;code&gt;01234&lt;/code&gt; to &lt;code&gt;1234&lt;/code&gt;. Those are customer IDs and zip codes — they're not numbers, they're strings that happen to contain digits. csvmedic detects the leading zeros and preserves them.&lt;/p&gt;

&lt;p&gt;This is the output you show your team lead when they ask "why do we need another dependency?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema pinning for recurring files
&lt;/h2&gt;

&lt;p&gt;Most real-world data work involves the same file format every month. csvmedic lets you save the detected schema and reuse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# First time: detect everything
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_export.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diagnosis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;file_profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_export.csvmedic.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Every time after: skip detection, apply cached schema
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_export.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monthly_export.csvmedic.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema file is plain JSON — you can commit it to your repo, review it in PRs, and share it across your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch reads with consensus
&lt;/h2&gt;

&lt;p&gt;When you're reading 12 monthly CSVs that should all have the same format, use consensus mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dfs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jan.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feb.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mar.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;use_consensus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;csvmedic samples each file, runs encoding and delimiter detection on all of them, and uses the majority result for every file. If 11 out of 12 files are UTF-8 with semicolons and one is mislabeled, consensus wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;csvmedic is not trying to be a data cleaning framework. It does exactly one thing: get a messy CSV into a clean DataFrame with correct types. It doesn't clean column names, remove duplicates, or handle missing value imputation. It's the first line of your pipeline, not the whole pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical decisions
&lt;/h2&gt;

&lt;p&gt;A few choices that might matter to you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is read as &lt;code&gt;dtype=str&lt;/code&gt; first.&lt;/strong&gt; csvmedic never lets pandas do type inference. It reads everything as strings, runs its own detection, and then applies conversions explicitly. This is the only way to prevent silent data loss (like leading zeros).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No dateutil.parser.&lt;/strong&gt; It's too permissive (it'll parse &lt;code&gt;"hello"&lt;/code&gt; as today's date) and too slow for batch column analysis. csvmedic uses &lt;code&gt;strptime&lt;/code&gt; with explicit format strings — 50-100x faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidence thresholds.&lt;/strong&gt; Every detection has a confidence score from 0 to 1. Below 0.75 (configurable), the column stays as a string. You can tighten this to 0.9 if you want csvmedic to be more conservative, or loosen it to 0.6 if you trust the heuristics.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;csvmedic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvmedic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_messy_file.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diagnosis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/csvmedic/" rel="noopener noreferrer"&gt;pypi.org/project/csvmedic&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Phantasm0009/CSVMedic" rel="noopener noreferrer"&gt;github.com/Phantasm0009/CSVMedic&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT licensed. Pure Python. Two dependencies (pandas + charset-normalizer).&lt;/p&gt;

&lt;p&gt;I'd love feedback — especially on edge cases that the date disambiguation misses. Open an issue or drop a comment here.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 02 Mar 2026 18:49:03 +0000</pubDate>
      <link>https://dev.to/phantasm0009/-1h29</link>
      <guid>https://dev.to/phantasm0009/-1h29</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/phantasm0009" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F984858%2F3540feae-c12a-412e-bcca-494f7e14c8e3.png" alt="phantasm0009"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/phantasm0009/building-a-webgpu-library-from-scratch-1ed5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Building a WebGPU Library from Scratch&lt;/h2&gt;
      &lt;h3&gt;Phantasm0009 ・ Mar 2&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building a WebGPU Library from Scratch</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 02 Mar 2026 18:33:51 +0000</pubDate>
      <link>https://dev.to/phantasm0009/building-a-webgpu-library-from-scratch-1ed5</link>
      <guid>https://dev.to/phantasm0009/building-a-webgpu-library-from-scratch-1ed5</guid>
      <description>&lt;p&gt;I wanted a NumPy-like API that ran on the GPU in the browser. No training graphs, no autograd—just arrays and ops. So I built one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use TensorFlow.js?
&lt;/h2&gt;

&lt;p&gt;TensorFlow.js is great, but it's heavy. I needed something small for demos and experiments. I also wanted to understand how GPU compute actually works under the hood. So I started a small library called accel-gpu.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;WebGPU exposes compute shaders via WGSL. You create buffers, write shaders, and dispatch workgroups. The tricky part is making that feel like &lt;code&gt;a.add(b)&lt;/code&gt; instead of "create bind group, set pipeline, dispatch, sync."&lt;/p&gt;

&lt;p&gt;I went with a simple model: each op has a precompiled WGSL shader. &lt;code&gt;add&lt;/code&gt; is a shader that does &lt;code&gt;out[i] = a[i] + b[i]&lt;/code&gt;. &lt;code&gt;relu&lt;/code&gt; is &lt;code&gt;out[i] = max(0, a[i])&lt;/code&gt;. No runtime shader generation, no graph IR—just a map of op names to shader strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@compute @workgroup_size(256)
fn main(@builtin(global_invocation_id) gid: vec3&amp;lt;u32&amp;gt;) {
  let i = gid.x;
  if (i &amp;lt; arrayLength(&amp;amp;out)) {
    out[i] = a[i] + b[i];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole add kernel. The runner creates a pipeline from it, binds buffers, and dispatches. For 100k elements, that's ~400 workgroups of 256 threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebGL fallback
&lt;/h2&gt;

&lt;p&gt;WebGPU isn't everywhere yet. Safari and Firefox need WebGL2. So I added a WebGL backend.&lt;/p&gt;

&lt;p&gt;WebGL doesn't have compute shaders. The trick is to use a full-screen quad and a fragment shader. You pack floats into RGBA8 (bit manipulation), render to a texture, and unpack. It's a bit hacky, but it works. Each "compute" op becomes a render pass.&lt;/p&gt;

&lt;p&gt;Then there's a CPU backend for Node and headless runs. Same API, different implementation. The API stays the same whether you're on WebGPU, WebGL, or CPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shape inference
&lt;/h2&gt;

&lt;p&gt;For matmul, you need M, N, and K. I didn't want users to pass those every time. So the library infers them from array shapes. If A is [2, 3] and B is [3, 4], it knows M=2, N=4, K=3. That's just a bit of shape logic, but it makes the API much nicer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reductions are annoying
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sum&lt;/code&gt; over 1M elements can't fit in one workgroup. You need a multi-pass reduction. Each pass sums pairs, halves the size, and repeats until you have one value. The first version had a bug where I didn't handle non-power-of-two sizes correctly. Took a while to track down.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;argmax&lt;/code&gt; is worse—you need both value and index. I ended up doing it on the CPU by reading the buffer back. Not ideal for huge arrays, but fine for typical use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Buffer pooling&lt;/strong&gt; — I added it later. Creating and destroying GPUBuffers every frame was slow. A simple pool helped a lot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error handling&lt;/strong&gt; — Early on, WebGPU errors were cryptic. I added clearer messages ("reshape: cannot reshape [2,3] to [4]"), and that saved a lot of debugging time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt; — I test on the CPU backend. Same ops, same results, no GPU setup. Makes CI straightforward.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The fun parts
&lt;/h2&gt;

&lt;p&gt;Softmax was satisfying: max for numerical stability, then exp, then normalize. Layer norm and attention scores followed. FFT was interesting—Cooley-Tukey, bit reversal, butterfly ops. All on the CPU for now, but the API is there.&lt;/p&gt;

&lt;p&gt;Conv2D and pooling are implemented on the CPU as well. They work, but a real GPU implementation would need proper 2D/3D dispatch and more thought about memory layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it worth it?
&lt;/h2&gt;

&lt;p&gt;For learning, yes. I understand WebGPU and WGSL much better now. For production, it depends. If you need a small, dependency-free GPU math lib, it might fit. If you need full training, use TensorFlow.js or a similar library.&lt;/p&gt;

&lt;p&gt;The repo is &lt;a href="https://github.com/Phantasm0009/accel-gpu" rel="noopener noreferrer"&gt;accel-gpu on GitHub&lt;/a&gt; if you want to poke around. The shaders live in &lt;code&gt;src/kernels/shaders.ts&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Looking to partner with a chrome extension partner.</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Tue, 20 Jun 2023 18:33:20 +0000</pubDate>
      <link>https://dev.to/phantasm0009/looking-to-partner-with-a-chrome-extension-partner-1kpo</link>
      <guid>https://dev.to/phantasm0009/looking-to-partner-with-a-chrome-extension-partner-1kpo</guid>
      <description>&lt;p&gt;Introducing the perfect collaboration: CodeCrafter and "your chrome extension"! Unite the power of two innovative extensions to supercharge your coding experience. Seamlessly integrate "your Chrome extension's" unique features with CodeCrafter's robust code snippet management capabilities. Together, we're revolutionizing the way developers work, bringing efficiency and productivity to the forefront. Discover the synergy of CodeCrafter and "your Chrome extension", and unlock a whole new level of coding awesomeness.&lt;/p&gt;

&lt;p&gt;In short we want to partner with a another chrome extension so we can reach people to install our chrome extension. Our chrome extension(which is also featured) - &lt;a href="https://chrome.google.com/webstore/detail/codecrafter/mllegkfjonhcfpbkhokboibclmkibdfl"&gt;https://chrome.google.com/webstore/detail/codecrafter/mllegkfjonhcfpbkhokboibclmkibdfl&lt;/a&gt;&lt;/p&gt;

</description>
      <category>extension</category>
      <category>chrome</category>
      <category>partner</category>
    </item>
    <item>
      <title>How to Build a Chatbot using Natural Language Processing (NLP)</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 20 Feb 2023 18:59:39 +0000</pubDate>
      <link>https://dev.to/phantasm0009/how-to-build-a-chatbot-using-natural-language-processing-nlp-11mj</link>
      <guid>https://dev.to/phantasm0009/how-to-build-a-chatbot-using-natural-language-processing-nlp-11mj</guid>
      <description>&lt;h1&gt;
  
  
  How to Build a Chatbot using Natural Language Processing (NLP)
&lt;/h1&gt;

&lt;p&gt;Chatbots have become increasingly popular in recent years, and with good reason. A chatbot is a computer program that is capable of simulating human conversation, and they can be used for a wide range of purposes, from providing customer support to delivering news updates.&lt;/p&gt;

&lt;p&gt;One of the key technologies that enables chatbots to simulate human conversation is Natural Language Processing (NLP). NLP is a field of study that focuses on the interactions between human language and computers. By using NLP, chatbots can understand and interpret user inputs, and generate appropriate responses.&lt;/p&gt;

&lt;p&gt;In this article, we will explore how to build a chatbot using NLP. We will cover the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define your chatbot's purpose.&lt;/li&gt;
&lt;li&gt;Choose your NLP framework.&lt;/li&gt;
&lt;li&gt;Define your chatbot's intents.&lt;/li&gt;
&lt;li&gt;Train your chatbot.&lt;/li&gt;
&lt;li&gt;Implement your chatbot's responses.&lt;/li&gt;
&lt;li&gt;Test your chatbot.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Define Your Chatbot's Purpose
&lt;/h2&gt;

&lt;p&gt;Before you start building your chatbot, you need to define its purpose. What problem will it solve? What tasks will it perform? What kind of user will it interact with? Defining your chatbot's purpose will help you to design its conversation flow and choose the appropriate NLP framework.&lt;/p&gt;

&lt;p&gt;Here are a few examples of chatbot purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer support: A chatbot that can help customers troubleshoot issues with a product or service.&lt;/li&gt;
&lt;li&gt;Sales assistant: A chatbot that can help customers find products or services that meet their needs.&lt;/li&gt;
&lt;li&gt;Personal assistant: A chatbot that can help users manage their schedules, set reminders, and perform other tasks.&lt;/li&gt;
&lt;li&gt;News bot: A chatbot that can deliver news updates and other information to users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Choose Your NLP Framework
&lt;/h2&gt;

&lt;p&gt;Once you have defined your chatbot's purpose, the next step is to choose your NLP framework. There are several NLP frameworks available, each with its own strengths and weaknesses.&lt;/p&gt;

&lt;p&gt;Here are a few popular NLP frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dialogflow: A Google-owned platform that allows developers to build conversational interfaces for websites, mobile applications, and messaging platforms.&lt;/li&gt;
&lt;li&gt;Microsoft Bot Framework: A framework for building intelligent bots that can be deployed across multiple platforms, including Skype, Slack, and Facebook Messenger.&lt;/li&gt;
&lt;li&gt;IBM Watson Assistant: A cloud-based platform that allows developers to build and deploy chatbots across multiple channels.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Define Your Chatbot's Intents
&lt;/h2&gt;

&lt;p&gt;Once you have chosen your NLP framework, the next step is to define your chatbot's intents. An intent is a specific goal or action that the user wants to achieve through their conversation with the chatbot. Defining your chatbot's intents will help you to design its conversation flow and train it to understand user inputs.&lt;/p&gt;

&lt;p&gt;Here are a few examples of chatbot intents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get weather information: A chatbot that can provide users with current weather information for a specific location.&lt;/li&gt;
&lt;li&gt;Book a hotel room: A chatbot that can help users book a hotel room based on their preferences.&lt;/li&gt;
&lt;li&gt;Order food: A chatbot that can help users order food from a restaurant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Train Your Chatbot
&lt;/h2&gt;

&lt;p&gt;Once you have defined your chatbot's intents, the next step is to train it. Training involves providing examples of user inputs and the corresponding intents that the chatbot should recognize. The more examples you provide, the more accurate your chatbot will become.&lt;/p&gt;

&lt;p&gt;Here's an example of how to train a chatbot in Dialogflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Dialogflow console and select your chatbot.&lt;/li&gt;
&lt;li&gt;In the intents tab, create a new intent.&lt;/li&gt;
&lt;li&gt;Define the intent's name and training phrases&lt;/li&gt;
&lt;li&gt;Provide examples of user inputs that correspond to the intent.&lt;/li&gt;
&lt;li&gt;Train the chatbot by saving the intent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Repeat this process for each of your chatbot's intents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Test Your Chatbot
&lt;/h2&gt;

&lt;p&gt;Once your chatbot is implemented, the final step is to test it. Testing involves interacting with the chatbot and making sure that it can understand user inputs and provide appropriate responses.&lt;/p&gt;

&lt;p&gt;Here are a few tips for testing your chatbot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with simple inputs and gradually increase complexity.&lt;/li&gt;
&lt;li&gt;Test the chatbot on different devices and platforms.&lt;/li&gt;
&lt;li&gt;Get feedback from users and use it to improve your chatbot's performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Building a chatbot using NLP can be a complex process, but it can also be very rewarding. By following the steps outlined in this article, you can build a chatbot that is capable of simulating human conversation and performing a wide range of tasks. Whether you are building a chatbot for customer support, sales, or personal assistance, NLP can help you to create a more engaging and effective user experience.&lt;/p&gt;

</description>
      <category>chatbot</category>
      <category>nlp</category>
      <category>programming</category>
    </item>
    <item>
      <title>6 Tips for Writing Cleaner and More Maintainable JavaScript Code</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Sun, 19 Feb 2023 19:46:26 +0000</pubDate>
      <link>https://dev.to/phantasm0009/6-tips-for-writing-cleaner-and-more-maintainable-javascript-code-4lbm</link>
      <guid>https://dev.to/phantasm0009/6-tips-for-writing-cleaner-and-more-maintainable-javascript-code-4lbm</guid>
      <description>&lt;h1&gt;
  
  
  6 Tips for Writing Cleaner and More Maintainable JavaScript Code
&lt;/h1&gt;

&lt;p&gt;As JavaScript continues to grow in popularity, it's becoming increasingly important to write clean and maintainable code. Writing clean code not only makes it easier to read and understand but also reduces the risk of introducing bugs and makes it easier to maintain and update code in the future. In this post, we'll cover 6 tips for writing cleaner and more maintainable JavaScript code.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Use Meaningful Variable Names
&lt;/h2&gt;

&lt;p&gt;Using meaningful variable names is essential to writing clean and maintainable code. It helps to make the code more readable and self-documenting. When choosing variable names, it's important to use names that accurately describe the purpose of the variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
const x = 10;
const y = 20;
const z = x + y;

// Good example
const width = 10;
const height = 20;
const area = width * height;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2.Use Constants for Values That Don't Change
&lt;/h2&gt;

&lt;p&gt;When a value won't change throughout the life of a program, it's best to use a constant instead of a variable. This helps to make the code more readable and ensures that the value doesn't &lt;br&gt;
accidentally get changed later in the program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
let pi = 3.14159;
pi = 3.14;

// Good example
const PI = 3.14159;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Avoid Magic Numbers
&lt;/h2&gt;

&lt;p&gt;Magic numbers are hard-coded values that have no explanation or context. They make the code difficult to read and understand. Instead of using magic numbers, use constants with descriptive names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
function calculateArea(radius) {
  return Math.PI * radius * radius;
}

// Good example
const PI = 3.14159;
function calculateArea(radius) {
  return PI * radius * radius;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Use Functions to Reduce Code Duplication
&lt;/h2&gt;

&lt;p&gt;Code duplication can make code harder to maintain and update. Instead of duplicating code, create functions that can be reused throughout the program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
const width = 10;
const height = 20;
const area = width * height;
const perimeter = 2 * (width + height);

// Good example

function rectangleArea(width, height) {
  return width * height;
}

function rectanglePerimeter(width, height) {
  return 2 * (width + height);
}

const width = 10;
const height = 20;
const area = rectangleArea(width, height);
const perimeter = rectanglePerimeter(width, height);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Use Arrow Functions for Concise Code
&lt;/h2&gt;

&lt;p&gt;Arrow functions provide a more concise syntax for defining functions. They also help to reduce the amount of code that needs to be written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
const add = function(x, y) {
  return x + y;
}

// Good example
const add = (x, y) =&amp;gt; x + y;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Use Template Literals for String Concatenation
&lt;/h2&gt;

&lt;p&gt;Template literals provide a more concise and readable way to concatenate strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bad example
const firstName = 'John';
const lastName = 'Doe';
const fullName = firstName + ' ' + lastName;

// Good example
const firstName = 'John';
const lastName = 'Doe';
const fullName = `${firstName} ${lastName}`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Exploring the Benefits and Drawbacks of Serverless Computing</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Sun, 19 Feb 2023 19:38:50 +0000</pubDate>
      <link>https://dev.to/phantasm0009/exploring-the-benefits-and-drawbacks-of-serverless-computing-3le6</link>
      <guid>https://dev.to/phantasm0009/exploring-the-benefits-and-drawbacks-of-serverless-computing-3le6</guid>
      <description>&lt;h1&gt;
  
  
  Exploring the Benefits and Drawbacks of Serverless Computing
&lt;/h1&gt;

&lt;p&gt;Serverless computing is a cloud computing model where the cloud provider manages the server infrastructure and automatically allocates resources as needed. In this model, the user does not need to manage or maintain any servers or infrastructure. In this article, we will explore the benefits and drawbacks of serverless computing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Serverless Computing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;One of the primary benefits of serverless computing is scalability. Serverless applications can handle a sudden spike in traffic without any need for manual intervention. The cloud provider automatically allocates resources based on the application's needs, allowing the application to scale up or down quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost-effective
&lt;/h3&gt;

&lt;p&gt;Serverless computing is also cost-effective. In the traditional model, the user needs to pay for the servers regardless of whether the servers are used or not. In serverless computing, the user only pays for the resources used during the execution of the application. This pay-as-you-go model reduces costs and makes serverless computing an attractive option for small and large businesses alike.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced Maintenance
&lt;/h3&gt;

&lt;p&gt;Serverless computing eliminates the need for server maintenance, which saves time and effort. The cloud provider takes care of all the server management, including updates, patches, and security. This frees up the user's time to focus on other tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster Development
&lt;/h3&gt;

&lt;p&gt;Serverless computing can speed up application development. Developers can focus on writing code rather than managing servers and infrastructure. The cloud provider handles all the backend tasks, such as scaling and resource allocation, allowing developers to concentrate on building applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawbacks of Serverless Computing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cold Start
&lt;/h3&gt;

&lt;p&gt;One of the drawbacks of serverless computing is the cold start problem. When a serverless function is triggered for the first time, the cloud provider needs to allocate resources to execute the function, which can cause a delay. This delay is called a cold start, and it can affect the performance of the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limited Control
&lt;/h3&gt;

&lt;p&gt;Serverless computing reduces the user's control over the infrastructure. Since the cloud provider manages the infrastructure, the user cannot customize the server environment as they would in a traditional model. This limited control can be a disadvantage for certain applications that require specific server configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging
&lt;/h3&gt;

&lt;p&gt;Debugging serverless applications can be challenging. In a traditional model, the user can log in to the server and debug the application. In serverless computing, the user needs to rely on logs and metrics provided by the cloud provider. This can make debugging more difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vendor Lock-in
&lt;/h3&gt;

&lt;p&gt;Serverless computing can also result in vendor lock-in. Since the user relies on the cloud provider to manage the infrastructure, it can be challenging to switch to a different provider. This can be a disadvantage if the cloud provider experiences downtime or if the user wants to switch to a provider with better features or pricing.&lt;/p&gt;

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

&lt;p&gt;Serverless computing has many benefits, including scalability, cost-effectiveness, reduced maintenance, and faster development. However, there are also drawbacks to consider, such as the cold start problem, limited control, debugging challenges, and vendor lock-in. When deciding whether to adopt serverless computing, it is essential to weigh the benefits and drawbacks carefully and consider the specific needs of the application.&lt;/p&gt;

&lt;p&gt;Here's an example of how you can implement a serverless function using AWS Lambda in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json

def lambda_handler(event, context):
    # handle event
    return {
        'statusCode': 200,
        'body': json.dumps('Hello, World!')
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function returns a JSON response with a 'Hello, World!' message. You can deploy this function on AWS Lambda and trigger it using an API Gateway or an event source.&lt;/p&gt;

&lt;p&gt;I hope this article helps you understand the benefits and drawbacks of serverless computing and how it can be used to build scalable and cost-effective applications in the cloud.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>How to record videos with puppeter</title>
      <dc:creator>Phantasm0009</dc:creator>
      <pubDate>Mon, 05 Dec 2022 20:40:32 +0000</pubDate>
      <link>https://dev.to/phantasm0009/how-to-record-videos-with-puppeter-2fji</link>
      <guid>https://dev.to/phantasm0009/how-to-record-videos-with-puppeter-2fji</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;p&amp;gt;I gathered everything you need to know about how to record videos with Puppeteer in one place. Enjoy!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  A quickstart
&lt;/h2&gt;

&lt;p&gt;To generate a video with Puppeteer for page, we are going to use the &lt;a href="https://www.npmjs.com/package/puppeteer-screen-recorder"&gt;puppeteer-screen-recorder&lt;/a&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i puppeteer-screen-recorder
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter fullscreen mode



Exit fullscreen mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;br&gt;


&lt;p&gt;Let's start with a simple example by generating a video for loading the landing page of &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; and opening their pricing page:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PuppeteerScreenRecorder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer-screen-recorder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;deviceScaleFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PuppeteerScreenRecorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&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://tailwindcss.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter fullscreen mode



Exit fullscreen mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;br&gt;


&lt;p&gt;I added a simple one-time scroll to make the video interactive. Look at the result, how beautiful it is:&lt;/p&gt;

GIF

&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cVSjRqjj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--FdlJRy8X--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_66%252Cw_880/https://screenshotone.com/blog/how-to-record-videos-with-puppeteer/animated.gif" alt="An animated screenshot" id="animated-0" width="880" height="495"&gt;

&lt;blockquote&gt;
&lt;p&gt;It is not an MP4 video, but GIF for demonstration because dev.to doesn't support direct upload of videos. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can generate videos in AVI, MP4, MOV, or WebM formats. You can use &lt;a href="https://caniuse.com/webm"&gt;WebM format if you target the latest versions of modern browsers, but not everyone supports it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you need to generate GIF (an image format, not video) for animated screenshots, you can use the &lt;a href="https://ffmpeg.org/"&gt;FFmpeg&lt;/a&gt; library to convert MP4 to GIF. Make sure it is installed locally. And then you can upgrade your script to generate GIFs on demand:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PuppeteerScreenRecorder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer-screen-recorder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;deviceScaleFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PuppeteerScreenRecorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&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://tailwindcss.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ffmpeg -i video.mp4 -qscale 0 animated.gif&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter fullscreen mode



Exit fullscreen mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;br&gt;


&lt;p&gt;Imagine how far you can go with animating screenshots. You can record movies and generate interactive mockups. It opens a new level of marketing for you. It is also a new level of debugging and generating reports for developers and QA automation engineers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improve your marketing with animated screenshots
&lt;/h3&gt;

&lt;p&gt;You can generate scrollable screenshots of the sites and apply them in many areas of your marketing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send personalized emails with animated screenshots of the lead site;&lt;/li&gt;
&lt;li&gt;place screenshots of parts of your site on your landing page;&lt;/li&gt;
&lt;li&gt;make your blog posts interactive with scrollable preview screenshots of the sites you share;&lt;/li&gt;
&lt;li&gt;And many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Debug
&lt;/h3&gt;

&lt;p&gt;In complex cases, if you run Puppetteer at production, you might want to debug what happens and record a video.&lt;/p&gt;

&lt;p&gt;Imagine you scrape a site with a complex scenario by navigating through many pages. Something happens, and the script fails to scrape one of the pages.&lt;/p&gt;

&lt;p&gt;Of course, the best way to debug is to reproduce the error locally, but if your environments are different, you can record a video of what happens in the script and review them.&lt;/p&gt;

&lt;p&gt;But please, always synchronize your environments and ensure they are identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate testing reports
&lt;/h3&gt;

&lt;p&gt;One of the most beautiful use cases, for me, is to generate a video on how automated tests are executed and then, if the test fails, attach videos to the report.&lt;br&gt;
It might shorten communication time between the QA team and developers and improve the overall R&amp;amp;D velocity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Interactive mockups and programmatic movies
&lt;/h3&gt;

&lt;p&gt;You can write simple scripts to make your site interactive. Scroll through it. And record the footage with Puppeteer.&lt;/p&gt;

&lt;p&gt;And even more. You can generate cartoons and movies by using JavaScript and CSS! Only your imagination is the limit.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use API to save time and energy
&lt;/h2&gt;

&lt;p&gt;If you don't have time to implement and generate animated screenshots and deal with video streaming infrastructure, you can rely on &lt;a href="https://screenshotone.com/"&gt;ScreenshotOne API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It supports &lt;a href="https://screenshotone.com/docs/animated-screenshots/"&gt;animated (including scrollable) screenshots&lt;/a&gt; of different types, caching based on the world's fastest and most potent CDN (Cloudflare) and blocking ads, cookie banners, and chats.&lt;/p&gt;

&lt;p&gt;In just one API request, you can quickly generate animated screenshots. And if you need more, a variety of options and use cases are covered.&lt;/p&gt;

&lt;p&gt;Grasp how simple it is:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.screenshotone.com/animate?url=https://tailwindcss.com&amp;amp;access_key=&amp;lt;your access key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter fullscreen mode



Exit fullscreen mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;br&gt;


&lt;p&gt;The result might differ a bit in case default options are changed, but as for now, it is similar to what I have shown in the video.&lt;/p&gt;

&lt;p&gt;And the result is the same:&lt;/p&gt;

GIF

&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cVSjRqjj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--FdlJRy8X--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_66%252Cw_880/https://screenshotone.com/blog/how-to-record-videos-with-puppeteer/animated.gif" alt="An animated screenshot" id="animated-1" width="880" height="495"&gt;

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

&lt;p&gt;Don't waste your time and jump on an opportunity. Level up your marketing and improve your R&amp;amp;D communication.&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      &amp;lt;/div&amp;gt;

    &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

</description>
    </item>
  </channel>
</rss>
