<?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: Joseph Hoppe</title>
    <description>The latest articles on DEV Community by Joseph Hoppe (@joehoppe).</description>
    <link>https://dev.to/joehoppe</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%2F1368723%2F477b2c36-dc1f-4e1e-ae53-0b9376080ce4.jpg</url>
      <title>DEV Community: Joseph Hoppe</title>
      <link>https://dev.to/joehoppe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joehoppe"/>
    <language>en</language>
    <item>
      <title>When you’re scheduled to go on your first international business trip, to India, on February 29, 2020 - My experience</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Thu, 22 Jan 2026 22:33:12 +0000</pubDate>
      <link>https://dev.to/joehoppe/when-youre-scheduled-to-go-on-your-first-international-business-trip-to-india-on-february-29-4i7j</link>
      <guid>https://dev.to/joehoppe/when-youre-scheduled-to-go-on-your-first-international-business-trip-to-india-on-february-29-4i7j</guid>
      <description>&lt;p&gt;Twelve days before the pandemic was declared, I boarded a plane to India — excited, nervous, and unaware that the world was about to change. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start with some level setting
&lt;/h2&gt;

&lt;p&gt;At the time, I did not do much business travel. I went on a domestic business trip maybe: once a year - most commonly for internal, company wide events.  &lt;/p&gt;

&lt;p&gt;Then in 2019, my boss suggested that we take a trip out of New Jersey, USA, to New Delhi, India, (Noida, to be specific) to continue to build rapport with the team of consultants that I was managing. We started to discuss the trip. But after a couple months, he concluded that he didn’t have the availability to go that winter. &lt;strong&gt;He suggested I take my first trip to India, or Asia for that matter, on my own&lt;/strong&gt; before malaria season came around. &lt;/p&gt;

&lt;p&gt;Around that time, my wife, a registered nurse, and I were tracking an illness in the news that was popping up in China and California, called COVID-19. Luckily it wasn’t reported around the NY Metropolitan area, or New Delhi, India… yet. But it was still a bit concerning.&lt;/p&gt;

&lt;p&gt;I was finally able to book &lt;strong&gt;my departure flight for… February 29th, 2020&lt;/strong&gt;, out of JFK International Airport, New York. Unfortunately none of my coworkers were able to join me.&lt;/p&gt;

&lt;p&gt;Revisiting the dates of the pandemic, &lt;strong&gt;the flight was only 12 days before the WHO would declare a pandemic&lt;/strong&gt;, after over 118,000 cases across 114 countries. My return flight on March 7th, was only 5 days before the declaration.&lt;/p&gt;

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

&lt;p&gt;On a Friday night, my coworker texted me. “Hey Joe, are you watching the news…  Are you still going tomorrow [to India]”? I had been reading the news heavily. With regards to the USA, cases of COVID-19 were sprouting up, but mostly in California. To my knowledge, the east coast had no known cases, and neither did the region of New Delhi. &lt;/p&gt;

&lt;p&gt;Some worries did pop into my head. What if I get sick in India? I don’t know what the health care is like there. Or the costs. What if I get stuck there for a while? I had never even been to Asia before!&lt;/p&gt;

&lt;p&gt;But I continued to check Slack, my email, and text messages. I had zero messages from leadership. The flight alone cost thousands. Surely they had thought of me, and would let me know if they had any misgivings… right? &lt;/p&gt;

&lt;p&gt;I didn’t want to appear weak, or wasteful with the company’s money. And I was excited to go. I love travel and foreign culture.&lt;/p&gt;

&lt;p&gt;I responded to my coworker, “I’m excited for the trip… no messages from leadership to cancel. The virus is still 2,400 miles away. Seems like it’s a go. Wish me luck!!! 😬”&lt;/p&gt;

&lt;p&gt;Early in the morning on February 29th, an Uber dropped me off at JFK.&lt;/p&gt;

&lt;p&gt;I strolled up to the departure gate. When asked for my work visa, I handed over the print out. “I need the other email”, requested the agent. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cringe&lt;/em&gt;. I realized my visa was on a work laptop I didn’t even bring—great start. With a stroke of luck, I was able to retrieve the email remotely, and boarded the flight.&lt;/p&gt;

&lt;p&gt;I was strictly advised that on arrival - your driver’s name is so and so… Do not get in the car with anyone else. &lt;/p&gt;

&lt;p&gt;I thought that I had the comfort of using my cell phone, with no changes, when I arrived in India. But I did have to contact T-Mobile support, after landing, to refresh the profile.&lt;/p&gt;

&lt;p&gt;Moving through the busy NYC airports of JFK and New Delhi were a bit concerning with COVID-19 hitting the headlines more each and every day. New York City’s population is a measly 8 million people compared to the 22 million of New Delhi.&lt;/p&gt;

&lt;p&gt;In addition to some exhaustion from Jetlag, I had a slight anxious sweat from being on the other side of the planet for the first time. My year round allergies continued to leave me with a slightly runny nose. Neither of those helped calm my fears of COVID-19 that week!&lt;/p&gt;

