<?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: jason rauch</title>
    <description>The latest articles on DEV Community by jason rauch (@jeah84).</description>
    <link>https://dev.to/jeah84</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%2F3887323%2F93f5f9f8-4245-46d0-82e1-3c501268251f.png</url>
      <title>DEV Community: jason rauch</title>
      <link>https://dev.to/jeah84</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeah84"/>
    <language>en</language>
    <item>
      <title>How I added Solana Pay USDC to a SaaS app — a real implementation, not a tutorial</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Sun, 10 May 2026 07:21:13 +0000</pubDate>
      <link>https://dev.to/jeah84/how-i-added-solana-pay-usdc-to-a-saas-app-a-real-implementation-not-a-tutorial-4h4g</link>
      <guid>https://dev.to/jeah84/how-i-added-solana-pay-usdc-to-a-saas-app-a-real-implementation-not-a-tutorial-4h4g</guid>
      <description>&lt;p&gt;Most Solana Pay tutorials are synthetic. Here's the actual code running in production on transpiler.us, a developer tools platform I built solo.&lt;br&gt;
Why Solana Pay instead of Stripe-only&lt;br&gt;
I already had Stripe. But Stripe doesn't work everywhere, and USDC settling instantly with near-zero fees is genuinely better for global users. It wasn't about looking good — it was the right technical choice.&lt;br&gt;
The implementation&lt;br&gt;
The flow: user clicks "Pay with USDC" → backend generates a reference keypair → frontend displays a QR code with the Solana Pay URL → backend polls the RPC endpoint until it sees a confirmed transaction matching the reference → credits are added to the user's account.&lt;br&gt;
No third-party wrapper. I read the Solana Pay spec and built it directly.&lt;br&gt;
[Walk through the key code sections — QR generation, reference keypair, polling loop, transaction verification]&lt;br&gt;
What I'd do differently&lt;br&gt;
Use a dedicated RPC endpoint from day one instead of the public mainnet endpoint. Public RPC rate limits will bite you in production.&lt;br&gt;
The full source is MIT licensed: &lt;a href="https://github.com/Jeah84/devtools-platform" rel="noopener noreferrer"&gt;https://github.com/Jeah84/devtools-platform&lt;/a&gt;&lt;br&gt;
Live platform: &lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;https://transpiler.us&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title># 5 Railway.io Config Mistakes That Silently Break Deployments (And How to Fix Them)</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Tue, 05 May 2026 19:22:34 +0000</pubDate>
      <link>https://dev.to/jeah84/-5-railwayio-config-mistakes-that-silently-break-deployments-and-how-to-fix-them-5c44</link>
      <guid>https://dev.to/jeah84/-5-railwayio-config-mistakes-that-silently-break-deployments-and-how-to-fix-them-5c44</guid>
      <description>&lt;p&gt;If you've used Railway.io for more than a week, you've probably experienced the special frustration of a deployment that &lt;em&gt;looks&lt;/em&gt; like it worked — green checkmark, no errors — but your app is completely unreachable. No traffic. No response. Just silence.&lt;/p&gt;

&lt;p&gt;Most of the time it comes down to a config mistake that Railway doesn't loudly flag. Here are the five that get developers most often, with exact fixes for each.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Hardcoding the PORT
&lt;/h2&gt;

&lt;p&gt;This is the #1 Railway gotcha and it catches almost everyone at least once.&lt;/p&gt;

&lt;p&gt;Railway injects a &lt;code&gt;$PORT&lt;/code&gt; environment variable dynamically at runtime. Your app &lt;strong&gt;must&lt;/strong&gt; read and listen on that port. If you hardcode port 3000 or 8080, your service starts fine but never receives traffic — Railway is sending requests to a port your app isn't listening on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3000"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting PORT as a static variable doesn't help — Railway's injected value overrides it anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed — in your app code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&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;Fixed — in a Dockerfile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Don't do this:&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Do this — let Railway set the port at runtime:&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# And in server.js: app.listen(process.env.PORT)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This applies to every language and framework. Python with Flask, Go with net/http, Ruby with Puma — they all need to bind to &lt;code&gt;process.env.PORT&lt;/code&gt; (or the equivalent env var read in your language).&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Using an Invalid Builder Value
&lt;/h2&gt;

&lt;p&gt;Railway supports four builders: &lt;code&gt;nixpacks&lt;/code&gt;, &lt;code&gt;dockerfile&lt;/code&gt;, &lt;code&gt;heroku&lt;/code&gt;, and &lt;code&gt;railpack&lt;/code&gt;. That's it. If you write anything else — &lt;code&gt;"node"&lt;/code&gt;, &lt;code&gt;"auto"&lt;/code&gt;, &lt;code&gt;"docker"&lt;/code&gt; — Railway either ignores it or fails silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nixpacks"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Railway's newer Metal infrastructure, you're likely moving to &lt;strong&gt;Railpack&lt;/strong&gt; (the successor to Nixpacks). In that case, use a &lt;code&gt;railpack.json&lt;/code&gt; file with the correct &lt;code&gt;providers&lt;/code&gt; array instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.railpack.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"providers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. No Restart Policy — Crashed Services Stay Down
&lt;/h2&gt;

&lt;p&gt;By default, if your service crashes Railway won't automatically restart it. You need to explicitly set &lt;code&gt;restartPolicyType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken (service stays dead after a crash):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"restartPolicyType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ON_FAILURE"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid values are &lt;code&gt;ON_FAILURE&lt;/code&gt;, &lt;code&gt;ALWAYS&lt;/code&gt;, and &lt;code&gt;NEVER&lt;/code&gt;. For most production services you want &lt;code&gt;ON_FAILURE&lt;/code&gt;. Using &lt;code&gt;ALWAYS&lt;/code&gt; means Railway will restart even intentional shutdowns — usually not what you want.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Missing or Broken healthcheckPath
&lt;/h2&gt;

&lt;p&gt;If you define a &lt;code&gt;healthcheckPath&lt;/code&gt;, it must start with a &lt;code&gt;/&lt;/code&gt;. If it doesn't, Railway either rejects the config or the health check never passes — causing your service to restart in a loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"healthcheckPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"health"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"healthcheckPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/health"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also make sure that route actually exists in your app and returns a 200 status. A common mistake is defining &lt;code&gt;/health&lt;/code&gt; in the config but forgetting to add the route handler in the code.&lt;/p&gt;

&lt;p&gt;If you're not ready to implement a health endpoint, remove the &lt;code&gt;healthcheckPath&lt;/code&gt; field entirely rather than leaving it broken — Railway will fall back to basic process monitoring.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. npm install Instead of npm ci in Build Steps
&lt;/h2&gt;

&lt;p&gt;This one is subtle but causes non-deterministic builds — meaning your app works locally and in one deployment but breaks in the next.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt; can silently upgrade packages within the ranges defined in &lt;code&gt;package.json&lt;/code&gt;. &lt;code&gt;npm ci&lt;/code&gt; installs exactly what's in your &lt;code&gt;package-lock.json&lt;/code&gt; — no surprises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken (nixpacks.toml):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[phases.install]&lt;/span&gt;
&lt;span class="py"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"npm install"&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;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[phases.install]&lt;/span&gt;
&lt;span class="py"&gt;cmds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"npm ci"&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;Broken (railpack.json):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"install"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cmds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"npm install"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fixed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"install"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cmds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"npm ci"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same rule applies to Dockerfiles — &lt;code&gt;RUN npm ci&lt;/code&gt; instead of &lt;code&gt;RUN npm install&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Catching These Automatically
&lt;/h2&gt;

&lt;p&gt;These five mistakes are easy to make and annoying to debug because Railway doesn't always give you a clear error message. I got tired of finding them manually so I built &lt;strong&gt;&lt;a href="https://railwaydevtools.com" rel="noopener noreferrer"&gt;Railway DevTools&lt;/a&gt;&lt;/strong&gt; — paste your config and Claude AI audits it instantly, flags issues by severity, and shows you the exact fix.&lt;/p&gt;