&lt;p&gt;After arriving, the taxi driver took me to my hotel. Being February of 2020, I remember thinking to myself, “I guess he (the driver) doesn’t read the news. Masks aren’t advised”. Masks would later be mandated in the USA, and I would later felt ashamed of my earlier, ignorant position. Only later, to hear the masking efforts so heavily debated.  &lt;/p&gt;

&lt;p&gt;The consulting agency was amazingly hospitable, and it was a once in a lifetime experience!&lt;/p&gt;

&lt;p&gt;There was an awkward moment where my Indian colleagues asked me, “The USA screened you before letting you on the plane, right?” In which I had to reply, “I don’t know what their screening process was before all of this. But to be honest, I don’t know if they did anything additional.” We looked at each other awkwardly, and continued on with our day. &lt;/p&gt;

&lt;p&gt;At one point, they did walk me past the lobby of a nearby Samsung office. They informed me that the building had a special camera to screen the health of employees before they entered the office. We were not approached, and there were no noticeable alarms raised. I said to myself, “I guess I am ok? And my colleagues are as well”? 🤷&lt;/p&gt;

&lt;p&gt;I was amazed to visit Swaminarayan Akshardham. I also had a completely new experience, walking barefoot in the temple Sri Harmandir Sahib. It was an interesting first for me - walking barefoot through a busy, public area, and the pandemic growing daily through the world. &lt;/p&gt;

&lt;p&gt;Through the week, I spent time asking myself, “I wonder what services my health insurance covers being in India?” “What would it be like to be stranded here for some time?” “I wonder what the healthcare is like here”. “Will my wife, 3 and 5 year old children stay healthy back home?” “How much stress will it put on them if I am stranded here?” “Will I get them sick when I return?” “Will I bring illness here to India, or back home to the states?”&lt;/p&gt;

&lt;p&gt;With my children being young, I had negotiated with my family to go for only 1 week, which was disappointing, because I have not made it back since. But it might have been a sign to head home before the illness spread even more. Thank goodness that, as far as my knowledge goes, I didn’t bring illness here or there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should I have asked leadership if I should have canceled my trip?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In hindsight, courage isn’t just about fulfilling your duty — it’s about caring enough to question the risks for everyone involved.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had watched the news. Ogled at the maps of the spread. Read the articles. Calculated the risks.  &lt;/p&gt;

&lt;p&gt;As a manager, I wanted to be strong and confident about my trip. It was my first solo business trip. First international business trip. First trip to India. It was a lot of firsts.&lt;/p&gt;

&lt;p&gt;I did not want to waste thousands of the company’s dollars by cancelling the trip.&lt;/p&gt;

&lt;p&gt;To ask the question to leadership would show weakness.&lt;/p&gt;

&lt;p&gt;In hindsight, I believe that &lt;strong&gt;the best approach would have been to start the discussion with my leadership by showing concern for the welfare of the consultants in India&lt;/strong&gt;. The welfare of the consulting agency and its employees was important! &lt;/p&gt;

&lt;p&gt;It would have also deflected the conversation from my willingness to go by framing the conversation around a different important topic. &lt;/p&gt;

&lt;p&gt;Were the consultants concerned that I would bring the virus with me? Did any of them have health risks, which would cause the exposure to be extra concerning?&lt;/p&gt;

&lt;p&gt;Given the opportunity again, this is the more selfless approach that I would have used to start the conversation, for their welfare and my own self interests. &lt;/p&gt;

&lt;p&gt;On February 29th, 2020, no one knew how the pandemic would explode into a multi-year tragedy.&lt;/p&gt;

&lt;p&gt;I am forever grateful to have had this opportunity of a lifetime. I am not aware of anyone directly falling ill around the time of my trip, and sincerely hope that that is the case.&lt;/p&gt;

&lt;p&gt;If any of my colleagues in India do read this, I apologize for putting you all at risk. Thank you for being such great hosts!&lt;/p&gt;

&lt;p&gt;Hindsight is… 2020.&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>How will AI affect the cost of software development in 2026?</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Fri, 09 Jan 2026 22:22:12 +0000</pubDate>
      <link>https://dev.to/joehoppe/how-will-ai-affect-the-cost-of-software-development-in-2026-a22</link>
      <guid>https://dev.to/joehoppe/how-will-ai-affect-the-cost-of-software-development-in-2026-a22</guid>
      <description>&lt;p&gt;Let's say your software engineer earns $155,000/year.&lt;/p&gt;

&lt;p&gt;If AI gives him or her 20% productivity gains - &lt;/p&gt;

&lt;p&gt;And you spend $1k in AI tools per developer - &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You may argue that he is now worth 20% less&lt;/strong&gt;, $124,000/year. Yes, his benefits package may be 30% of his salary. But there is also value in having full-time employees on staff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Costs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you factor in the cost of AI, that may raise your spend from $124k to $125k.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As the technologies improve and economies of scale kick in, the cost of AI tools may decrease.&lt;/p&gt;

&lt;p&gt;But with the current trends in the factors below, &lt;strong&gt;I only see the cost of AI tooling increasing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI tool training&lt;/li&gt;
&lt;li&gt;Vendor lock-in&lt;/li&gt;
&lt;li&gt;Rising compute, hardware, and energy costs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What do we do with newfound efficiency?
&lt;/h2&gt;