&lt;p&gt;It supports railway.json, railway.toml, Dockerfile, nixpacks.toml, railpack.json, and Procfile. Free to try with no sign-up.&lt;/p&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hardcoded PORT&lt;/td&gt;
&lt;td&gt;App unreachable&lt;/td&gt;
&lt;td&gt;Read &lt;code&gt;process.env.PORT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalid builder&lt;/td&gt;
&lt;td&gt;Build fails silently&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;nixpacks&lt;/code&gt;, &lt;code&gt;dockerfile&lt;/code&gt;, or &lt;code&gt;heroku&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No restart policy&lt;/td&gt;
&lt;td&gt;Crashed service stays down&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;restartPolicyType: ON_FAILURE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bad healthcheckPath&lt;/td&gt;
&lt;td&gt;Restart loop&lt;/td&gt;
&lt;td&gt;Start path with &lt;code&gt;/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm install vs npm ci&lt;/td&gt;
&lt;td&gt;Non-deterministic builds&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;npm ci&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you've run into other Railway gotchas that aren't on this list, drop them in the comments — I'll add them to the article and potentially to the validator too.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built an AI That Translates Code Between 27 Languages (Solo, No Budget)</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Tue, 28 Apr 2026 23:15:10 +0000</pubDate>
      <link>https://dev.to/jeah84/how-i-built-an-ai-that-translates-code-between-27-languages-solo-no-budget-1ebp</link>
      <guid>https://dev.to/jeah84/how-i-built-an-ai-that-translates-code-between-27-languages-solo-no-budget-1ebp</guid>
      <description>&lt;p&gt;Every developer has hit this wall.&lt;/p&gt;

&lt;p&gt;You have working code. It does exactly what you need. But it's in the wrong language. Maybe you're migrating a Python service to Go. Maybe a client needs a JavaScript utility rewritten in TypeScript. Maybe you inherited a PHP codebase and the whole team wants out.&lt;/p&gt;

&lt;p&gt;So you copy it into ChatGPT and ask it to translate. And it kind of works — until you look closer and realize the imports are wrong, the error handling is missing, and the output would never survive a code review.&lt;/p&gt;

&lt;p&gt;That frustration is what led me to build &lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;transpiler.us&lt;/a&gt;.&lt;/p&gt;




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

&lt;p&gt;Transpiler.us is an AI-powered code translator built specifically for this problem. Not a general-purpose chatbot with a translation prompt bolted on — a tool built from the ground up to produce production-quality output across 27 programming languages.&lt;/p&gt;

&lt;p&gt;That means real imports. Real error handling. Idiomatic patterns for the target language, not just syntax-swapped source code.&lt;/p&gt;

&lt;p&gt;Beyond translation, the platform includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Code Reviewer&lt;/strong&gt; — structured feedback covering bugs, security, performance, and style&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Integration&lt;/strong&gt; — translate and push directly to any repository via OAuth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON/YAML Formatter&lt;/strong&gt; — validate, format, and convert between formats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex Tester&lt;/strong&gt; — live pattern matching with named group support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Tool&lt;/strong&gt; — &lt;code&gt;code-translator-ai&lt;/code&gt; on npm for terminal and pipeline use&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;I kept it simple because I was building alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React + Vite + TailwindCSS, deployed on Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Express + TypeScript, deployed on Railway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Model:&lt;/strong&gt; Llama 3.3 70B via Together AI API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe (credit packs + $20/month Pro subscription)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; GitHub OAuth for the repo integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI choice was deliberate. I tested several models and Llama 3.3 70B consistently produced the cleanest translations — especially for less common language pairs like Rust to Zig or COBOL to Java.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Goes Into a Good Translation
&lt;/h2&gt;

&lt;p&gt;The naive approach is: send the code to an LLM, return the result. That produces garbage 30% of the time.&lt;/p&gt;

&lt;p&gt;What actually works is being very specific in the prompt about what "good" means in the target language, and structuring the output so it can be parsed reliably. Here's the actual system prompt used in the translator:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;preserveComments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;addExplanations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You are an expert code translator. Translate code from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; accurately.

Rules:
1. Translate faithfully — preserve all logic and behavior
2. Use idiomatic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; patterns
3. Use appropriate &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; standard library equivalents
4. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;preserveComments&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Preserve all comments&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Remove comments unless critical&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
5. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;addExplanations&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add brief inline comments explaining non-obvious translations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Keep output clean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
6. Output ONLY the translated code — no markdown fences, no explanations before or after
7. If something cannot be directly translated, add a comment explaining why

After the code, on a new line write "NOTES:" followed by any important notes.
After that, write "WARNINGS:" followed by any potential issues.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the response parser that extracts the code, notes, and warnings cleanly:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TranslationResult&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;notesSplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^NOTES:/m&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;codeSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesSplit&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notesSplit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&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;warnSplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesSplit&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^WARNINGS:/m&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;notesText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warnSplit&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="nf"&gt;trim&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;warningsText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warnSplit&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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;notesText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;notesText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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="nx"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-*•&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;warningsText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;warningsText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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="nx"&gt;warnings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warningsText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-*•&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeSection&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;/m, ""&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="err"&gt;?
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```$/m, "")
    .trim();

  return { translatedCode: cleaned, notes, warnings };
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model runs at &lt;code&gt;temperature: 0.1&lt;/code&gt; — low creativity, high consistency. The structured NOTES/WARNINGS format means the UI can surface important information separately from the code itself without any guesswork.&lt;/p&gt;

&lt;p&gt;The difference between a naive "translate this" prompt and this approach is enormous. Output goes from "this kind of works" to "I'd actually commit this."&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the CLI
&lt;/h2&gt;

&lt;p&gt;One of the best decisions I made was publishing a CLI alongside the web app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; code-translator-ai

translate file app.py &lt;span class="nt"&gt;--from&lt;/span&gt; python &lt;span class="nt"&gt;--to&lt;/span&gt; go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers spend most of their time in the terminal. A CLI means the tool fits into their existing workflow instead of requiring a context switch to a browser. It also drives organic discovery through npm and developer communities.&lt;/p&gt;

&lt;p&gt;The CLI supports batch translation of entire directories, code review, JSON/YAML formatting, and regex testing — the full feature set, accessible from the terminal or piped into a build script.&lt;/p&gt;

&lt;p&gt;I licensed it MIT with Commons Clause, which means free to use and modify but not to resell as a commercial service. That keeps it open for developers while protecting the commercial side of the platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  The GitHub Integration
&lt;/h2&gt;

&lt;p&gt;This was the feature that took the most work but made the biggest difference in how the product felt.&lt;/p&gt;

&lt;p&gt;Without GitHub integration, the workflow is: translate on the web → copy the output → switch to your editor → paste → commit. That's four steps where one would do.&lt;/p&gt;

&lt;p&gt;With it: translate → select your repo and branch → push. Done.&lt;/p&gt;

&lt;p&gt;Getting the OAuth flow right, handling different repo structures, and making the push feel instant took real effort. But developers who try it don't want to go back to copying and pasting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Revenue Model
&lt;/h2&gt;

&lt;p&gt;Three tiers, all live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt; — 10 translations to try it, no card required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit packs&lt;/strong&gt; — one-time purchases for casual users (Starter, Builder, Power)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro&lt;/strong&gt; — $20/month for unlimited access to everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The free tier exists to get people in the door. The credit packs serve developers who need translation occasionally. Pro is for teams and power users who are using it regularly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with one language pair.&lt;/strong&gt; I built support for 27 languages on launch. In hindsight, Python → TypeScript alone would have been enough to validate the idea, and I could have added languages based on what users actually asked for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship the CLI earlier.&lt;/strong&gt; The npm package drives more genuine interest than anything else I've done for distribution. Developers trust tools that live in their terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't underestimate the prompt engineering.&lt;/strong&gt; The quality difference between a naive prompt and a carefully engineered one is significant. I spent more time on prompts than on any other single part of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The individual developer use case is the wedge. The real opportunity is enterprise code modernization — companies running legacy COBOL, PHP, or Python 2 codebases that need to migrate at scale.&lt;/p&gt;

&lt;p&gt;On the roadmap: VS Code extension, team plans, CI/CD integration, and a validation engine that compile-checks translated output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If you've ever spent an afternoon manually porting code from one language to another, transpiler.us is built for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web app: &lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;transpiler.us&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CLI: &lt;code&gt;npm install -g code-translator-ai&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Jeah84/transpiler-app" rel="noopener noreferrer"&gt;Jeah84/transpiler-app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free tier includes 10 translations — no card required. Break it, tell me what's wrong, and I'll fix it.&lt;/p&gt;

&lt;p&gt;Building in public. Happy to answer anything in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How I Built an AI That Translates Code Between 27 Languages (Solo, No Budget)</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Fri, 24 Apr 2026 22:16:49 +0000</pubDate>
      <link>https://dev.to/jeah84/how-i-built-an-ai-that-translates-code-between-27-languages-solo-no-budget-en9</link>
      <guid>https://dev.to/jeah84/how-i-built-an-ai-that-translates-code-between-27-languages-solo-no-budget-en9</guid>
      <description>&lt;p&gt;Every developer has hit this wall.&lt;/p&gt;

&lt;p&gt;You have working code. It does exactly what you need. But it's in the wrong language. Maybe you're migrating a Python service to Go. Maybe a client needs a JavaScript utility rewritten in TypeScript. Maybe you inherited a PHP codebase and the whole team wants out.&lt;/p&gt;

&lt;p&gt;So you copy it into ChatGPT and ask it to translate. And it kind of works — until you look closer and realize the imports are wrong, the error handling is missing, and the output would never survive a code review.&lt;/p&gt;

&lt;p&gt;That frustration is what led me to build &lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;transpiler.us&lt;/a&gt;.&lt;/p&gt;




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

&lt;p&gt;Transpiler.us is an AI-powered code translator built specifically for this problem. Not a general-purpose chatbot with a translation prompt bolted on — a tool built from the ground up to produce production-quality output across 27 programming languages.&lt;/p&gt;

&lt;p&gt;That means real imports. Real error handling. Idiomatic patterns for the target language, not just syntax-swapped source code.&lt;/p&gt;

&lt;p&gt;Beyond translation, the platform includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Code Reviewer&lt;/strong&gt; — structured feedback covering bugs, security, performance, and style&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Integration&lt;/strong&gt; — translate and push directly to any repository via OAuth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON/YAML Formatter&lt;/strong&gt; — validate, format, and convert between formats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex Tester&lt;/strong&gt; — live pattern matching with named group support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Tool&lt;/strong&gt; — &lt;code&gt;code-translator-ai&lt;/code&gt; on npm for terminal and pipeline use&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;I kept it simple because I was building alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React + Vite + TailwindCSS, deployed on Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Express + TypeScript, deployed on Railway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Model:&lt;/strong&gt; Llama 3.3 70B via Together AI API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe (credit packs + $20/month Pro subscription)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; GitHub OAuth for the repo integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI choice was deliberate. I tested several models and Llama 3.3 70B consistently produced the cleanest translations — especially for less common language pairs like Rust to Zig or COBOL to Java.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Goes Into a Good Translation
&lt;/h2&gt;

&lt;p&gt;The naive approach is: send the code to an LLM, return the result. That produces garbage 30% of the time.&lt;/p&gt;

&lt;p&gt;What actually works is being very specific in the prompt about what "good" means in the target language, and structuring the output so it can be parsed reliably. Here's the actual system prompt used in the translator:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;preserveComments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;addExplanations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You are an expert code translator. Translate code from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; accurately.

Rules:
1. Translate faithfully — preserve all logic and behavior
2. Use idiomatic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; patterns
3. Use appropriate &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; standard library equivalents
4. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;preserveComments&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Preserve all comments&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Remove comments unless critical&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
5. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;addExplanations&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add brief inline comments explaining non-obvious translations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Keep output clean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
6. Output ONLY the translated code — no markdown fences, no explanations before or after
7. If something cannot be directly translated, add a comment explaining why

After the code, on a new line write "NOTES:" followed by any important notes.
After that, write "WARNINGS:" followed by any potential issues.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the response parser that extracts the code, notes, and warnings cleanly:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TranslationResult&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;notesSplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^NOTES:/m&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;codeSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesSplit&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notesSplit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&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;warnSplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesSplit&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^WARNINGS:/m&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;notesText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warnSplit&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="nf"&gt;trim&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;warningsText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warnSplit&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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;notesText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;notesText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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="nx"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notesText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-*•&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;warningsText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;warningsText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&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="nx"&gt;warnings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;warningsText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-*•&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeSection&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;/m, ""&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="err"&gt;?
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```$/m, "")
    .trim();

  return { translatedCode: cleaned, notes, warnings };
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model runs at &lt;code&gt;temperature: 0.1&lt;/code&gt; — low creativity, high consistency. The structured NOTES/WARNINGS format means the UI can surface important information separately from the code itself without any guesswork.&lt;/p&gt;