&lt;p&gt;Let's not forget that in a capitalist society, &lt;strong&gt;society doesn't truly reap productivity gains&lt;/strong&gt;. It instead &lt;strong&gt;aggressively implements them to compete with each other&lt;/strong&gt;. We do not lazily enjoy our new efficiencies.&lt;/p&gt;

&lt;p&gt;If the costs rise from $1k per developer to $10k, that increases your cost from $125k to $135k.&lt;/p&gt;

&lt;p&gt;In this post, we decreased the cost of software development from $155k to $135k.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supply of Developers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What we've really done is shift revenue from software developers to AI tooling... which requires new jobs, including software developers, to build them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're also scaring the youth away from learning software development, &lt;strong&gt;increasing scarcity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s not get into politics - But in the USA: the recent changes in H1-B Visas, and &lt;strong&gt;anti-immigration policies may slowly decrease the number of developers in the country&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thirst for Software Development
&lt;/h2&gt;

&lt;p&gt;We’ve had a seemingly endless thirst for software development. Tickets in the backlog are often never gotten to. Tech debt does not get addressed. Aside from investment money being redirected to other technologies, &lt;strong&gt;will the demand for software development ever decrease?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Outsourcing, offshoring, near-shoring, remote work... It's always been an interesting time to be a software engineer! &lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>Neural Networks: Developing a Really Simple Neuron in TypeScript</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Wed, 23 Jul 2025 00:18:05 +0000</pubDate>
      <link>https://dev.to/joehoppe/neural-networks-developing-a-really-simple-neuron-in-typescript-4fc1</link>
      <guid>https://dev.to/joehoppe/neural-networks-developing-a-really-simple-neuron-in-typescript-4fc1</guid>
      <description>&lt;p&gt;Neural networks, at their core, are complex systems built from simple mathematical units called neurons (or perceptrons).&lt;/p&gt;

&lt;p&gt;Today, we will implement a very simple neuron in TypeScript.&lt;/p&gt;

&lt;p&gt;For context, a neural network is composed of one or more layers. Each is composed of one or more neurons. But we’ll save neural network layers for another blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Simple Neuron / Perceptron
&lt;/h2&gt;

&lt;p&gt;The simplest form of a neuron is the &lt;em&gt;perceptron&lt;/em&gt;. Let’s create a perceptron service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PerceptronService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;activationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BinaryStepService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;inputSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nx"&gt;inputSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&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;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Inputs, Weights, Biases, and Random Initialization
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inputs:&lt;/strong&gt; A neuron takes one or more inputs. Each input will be evaluated against a corresponding, preconfigured weight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weights:&lt;/strong&gt; Weights determine how much influence each input has on the neuron's output. In a future post, we will &lt;em&gt;train&lt;/em&gt; the network. Training is accomplished by tweaking (updating) the weights to improve the responses that the neural network generates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random Initialization:&lt;/strong&gt; We want to start in a state in which the model produces &lt;em&gt;seemingly random&lt;/em&gt; responses. As it is trained, each weight may be updated to “teach” the model to provide higher quality responses. To produce this initial randomness, it is recommended to initialize the model with random weights — in this case with values between -1 and 1.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bias:&lt;/strong&gt; Many neurons use a bias, instead of a threshold. For simplicity, we will skip biases today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Activation Functions: Binary Step Activation
&lt;/h2&gt;

&lt;p&gt;A neural network needs to determine if a neuron should fire, also known as &lt;strong&gt;activation&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
There are many different types of activation functions. One of the simplest activation functions is the &lt;strong&gt;binary step function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This activation function works by comparing the input value against a threshold. If the input is greater than (exceeds) the threshold number, the neuron is activated, which we will indicate by returning the number &lt;code&gt;1&lt;/code&gt;. Otherwise, it outputs &lt;code&gt;0&lt;/code&gt; for “not activated.”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BinaryStepService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold allows us to control the sensitivity of the neuron. A lower threshold makes the neuron more likely to activate, while a higher threshold makes it more selective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Predictions — What the Neuron Does with Each Input and Weight
&lt;/h2&gt;

&lt;p&gt;To evaluate if the neuron should fire, each input and weight is evaluated.&lt;/p&gt;