&lt;p&gt;The difference between a naive "translate this" prompt and this approach is enormous. Output goes from "this kind of works" to "I'd actually commit this."&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the CLI
&lt;/h2&gt;

&lt;p&gt;One of the best decisions I made was publishing a CLI alongside the web app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; code-translator-ai

translate file app.py &lt;span class="nt"&gt;--from&lt;/span&gt; python &lt;span class="nt"&gt;--to&lt;/span&gt; go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers spend most of their time in the terminal. A CLI means the tool fits into their existing workflow instead of requiring a context switch to a browser. It also drives organic discovery through npm and developer communities.&lt;/p&gt;

&lt;p&gt;The CLI supports batch translation of entire directories, code review, JSON/YAML formatting, and regex testing — the full feature set, accessible from the terminal or piped into a build script.&lt;/p&gt;

&lt;p&gt;I licensed it MIT with Commons Clause, which means free to use and modify but not to resell as a commercial service. That keeps it open for developers while protecting the commercial side of the platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  The GitHub Integration
&lt;/h2&gt;

&lt;p&gt;This was the feature that took the most work but made the biggest difference in how the product felt.&lt;/p&gt;

&lt;p&gt;Without GitHub integration, the workflow is: translate on the web → copy the output → switch to your editor → paste → commit. That's four steps where one would do.&lt;/p&gt;

&lt;p&gt;With it: translate → select your repo and branch → push. Done.&lt;/p&gt;

&lt;p&gt;Getting the OAuth flow right, handling different repo structures, and making the push feel instant took real effort. But developers who try it don't want to go back to copying and pasting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Revenue Model
&lt;/h2&gt;

&lt;p&gt;Three tiers, all live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt; — 10 translations to try it, no card required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit packs&lt;/strong&gt; — one-time purchases for casual users (Starter, Builder, Power)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro&lt;/strong&gt; — $20/month for unlimited access to everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The free tier exists to get people in the door. The credit packs serve developers who need translation occasionally. Pro is for teams and power users who are using it regularly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with one language pair.&lt;/strong&gt; I built support for 27 languages on launch. In hindsight, Python → TypeScript alone would have been enough to validate the idea, and I could have added languages based on what users actually asked for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship the CLI earlier.&lt;/strong&gt; The npm package drives more genuine interest than anything else I've done for distribution. Developers trust tools that live in their terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't underestimate the prompt engineering.&lt;/strong&gt; The quality difference between a naive prompt and a carefully engineered one is significant. I spent more time on prompts than on any other single part of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The individual developer use case is the wedge. The real opportunity is enterprise code modernization — companies running legacy COBOL, PHP, or Python 2 codebases that need to migrate at scale.&lt;/p&gt;

&lt;p&gt;On the roadmap: VS Code extension, team plans, CI/CD integration, and a validation engine that compile-checks translated output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If you've ever spent an afternoon manually porting code from one language to another, transpiler.us is built for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web app: &lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;transpiler.us&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CLI: &lt;code&gt;npm install -g code-translator-ai&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Jeah84/transpiler-app" rel="noopener noreferrer"&gt;Jeah84/transpiler-app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free tier includes 10 translations — no card required. Break it, tell me what's wrong, and I'll fix it.&lt;/p&gt;

&lt;p&gt;Building in public. Happy to answer anything in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>I Built an AI Customer Service Platform You Can Deploy in One Click</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Fri, 24 Apr 2026 01:18:04 +0000</pubDate>
      <link>https://dev.to/jeah84/i-built-an-ai-customer-service-platform-you-can-deploy-in-one-click-lc7</link>
      <guid>https://dev.to/jeah84/i-built-an-ai-customer-service-platform-you-can-deploy-in-one-click-lc7</guid>
      <description>&lt;h2&gt;
  
  
  I Built an AI Customer Service Platform You Can Deploy in One Click 🤖
&lt;/h2&gt;

&lt;p&gt;After spending weeks building customer service bots for different projects, I kept rebuilding the same infrastructure: database setup, Redis caching, AI integration, sentiment analysis, escalation logic...&lt;/p&gt;

&lt;p&gt;So I packaged it all into a &lt;strong&gt;one-click deployable template&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Is
&lt;/h2&gt;