&lt;p&gt;The standard neuron computation process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dot Product Calculation:&lt;/strong&gt; Apply a mathematical function to the weights and inputs — see &lt;em&gt;Dot Product - The Core of the Computation&lt;/em&gt; below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activation:&lt;/strong&gt; We take the value returned by the dot product calculation. If this value exceeds the threshold, the activation function returns &lt;code&gt;1&lt;/code&gt; to indicate that the neuron is activated.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Weights not set or initialized&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Input features length (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) does not match weights length (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;dotProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotProduct&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;
  
  
  Dot Product — The Core of the Computation
&lt;/h2&gt;

&lt;p&gt;Earlier, we assigned a weight to each input. Now, we multiply each weight by its corresponding input and sum up the results into a single number. This calculation is a formula from linear algebra called the &lt;strong&gt;dot product&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every neuron will run this calculation. A neural network may customize this method or use an existing math package to optimize its performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Vectors must have the same length&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;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Weight Management
&lt;/h2&gt;

&lt;p&gt;These getters and setters allow external training algorithms to get the neuron’s current weights, and update the weights with their learnings. The getter returns a copy of the weights, so that they are not accidentally modified.&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="nf"&gt;setWeights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;getWeights&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&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;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here is the full code with a couple of example usages for this simple neuron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BinaryStepService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PerceptronService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;activationService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BinaryStepService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;inputSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nx"&gt;inputSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&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;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setWeights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getWeights&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Weights not set or initialized&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Input features length (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) does not match weights length (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;dotProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFeatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Vectors must have the same length&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;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Neuron Does Not Fire / Activate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Do not activate the neuron unless the prediction exceeds 0.7&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BinaryStepService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a perceptron with 2 inputs and randomize the weights&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;neuron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PerceptronService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activation&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="c1"&gt;// Set specific weights for each input&lt;/span&gt;
&lt;span class="c1"&gt;// This is typically done after we have started to train the model&lt;/span&gt;
&lt;span class="c1"&gt;// But let's set specific values so that we can see the math&lt;/span&gt;
&lt;span class="nx"&gt;neuron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setWeights&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.5&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="c1"&gt;// Make a prediction with input [0.8, 0.4]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;neuron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Neuron output: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Neuron output: 0 (is not activated)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input 1: 0.8 × Weight 1: 0.5 = 0.4&lt;/li&gt;
&lt;li&gt;Input 2: 0.4 × Weight 2: 0 = 0&lt;/li&gt;
&lt;li&gt;Sum = 0.4 + 0 = 0.4 &amp;lt; threshold 0.7 → no activation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: The Same Neuron Fires After its Weights have been Updated ("Training")
&lt;/h3&gt;

&lt;p&gt;Let's say that training indicates the neuron should fire for these same inputs. Training updates the weights, and runs the calculation to see if the same inputs now activate the neuron. We will manually set the weights here, but a real neural network would use a method like backpropagation to update weights automatically.&lt;/p&gt;

&lt;p&gt;Append this code to the script above:&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;// Update the weights to new values - increase the second weight from 0 to 0.9&lt;/span&gt;
&lt;span class="nx"&gt;neuron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setWeights&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Make a prediction with the same input [0.8, 0.4]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;neuron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Neuron output: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;output2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Neuron output: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explanation:&lt;br&gt;
0.8 × 0.5 = 0.4&lt;br&gt;
0.4 × 0.9 = 0.36&lt;br&gt;
Sum = 0.76 &amp;gt; threshold 0.7 → neuron fires&lt;/p&gt;

&lt;h2&gt;
  
  
  Why TypeScript — Shouldn’t You Have Used Python?
&lt;/h2&gt;

&lt;p&gt;Yes, Python is the industry standard in artificial intelligence and neural network development. This post is just a quick prototype for TypeScript developers to get started with neural networks.&lt;/p&gt;

&lt;p&gt;I would recommend a language like Python for its memory management, vast data science community, and scientific computing packages (NumPy, TensorFlow, PyTorch, etc.), but TypeScript is great for learning.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>machinelearning</category>
      <category>neuralnetworks</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Universal Language Translator with Python and LangChain</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Sat, 12 Jul 2025 01:53:42 +0000</pubDate>
      <link>https://dev.to/joehoppe/building-a-universal-language-translator-with-python-and-langchain-212h</link>
      <guid>https://dev.to/joehoppe/building-a-universal-language-translator-with-python-and-langchain-212h</guid>
      <description>&lt;p&gt;Want to convert any text into any language? Let’s create a simple Python script to do it!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What We're Building&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our translator will be a Python script that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompts the user to specify their target language
&lt;/li&gt;
&lt;li&gt;Accepts text input for translation
&lt;/li&gt;
&lt;li&gt;Uses OpenAI's GPT model through LangChain to perform the translation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the Artificial Intelligence APIs available today, it is incredibly simple.&lt;/p&gt;

&lt;p&gt;Instead of validating the name of the language that the user inputs, let’s leave it open-ended for some creativity. I.e., we can translate to natural language descriptions such as "Spanish," "French," or "Shakespearean English", or even “Chandler Bing” (from the TV show Friends).&lt;/p&gt;

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

&lt;p&gt;Before we dive in, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3 installed on your system. This was written with 3.13.
&lt;/li&gt;
&lt;li&gt;An OpenAI API key (available at &lt;a href="http://platform.openai.com" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I purchased $5 worth of OpenAI credits to play around with.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting Up Your Environment&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First, let's install the required dependencies. Open your terminal and run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install langchain-openai python-dotenv&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This blog post was written with these specific package versions:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install langchain==0.3.15 python-dotenv==1.0.1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These packages provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;langchain-openai&lt;/code&gt;: Integration between LangChain and OpenAI's models
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;python-dotenv&lt;/code&gt;: Loading the environment variables from a .env file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, create a &lt;code&gt;.env&lt;/code&gt; file in your project directory to store your OpenAI API key securely:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OPENAI\_API\_KEY=your\_api\_key\_here&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;your_api_key_here&lt;/code&gt; with your actual OpenAI API key. This approach keeps sensitive information out of your source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Translation Script&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

load_dotenv()

language = input("Enter the language to translate into: ").strip()
text_to_translate = input("Enter the text to translate: ").strip()

try:
    model = ChatOpenAI(model="gpt-4o-mini")
    messages = [
        SystemMessage("You are a translator. Translate the given text into the specified language. Respond with the translation and no other text."),
        HumanMessage(f"Translate '{text_to_translate}' into {language}"),
    ]
    print(model.invoke(messages).content)
except Exception as e:
    print(f"Translation failed: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Breaking Down the Code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's examine each component of our translator:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Environment Setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;from dotenv import load\_dotenv  &lt;br&gt;
load\_dotenv()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;load_dotenv()&lt;/code&gt; function reads our &lt;code&gt;.env&lt;/code&gt; file and makes the OpenAI API key available to the application without hardcoding sensitive information.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;User Input Collection&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;language \= input("Enter the language to translate into: ").strip()  
text\_to\_translate \= input("Enter the text to translate: ").strip()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We collect two pieces of information from the user: the target language and the text to translate. The &lt;code&gt;.strip()&lt;/code&gt; method removes any leading or trailing whitespace.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Model Initialization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;model \= ChatOpenAI(model="gpt-4o-mini")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There is a whole world of AI models to choose from. Let’s choose &lt;code&gt;gpt-4o-mini&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Message Construction&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;messages \= \[  
        SystemMessage("You are a translator. Translate the given text into the specified language. Respond with the translation and no other text."),  
        HumanMessage(f"Translate '{text\_to\_translate}' into {language}"),  
\]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;LangChain uses a message-based approach. The &lt;code&gt;SystemMessage&lt;/code&gt; provides instructions to the model, while the &lt;code&gt;HumanMessage&lt;/code&gt; contains the actual text to translate.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Translation and Output&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;print(model.invoke(messages).content)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We invoke the model with our messages and print the translated content directly to the console.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Error Handling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The try-except block ensures our application handles errors gracefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try:  
    \# Translation logic  
except Exception as e:  
    print(f"Translation failed: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches various potential issues such as network connectivity problems, API rate limits, or invalid API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running Your Translator&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Save the script as &lt;code&gt;translate.py&lt;/code&gt; and run it from your terminal:&lt;/p&gt;

&lt;p&gt;python3 translate.py&lt;/p&gt;

&lt;p&gt;The script will prompt you for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Target language (e.g., "French," "Japanese," "Italian")
&lt;/li&gt;
&lt;li&gt;Text to translate&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 .\\translate.py**  
Enter the language to translate into:** Spanish  
Enter the text to translate:** Where is the black cat?  
¿Dónde está el gato negro?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 .\\translate.py**  
Enter the language to translate into:** Chandler Bing  
Enter the text to translate**: Did you go to the store today  
Could I \*be\* any more curious if you hit up the store today?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Cost Considerations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The OpenAI API charges based on token usage. The more you run this script, the more of your OpenAI credits you will use! &lt;/p&gt;

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

&lt;p&gt;Building a universal language translator with Python and LangChain demonstrates the power of modern AI tools in solving real-world problems. With just a few lines of code, we've created a flexible translation system that can handle any language pair and provides natural, contextually appropriate translations.&lt;/p&gt;

&lt;p&gt;The simplicity of this approach makes it an excellent starting point for more complex multilingual applications. Whether you're building a customer service tool, a content management system, or a personal productivity app, this foundation can be extended to meet your specific needs.&lt;/p&gt;

&lt;p&gt;And remember - AI doesn’t always get it right!&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://github.com/joehoppe/universal-translator" rel="noopener noreferrer"&gt;my GitHub repository with the Universal Translator script&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openai</category>
      <category>langchain</category>
      <category>python</category>
    </item>
    <item>
      <title>Fixing VPN and VPC Connectivity Issues with T-Mobile Home Internet</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Sat, 07 Jun 2025 13:21:42 +0000</pubDate>
      <link>https://dev.to/joehoppe/fixing-vpn-and-vpc-connectivity-issues-with-t-mobile-home-internet-4gn1</link>
      <guid>https://dev.to/joehoppe/fixing-vpn-and-vpc-connectivity-issues-with-t-mobile-home-internet-4gn1</guid>
      <description>&lt;p&gt;I have been happy with &lt;strong&gt;T-Mobile Home Internet&lt;/strong&gt;. I use it for remote work, and my family often streams a couple of TV shows, all concurrently, without any issues. About two weeks ago, in May 2025, I started encountering &lt;strong&gt;"server took too long to respond"&lt;/strong&gt; errors when accessing remote or cloud resources such as &lt;strong&gt;AWS OpenSearch&lt;/strong&gt; or a &lt;strong&gt;PostGresSQL database&lt;/strong&gt;. After researching online, it appears to be a common error for T-Mobile Home Internet users. Here is a step-by-step guide on how to restore your connectivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nov 18 2025 Update:&lt;/strong&gt; Added Remote PostGresSQL databases as another component that may experience issues, and that this article resolves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem: T-Mobile Home Internet + VPN = Timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; You can establish and maintain a VPN connection. But when accessing cloud resources, you get timeout errors in your browser. When you try to access the same resources over other ISPs, they work smoothly without any issues. &lt;/p&gt;

&lt;p&gt;Connecting over a T-Mobile hotspot worked, maybe, once or twice, but no more than that.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; From what I’m reading, T-Mobile Home Internet uses Carrier-Grade NAT (CGNAT), which adds packet overhead and can interfere with VPN traffic, especially when using standard MTU sizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial Symptoms and Diagnostics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Works vs. What Doesn't&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Working:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPN connection establishes successfully
&lt;/li&gt;
&lt;li&gt;Basic network connectivity through VPN
&lt;/li&gt;
&lt;li&gt;Command-line tools like &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;openssl s_client&lt;/code&gt; can connect
&lt;/li&gt;
&lt;li&gt;Resources accessible when using other ISPs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Not Working:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser requests to web applications result in timeout errors (in this case, with AWS OpenSearch over VPC)
&lt;/li&gt;
&lt;li&gt;Specific error: "server took too long to respond"
&lt;/li&gt;
&lt;li&gt;Interactive dashboards and web interfaces fail to load&lt;/li&gt;
&lt;li&gt;Database queries result in: "The connection attempt failed. SSL error: Read timed out".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Initial Diagnostic Commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before diving into solutions, establish what's working:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Test SSL/TLS connection (this often works)&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;openssl s_client -connect your-resource.amazonaws.com:443&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Test HTTP connection (this usually hangs)&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;curl -I https://your-resource.amazonaws.com/path&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At first, this stumped me. How can I connect to the port, but not get a response from a CURL HTTP request?&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;openssl s_client&lt;/code&gt; works but &lt;code&gt;curl&lt;/code&gt; hangs, it indicates an application-layer timeout issue, not a transport/network connectivity problem.&lt;/p&gt;

&lt;p&gt;**If this is not your issue, or the solution below does not resolve your issue, some suggestions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review your ACL/IAM permissions&lt;/li&gt;
&lt;li&gt;Try different DNS servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Solution: MTU Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the MTU Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maximum Transmission Unit (MTU)&lt;/strong&gt; determines the largest packet size that can be transmitted over a network, without fragmentation. I’m reading that T-Mobile's CGNAT infrastructure adds overhead to packets, causing fragmentation when using standard MTU sizes (1500 bytes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; Reduce the MTU on your VPN tunnel interface to accommodate T-Mobile's packet overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step MTU Fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Identify Your VPN Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After connecting to your VPN&lt;/strong&gt;, find the tunnel interface. We will modify the VPN interface settings directly. This will allow us to connect these cloud resources over VPN, while maintaining the default, optimal setting for most other use cases.&lt;/p&gt;

&lt;p&gt;Searching for &lt;code&gt;tun\&lt;/code&gt; is commonly used to find VPN tunnels:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# On macOS/Linux:&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;ifconfig | grep -A 2 tun&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Look for an interface with your VPN IP, example output:&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;# utun4: flags=8051&amp;lt;UP,POINTOPOINT,RUNNING,MULTICAST&amp;gt; mtu 1500&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;# inet 192.168.123.456 --&amp;gt; 192.168.654.987 netmask 0xfxff0f00&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Temporarily Set MTU&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Replace 'utun4' with your actual interface name&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;sudo ifconfig utun4 mtu 1200&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify the change:&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;ifconfig utun4 | grep mtu&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Test Immediately&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Test your connection right after changing MTU:&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;curl --connect-timeout 30 --max-time 60 -I https://your-resource.amazonaws.com/path&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Test in Browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open your browser and try accessing the resource that was previously timing out!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Find Your Optimal MTU&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am reading that these different networks may require different MTU values, and that these are common values and the context in which to use them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1200&lt;/strong&gt;: Most conservative, works on nearly all networks
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1300&lt;/strong&gt;: Good balance of compatibility and performance
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1380&lt;/strong&gt;: Higher performance if it works on your network
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1400&lt;/strong&gt;: Maximum recommended for problematic networks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My connection has been working well with an MTU setting of 1300, and I will play around with increasing it for kicks.&lt;/p&gt;

&lt;p&gt;You may notice that your ethernet or WiFi network adapters may not support an MTU value lower than 1280. But we can make these changes directly on your VPN interface, which will not impact other network connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Remember to execute this command each time you connect to your VPN (after establishing the connection)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Replace 'utun4' with your actual interface name&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;sudo ifconfig utun4 mtu 1200&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making MTU Changes Permanent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some VPN clients may support configuring your MTU size. Look for an MTU or "Maximum Segment Size" setting in your VPN client's configuration. &lt;/p&gt;

&lt;p&gt;My VPN provider did not easily support this, so I execute this command whenever I need to connect to the troublesome resources. This should last for the whole VPN session:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo ifconfig utun4 mtu 1300&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Happens Without MTU Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;T-Mobile Home uses CGNAT, which creates multiple layers of network address translation.&lt;/p&gt;

&lt;p&gt;Each layer adds packet overhead. The sum of PACKETSIZE + VPN + CGNAT makes the original packet larger than the MTU size (1500). The packets then need to be fragmented into more packets. Received packets must then be reassembled, and if necessary, sorted into the correct order. Now that more packets need to be generated, they are less likely to arrive in the correct order. And if they do not arrive within the TCP timeout window, the HTTP requests will fail. &lt;/p&gt;

&lt;p&gt;If we set the MTU value optimally, the network interfaces do not have to send a large number of small packets, nor reassemble a large number of large packets. This also optimizes the CPU utilization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the original MTU value of 1500, latency may be increased:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packet fragmentation: +50-200ms
&lt;/li&gt;
&lt;li&gt;Fragment reassembly: +20-100ms
&lt;/li&gt;
&lt;li&gt;Retransmission delays: +1000-5000ms (timeouts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why does this affect VPN traffic more than others&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without visibility into the encrypted network traffic, T-Mobile can’t inspect and optimize the packets, and cannot execute traffic shaping. &lt;/p&gt;

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

&lt;p&gt;I resolved my VPN connectivity issue with T-Mobile Home Internet, which is reportedly caused by packet fragmentation due to CGNAT overhead. Reducing the MTU on your VPN tunnel interface to 1200-1400 bytes worked for me.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Don't code a new website for your blog</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Mon, 09 Dec 2024 12:52:55 +0000</pubDate>
      <link>https://dev.to/joehoppe/dont-code-a-new-website-for-your-blog-3epb</link>
      <guid>https://dev.to/joehoppe/dont-code-a-new-website-for-your-blog-3epb</guid>
      <description>&lt;h2&gt;
  
  
  Coding your own blog may become more of a nuisance than a fun hobby
&lt;/h2&gt;

&lt;p&gt;Like many, I wanted a coding side project to showcase my skills. I decided to code a new website to host my blog.&lt;/p&gt;

&lt;p&gt;It would enable me to post blog entries. It would also give me a landing page to link to my LinkedIn, PluralSight, and LeetCode profiles, and other curated online personas. You may have a few of these, and the links will bloat your resume.&lt;/p&gt;

&lt;p&gt;A blogging website was not my passion. I did not intend to revolutionize the blogging industry.&lt;/p&gt;

&lt;p&gt;And it did become a nuisance.&lt;/p&gt;

&lt;p&gt;My friend showed me &lt;strong&gt;a really simple&lt;/strong&gt; alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And with all of the problems in the world, do you want to spend your time writing code to enable a single person to publish blog articles?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple alternative - GitHub Pages
&lt;/h2&gt;

&lt;p&gt;One day my friend Brandon Boone presented me with a quick and easy way to get a free landing page with a clean subdomain - &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It can host and serve an HTML page. Or, similar to dev.to - it can convert a Markdown file to markdown with minimal setup. And you get a free GitHub subdomain in the form of https://&lt;em&gt;username&lt;/em&gt;.github.io.&lt;/p&gt;

&lt;p&gt;You can also easily point a new domain to your GitHub pages with a CNAME record.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's this easy:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clone the new repository &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/username/username.github.io&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an index.html file with content&lt;/li&gt;
&lt;li&gt;Commit and push&lt;/li&gt;
&lt;li&gt;Launch your favorite browser and navigate to 
https://&lt;strong&gt;username&lt;/strong&gt;.github.io.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Want to use Markdown instead of HTML?
&lt;/h2&gt;

&lt;p&gt;Unite instead of div.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a README.md with content (instead of an index.html)&lt;/li&gt;
&lt;li&gt;Add this &lt;a href="https://github.com/joehoppe/joehoppe.github.io/blob/main/_config.yml" rel="noopener noreferrer"&gt;YAML Jekyll file&lt;/a&gt; to the root of your repo
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;theme: jekyll-theme-slate
kramdown:
  input: Kramdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Still want to have a blog?
&lt;/h2&gt;

&lt;p&gt;Link from the markdown file to your blog or blog posts on an established blogging platform such as &lt;a href="//medium.com"&gt;medium.com&lt;/a&gt; or &lt;a href="//dev.to"&gt;dev.to&lt;/a&gt;. They will be free, feature-rich, and may even market your posts for you.&lt;br&gt;
 &lt;br&gt;
&lt;strong&gt;Don't code your own blog&lt;/strong&gt;. Do something more meaningful with your life. Maybe publish a new GitHub repository to solve a problem that hasn't been solved yet, join an open-source project, etc.&lt;/p&gt;

&lt;p&gt;Check out my simple GitHub landing page at &lt;a href="https://joehoppe.github.io" rel="noopener noreferrer"&gt;https://joehoppe.github.io&lt;/a&gt;. Leave a comment on what you think, or suggest improvements to my own!&lt;/p&gt;

</description>
      <category>github</category>
    </item>
    <item>
      <title>AWS — Properly delete messages between Lambdas and SQS Queues</title>
      <dc:creator>Joseph Hoppe</dc:creator>
      <pubDate>Mon, 06 May 2024 00:55:07 +0000</pubDate>
      <link>https://dev.to/joehoppe/aws-properly-delete-messages-between-lambdas-and-sqs-queues-41md</link>
      <guid>https://dev.to/joehoppe/aws-properly-delete-messages-between-lambdas-and-sqs-queues-41md</guid>
      <description>&lt;p&gt;AWS - Properly delete messages between Lambdas and SQS Queues&lt;br&gt;
A lot of people may already know this, but posting for some who need clarity after sifting through the conflicting information that I found on the net.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Summary (TLDR)
&lt;/h2&gt;

&lt;p&gt;When using &lt;strong&gt;SQS Event Triggers&lt;/strong&gt;, the default behavior is that SQS messages will be &lt;strong&gt;automatically deleted&lt;/strong&gt;. There are certain conditions that also need to be met.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prior to the release of SQS Event Triggers in 2018&lt;/strong&gt;, the opposite was true - messages used to need to be deleted explicitly, or the message would become visible again after the visibility timeout.&lt;/p&gt;

&lt;p&gt;Back then, a common pattern would be to poll an SQS queue for messages, possibly using a chron lambda trigger. The SQS message would become invisible to other consumers, and when successfully processed, &lt;strong&gt;the lambda would need to explicitly delete the message from the queue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because of this paradigm shift in 2018, there is information on the internet stating that lambdas "automatically delete the message after processing", and also information that they "do need to manually delete SQS messages", without clearly specifying. The truth is that it depends on the lambda trigger.&lt;/p&gt;

&lt;p&gt;SQS Events are the de factor trigger for many use cases. To reiterate, for this popular use case today, if specific conditions are met, the lambda will automatically delete the messages on completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backstory (2017 and earlier)
&lt;/h2&gt;

&lt;p&gt;Way back when, developers had to manually wire up the code to poll an SQS queue. One approach would be to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have a lambda execute on a chron schedule. Use the ReceiveMessage API to manually poll and retrieve messages.&lt;/li&gt;
&lt;li&gt;After processing the message, the lambda would then be responsible for explicitly deleting the message from the SQS queue using the DeleteMessage API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the message was not deleted, and the message visibility timeout elapses, the message would become visible to other consumers.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQS Event Triggers are released in 2018
&lt;/h2&gt;

&lt;p&gt;AWS greatly simplified the process by adding a new event source to streamline this process in 2018. The new process became:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an Event Source Mapping (ESM) connecting the Lambda function to the SQS queue. This triggers the lambda when messages are received, and pushes the messages to the queue as the SQS message body.&lt;/li&gt;
&lt;li&gt;The lambda does not need to explicitly delete the message from the queue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No code is needed to explicitly poll the queue or delete the message, and can focus on the core processing logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  More on SQS Event Triggers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If I do not need to explicitly delete the SQS messages, why are messages being routed to the Dead Letter Queue (DLQ)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the lambda throws an uncaught error, the message(s) in the batch will be retried after the message visibility timeout. If the maxReceiveCount is met, the message will be routed to DLQ.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;But how do I retry a subset of messages that came in a batch?&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
First, ReportBatchItemFailures must be enabled on the lambda trigger itself.&lt;/p&gt;

&lt;p&gt;If a message should &lt;strong&gt;not&lt;/strong&gt; be deleted, the lambda must return an SQSBatchResponse object (if using TypeScript) containing an array of message ids that should not be deleted. The return type is below, with itemIdentifier being the messageId:&lt;br&gt;
&lt;code&gt;export interface SQSBatchResponse {&lt;br&gt;
    batchItemFailures: SQSBatchItemFailure[];&lt;br&gt;
}&lt;br&gt;
export interface SQSBatchItemFailure {&lt;br&gt;
    itemIdentifier: string;&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;A few more nuances to be aware of (from the AWS Documentation):&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Success and failure conditions&lt;br&gt;
Lambda treats a batch as a complete success if your function returns any of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An empty batchItemFailures list&lt;/li&gt;
&lt;li&gt;A null batchItemFailures list&lt;/li&gt;
&lt;li&gt;An empty EventResponse&lt;/li&gt;
&lt;li&gt;A null EventResponse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lambda treats a batch as a complete failure if your function returns any of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An invalid JSON response&lt;/li&gt;
&lt;li&gt;An empty string itemIdentifier&lt;/li&gt;
&lt;li&gt;A null itemIdentifier&lt;/li&gt;
&lt;li&gt;An itemIdentifier with a bad key name&lt;/li&gt;
&lt;li&gt;An itemIdentifier value with a message ID that doesn't exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more information, see the AWS Documentation on Reporting partial batch responses, which is a subsection of the page: Using Lambda with Amazon SQS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@JosephHoppe/aws-properly-delete-messages-between-lambdas-and-sqs-queues-54db2c55106a" rel="noopener noreferrer"&gt;I originally posted this article on Medium&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>sqs</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