&lt;p&gt;An open-source, production-ready AI customer service platform that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💬 &lt;strong&gt;Multi-channel support&lt;/strong&gt; - Chat, email, and SMS (via Twilio)&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Claude AI integration&lt;/strong&gt; - Intelligent, context-aware responses&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Sentiment analysis&lt;/strong&gt; - Detects frustrated customers automatically&lt;/li&gt;
&lt;li&gt;🚨 &lt;strong&gt;Smart escalation&lt;/strong&gt; - Knows when to hand off to humans&lt;/li&gt;
&lt;li&gt;💾 &lt;strong&gt;Full conversation history&lt;/strong&gt; - PostgreSQL database with analytics&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Redis caching&lt;/strong&gt; - Fast response times at scale&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;Real-time WebSockets&lt;/strong&gt; - Live updates via Socket.io&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;Most AI customer service solutions are either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise-only&lt;/strong&gt; (expensive, complex)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code-heavy&lt;/strong&gt; (requires weeks of setup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closed-source&lt;/strong&gt; (can't customize)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted something that &lt;strong&gt;just works&lt;/strong&gt; - deploy it, add your API key, and you're handling customer support with AI in minutes.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Core dependencies&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Claude&lt;/span&gt; &lt;span class="nc"&gt;AI &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;brain&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;PostgreSQL&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Conversation&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Session&lt;/span&gt; &lt;span class="nx"&gt;caching&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="nx"&gt;connections&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Runtime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Features I'm Proud Of
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Intelligent Escalation
&lt;/h3&gt;

&lt;p&gt;The bot doesn't just blindly respond. It analyzes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer sentiment (positive/negative/neutral)&lt;/li&gt;
&lt;li&gt;Message intent (question/complaint/request)&lt;/li&gt;
&lt;li&gt;Conversation complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it detects frustration or confusion, it &lt;strong&gt;automatically&lt;/strong&gt; suggests human escalation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Channel Support
&lt;/h3&gt;

&lt;p&gt;Same conversation, different channels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Customer starts on chat&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;conversations&lt;/span&gt;

&lt;span class="c1"&gt;// Switches to email&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;conversations&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;

&lt;span class="c1"&gt;// Bot maintains context across channels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Built-in Knowledge Base
&lt;/h3&gt;

&lt;p&gt;Feed it your docs, FAQs, product info - it'll reference them in responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kbArticles&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;aiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchKnowledgeBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;aiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;kbArticles&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One-Click Deploy
&lt;/h2&gt;

&lt;p&gt;The entire thing deploys to Railway in &lt;strong&gt;literally 60 seconds&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://railway.com/deploy/ddWbPN" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frailway.app%2Fbutton.svg" alt="Deploy on Railway" width="183" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the button&lt;/li&gt;
&lt;li&gt;Add your Anthropic API key&lt;/li&gt;
&lt;li&gt;Done. PostgreSQL and Redis auto-configure.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Check it out running live: &lt;a href="https://ai-customer-service-agent-production.up.railway.app" rel="noopener noreferrer"&gt;ai-customer-service-agent-production.up.railway.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/health&lt;/code&gt; endpoint shows all services connected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-23T00:34:08.719Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ai"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Endpoints
&lt;/h2&gt;

&lt;p&gt;Once deployed, you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET  /health                          &lt;span class="c"&gt;# Health check&lt;/span&gt;
POST /api/customers                   &lt;span class="c"&gt;# Create/get customer&lt;/span&gt;
POST /api/conversations               &lt;span class="c"&gt;# Start conversation&lt;/span&gt;
POST /api/conversations/:id/messages  &lt;span class="c"&gt;# Send message&lt;/span&gt;
GET  /api/conversations               &lt;span class="c"&gt;# List conversations&lt;/span&gt;
POST /api/conversations/:id/escalate  &lt;span class="c"&gt;# Escalate to human&lt;/span&gt;
GET  /api/dashboard                   &lt;span class="c"&gt;# Analytics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How AI Responses Work
&lt;/h2&gt;

&lt;p&gt;Here's the flow when a customer sends a message:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Search knowledge base&lt;/strong&gt; for relevant articles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyze sentiment&lt;/strong&gt; of customer message&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract intent&lt;/strong&gt; (question/issue/request)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate response&lt;/strong&gt; using Claude with context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check escalation&lt;/strong&gt; - does this need a human?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save everything&lt;/strong&gt; to PostgreSQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broadcast via WebSocket&lt;/strong&gt; for real-time updates
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiResponse&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;aiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;messageHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;knowledgeBaseArticles&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;aiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;needsEscalation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;escalateToHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Voice support (Twilio Voice API)&lt;/li&gt;
&lt;li&gt;[ ] Multi-language detection&lt;/li&gt;
&lt;li&gt;[ ] Custom AI training on conversation history&lt;/li&gt;
&lt;li&gt;[ ] Slack integration&lt;/li&gt;
&lt;li&gt;[ ] API rate limiting per customer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Jeah84/ai-customer-service-agent" rel="noopener noreferrer"&gt;github.com/Jeah84/ai-customer-service-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy:&lt;/strong&gt; &lt;a href="https://railway.com/deploy/ddWbPN" rel="noopener noreferrer"&gt;railway.com/deploy/ddWbPN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Node.js, Claude AI, PostgreSQL, Redis, Socket.io&lt;/p&gt;




&lt;p&gt;Built this because I needed it for my own projects. Figured others might too.&lt;/p&gt;

&lt;p&gt;What features would you add? Drop a comment! 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Also submitted this as a Railway Template - hoping to help more developers ship AI-powered support faster.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>customerservice</category>
      <category>claude</category>
    </item>
    <item>
      <title>How I turned a code translator into a full dev toolkit — GitHub push, code review, and more</title>
      <dc:creator>jason rauch</dc:creator>
      <pubDate>Sun, 19 Apr 2026 12:11:07 +0000</pubDate>
      <link>https://dev.to/jeah84/how-i-turned-a-code-translator-into-a-full-dev-toolkit-github-push-code-review-and-more-511b</link>
      <guid>https://dev.to/jeah84/how-i-turned-a-code-translator-into-a-full-dev-toolkit-github-push-code-review-and-more-511b</guid>
      <description>&lt;p&gt;A while back I built transpiler.us — an AI tool for translating code between 27 programming languages. The core use case was simple: you have working code in Python, you need it in Go.&lt;/p&gt;

&lt;p&gt;Since then I've kept building. Here's what's been added and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Integration
&lt;/h2&gt;

&lt;p&gt;The most-requested feature. You connect your GitHub account via OAuth and after translating, you can push the output directly to any repo — pick the branch, set the file path, commit message, and push. No copy-paste, no local git required.&lt;/p&gt;

&lt;p&gt;This was the most technically interesting piece. Managing OAuth token refresh, handling repos with existing files vs new files, and making the UX feel simple on top of what's actually a multi-step API flow was a good challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Code Reviewer
&lt;/h2&gt;

&lt;p&gt;Paste any code and get back a structured review: bugs, security issues, performance improvements, and general suggestions. Llama 3.3 70B handles this surprisingly well.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON/YAML Formatter
&lt;/h2&gt;

&lt;p&gt;Validate, format, and convert between JSON and YAML. I use this one constantly myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regex Tester
&lt;/h2&gt;

&lt;p&gt;Write regex patterns and see live match highlighting as you type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editor Customization
&lt;/h2&gt;

&lt;p&gt;Choose your theme (VS Code Dark, GitHub Dark, Monokai, Dracula and more), font size, line numbers, and word wrap.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Frontend: React + Vite + TailwindCSS on Vercel&lt;/li&gt;
&lt;li&gt;Backend: Express + TypeScript on Railway&lt;/li&gt;
&lt;li&gt;AI: Llama 3.3 70B via Together.ai&lt;/li&gt;
&lt;li&gt;Auth: JWT + email verification&lt;/li&gt;
&lt;li&gt;Payments: Stripe&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CLI Tool
&lt;/h2&gt;

&lt;p&gt;Also published as code-translator-ai on npm if you want terminal access:&lt;/p&gt;

&lt;p&gt;npm install -g code-translator-ai&lt;br&gt;
translate file script.py --from python --to javascript&lt;br&gt;
translate review myfile.ts&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Free tier included — no credit card needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://transpiler.us" rel="noopener noreferrer"&gt;https://transpiler.us&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback — what would you build next?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>github</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
