<?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: Sahinur</title>
    <description>The latest articles on DEV Community by Sahinur (@sahinur).</description>
    <link>https://dev.to/sahinur</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F575294%2F036e7718-e7f8-48d8-90c8-bcb4e4bd12fd.jpg</url>
      <title>DEV Community: Sahinur</title>
      <link>https://dev.to/sahinur</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sahinur"/>
    <language>en</language>
    <item>
      <title>Building `aws-ec2-check`: A Small Open-Source AWS Utility with Node.js</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/building-aws-ec2-check-a-small-open-source-aws-utility-with-nodejs-194a</link>
      <guid>https://dev.to/sahinur/building-aws-ec2-check-a-small-open-source-aws-utility-with-nodejs-194a</guid>
      <description>&lt;p&gt;As I started working more deeply with AWS, I noticed a small but recurring workflow:&lt;/p&gt;

&lt;p&gt;When debugging an EC2-based application, I often need to quickly answer a few basic questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the instance running?&lt;/li&gt;
&lt;li&gt;What type of instance is it?&lt;/li&gt;
&lt;li&gt;Which region and Availability Zone is it in?&lt;/li&gt;
&lt;li&gt;What are its public and private IPs?&lt;/li&gt;
&lt;li&gt;Are the EC2 status checks healthy?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AWS Console provides all of this information, but sometimes I just want a quick answer from my terminal.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;code&gt;aws-ec2-check&lt;/code&gt;&lt;/strong&gt;, a small open-source CLI for inspecting AWS EC2 instances directly from the command line.&lt;/p&gt;

&lt;p&gt;The project is now publicly available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/devSahinur/aws-ec2-check" rel="noopener noreferrer"&gt;https://github.com/devSahinur/aws-ec2-check&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository is written in &lt;strong&gt;TypeScript&lt;/strong&gt; and is published under the &lt;strong&gt;MIT License&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;When working with EC2-based applications, a lot of troubleshooting starts with basic infrastructure information.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the instance running?

Is the instance healthy?

Which Availability Zone is it using?

What's the public IP?

What's the instance type?

Are the EC2 status checks passing?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could open the AWS Console and look these up.&lt;/p&gt;

&lt;p&gt;But when I'm already working in the terminal, switching between tools feels unnecessary for such a small task.&lt;/p&gt;

&lt;p&gt;That was the motivation behind the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;aws-ec2-check&lt;/code&gt; does
&lt;/h2&gt;

&lt;p&gt;The idea is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terminal
   ↓
aws-ec2-check
   ↓
AWS SDK for JavaScript
   ↓
Amazon EC2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a specific instance, the CLI is designed to provide useful EC2 information such as instance state, metadata, and health/status information.&lt;/p&gt;

&lt;p&gt;A typical command looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws-ec2-check i-0123456789abcdef0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project also supports a CLI-first workflow rather than requiring users to interact with the AWS Console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build another AWS tool?
&lt;/h2&gt;

&lt;p&gt;There is already an official AWS CLI.&lt;/p&gt;

&lt;p&gt;So I'm not trying to replace it.&lt;/p&gt;

&lt;p&gt;The goal of this project is much smaller:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make one common EC2 inspection workflow convenient from the terminal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction is important.&lt;/p&gt;

&lt;p&gt;I don't think every open-source project needs to become a massive platform.&lt;/p&gt;

&lt;p&gt;Sometimes a small developer utility is enough to solve a real annoyance.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;p&gt;I chose &lt;strong&gt;TypeScript&lt;/strong&gt; because it fits naturally with my Node.js backend background while giving the project strong typing and a better development experience.&lt;/p&gt;

&lt;p&gt;The repository is currently categorized around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS
CLI
DevOps
EC2
Node.js
TypeScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and is publicly available as &lt;code&gt;devSahinur/aws-ec2-check&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At a high level, the architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
    ↓
CLI
    ↓
TypeScript
    ↓
AWS SDK for JavaScript
    ↓
EC2 API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI acts as a small abstraction over the EC2 APIs needed for inspection.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS SDK instead of the Console
&lt;/h2&gt;

&lt;p&gt;One thing I wanted to learn from this project was what happens underneath the AWS Console.&lt;/p&gt;

&lt;p&gt;When I use the Console, a lot of AWS operations feel like buttons and tables.&lt;/p&gt;

&lt;p&gt;When building a CLI with the AWS SDK, those same workflows become explicit API calls.&lt;/p&gt;

&lt;p&gt;That makes the architecture easier to reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI command
    ↓
Application logic
    ↓
AWS SDK
    ↓
EC2 API
    ↓
Response
    ↓
Formatted terminal output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For me, this was a useful way to connect AWS infrastructure knowledge with backend development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security matters
&lt;/h2&gt;

&lt;p&gt;Because this tool interacts with AWS, credential handling was one of the parts I paid close attention to.&lt;/p&gt;

&lt;p&gt;I don't want AWS credentials embedded in the source code.&lt;/p&gt;

&lt;p&gt;The tool should work with AWS's standard credential resolution mechanisms rather than inventing its own authentication approach.&lt;/p&gt;

&lt;p&gt;That means developers can use their existing AWS configuration or appropriate AWS roles.&lt;/p&gt;

&lt;p&gt;Just as importantly, the tool is intended for &lt;strong&gt;inspection&lt;/strong&gt;, not unnecessary infrastructure modification.&lt;/p&gt;

&lt;p&gt;That led to another important design principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A small tool should ask for small permissions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's no reason for an EC2 inspection tool to require administrator access.&lt;/p&gt;

&lt;h2&gt;
  
  
  IAM and least privilege
&lt;/h2&gt;

&lt;p&gt;AWS permissions can become dangerous when developers solve every &lt;code&gt;AccessDenied&lt;/code&gt; problem by attaching broad policies.&lt;/p&gt;

&lt;p&gt;For this project, I wanted the permissions model to follow the same principle I discussed in my earlier IAM article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required action
      ↓
Required resource
      ↓
Minimum permission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required action
      ↓
AdministratorAccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small CLI, but the security principle is the same as it is for a large production system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the CLI simple
&lt;/h2&gt;

&lt;p&gt;I wanted the command structure to stay easy to understand.&lt;/p&gt;

&lt;p&gt;The core idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws-ec2-check &amp;lt;instance-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And as the project evolves, additional commands and options can support common workflows without making the CLI feel complicated.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws-ec2-check &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws-ec2-check &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;make the tool easier to discover and use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why TypeScript?
&lt;/h2&gt;

&lt;p&gt;For a small project like this, TypeScript isn't required.&lt;/p&gt;

&lt;p&gt;But I still prefer it because it gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better type safety&lt;/li&gt;
&lt;li&gt;Better editor support&lt;/li&gt;
&lt;li&gt;Easier refactoring&lt;/li&gt;
&lt;li&gt;Clearer function contracts&lt;/li&gt;
&lt;li&gt;Better maintainability as the project grows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also keeps the project close to the technologies I already use in backend development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the project
&lt;/h2&gt;

&lt;p&gt;Another thing I wanted to avoid was creating a CLI that only works on my machine.&lt;/p&gt;

&lt;p&gt;A useful open-source project needs to be reproducible.&lt;/p&gt;

&lt;p&gt;So the project is structured around the usual development workflow:&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;npm run build
npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run lint
npm run typecheck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to keep the project easy to validate locally and suitable for automated checks through GitHub.&lt;/p&gt;

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

&lt;p&gt;The interesting part of building this project wasn't only calling the EC2 API.&lt;/p&gt;

&lt;p&gt;It made me think more carefully about the difference between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I can write the code."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I can publish something other developers can actually use."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That difference includes a lot of small details.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI design
&lt;/h3&gt;

&lt;p&gt;A CLI needs clear commands and understandable output.&lt;/p&gt;

&lt;p&gt;The user shouldn't have to understand the internal implementation just to get an EC2 status.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS authentication
&lt;/h3&gt;

&lt;p&gt;AWS credential handling shouldn't be reinvented.&lt;/p&gt;

&lt;p&gt;Using established AWS mechanisms is safer and easier to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error handling
&lt;/h3&gt;

&lt;p&gt;AWS errors can contain useful technical information, but exposing raw errors to users isn't always the best experience.&lt;/p&gt;

&lt;p&gt;A good CLI should turn those failures into useful messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Open source isn't just about code.&lt;/p&gt;

&lt;p&gt;A developer should be able to discover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What does this tool do?
How do I install it?
How do I authenticate?
What permissions are required?
What commands are available?
What should I do when something fails?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the README can't answer those questions, the project isn't finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd like to add next
&lt;/h2&gt;

&lt;p&gt;The first version is intentionally focused.&lt;/p&gt;

&lt;p&gt;But there are several areas that can make the tool more useful over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instance filtering
Tag-based filtering
Multi-region support
JSON output improvements
More detailed health information
Additional EC2 inspection commands
Better automation support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is adding features based on real developer needs rather than adding functionality just to make the project bigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source?
&lt;/h2&gt;

&lt;p&gt;One of my goals with this project was to move beyond simply learning AWS privately.&lt;/p&gt;

&lt;p&gt;I wanted to follow a cycle like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Learn
  ↓
Build
  ↓
Document
  ↓
Publish
  ↓
Get feedback
  ↓
Improve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's one of the reasons I'm interested in building in public.&lt;/p&gt;

&lt;p&gt;A project becomes more valuable when other developers can inspect it, use it, report issues, and contribute ideas.&lt;/p&gt;

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

&lt;p&gt;The repository is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/devSahinur/aws-ec2-check" rel="noopener noreferrer"&gt;https://github.com/devSahinur/aws-ec2-check&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a public GitHub repository built with TypeScript and focused on AWS EC2 inspection.&lt;/p&gt;

&lt;p&gt;You can explore the implementation, open an issue, suggest an improvement, or contribute to the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;I didn't want to build a huge AWS platform.&lt;/p&gt;

&lt;p&gt;I wanted to solve one small problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Make EC2 inspection easier from the terminal.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small problem turned into a useful learning project covering:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript + Node.js + AWS SDK + EC2 + IAM + CLI development + Open Source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, that's the real value of building small developer tools.&lt;/p&gt;

&lt;p&gt;You don't always need a huge project to learn something meaningful.&lt;/p&gt;

&lt;p&gt;Sometimes you just need to take one annoying workflow and make it a little better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build something small. Make it useful. Publish it. Then keep improving it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>aws</category>
      <category>github</category>
      <category>npm</category>
    </item>
    <item>
      <title>Load Testing My Node.js API on AWS: What I Learned</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Thu, 29 Oct 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/load-testing-my-nodejs-api-on-aws-what-i-learned-12d</link>
      <guid>https://dev.to/sahinur/load-testing-my-nodejs-api-on-aws-what-i-learned-12d</guid>
      <description>&lt;p&gt;An API can work perfectly when I'm testing it alone.&lt;/p&gt;

&lt;p&gt;Send one request.&lt;/p&gt;

&lt;p&gt;It responds quickly.&lt;/p&gt;

&lt;p&gt;Everything looks fine.&lt;/p&gt;

&lt;p&gt;But what happens when 10 users make requests at the same time?&lt;/p&gt;

&lt;p&gt;What about 100?&lt;/p&gt;

&lt;p&gt;What about 1,000?&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;load testing&lt;/strong&gt; becomes important.&lt;/p&gt;

&lt;p&gt;As a backend developer working with Node.js and Express, I want to know how an API behaves before real traffic exposes its limitations.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through how I think about load testing a Node.js API running on AWS EC2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why load test?
&lt;/h2&gt;

&lt;p&gt;Functional testing answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does the API work?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Load testing asks a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How does the API behave under pressure?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An API might return the correct response for a single request but struggle when concurrency increases.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 request
   ↓
Everything looks good

100 concurrent requests
   ↓
Latency increases

500 concurrent requests
   ↓
Database becomes a bottleneck

1000 concurrent requests
   ↓
Errors / timeouts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without load testing, I may discover these limitations only after production traffic arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;For a typical Node.js backend, my architecture looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Nginx
   ↓
AWS EC2
   ↓
Node.js + Express
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API may contain endpoints such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/users
GET    /api/products
POST   /api/orders
GET    /api/orders/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For load testing, I don't want to test every endpoint equally.&lt;/p&gt;

&lt;p&gt;I focus on the endpoints that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequently used&lt;/li&gt;
&lt;li&gt;Business-critical&lt;/li&gt;
&lt;li&gt;Database-heavy&lt;/li&gt;
&lt;li&gt;Computationally expensive&lt;/li&gt;
&lt;li&gt;Likely to experience high concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tooling
&lt;/h2&gt;

&lt;p&gt;There are several tools I can use for API load testing.&lt;/p&gt;

&lt;p&gt;Some popular options include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;k6&lt;/li&gt;
&lt;li&gt;Artillery&lt;/li&gt;
&lt;li&gt;Locust&lt;/li&gt;
&lt;li&gt;Apache JMeter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a Node.js/backend workflow, &lt;strong&gt;k6&lt;/strong&gt; is particularly convenient because the test scenarios are easy to define and version-control.&lt;/p&gt;

&lt;p&gt;The important thing isn't which tool I choose.&lt;/p&gt;

&lt;p&gt;It's being able to reproduce the same test consistently.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple k6 test
&lt;/h2&gt;

&lt;p&gt;A basic test could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;stages&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="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;target&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/api/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status is 200&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;r&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&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 idea is to gradually increase traffic rather than immediately sending a huge number of requests.&lt;/p&gt;

&lt;p&gt;This gives me a better understanding of how the system behaves as load increases.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test stages
&lt;/h2&gt;

&lt;p&gt;A useful load test isn't just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Send lots of requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer thinking in stages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          Users
            │
        100 │              ╭──────
            │             ╱
         50 │        ╭───╯
            │       ╱
         10 │──────╯
            │
            └──────────────────── Time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1 — Baseline
&lt;/h3&gt;

&lt;p&gt;Start with a small number of users.&lt;/p&gt;

&lt;p&gt;The purpose is to understand normal performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2 — Ramp up
&lt;/h3&gt;

&lt;p&gt;Gradually increase concurrent users.&lt;/p&gt;

&lt;p&gt;Now I'm looking for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increasing latency&lt;/li&gt;
&lt;li&gt;CPU changes&lt;/li&gt;
&lt;li&gt;Memory growth&lt;/li&gt;
&lt;li&gt;Database pressure&lt;/li&gt;
&lt;li&gt;Error rates&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stage 3 — Peak load
&lt;/h3&gt;

&lt;p&gt;Push the API toward the expected production workload.&lt;/p&gt;

&lt;p&gt;This is where bottlenecks often become visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4 — Recovery
&lt;/h3&gt;

&lt;p&gt;Reduce traffic again.&lt;/p&gt;

&lt;p&gt;I want to see whether the system returns to its normal state.&lt;/p&gt;

&lt;p&gt;This last step is easy to overlook.&lt;/p&gt;

&lt;p&gt;A system that survives peak traffic but never properly recovers can still have a serious problem.&lt;/p&gt;

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

&lt;p&gt;There are several metrics I care about.&lt;/p&gt;

&lt;h3&gt;
  
  
  P50 latency
&lt;/h3&gt;

&lt;p&gt;P50 represents the median request latency.&lt;/p&gt;

&lt;p&gt;It tells me what a typical request looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  P95 latency
&lt;/h3&gt;

&lt;p&gt;P95 is more interesting for production behavior.&lt;/p&gt;

&lt;p&gt;It tells me that approximately 95% of requests are faster than that value.&lt;/p&gt;

&lt;p&gt;If P50 looks good but P95 becomes very high, some users are experiencing significantly worse performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  P99 latency
&lt;/h3&gt;

&lt;p&gt;P99 helps expose the tail of the latency distribution.&lt;/p&gt;

&lt;p&gt;This is especially useful when investigating occasional slow requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error rate
&lt;/h3&gt;

&lt;p&gt;Latency isn't the only concern.&lt;/p&gt;

&lt;p&gt;I also want to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How many requests failed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A fast API returning lots of 500 errors isn't a healthy API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput
&lt;/h3&gt;

&lt;p&gt;I also want to understand how many requests the system can handle.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requests/sec
     │
     │              ───────
     │           ───
     │        ───
     │     ───
     │────
     └──────────────────── Load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some point, adding more traffic may no longer increase useful throughput.&lt;/p&gt;

&lt;p&gt;That's an important signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;For a real test, I'd record the results in a table like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P50 latency&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P95 latency&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P99 latency&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error rate&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max sustained RPS&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak concurrent users&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I prefer recording these numbers rather than simply saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The API was fast."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Numbers make performance discussions much more useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching the EC2 instance
&lt;/h2&gt;

&lt;p&gt;While the load test is running, I also want to know what is happening on the server.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Node.js application running with PM2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 monit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps correlate API performance with system resources.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic increases
      ↓
CPU increases
      ↓
Latency increases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic increases
      ↓
CPU remains reasonable
      ↓
Database connections increase
      ↓
Latency increases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those two scenarios require completely different solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The surprise: the API isn't always the bottleneck
&lt;/h2&gt;

&lt;p&gt;One of the biggest things load testing can reveal is that the application server isn't necessarily the problem.&lt;/p&gt;

&lt;p&gt;Consider this architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load
 ↓
EC2
 ↓
Node.js
 ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might expect the EC2 server to reach its limit first.&lt;/p&gt;

&lt;p&gt;But the actual bottleneck could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MongoDB queries
Database connections
Slow indexes
Network latency
External APIs
Large responses
Memory usage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why I don't look at only one metric.&lt;/p&gt;

&lt;p&gt;I want to correlate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API latency
+
Error rate
+
CPU
+
Memory
+
Database behavior
+
Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A common database problem
&lt;/h2&gt;

&lt;p&gt;Suppose an endpoint performs:&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;users&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a small dataset, this might appear completely fine.&lt;/p&gt;

&lt;p&gt;But under load, if the query returns a large number of documents, the application may spend significantly more time processing the result.&lt;/p&gt;

&lt;p&gt;That can increase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response latency&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;Database workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pagination can help:&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;users&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&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;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indexes can also be important.&lt;/p&gt;

&lt;p&gt;For example, if the application frequently searches by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status
email
userId
createdAt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want to make sure the database query patterns are properly supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when the test gets too aggressive?
&lt;/h2&gt;

&lt;p&gt;A load test can eventually push the system beyond its intended capacity.&lt;/p&gt;

&lt;p&gt;That's useful information—but it should be done carefully.&lt;/p&gt;

&lt;p&gt;If the system reaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High CPU
High memory
Database saturation
High error rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't immediately conclude that the server needs to be bigger.&lt;/p&gt;

&lt;p&gt;First I ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What became the bottleneck?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only then do I decide whether the solution is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better application code&lt;/li&gt;
&lt;li&gt;Database optimization&lt;/li&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Connection-pool tuning&lt;/li&gt;
&lt;li&gt;Horizontal scaling&lt;/li&gt;
&lt;li&gt;Vertical scaling&lt;/li&gt;
&lt;li&gt;Architecture changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Changes I would make after testing
&lt;/h2&gt;

&lt;p&gt;Depending on the results, the next step could be one or more of these.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Optimize database queries
&lt;/h3&gt;

&lt;p&gt;Reduce unnecessary data and make frequently used queries efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add pagination
&lt;/h3&gt;

&lt;p&gt;Avoid returning huge datasets in a single request.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Improve connection handling
&lt;/h3&gt;

&lt;p&gt;Make sure database connections are managed efficiently under concurrency.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Add caching
&lt;/h3&gt;

&lt;p&gt;For frequently requested data that doesn't change often, caching can reduce unnecessary database work.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Scale the application
&lt;/h3&gt;

&lt;p&gt;If the application is genuinely CPU or memory constrained, scaling the EC2 infrastructure may be appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Improve observability
&lt;/h3&gt;

&lt;p&gt;Load testing should ideally be combined with monitoring so that performance problems can be investigated instead of guessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Load testing isn't about finding one magic number
&lt;/h2&gt;

&lt;p&gt;One mistake I try to avoid is thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Our API supports 500 requests per second."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's rarely the whole story.&lt;/p&gt;

&lt;p&gt;The real question is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500 requests/sec
under what conditions?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which endpoint?&lt;/li&gt;
&lt;li&gt;What payload size?&lt;/li&gt;
&lt;li&gt;How many concurrent users?&lt;/li&gt;
&lt;li&gt;What database size?&lt;/li&gt;
&lt;li&gt;What response size?&lt;/li&gt;
&lt;li&gt;What error rate?&lt;/li&gt;
&lt;li&gt;What latency target?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A meaningful performance number always needs context.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple performance checklist
&lt;/h2&gt;

&lt;p&gt;Before considering an API ready for significant traffic, I'd check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Baseline performance measured
[ ] Peak expected traffic tested
[ ] P50 latency recorded
[ ] P95 latency recorded
[ ] P99 latency recorded
[ ] Error rate recorded
[ ] Maximum sustainable throughput measured
[ ] CPU monitored
[ ] Memory monitored
[ ] Database performance monitored
[ ] Slow queries investigated
[ ] Recovery after peak load tested
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Load testing changed the way I think about backend performance.&lt;/p&gt;

&lt;p&gt;Before testing, I might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is the API fast?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After testing, I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How does the API behave as concurrency increases?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much better question.&lt;/p&gt;

&lt;p&gt;A backend isn't truly production-ready just because it works with a few requests.&lt;/p&gt;

&lt;p&gt;I want to understand how it behaves when:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic increases
      ↓
Resources increase
      ↓
Latency changes
      ↓
Bottlenecks appear
      ↓
System reaches its limits
      ↓
System recovers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Load testing isn't something I want to do only after an application starts having production problems.&lt;/p&gt;

&lt;p&gt;It should be part of understanding the system.&lt;/p&gt;

&lt;p&gt;For a Node.js + Express backend running on AWS EC2, I want to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much traffic can it handle?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does it start slowing down?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What becomes the bottleneck?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it recover correctly?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those answers are much more valuable than simply knowing that the API works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't wait for production traffic to discover your application's limits. Test those limits before your users do.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>performance</category>
      <category>loadtesting</category>
      <category>node</category>
    </item>
    <item>
      <title>Migrating a Node.js Endpoint to AWS Lambda: What I Would Watch For</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Wed, 21 Oct 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/migrating-a-nodejs-endpoint-to-aws-lambda-what-i-would-watch-for-448j</link>
      <guid>https://dev.to/sahinur/migrating-a-nodejs-endpoint-to-aws-lambda-what-i-would-watch-for-448j</guid>
      <description>&lt;p&gt;Moving an endpoint from a traditional Node.js backend to AWS Lambda sounds straightforward.&lt;/p&gt;

&lt;p&gt;Take the business logic, put it inside a Lambda function, connect API Gateway, and you're done.&lt;/p&gt;

&lt;p&gt;In practice, the architecture changes more than the deployment model.&lt;/p&gt;

&lt;p&gt;A traditional Express application is designed around a continuously running server.&lt;/p&gt;

&lt;p&gt;Lambda is designed around &lt;strong&gt;short-lived function executions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That difference affects application structure, database connections, configuration, authentication, logging, and performance.&lt;/p&gt;

&lt;p&gt;Coming from a Node.js + Express background, these are the things I would pay attention to when migrating an endpoint to Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why migrate only one endpoint?
&lt;/h2&gt;

&lt;p&gt;I wouldn't start by moving an entire monolith to serverless.&lt;/p&gt;

&lt;p&gt;That's a much bigger risk.&lt;/p&gt;

&lt;p&gt;A better approach is to select one endpoint and use it as a migration experiment.&lt;/p&gt;

&lt;p&gt;A good candidate could have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relatively independent business logic&lt;/li&gt;
&lt;li&gt;Predictable input/output&lt;/li&gt;
&lt;li&gt;Limited dependencies on the rest of the monolith&lt;/li&gt;
&lt;li&gt;No requirement for a long-running process&lt;/li&gt;
&lt;li&gt;A workload that can benefit from independent scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture could change from:&lt;br&gt;
&lt;/p&gt;

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

Client
   ↓
Nginx
   ↓
EC2
   ↓
Node.js + Express
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

Client
   ↓
API Gateway
   ↓
Lambda
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rest of the application can continue running on EC2 while the selected endpoint is handled by Lambda.&lt;/p&gt;

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

&lt;p&gt;I would break the migration into small steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Identify the endpoint
&lt;/h3&gt;

&lt;p&gt;First, understand exactly what the endpoint does.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint might:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate the request&lt;/li&gt;
&lt;li&gt;Authenticate the user&lt;/li&gt;
&lt;li&gt;Read data&lt;/li&gt;
&lt;li&gt;Perform business logic&lt;/li&gt;
&lt;li&gt;Write to MongoDB&lt;/li&gt;
&lt;li&gt;Return a response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before moving it, I would identify every dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Endpoint
   │
   ├── Authentication
   ├── Validation
   ├── Database
   ├── Services
   ├── Environment variables
   └── External APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes hidden dependencies visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Separate business logic from Express
&lt;/h2&gt;

&lt;p&gt;One of the biggest advantages of separating application logic from the HTTP framework is that the business logic becomes easier to reuse.&lt;/p&gt;

&lt;p&gt;Instead of putting everything inside:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// everything here&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would separate the business logic:&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;createOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// business logic&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then Express can call it:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&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 a Lambda handler can call the same logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&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 important idea is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The business logic shouldn't depend heavily on Express.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Add API Gateway
&lt;/h2&gt;

&lt;p&gt;The next layer is API Gateway.&lt;/p&gt;

&lt;p&gt;The request flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
API Gateway
   ↓
Lambda
   ↓
Business Logic
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API Gateway becomes responsible for receiving the HTTP request and invoking the Lambda function.&lt;/p&gt;

&lt;p&gt;Depending on the architecture, authentication and authorization can also be handled through appropriate API Gateway/Lambda mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can break?
&lt;/h2&gt;

&lt;p&gt;This is where serverless migrations become interesting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1: Database connections
&lt;/h3&gt;

&lt;p&gt;This is one of the first things I'd investigate.&lt;/p&gt;

&lt;p&gt;With an Express application on EC2, the Node.js process stays alive.&lt;/p&gt;

&lt;p&gt;A MongoDB connection can remain available between requests.&lt;/p&gt;

&lt;p&gt;Lambda works differently.&lt;/p&gt;

&lt;p&gt;A Lambda execution environment can be created, reused, or terminated.&lt;/p&gt;

&lt;p&gt;If every invocation creates a new database connection, a high number of requests can create unnecessary connection pressure.&lt;/p&gt;

&lt;p&gt;A naive pattern might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&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;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// process request&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this without considering connection reuse can become problematic.&lt;/p&gt;

&lt;p&gt;A better pattern is to cache the connection/promise at the module level so that warm Lambda environments can reuse it.&lt;/p&gt;

&lt;p&gt;The important lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't treat Lambda like a traditional always-running Express server.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Problem 2: Cold starts
&lt;/h2&gt;

&lt;p&gt;Another difference is cold-start latency.&lt;/p&gt;

&lt;p&gt;When AWS needs a new execution environment, Lambda has to initialize the runtime and application code.&lt;/p&gt;

&lt;p&gt;That can add latency to some requests.&lt;/p&gt;

&lt;p&gt;The simplified flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
New Lambda environment
   ↓
Initialize runtime
   ↓
Load application
   ↓
Connect / initialize dependencies
   ↓
Execute handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A warm invocation can be much simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
Existing environment
   ↓
Execute handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means application startup time matters.&lt;/p&gt;

&lt;p&gt;Large dependency trees and heavy initialization can make cold starts worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: Environment variables
&lt;/h2&gt;

&lt;p&gt;A traditional application may have environment configuration such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGODB_URI
JWT_SECRET
API_KEY
NODE_ENV
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When moving the endpoint to Lambda, those configuration values need to be available to the function.&lt;/p&gt;

&lt;p&gt;I would avoid putting sensitive values directly into source code.&lt;/p&gt;

&lt;p&gt;The deployment architecture should separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code
+
Configuration
+
Secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially important when the same function has different configurations for development, staging, and production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 4: Express assumptions
&lt;/h2&gt;

&lt;p&gt;A monolithic Express application has access to things such as:&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="nx"&gt;req&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;
&lt;span class="nx"&gt;middleware&lt;/span&gt;
&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lambda doesn't naturally work with that same request lifecycle.&lt;/p&gt;

&lt;p&gt;Instead, the handler receives an event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// event&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means Express middleware cannot always be moved directly without modification.&lt;/p&gt;

&lt;p&gt;Authentication, validation, error handling, and request parsing may need to be adapted.&lt;/p&gt;

&lt;p&gt;This is one reason I would avoid simply copying an Express controller into Lambda and expecting everything to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 5: Logging changes
&lt;/h2&gt;

&lt;p&gt;With an EC2-based application, I might inspect application logs using tools such as PM2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Lambda, logging is centered around CloudWatch Logs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Processing order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be captured by CloudWatch.&lt;/p&gt;

&lt;p&gt;This changes the operational workflow.&lt;/p&gt;

&lt;p&gt;Instead of thinking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SSH → server → PM2 logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the workflow becomes more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lambda
   ↓
CloudWatch Logs
   ↓
Metrics / Logs / Alarms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a production migration, I'd make sure observability is ready before moving traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would reduce migration risk
&lt;/h2&gt;

&lt;p&gt;I wouldn't immediately switch 100% of production traffic.&lt;/p&gt;

&lt;p&gt;A safer approach is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Existing Endpoint
       │
       ├────────→ EC2 / Express
       │
       └────────→ Lambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the routing strategy, traffic can be moved gradually.&lt;/p&gt;

&lt;p&gt;The migration should have a clear rollback path.&lt;/p&gt;

&lt;p&gt;If Lambda introduces unexpected latency or errors, the original endpoint should remain available until confidence is established.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing before production
&lt;/h2&gt;

&lt;p&gt;Before migrating traffic, I'd test more than just whether the Lambda returns HTTP 200.&lt;/p&gt;

&lt;p&gt;I'd test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Authentication
[ ] Validation
[ ] Database operations
[ ] Error handling
[ ] External API calls
[ ] Environment variables
[ ] Cold-start behavior
[ ] Concurrent requests
[ ] Database connection behavior
[ ] Response latency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd also test the endpoint using realistic payload sizes and data volumes.&lt;/p&gt;

&lt;p&gt;A function that works perfectly with one request isn't necessarily ready for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Lambda always better?
&lt;/h2&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;This is probably the most important lesson.&lt;/p&gt;

&lt;p&gt;Lambda can be a great fit for certain workloads, but migrating an endpoint simply because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Serverless is modern"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;isn't a good architectural reason.&lt;/p&gt;

&lt;p&gt;A continuously running Express service may still be a better choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The workload is consistently high&lt;/li&gt;
&lt;li&gt;The application has long-running processes&lt;/li&gt;
&lt;li&gt;The architecture is tightly coupled&lt;/li&gt;
&lt;li&gt;Existing infrastructure is already efficient&lt;/li&gt;
&lt;li&gt;The team benefits from the traditional server model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lambda becomes more attractive when the workload is well suited to event-driven execution and we want AWS to handle more of the underlying infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would measure
&lt;/h2&gt;

&lt;p&gt;Before and after migration, I'd compare:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average latency&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p95 latency&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error rate&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold-start latency&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requests/sec&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database connections&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure overhead&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;td&gt;Measure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The point isn't to prove Lambda is better.&lt;/p&gt;

&lt;p&gt;The point is to determine whether &lt;strong&gt;this particular endpoint&lt;/strong&gt; benefits from the migration.&lt;/p&gt;

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

&lt;p&gt;For me, the answer would depend entirely on the measurements.&lt;/p&gt;

&lt;p&gt;If the migration provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Lower operational overhead&lt;/li&gt;
&lt;li&gt;Appropriate latency&lt;/li&gt;
&lt;li&gt;Reliable database behavior&lt;/li&gt;
&lt;li&gt;Reasonable cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then it can be a good architectural decision.&lt;/p&gt;

&lt;p&gt;But if the endpoint becomes significantly more complicated without providing meaningful benefits, keeping it on the existing infrastructure may be the better decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I take from a Lambda migration is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Moving code to Lambda isn't the same as designing for serverless.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A successful migration requires thinking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Runtime
Database connections
Cold starts
Configuration
Authentication
Observability
Scaling
Failure handling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best approach isn't to rewrite everything at once.&lt;/p&gt;

&lt;p&gt;Start with one suitable endpoint.&lt;/p&gt;

&lt;p&gt;Measure its behavior.&lt;/p&gt;

&lt;p&gt;Learn from the migration.&lt;/p&gt;

&lt;p&gt;Then decide whether the pattern makes sense for the rest of the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good architecture isn't about moving everything to the newest technology.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's about choosing the right architecture for the workload.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>serverless</category>
      <category>node</category>
    </item>
    <item>
      <title>IAM Permissions I Got Wrong (And What I Learned)</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Wed, 07 Oct 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/iam-permissions-i-got-wrong-and-what-i-learned-45p0</link>
      <guid>https://dev.to/sahinur/iam-permissions-i-got-wrong-and-what-i-learned-45p0</guid>
      <description>&lt;p&gt;IAM is one of those AWS services that looks simple at first.&lt;/p&gt;

&lt;p&gt;Create a user or role, attach a policy, give it access, and move on.&lt;/p&gt;

&lt;p&gt;Then one day you deploy an application and see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And suddenly you're asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which permission is missing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Working with AWS has taught me that IAM isn't just about making something work.&lt;/p&gt;

&lt;p&gt;It's about giving the application &lt;strong&gt;exactly the permissions it needs—and nothing more.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some of the lessons I've learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 1: Using &lt;code&gt;*&lt;/code&gt; just to get it working
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to solve an AWS permission problem is to make the policy extremely broad.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight 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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;Technically, this can make many permission errors disappear.&lt;/p&gt;

&lt;p&gt;But it creates a much bigger security problem.&lt;/p&gt;

&lt;p&gt;The application now has permission to perform actions it may never need.&lt;/p&gt;

&lt;p&gt;For example, if a backend only needs to upload objects to one S3 bucket, it shouldn't automatically have permission to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Delete buckets
Create IAM users
Modify EC2 instances
Read unrelated databases
Access every S3 bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The better approach is to scope permissions to the actual requirement.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight 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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"s3:PutObject"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-app-uploads/*"&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;Now the role has a much smaller permission boundary.&lt;/p&gt;

&lt;p&gt;The lesson is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't use broad permissions as the permanent solution to a permission error.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Mistake 2: Confusing users and roles
&lt;/h2&gt;

&lt;p&gt;Another important concept is understanding when to use an IAM user and when to use an IAM role.&lt;/p&gt;

&lt;p&gt;For applications running on AWS, I prefer the application to assume a role rather than embedding long-term AWS credentials inside the application.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EC2
  ↓
IAM Role
  ↓
AWS Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EC2
  ↓
AWS Access Key
  ↓
AWS Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The role-based approach allows AWS services to obtain temporary credentials without storing permanent access keys in the application.&lt;/p&gt;

&lt;p&gt;This is especially important for workloads running on services such as EC2, Lambda, and ECS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 3: Giving the wrong resource access
&lt;/h2&gt;

&lt;p&gt;Another common IAM problem is getting the action right but the resource wrong.&lt;/p&gt;

&lt;p&gt;For example, you may allow:&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="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the resource doesn't match the actual object ARN.&lt;/p&gt;

&lt;p&gt;A policy can look correct at first glance while still producing:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That's why debugging IAM requires checking both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Action
+
Resource
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For S3, for example, bucket-level and object-level ARNs can be different depending on the operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 4: Forgetting that permissions can come from multiple places
&lt;/h2&gt;

&lt;p&gt;IAM authorization isn't always as simple as looking at one policy.&lt;/p&gt;

&lt;p&gt;When debugging access, I consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identity-based policies
        +
Resource-based policies
        +
Permissions boundaries
        +
Session policies
        +
Service control policies
        +
Resource configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one reason IAM errors can sometimes be confusing.&lt;/p&gt;

&lt;p&gt;You might add an &lt;code&gt;Allow&lt;/code&gt; policy and still receive &lt;code&gt;AccessDenied&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An explicit &lt;code&gt;Deny&lt;/code&gt; somewhere else can override an &lt;code&gt;Allow&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So my debugging process starts with understanding &lt;strong&gt;where the permission decision is actually coming from&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I approach an AccessDenied error
&lt;/h2&gt;

&lt;p&gt;When an AWS operation fails, I don't immediately start adding more permissions.&lt;/p&gt;

&lt;p&gt;I first identify:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Who is making the request?
&lt;/h3&gt;

&lt;p&gt;Is it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM User?
IAM Role?
EC2 Instance Role?
Lambda Execution Role?
ECS Task Role?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. What action is being attempted?
&lt;/h3&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s3:GetObject
s3:PutObject
dynamodb:GetItem
secretsmanager:GetSecretValue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Which resource is being accessed?
&lt;/h3&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3 bucket/object
DynamoDB table
Secrets Manager secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Which policy should allow the operation?
&lt;/h3&gt;

&lt;p&gt;Then I check the relevant policy instead of randomly adding permissions.&lt;/p&gt;

&lt;p&gt;This makes debugging much more systematic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Least privilege
&lt;/h2&gt;

&lt;p&gt;The principle I try to follow is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Give the application the minimum permissions required to perform its job.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine an API that only needs to upload user profile images.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3: *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd want something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s3:PutObject
       ↓
specific bucket
       ↓
specific path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same principle applies to other AWS services.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DynamoDB: *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use the specific actions required by the application.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Secrets Manager: *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;allow access only to the secrets the application actually needs.&lt;/p&gt;

&lt;p&gt;This reduces the potential impact if credentials or an application component are compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools that help
&lt;/h2&gt;

&lt;p&gt;AWS provides several tools that make IAM troubleshooting easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Policy Simulator
&lt;/h3&gt;

&lt;p&gt;The IAM Policy Simulator can help test whether a policy allows or denies specific actions.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly deploying an application just to test permissions, you can reason about the policy directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Access Analyzer
&lt;/h3&gt;

&lt;p&gt;Access Analyzer can help identify overly broad access and unintended external access.&lt;/p&gt;

&lt;p&gt;It's particularly useful when reviewing policies and trying to move toward least-privilege permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudTrail
&lt;/h3&gt;

&lt;p&gt;When debugging AWS activity, CloudTrail can provide valuable information about API calls.&lt;/p&gt;

&lt;p&gt;It helps answer questions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who made the request?

What AWS API was called?

When did it happen?

What resource was involved?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That context can make an IAM problem much easier to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better IAM workflow
&lt;/h2&gt;

&lt;p&gt;My preferred workflow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application needs AWS access
          ↓
Identify exact AWS action
          ↓
Identify exact resource
          ↓
Create minimum required policy
          ↓
Test
          ↓
Review permissions
          ↓
Monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AccessDenied
    ↓
Add AdministratorAccess
    ↓
Problem solved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach may be faster for five minutes, but it's much harder to defend from a security perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  IAM checklist
&lt;/h2&gt;

&lt;p&gt;Before considering an application role ready for production, I'd ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Does the application use an IAM role where appropriate?
[ ] Are long-term credentials avoided?
[ ] Are permissions limited to required actions?
[ ] Are resources scoped as narrowly as practical?
[ ] Have wildcard permissions been reviewed?
[ ] Have explicit denies been considered?
[ ] Have IAM Access Analyzer findings been reviewed?
[ ] Have policies been tested?
[ ] Are production and development permissions separated?
[ ] Are unused permissions removed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The biggest lesson is that &lt;strong&gt;IAM isn't something you should solve by trial and error forever.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;I now try to understand the authorization path instead of immediately adding another policy.&lt;/p&gt;

&lt;p&gt;The question isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can I give this application more access?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the smallest amount of access this application actually needs?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That mindset changes the way you design AWS infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;IAM can feel frustrating when you're starting with AWS.&lt;/p&gt;

&lt;p&gt;But once you stop treating permissions as random configuration and start thinking in terms of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Principal
   ↓
Action
   ↓
Resource
   ↓
Policy
   ↓
Authorization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the system becomes much easier to reason about.&lt;/p&gt;

&lt;p&gt;And one of the most important AWS security habits I've learned is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make it work with the minimum permissions—not maximum permissions.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>security</category>
      <category>cloudsecurity</category>
    </item>
    <item>
      <title>Setting Up CI/CD for a Node.js Backend with GitHub Actions and AWS</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Wed, 30 Sep 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/setting-up-cicd-for-a-nodejs-backend-with-github-actions-and-aws-3e9</link>
      <guid>https://dev.to/sahinur/setting-up-cicd-for-a-nodejs-backend-with-github-actions-and-aws-3e9</guid>
      <description>&lt;p&gt;Deploying a backend manually works when a project is small.&lt;/p&gt;

&lt;p&gt;SSH into the server, pull the latest code, install dependencies, restart the application, and check whether everything is working.&lt;/p&gt;

&lt;p&gt;But as the application and team grow, this process becomes repetitive—and manual steps are easy to get wrong.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;CI/CD&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through a practical CI/CD approach for a &lt;strong&gt;Node.js + Express backend deployed on AWS EC2&lt;/strong&gt; using &lt;strong&gt;GitHub Actions&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before: How we deployed
&lt;/h2&gt;

&lt;p&gt;A simple manual deployment can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
   ↓
Git Push
   ↓
GitHub
   ↓
SSH into EC2
   ↓
git pull
   ↓
npm install
   ↓
PM2 restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;But every deployment depends on someone remembering the correct sequence of commands.&lt;/p&gt;

&lt;p&gt;A small mistake can cause problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting to pull the latest code&lt;/li&gt;
&lt;li&gt;Installing dependencies incorrectly&lt;/li&gt;
&lt;li&gt;Restarting the wrong process&lt;/li&gt;
&lt;li&gt;Deploying from the wrong branch&lt;/li&gt;
&lt;li&gt;Forgetting an environment variable&lt;/li&gt;
&lt;li&gt;Restarting the application before checking the build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal of CI/CD is to make this process more predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline I want
&lt;/h2&gt;

&lt;p&gt;For a Node.js backend, a simple pipeline can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
    ↓
GitHub
    ↓
GitHub Actions
    ↓
Install Dependencies
    ↓
Run Tests
    ↓
Build / Validate
    ↓
Deploy to AWS EC2
    ↓
Restart Application
    ↓
Health Check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important idea is that a deployment should not simply mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Push code → restart server."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Push code → verify code → deploy → verify deployment."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: Create the workflow
&lt;/h2&gt;

&lt;p&gt;GitHub Actions workflows live inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/
└── workflows/
    └── deploy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A basic workflow can start like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Node.js API&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every push to the &lt;code&gt;main&lt;/code&gt; branch can automatically trigger the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Validate before deploying
&lt;/h2&gt;

&lt;p&gt;One of the most important principles I follow with CI/CD is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't deploy code that hasn't passed basic checks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also include linting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run lint&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if the project has a build step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build application&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact commands depend on the project.&lt;/p&gt;

&lt;p&gt;The important thing is that the deployment job should depend on these checks succeeding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Deploy to EC2
&lt;/h2&gt;

&lt;p&gt;There are several ways to deploy an application to EC2.&lt;/p&gt;

&lt;p&gt;A straightforward approach is to connect to the server and execute deployment commands.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Actions
      ↓
SSH
      ↓
EC2
      ↓
Pull latest code
      ↓
Install dependencies
      ↓
Restart PM2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A deployment command might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/my-api

git pull origin main

npm ci

pm2 restart my-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact directory, process name, and deployment strategy depend on the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Keep secrets out of Git
&lt;/h2&gt;

&lt;p&gt;This is one of the most important parts of CI/CD.&lt;/p&gt;

&lt;p&gt;I don't want credentials such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
DATABASE_URL
JWT_SECRET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside the repository.&lt;/p&gt;

&lt;p&gt;Instead, sensitive values should be managed through appropriate secret-management mechanisms.&lt;/p&gt;

&lt;p&gt;For GitHub Actions, repository or environment secrets can be used for CI/CD credentials.&lt;/p&gt;

&lt;p&gt;For the application itself, production configuration should ideally be managed separately from source code.&lt;/p&gt;

&lt;p&gt;The basic rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Source code can be committed. Secrets should not be.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What broke the first time
&lt;/h2&gt;

&lt;p&gt;CI/CD looks simple when drawn as a diagram.&lt;/p&gt;

&lt;p&gt;Real deployments are rarely that simple.&lt;/p&gt;

&lt;p&gt;Some common problems I watch for are:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Environment differences
&lt;/h3&gt;

&lt;p&gt;The application works locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local → Works
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But production has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Production → Missing environment variable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code may be correct while the deployment configuration is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Node.js version mismatch
&lt;/h3&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local:
Node.js 20

CI:
Node.js 18

Production:
Node.js 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can create confusing build or dependency issues.&lt;/p&gt;

&lt;p&gt;That's why I prefer explicitly defining the Node.js version in CI.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dependency installation
&lt;/h3&gt;

&lt;p&gt;Using:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can produce different dependency resolution behavior depending on the project state.&lt;/p&gt;

&lt;p&gt;For CI environments, I generally prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when a valid &lt;code&gt;package-lock.json&lt;/code&gt; is available.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Restarting the wrong process
&lt;/h3&gt;

&lt;p&gt;With PM2, the deployment isn't finished just because the new code reached the server.&lt;/p&gt;

&lt;p&gt;The correct application process needs to be restarted or reloaded.&lt;/p&gt;

&lt;p&gt;After deployment, I want to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and inspect logs when necessary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Health checks matter
&lt;/h2&gt;

&lt;p&gt;A successful deployment command doesn't necessarily mean a successful deployment.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git pull       ✓
npm ci         ✓
pm2 restart    ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application could still be returning:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That's why a health check is useful.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-f&lt;/span&gt; https://api.example.com/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the health endpoint fails, the deployment should be treated as unsuccessful.&lt;/p&gt;

&lt;p&gt;This gives the pipeline a much better definition of "success."&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback strategy
&lt;/h2&gt;

&lt;p&gt;One of the biggest things I want from a deployment system is a safe way to recover.&lt;/p&gt;

&lt;p&gt;A simple rollback strategy can be based on Git commits.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version A
   ↓
Deploy
   ↓
Version B
   ↓
Problem detected
   ↓
Rollback
   ↓
Version A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before making the deployment more advanced, I want to make sure the team knows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What version is currently running?&lt;/li&gt;
&lt;li&gt;What changed?&lt;/li&gt;
&lt;li&gt;How do we return to the previous version?&lt;/li&gt;
&lt;li&gt;How do we verify the rollback?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For larger systems, more advanced strategies such as blue/green or canary deployments can provide safer releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a mature pipeline looks like
&lt;/h2&gt;

&lt;p&gt;Eventually, the workflow can become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              ┌──────────────┐
              │   Developer  │
              └──────┬───────┘
                     │
                     ▼
                ┌─────────┐
                │ GitHub  │
                └────┬────┘
                     │
                     ▼
             ┌───────────────┐
             │ GitHub Actions│
             └───────┬───────┘
                     │
          ┌──────────┴──────────┐
          ▼                     ▼
     Install/Test            Lint/Build
          │                     │
          └──────────┬──────────┘
                     ▼
                  Deploy
                     │
                     ▼
                  AWS EC2
                     │
                     ▼
               Health Check
                     │
              ┌──────┴──────┐
              ▼             ▼
           Success        Failure
              │             │
              ▼             ▼
          Complete       Rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to create the most complicated pipeline possible.&lt;/p&gt;

&lt;p&gt;It's to remove unnecessary manual work while making deployments safer.&lt;/p&gt;

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

&lt;p&gt;The biggest lesson for me is that &lt;strong&gt;CI/CD isn't just automation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's about creating a repeatable engineering process.&lt;/p&gt;

&lt;p&gt;A good pipeline should answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has the code been tested?&lt;/li&gt;
&lt;li&gt;Can it be deployed safely?&lt;/li&gt;
&lt;li&gt;What version was deployed?&lt;/li&gt;
&lt;li&gt;Did the deployment actually work?&lt;/li&gt;
&lt;li&gt;Can we recover if something goes wrong?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a relatively simple Node.js backend can benefit from this mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;Manual deployment isn't necessarily bad.&lt;/p&gt;

&lt;p&gt;It's just difficult to scale.&lt;/p&gt;

&lt;p&gt;As projects grow, automation becomes valuable because it reduces repetitive work and makes the deployment process predictable.&lt;/p&gt;

&lt;p&gt;For a Node.js + Express backend on AWS EC2, a simple GitHub Actions pipeline can provide a solid starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code
 ↓
Test
 ↓
Build
 ↓
Deploy
 ↓
Health Check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, the pipeline can evolve with the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal of CI/CD isn't to deploy faster at any cost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's to deploy &lt;strong&gt;more consistently, more safely, and with greater confidence.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>devops</category>
      <category>node</category>
    </item>
    <item>
      <title>How I Approach AWS Cost Optimization as a Backend Developer</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/how-i-approach-aws-cost-optimization-as-a-backend-developer-16m</link>
      <guid>https://dev.to/sahinur/how-i-approach-aws-cost-optimization-as-a-backend-developer-16m</guid>
      <description>&lt;p&gt;AWS makes it very easy to deploy an application.&lt;/p&gt;

&lt;p&gt;You can launch an EC2 instance, connect your backend, configure a database, add storage, and have a production system running relatively quickly.&lt;/p&gt;

&lt;p&gt;But there's another side of cloud infrastructure that becomes important as an application grows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much is it actually costing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a backend developer, I've learned that AWS cost optimization isn't about simply choosing the cheapest service.&lt;/p&gt;

&lt;p&gt;It's about understanding &lt;strong&gt;what you're running, why you're running it, and whether you're actually using the resources you're paying for.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some of the things I look at when thinking about AWS costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point
&lt;/h2&gt;

&lt;p&gt;For a typical Node.js backend running on AWS, the infrastructure might look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Nginx
   ↓
EC2
   ↓
Node.js + Express
   ↓
MongoDB

Additional services:
- S3
- CloudWatch
- Load Balancer
- Data Transfer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service can contribute to the overall infrastructure cost.&lt;/p&gt;

&lt;p&gt;The important thing is that the bill isn't always dominated by the service you expect.&lt;/p&gt;

&lt;p&gt;Sometimes a small collection of unused resources can quietly generate costs over time.&lt;/p&gt;

&lt;p&gt;That's why I prefer to start with &lt;strong&gt;visibility before optimization&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change 1: Right-size compute resources
&lt;/h2&gt;

&lt;p&gt;One of the first things I look at is whether the EC2 instance is appropriately sized for the workload.&lt;/p&gt;

&lt;p&gt;It's tempting to choose a larger instance because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"More CPU and RAM means better performance."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But if the application consistently uses only a fraction of those resources, we're paying for capacity we're not using.&lt;/p&gt;

&lt;p&gt;I look at metrics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU utilization&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Network traffic&lt;/li&gt;
&lt;li&gt;Request patterns&lt;/li&gt;
&lt;li&gt;Application workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the smallest server possible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the right server for the workload.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, if an application consistently operates far below the capacity of its instance, moving to an appropriately sized instance can reduce unnecessary infrastructure cost.&lt;/p&gt;

&lt;p&gt;But this needs to be done carefully.&lt;/p&gt;

&lt;p&gt;Reducing resources too aggressively can create performance and reliability problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change 2: Review storage usage
&lt;/h2&gt;

&lt;p&gt;Storage is another area I pay attention to.&lt;/p&gt;

&lt;p&gt;For applications using Amazon S3, not every object necessarily needs to remain in the same storage class forever.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New data
   ↓
Frequently accessed
   ↓
Less frequently accessed
   ↓
Archive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where lifecycle policies can become useful.&lt;/p&gt;

&lt;p&gt;Instead of manually moving old data, lifecycle rules can automatically transition objects according to the application's requirements.&lt;/p&gt;

&lt;p&gt;The important question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this data still need the same level of accessibility as when it was created?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If not, there may be an opportunity to optimize storage costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change 3: Remove unused resources
&lt;/h2&gt;

&lt;p&gt;This is one of the simplest optimization opportunities.&lt;/p&gt;

&lt;p&gt;Cloud infrastructure can accumulate resources that were created for testing, debugging, or previous versions of an application.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unused EC2 instances
Old EBS volumes
Old snapshots
Unused load balancers
Unused Elastic IPs
Old test resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A resource that isn't being used can still cost money.&lt;/p&gt;

&lt;p&gt;This is why I think regular infrastructure cleanup is important.&lt;/p&gt;

&lt;p&gt;Before deleting anything, I verify that the resource isn't required by another application or recovery process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change 4: Understand traffic and data transfer
&lt;/h2&gt;

&lt;p&gt;Another area that can be overlooked is data transfer.&lt;/p&gt;

&lt;p&gt;An application might have perfectly reasonable compute costs but generate unexpected costs through network traffic.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  ↓
Application
  ↓
Database
  ↓
Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When data moves between different services, regions, or external systems, the traffic pattern can affect the overall cost.&lt;/p&gt;

&lt;p&gt;So when investigating AWS costs, I don't look only at CPU and memory.&lt;/p&gt;

&lt;p&gt;I also consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request volume&lt;/li&gt;
&lt;li&gt;Network traffic&lt;/li&gt;
&lt;li&gt;Data transfer&lt;/li&gt;
&lt;li&gt;Storage growth&lt;/li&gt;
&lt;li&gt;External API usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The tools I use to think about cost
&lt;/h2&gt;

&lt;p&gt;AWS provides several tools that can help with cost visibility and optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Cost Explorer
&lt;/h3&gt;

&lt;p&gt;Cost Explorer helps break down AWS spending and identify where costs are coming from.&lt;/p&gt;

&lt;p&gt;Instead of looking only at the total bill, I want to answer questions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which service costs the most?

Which resource changed recently?

Is the cost increasing?

What caused the increase?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the bill much more useful as an engineering signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Budgets
&lt;/h3&gt;

&lt;p&gt;Budgets can help establish spending thresholds and alerts.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monthly budget
      ↓
Usage tracking
      ↓
Threshold reached
      ↓
Alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially useful for projects where unexpected resource usage could result in an unexpected bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Compute Optimizer
&lt;/h3&gt;

&lt;p&gt;For compute resources, AWS Compute Optimizer can provide recommendations based on observed workload patterns.&lt;/p&gt;

&lt;p&gt;This is useful because infrastructure decisions shouldn't be based entirely on assumptions.&lt;/p&gt;

&lt;p&gt;Actual usage data is much more valuable.&lt;/p&gt;

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

&lt;p&gt;One of the biggest lessons for me is that &lt;strong&gt;cost optimization is an engineering responsibility, not only a finance responsibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every architecture decision can have a cost implication.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;More instances
      ↓
Higher compute cost

More storage
      ↓
Higher storage cost

More traffic
      ↓
Potentially higher transfer cost

More logging
      ↓
Potentially higher observability cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't mean we should avoid using AWS services.&lt;/p&gt;

&lt;p&gt;It means we should understand the trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost optimization vs performance
&lt;/h2&gt;

&lt;p&gt;There is also an important balance.&lt;/p&gt;

&lt;p&gt;You shouldn't optimize cost by making the application unreliable.&lt;/p&gt;

&lt;p&gt;For example, reducing an EC2 instance from a large size to a tiny instance might save money, but if the application starts running out of memory under production traffic, the optimization wasn't successful.&lt;/p&gt;

&lt;p&gt;The same applies to databases, storage, networking, and monitoring.&lt;/p&gt;

&lt;p&gt;A better principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Optimize unnecessary cost, not necessary capacity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A simple checklist I follow
&lt;/h2&gt;

&lt;p&gt;Before considering an AWS environment optimized, I'd ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Are compute resources appropriately sized?
[ ] Are unused resources removed?
[ ] Are storage lifecycle policies being used where appropriate?
[ ] Are unexpected traffic costs understood?
[ ] Are AWS costs being monitored regularly?
[ ] Are spending alerts configured?
[ ] Are production resources separated from temporary resources?
[ ] Are optimization decisions based on actual usage?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't need to be a complicated process.&lt;/p&gt;

&lt;p&gt;Even a regular review can reveal resources that are no longer necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;AWS cost optimization isn't about making everything as cheap as possible.&lt;/p&gt;

&lt;p&gt;It's about making sure the infrastructure you're paying for is actually providing value.&lt;/p&gt;

&lt;p&gt;As a backend developer, I now try to think about three things together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance + Reliability + Cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A good cloud architecture should balance all three.&lt;/p&gt;

&lt;p&gt;The cheapest architecture isn't necessarily the best architecture.&lt;/p&gt;

&lt;p&gt;The best architecture is the one that provides the required performance and reliability &lt;strong&gt;without paying for unnecessary capacity.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>costoptimization</category>
      <category>cloudcomputing</category>
      <category>finops</category>
    </item>
    <item>
      <title>RDS vs DynamoDB: How I Think About Choosing an AWS Database</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Thu, 17 Sep 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/rds-vs-dynamodb-how-i-think-about-choosing-an-aws-database-7eh</link>
      <guid>https://dev.to/sahinur/rds-vs-dynamodb-how-i-think-about-choosing-an-aws-database-7eh</guid>
      <description>&lt;p&gt;Choosing a database is one of those decisions that can look simple at the beginning of a project.&lt;/p&gt;

&lt;p&gt;You need to store some data, so you pick a database and start building.&lt;/p&gt;

&lt;p&gt;But once you start thinking about &lt;strong&gt;scale, queries, cost, performance, and maintenance&lt;/strong&gt;, the decision becomes much more interesting.&lt;/p&gt;

&lt;p&gt;As a Node.js backend developer, I've mostly worked with MongoDB, but while learning and working with AWS, I've spent more time understanding how AWS database services such as &lt;strong&gt;Amazon RDS&lt;/strong&gt; and &lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; fit into backend architectures.&lt;/p&gt;

&lt;p&gt;They solve very different problems.&lt;/p&gt;

&lt;p&gt;Here's how I approach the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data shape comes first
&lt;/h2&gt;

&lt;p&gt;Before choosing a database, I think about the data and, more importantly, &lt;strong&gt;how the application will access that data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, imagine an e-commerce application.&lt;/p&gt;

&lt;p&gt;We might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Users
Products
Orders
Payments
Reviews
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An order might contain relationships between multiple entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓
Orders
  ↓
Products
  ↓
Payments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application requires complex relationships, joins, transactions, and flexible SQL queries, a relational database becomes very attractive.&lt;/p&gt;

&lt;p&gt;That's where RDS can make sense.&lt;/p&gt;

&lt;p&gt;But not every application needs that model.&lt;/p&gt;

&lt;p&gt;Imagine an application where the primary access pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get user by ID
Get user's notifications
Get latest notifications
Mark notification as read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If these access patterns are predictable and the application needs massive scalability, DynamoDB becomes an interesting option.&lt;/p&gt;

&lt;p&gt;The key lesson is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't start with the database. Start with the data and access patterns.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What pulls me toward RDS
&lt;/h2&gt;

&lt;p&gt;Amazon RDS provides managed relational databases such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;MariaDB&lt;/li&gt;
&lt;li&gt;Oracle&lt;/li&gt;
&lt;li&gt;SQL Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the biggest advantages is the relational model.&lt;/p&gt;

&lt;p&gt;For applications with relationships between entities, SQL can make the data model and queries much easier to reason about.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type of relationship-oriented query is natural in a relational database.&lt;/p&gt;

&lt;p&gt;RDS is particularly attractive when I need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex queries&lt;/li&gt;
&lt;li&gt;Relationships&lt;/li&gt;
&lt;li&gt;Joins&lt;/li&gt;
&lt;li&gt;Transactions&lt;/li&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;li&gt;Reporting&lt;/li&gt;
&lt;li&gt;Existing relational database expertise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another advantage is that many backend developers already understand SQL and relational database design.&lt;/p&gt;

&lt;p&gt;That can significantly reduce the learning curve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What pulls me toward DynamoDB
&lt;/h2&gt;

&lt;p&gt;DynamoDB is fundamentally different.&lt;/p&gt;

&lt;p&gt;Instead of starting with relationships and joins, I think about &lt;strong&gt;access patterns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GetUserById
GetOrdersByUser
GetLatestOrders
GetOrderById
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table and indexes are designed around how the application will query the data.&lt;/p&gt;

&lt;p&gt;This approach can be extremely powerful for applications that need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High scalability&lt;/li&gt;
&lt;li&gt;Low-latency access&lt;/li&gt;
&lt;li&gt;Predictable access patterns&lt;/li&gt;
&lt;li&gt;Serverless architectures&lt;/li&gt;
&lt;li&gt;High request volumes&lt;/li&gt;
&lt;li&gt;Automatic scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DynamoDB also removes much of the traditional database server management.&lt;/p&gt;

&lt;p&gt;That makes it particularly interesting when building serverless applications with services such as Lambda and API Gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architectural difference
&lt;/h2&gt;

&lt;p&gt;A traditional RDS-based backend might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
API
   ↓
Node.js / Express
   ↓
Amazon RDS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application connects to a relational database and performs SQL queries.&lt;/p&gt;

&lt;p&gt;A serverless architecture could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
API Gateway
   ↓
Lambda
   ↓
DynamoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the database fits naturally into an event-driven or serverless architecture.&lt;/p&gt;

&lt;p&gt;Neither architecture is automatically better.&lt;/p&gt;

&lt;p&gt;The workload determines which one makes more sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about MongoDB?
&lt;/h2&gt;

&lt;p&gt;This is especially relevant to me because my backend experience includes &lt;strong&gt;MongoDB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MongoDB uses a document-oriented model:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sahinur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Node.js&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;Express&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;MongoDB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be very convenient for applications where the data naturally fits into documents.&lt;/p&gt;

&lt;p&gt;It also feels familiar when working with JavaScript and Node.js.&lt;/p&gt;

&lt;p&gt;That experience made me realize something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database choice isn't only about technical specifications.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Developer experience, existing knowledge, application requirements, and team expertise all matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I would make the decision
&lt;/h2&gt;

&lt;p&gt;If I were starting a new AWS backend, I'd ask these questions first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Do I need complex relationships?
&lt;/h3&gt;

&lt;p&gt;If the application heavily depends on relationships and joins, I'd lean toward a relational database such as RDS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Are my access patterns predictable?
&lt;/h3&gt;

&lt;p&gt;If I know exactly how the application will read and write data and need highly scalable low-latency access, DynamoDB becomes more attractive.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Do I need SQL?
&lt;/h3&gt;

&lt;p&gt;If the team relies heavily on SQL queries, reporting, and relational data analysis, RDS is likely the easier choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Is the application serverless?
&lt;/h3&gt;

&lt;p&gt;If I'm building around Lambda and API Gateway, DynamoDB can fit naturally into that architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. What does the team already know?
&lt;/h3&gt;

&lt;p&gt;A technically powerful database isn't useful if the team doesn't understand how to model and operate it effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;RDS&lt;/th&gt;
&lt;th&gt;DynamoDB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database model&lt;/td&gt;
&lt;td&gt;Relational&lt;/td&gt;
&lt;td&gt;NoSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query model&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;Key/index based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Joins&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No traditional joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactions&lt;/td&gt;
&lt;td&gt;Strong relational transactions&lt;/td&gt;
&lt;td&gt;Supported, but different model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Managed, but capacity planning still matters&lt;/td&gt;
&lt;td&gt;Designed for large-scale automatic scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;td&gt;Structured&lt;/td&gt;
&lt;td&gt;Flexible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Relational applications&lt;/td&gt;
&lt;td&gt;High-scale access-pattern-driven workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless fit&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The biggest lesson
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I've learned while studying different database architectures is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't choose a database because it's popular. Choose it because its data model matches your application.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A database can be extremely fast and scalable but still be the wrong choice if your application's access patterns don't fit it.&lt;/p&gt;

&lt;p&gt;Similarly, choosing a relational database when your application has simple key-value access patterns can introduce unnecessary complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do before committing
&lt;/h2&gt;

&lt;p&gt;Before choosing a database, I'd write down the application's most important operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Create user
2. Get user by ID
3. Get user's orders
4. Create order
5. Update order
6. Get latest orders
7. Generate reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I'd evaluate how naturally each database handles those operations.&lt;/p&gt;

&lt;p&gt;This exercise can reveal more than a generic database comparison ever will.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;For me, the RDS vs DynamoDB discussion isn't really about which service is better.&lt;/p&gt;

&lt;p&gt;It's about &lt;strong&gt;matching the database model to the application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If I need relational data, SQL, joins, and complex queries, RDS is a strong candidate.&lt;/p&gt;

&lt;p&gt;If I have predictable access patterns and need highly scalable, low-latency NoSQL access, DynamoDB is worth serious consideration.&lt;/p&gt;

&lt;p&gt;And my experience with MongoDB has reinforced the same principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The right database is the one that makes your application's most important operations simple, reliable, and scalable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the way I now approach database decisions when designing backend systems on AWS.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>dynamodb</category>
      <category>rds</category>
    </item>
    <item>
      <title>How I Would Structure a Node.js Backend on AWS: EC2 vs Lambda</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Thu, 10 Sep 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/sahinur/how-i-would-structure-a-nodejs-backend-on-aws-ec2-vs-lambda-44oh</link>
      <guid>https://dev.to/sahinur/how-i-would-structure-a-nodejs-backend-on-aws-ec2-vs-lambda-44oh</guid>
      <description>&lt;p&gt;When building a backend on AWS, one of the first decisions is not which framework to use.&lt;/p&gt;

&lt;p&gt;It's &lt;strong&gt;where and how the backend should run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a Node.js developer, I've worked with Express-based APIs and AWS EC2, and one question that keeps coming up is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I run my backend on a traditional server, or move toward a serverless architecture such as API Gateway + Lambda?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There isn't one answer for every application.&lt;/p&gt;

&lt;p&gt;The right choice depends on traffic, workload, architecture, team experience, and operational requirements.&lt;/p&gt;

&lt;p&gt;Here's how I think about the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The requirement
&lt;/h2&gt;

&lt;p&gt;For a typical Node.js backend, I usually need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;li&gt;Authentication and authorization&lt;/li&gt;
&lt;li&gt;MongoDB or another database&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;li&gt;Background processing&lt;/li&gt;
&lt;li&gt;Logging and monitoring&lt;/li&gt;
&lt;li&gt;Reliable deployment&lt;/li&gt;
&lt;li&gt;The ability to handle changing traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple backend might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Node.js + Express API
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When deploying this to AWS, there are several ways to run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options I considered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option A: EC2 + Node.js + Express
&lt;/h3&gt;

&lt;p&gt;This is the traditional approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Nginx
   ↓
AWS EC2
   ↓
Node.js + Express
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The biggest advantage is control.&lt;/p&gt;

&lt;p&gt;I can manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The operating system&lt;/li&gt;
&lt;li&gt;Node.js process&lt;/li&gt;
&lt;li&gt;PM2&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;Installed dependencies&lt;/li&gt;
&lt;li&gt;Background processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's also a straightforward model for developers who already understand traditional server deployment.&lt;/p&gt;

&lt;p&gt;But that control also creates more responsibility.&lt;/p&gt;

&lt;p&gt;I need to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server maintenance&lt;/li&gt;
&lt;li&gt;Scaling&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Process management&lt;/li&gt;
&lt;li&gt;Security updates&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;High availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've seen this firsthand with production Node.js applications: the application itself may be working correctly, but server resources such as memory can become a problem under real traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: API Gateway + Lambda
&lt;/h3&gt;

&lt;p&gt;The serverless approach changes the architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
API Gateway
   ↓
Lambda
   ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of keeping a Node.js server running continuously, individual Lambda functions execute in response to requests.&lt;/p&gt;

&lt;p&gt;This can reduce the amount of server infrastructure I need to manage.&lt;/p&gt;

&lt;p&gt;It can also make automatic scaling much easier for workloads that aren't constantly running at high utilization.&lt;/p&gt;

&lt;p&gt;But serverless introduces its own considerations.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold starts&lt;/li&gt;
&lt;li&gt;Execution limits&lt;/li&gt;
&lt;li&gt;Function design&lt;/li&gt;
&lt;li&gt;Database connection management&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Local development&lt;/li&gt;
&lt;li&gt;Vendor-specific architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So serverless isn't automatically "better."&lt;/p&gt;

&lt;h3&gt;
  
  
  Option C: ECS / Fargate
&lt;/h3&gt;

&lt;p&gt;Another option is containerizing the Node.js application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Load Balancer
   ↓
ECS / Fargate
   ↓
Node.js + Express
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives me many of the benefits of containers without requiring me to manage the underlying EC2 instances directly.&lt;/p&gt;

&lt;p&gt;For larger applications or teams already using Docker, this can be a very attractive middle ground.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture I'd choose
&lt;/h2&gt;

&lt;p&gt;For a small-to-medium Node.js API with predictable traffic, EC2 can still be a practical choice.&lt;/p&gt;

&lt;p&gt;A basic architecture could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌──────────────┐
                 │    Client    │
                 └──────┬───────┘
                        │
                        ▼
                 ┌──────────────┐
                 │    Nginx     │
                 └──────┬───────┘
                        │
                        ▼
              ┌──────────────────┐
              │     AWS EC2      │
              │                  │
              │ Node.js +        │
              │ Express + PM2    │
              └────────┬─────────┘
                       │
                       ▼
                 ┌──────────────┐
                 │   MongoDB    │
                 └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture is simple, familiar, and gives me direct control over the application environment.&lt;/p&gt;

&lt;p&gt;But I wouldn't automatically use it for every new project.&lt;/p&gt;

&lt;p&gt;If the application has highly variable traffic, independent functions, or a strong serverless requirement, I'd seriously consider API Gateway + Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade-offs
&lt;/h2&gt;

&lt;p&gt;The interesting part isn't choosing the "most modern" AWS service.&lt;/p&gt;

&lt;p&gt;It's understanding what you're giving up and what you're gaining.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;EC2&lt;/th&gt;
&lt;th&gt;Lambda&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server management&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Requires configuration&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running processes&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold starts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure control&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment model&lt;/td&gt;
&lt;td&gt;Server/application&lt;/td&gt;
&lt;td&gt;Function-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational complexity&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Different, not zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Persistent services&lt;/td&gt;
&lt;td&gt;Event/API-driven workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is why I don't think &lt;strong&gt;"serverless means no problems."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It means you're moving some operational responsibilities away from servers and taking on a different set of architectural considerations.&lt;/p&gt;

&lt;h2&gt;
  
  
  A lesson from production
&lt;/h2&gt;

&lt;p&gt;One thing that changed how I think about architecture is realizing that application behavior matters just as much as infrastructure choice.&lt;/p&gt;

&lt;p&gt;For example, a Node.js API can run perfectly on a powerful EC2 instance and still have problems if an endpoint loads thousands of MongoDB documents into memory.&lt;/p&gt;

&lt;p&gt;Likewise, moving that endpoint to Lambda wouldn't automatically fix inefficient data processing.&lt;/p&gt;

&lt;p&gt;The architecture can help, but &lt;strong&gt;good application design still matters.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd change next time
&lt;/h2&gt;

&lt;p&gt;If I were starting a new AWS backend today, I'd make the architecture decision based on the workload instead of choosing a service simply because it's popular.&lt;/p&gt;

&lt;p&gt;I'd ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How predictable is the traffic?&lt;/li&gt;
&lt;li&gt;Does the application need long-running processes?&lt;/li&gt;
&lt;li&gt;How much infrastructure do we want to manage?&lt;/li&gt;
&lt;li&gt;What are the database access patterns?&lt;/li&gt;
&lt;li&gt;How important is automatic scaling?&lt;/li&gt;
&lt;li&gt;What are the latency requirements?&lt;/li&gt;
&lt;li&gt;Does the team already have experience with containers or serverless?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after answering those questions would I choose between EC2, ECS/Fargate, and Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;There is no single AWS architecture that is perfect for every Node.js application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2, ECS/Fargate, and Lambda solve different problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, the important lesson is to start with the application's requirements and workload, then choose the AWS service that fits.&lt;/p&gt;

&lt;p&gt;The goal isn't to use the most services.&lt;/p&gt;

&lt;p&gt;The goal is to build a system that is &lt;strong&gt;reliable, maintainable, scalable, and appropriate for the actual workload.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that's one of the things I'm continuing to learn as I work deeper with AWS and backend engineering.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>architecture</category>
      <category>node</category>
    </item>
    <item>
      <title>A Node.js Backend Memory Issue That Only Showed Up in Production</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Wed, 02 Sep 2026 04:27:15 +0000</pubDate>
      <link>https://dev.to/sahinur/a-nodejs-backend-memory-issue-that-only-showed-up-in-production-2ljf</link>
      <guid>https://dev.to/sahinur/a-nodejs-backend-memory-issue-that-only-showed-up-in-production-2ljf</guid>
      <description>&lt;p&gt;One of the most frustrating backend problems is an issue that works perfectly in development and staging, but starts failing when real production traffic arrives.&lt;/p&gt;

&lt;p&gt;I recently worked with a &lt;strong&gt;Node.js + Express + MongoDB&lt;/strong&gt; backend running on an &lt;strong&gt;AWS EC2 instance&lt;/strong&gt;, where we started seeing unexpected memory usage in production.&lt;/p&gt;

&lt;p&gt;The application was working normally at first. Then, as traffic increased, the server's memory usage kept climbing until the Node.js process eventually crashed.&lt;/p&gt;

&lt;p&gt;Here's how I approached the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;The backend was built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Express.js&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;AWS EC2&lt;/li&gt;
&lt;li&gt;PM2 for process management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The basic architecture looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Nginx
   ↓
Node.js + Express
   ↓
MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application handled API requests normally during development and staging.&lt;/p&gt;

&lt;p&gt;The EC2 instance also had enough resources for the expected workload, so initially there was no obvious reason for the server to run out of memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong
&lt;/h2&gt;

&lt;p&gt;After the application had been running under production traffic for some time, memory usage started increasing.&lt;/p&gt;

&lt;p&gt;We noticed the EC2 instance's memory utilization gradually climbing instead of returning to its normal level after requests were completed.&lt;/p&gt;

&lt;p&gt;Eventually, the Node.js process became unstable and crashed.&lt;/p&gt;

&lt;p&gt;A representative error looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;--- Last few GCs ---&amp;gt;

FATAL ERROR: Reached heap limit
Allocation failed - JavaScript heap out of memory

Node.js v20.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, increasing the server resources seemed like the easiest solution.&lt;/p&gt;

&lt;p&gt;But that would only hide the actual problem.&lt;/p&gt;

&lt;p&gt;The important question was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why was the application continuously consuming more memory?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why staging didn't catch it
&lt;/h2&gt;

&lt;p&gt;This was one of the interesting parts of the problem.&lt;/p&gt;

&lt;p&gt;The application worked correctly in staging because the environment was significantly different from production.&lt;/p&gt;

&lt;p&gt;The main differences were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower traffic&lt;/li&gt;
&lt;li&gt;Fewer concurrent requests&lt;/li&gt;
&lt;li&gt;Smaller datasets&lt;/li&gt;
&lt;li&gt;Shorter test duration&lt;/li&gt;
&lt;li&gt;Less realistic production usage patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A memory problem doesn't necessarily appear immediately.&lt;/p&gt;

&lt;p&gt;An application can use a little more memory after every request without causing any visible problem during a short test.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 1  →  500 MB
Request 100 → 550 MB
Request 1,000 → 700 MB
Request 10,000 → 1.2 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eventually, the server reaches its available memory limit.&lt;/p&gt;

&lt;p&gt;This made the problem difficult to reproduce in staging.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I investigated the problem
&lt;/h2&gt;

&lt;p&gt;The first thing I did was check the EC2 instance's resource usage.&lt;/p&gt;

&lt;p&gt;I used system-level monitoring to check memory consumption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also checked running processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and inspected the Node.js process managed by PM2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 status
pm2 monit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important observation was that CPU usage wasn't consistently high.&lt;/p&gt;

&lt;p&gt;Memory usage, however, continued increasing.&lt;/p&gt;

&lt;p&gt;That changed the direction of the investigation.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why is the API slow?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What is keeping objects in memory longer than expected?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Looking at the application
&lt;/h2&gt;

&lt;p&gt;I started reviewing endpoints that processed large amounts of data.&lt;/p&gt;

&lt;p&gt;One suspicious pattern was fetching a large dataset from MongoDB and keeping the entire result in memory before processing it.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// process user&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can become expensive when the collection grows.&lt;/p&gt;

&lt;p&gt;If MongoDB returns thousands or tens of thousands of documents, Node.js has to hold those JavaScript objects in memory.&lt;/p&gt;

&lt;p&gt;The problem becomes worse if multiple requests are doing the same thing concurrently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The first improvement was to avoid loading unnecessary records into memory.&lt;/p&gt;

&lt;p&gt;Instead of retrieving everything, we introduced pagination:&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&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;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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;users&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&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;skip&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application only processes a limited number of documents per request.&lt;/p&gt;

&lt;p&gt;We also reviewed queries to make sure we weren't returning fields that the API didn't actually need.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name email status&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;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduced unnecessary data being loaded into the Node.js process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring after the fix
&lt;/h2&gt;

&lt;p&gt;After making the changes, I continued monitoring the application rather than assuming the problem was solved.&lt;/p&gt;

&lt;p&gt;The important thing was to observe memory usage over a longer period under realistic traffic.&lt;/p&gt;

&lt;p&gt;The expected pattern was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Memory
  │
  │       ╭──╮      ╭──╮
  │    ╭──╯  ╰──╮ ╭─╯  ╰──
  │────╯────────╰─╯──────────
  │
  └────────────────────────── Time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of continuously climbing, memory usage should stabilize within a reasonable range.&lt;/p&gt;

&lt;p&gt;I also kept PM2 monitoring in place so that unexpected process behavior could be detected quickly.&lt;/p&gt;

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

&lt;p&gt;This incident taught me an important lesson:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A backend can work perfectly in development and still have serious resource problems in production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For Node.js applications, I now pay much more attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large MongoDB queries&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Selecting only required fields&lt;/li&gt;
&lt;li&gt;Processing large datasets&lt;/li&gt;
&lt;li&gt;Concurrent requests&lt;/li&gt;
&lt;li&gt;Memory usage over time&lt;/li&gt;
&lt;li&gt;Production-like load testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also learned that simply increasing the EC2 instance size isn't always the correct solution.&lt;/p&gt;

&lt;p&gt;More RAM can postpone the crash, but it doesn't necessarily fix the underlying problem.&lt;/p&gt;

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

&lt;p&gt;If I were setting up the same system again, I'd introduce memory monitoring and load testing much earlier.&lt;/p&gt;

&lt;p&gt;I'd also test endpoints with production-sized datasets instead of relying only on small staging datasets.&lt;/p&gt;

&lt;p&gt;The biggest takeaway for me was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't just test whether your API works. Test how it behaves after running for a long time under realistic load.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where some of the most interesting backend problems reveal themselves.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>backend</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Book Review: "Software Development From A to Z" — The Book I Wish I'd Read Before My First Dev Job</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Thu, 20 Aug 2026 09:27:19 +0000</pubDate>
      <link>https://dev.to/sahinur/book-review-software-development-from-a-to-z-the-book-i-wish-id-read-before-my-first-dev-job-299a</link>
      <guid>https://dev.to/sahinur/book-review-software-development-from-a-to-z-the-book-i-wish-id-read-before-my-first-dev-job-299a</guid>
      <description>&lt;p&gt;Most programming books teach you one thing deeply: a language, a framework, a database. Very few explain how all of those things fit together inside an actual team, or why the product manager keeps asking you for estimates, or what the designer is doing for three weeks before you get a single mockup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software Development From A to Z&lt;/strong&gt; by Olga Filipova and Rui Vilão (Apress, 2018) tries to fill exactly that gap. I finished it recently, and here's my take.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the book is about
&lt;/h2&gt;

&lt;p&gt;The premise is simple: follow one small team building one product, an online learning platform, from "hey, I have an idea" all the way to maintaining it in production. Each chapter is narrated from the seat of a different role:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Having an idea (and turning it into an MVP)&lt;/li&gt;
&lt;li&gt;Roles, responsibilities, Scrum and Kanban&lt;/li&gt;
&lt;li&gt;The product manager: requirements and deadlines&lt;/li&gt;
&lt;li&gt;The designer: user-centered design&lt;/li&gt;
&lt;li&gt;The backend engineer&lt;/li&gt;
&lt;li&gt;The frontend engineer&lt;/li&gt;
&lt;li&gt;The QA engineer&lt;/li&gt;
&lt;li&gt;DevOps: going live&lt;/li&gt;
&lt;li&gt;Maintaining and improving what you shipped&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's around 300 pages, and it moves fast.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The running example actually runs.&lt;/strong&gt; A lot of "big picture" books stay abstract. This one doesn't. You watch the requirements table get written, the personas get invented, the Spring Boot backend get scaffolded, the Vue/Nuxt frontend get wired up, and the whole thing get deployed to Heroku through Travis CI. Seeing the same feature (user registration, for example) pass through the hands of five different roles is the single most valuable thing in the book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's honest about messy reality.&lt;/strong&gt; There's a whole section admitting that role titles mean different things at different companies and that it's normal to be confused. The Scrum-vs-Kanban comparison doesn't pick a winner; it rates each on a strictness scale and then shows the hybrid the authors actually use. That felt refreshingly practical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interviews.&lt;/strong&gt; Several chapters include short interviews with a real product manager, designer, and DevOps engineer. The "biggest screw-up / biggest success" questions in particular give the book a human texture that pure tutorials lack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The MVP chapter.&lt;/strong&gt; If you've never internalized the difference between delivering a wheel and delivering a skateboard, chapter 1 will fix that. It's the clearest explanation of "minimum viable" I've read in a book aimed at developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I didn't love
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Depth is uneven.&lt;/strong&gt; The backend and frontend chapters go fairly deep into code, while the DevOps chapter stays more conceptual. If you're reading for the technical content, you'll find the engineering chapters meatier than the rest. If you're reading for the big picture, the code may feel like a detour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack is dated.&lt;/strong&gt; Java/Spring Boot and Vue 2 with Nuxt are still perfectly reasonable choices, but Travis CI and Heroku's free tier are not what they were in 2018. Read the tooling chapters for the &lt;em&gt;ideas&lt;/em&gt; (CI on every push, automated deploys from the main branch, platform-as-a-service tradeoffs), not the specific buttons to click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's an overview, by design.&lt;/strong&gt; No chapter will make you a designer or a DevOps engineer. That's fine, but set expectations accordingly: this is the map, not the territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who should read it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootcamp grads and juniors&lt;/strong&gt; about to join their first team. This will decode half the meetings you'll sit in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Career changers&lt;/strong&gt; (QA, support, design, product) who want to understand what engineers actually do all day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo founders&lt;/strong&gt; planning to build an MVP and wondering which hats they'll need to wear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experienced devs&lt;/strong&gt; who want something to hand to non-technical colleagues. It's approachable without being condescending.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip it if you already have a few years on cross-functional teams; you'll nod along but learn little.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4 / 5.&lt;/strong&gt; Not a reference book, and not the place to learn any single technology. But as a guided tour through how software gets made, and especially through how the &lt;em&gt;people&lt;/em&gt; who make it depend on each other, it's one of the better ones out there. I'd put it on the same shelf as &lt;em&gt;The Phoenix Project&lt;/em&gt;, just less narrative and more hands-on.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you read it? What's your go-to "how teams actually work" book for new developers? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>softwaredevelopment</category>
      <category>books</category>
    </item>
    <item>
      <title>Cloud Computing for Beginners: A Simple Guide for Students &amp; New Developers ☁️</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Mon, 12 Jan 2026 12:17:59 +0000</pubDate>
      <link>https://dev.to/sahinur/cloud-computing-for-beginners-a-simple-guide-for-students-new-developers-3hop</link>
      <guid>https://dev.to/sahinur/cloud-computing-for-beginners-a-simple-guide-for-students-new-developers-3hop</guid>
      <description>&lt;p&gt;Cloud computing is no longer just a buzzword — it’s a &lt;strong&gt;core skill&lt;/strong&gt; for modern developers. Whether you’re a student, a beginner, or someone curious about how applications scale globally, understanding cloud basics can open many doors.&lt;/p&gt;

&lt;p&gt;In this article, I’ll explain &lt;strong&gt;cloud computing in simple terms&lt;/strong&gt;, without heavy jargon, and help you take your &lt;strong&gt;first step into the cloud&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Cloud Computing? (In Simple Words)
&lt;/h2&gt;

&lt;p&gt;Cloud computing means &lt;strong&gt;using computing resources over the internet instead of your own computer or physical servers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buying servers
&lt;/li&gt;
&lt;li&gt;Maintaining hardware
&lt;/li&gt;
&lt;li&gt;Managing storage manually
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You rent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servers&lt;/li&gt;
&lt;li&gt;Storage&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Networking
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 All provided by cloud platforms like &lt;strong&gt;Microsoft Azure, AWS, and Google Cloud&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of the cloud like &lt;strong&gt;renting electricity&lt;/strong&gt; instead of building your own power plant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Should Beginners Learn Cloud Computing?
&lt;/h2&gt;

&lt;p&gt;Here’s why cloud skills matter, even if you’re just starting:&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 High Industry Demand
&lt;/h3&gt;

&lt;p&gt;Most modern applications — from startups to enterprises — run on cloud infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  💼 Career Opportunities
&lt;/h3&gt;

&lt;p&gt;Cloud knowledge is useful for roles like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Software Engineer
&lt;/li&gt;
&lt;li&gt;DevOps Engineer
&lt;/li&gt;
&lt;li&gt;Cloud Engineer
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📈 Easy Scalability
&lt;/h3&gt;

&lt;p&gt;Cloud platforms allow applications to scale from &lt;strong&gt;10 users to millions&lt;/strong&gt; without rebuilding everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Cloud Concepts You Should Know
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Compute
&lt;/h3&gt;

&lt;p&gt;Compute services run your applications and code.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Azure Virtual Machines
&lt;/li&gt;
&lt;li&gt;Azure App Service
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2️⃣ Storage
&lt;/h3&gt;

&lt;p&gt;Storage services keep files, images, backups, and logs.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Azure Blob Storage
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3️⃣ Networking
&lt;/h3&gt;

&lt;p&gt;Networking connects cloud resources securely.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Virtual Networks
&lt;/li&gt;
&lt;li&gt;Load Balancers
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4️⃣ Databases
&lt;/h3&gt;

&lt;p&gt;Managed databases remove the pain of setup and maintenance.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Azure SQL Database
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Cloud Service Models Explained
&lt;/h2&gt;

&lt;p&gt;Understanding these models is &lt;strong&gt;very important&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧱 IaaS – Infrastructure as a Service
&lt;/h3&gt;

&lt;p&gt;You manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operating system&lt;/li&gt;
&lt;li&gt;Application software
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud provider manages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical hardware
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Virtual Machines
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧩 PaaS – Platform as a Service
&lt;/h3&gt;

&lt;p&gt;You focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud provider manages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servers&lt;/li&gt;
&lt;li&gt;OS&lt;/li&gt;
&lt;li&gt;Scaling
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Azure App Service
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ☁️ SaaS – Software as a Service
&lt;/h3&gt;

&lt;p&gt;You simply &lt;strong&gt;use the software&lt;/strong&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Microsoft 365
&lt;/li&gt;
&lt;li&gt;Gmail
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Microsoft Learn Is Perfect for Beginners
&lt;/h2&gt;

&lt;p&gt;Learning cloud from documentation alone can be overwhelming.&lt;br&gt;&lt;br&gt;
That’s why &lt;strong&gt;Microsoft Learn&lt;/strong&gt; is a great starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Free&lt;/li&gt;
&lt;li&gt;✅ Beginner-friendly&lt;/li&gt;
&lt;li&gt;✅ Hands-on learning&lt;/li&gt;
&lt;li&gt;✅ No credit card required
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You learn by &lt;strong&gt;doing&lt;/strong&gt;, not just reading.&lt;/p&gt;

&lt;p&gt;🔗 Start learning here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://learn.microsoft.com?wt.mc_id=studentamb_502522" rel="noopener noreferrer"&gt;https://learn.microsoft.com?wt.mc_id=studentamb_502522&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Beginner Learning Path
&lt;/h2&gt;

&lt;p&gt;If you’re starting today, follow this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cloud fundamentals
&lt;/li&gt;
&lt;li&gt;Azure basics
&lt;/li&gt;
&lt;li&gt;Compute &amp;amp; storage concepts
&lt;/li&gt;
&lt;li&gt;Deploy a simple application
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consistency matters more than speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;You don’t need to be an expert to start learning cloud computing.&lt;/p&gt;

&lt;p&gt;All you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Curiosity
&lt;/li&gt;
&lt;li&gt;Consistency
&lt;/li&gt;
&lt;li&gt;The right learning resources
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every cloud expert was once a beginner.&lt;br&gt;&lt;br&gt;
Start small, stay consistent, and keep building ☁️🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sahinur Islam&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Full-Stack Developer | Cloud &amp;amp; DevOps Enthusiast&lt;br&gt;&lt;br&gt;
Microsoft Learn Student Ambassador – Registered Member  &lt;/p&gt;

</description>
      <category>cloud</category>
      <category>azure</category>
      <category>beginners</category>
      <category>developer</category>
    </item>
    <item>
      <title>When Should You Use AWS Lambda? A Beginner's Perspective</title>
      <dc:creator>Sahinur</dc:creator>
      <pubDate>Sun, 11 Jan 2026 11:32:51 +0000</pubDate>
      <link>https://dev.to/sahinur/when-should-you-use-aws-lambda-a-beginners-perspective-2fmd</link>
      <guid>https://dev.to/sahinur/when-should-you-use-aws-lambda-a-beginners-perspective-2fmd</guid>
      <description>&lt;p&gt;If you're new to cloud computing, you've probably heard the buzz around &lt;strong&gt;AWS Lambda&lt;/strong&gt; and &lt;strong&gt;serverless architecture&lt;/strong&gt;. But here's the million-dollar question: &lt;em&gt;When should you actually use it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, I'll break down AWS Lambda in simple terms, explain when it's the perfect choice, and when you might want to look elsewhere. Let's dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 What is AWS Lambda, Anyway?
&lt;/h2&gt;

&lt;p&gt;AWS Lambda is Amazon's &lt;strong&gt;serverless compute service&lt;/strong&gt;. In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You write code, upload it to Lambda, and AWS runs it for you — no servers to manage, no infrastructure to worry about.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lambda only runs your code when something triggers it (like an API request, a file upload, or a database change). When there's nothing to do, Lambda sits idle — and you pay nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event-driven&lt;/strong&gt;: Runs only when triggered&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scaling&lt;/strong&gt;: Handles 1 request or 10,000 automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay-per-use&lt;/strong&gt;: Charged only for execution time (in milliseconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero server management&lt;/strong&gt;: AWS handles everything&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ When Should You Use AWS Lambda?
&lt;/h2&gt;

&lt;p&gt;Here are the scenarios where Lambda truly shines:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;API Backends &amp;amp; Microservices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Building a REST API? Lambda + API Gateway is a killer combo.&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;// Simple Lambda function for an API endpoint&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch user from database&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Perfect for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile app backends&lt;/li&gt;
&lt;li&gt;Web application APIs&lt;/li&gt;
&lt;li&gt;Microservices architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;File Processing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When users upload files to S3, Lambda can automatically process them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image resizing/compression&lt;/li&gt;
&lt;li&gt;Video transcoding&lt;/li&gt;
&lt;li&gt;PDF generation&lt;/li&gt;
&lt;li&gt;CSV/Excel data processing
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Triggered when file is uploaded to S3
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Process the uploaded file
&lt;/span&gt;    &lt;span class="nf"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Scheduled Tasks (Cron Jobs)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Need to run something every hour? Every day at midnight? Lambda + EventBridge is your friend.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Daily database cleanup&lt;/li&gt;
&lt;li&gt;Sending scheduled reports&lt;/li&gt;
&lt;li&gt;Periodic data synchronization&lt;/li&gt;
&lt;li&gt;Health checks
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using AWS SAM template&lt;/span&gt;
&lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ScheduledEvent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Schedule&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate(1 hour)&lt;/span&gt;  &lt;span class="c1"&gt;# Runs every hour&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;strong&gt;Real-time Data Processing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Process streaming data from Kinesis or DynamoDB Streams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IoT sensor data processing&lt;/li&gt;
&lt;li&gt;Real-time analytics&lt;/li&gt;
&lt;li&gt;Log aggregation&lt;/li&gt;
&lt;li&gt;Change data capture (CDC)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Webhooks &amp;amp; Event Handlers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Respond to external events instantly.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Payment gateway callbacks (Stripe, PayPal)&lt;/li&gt;
&lt;li&gt;GitHub/GitLab webhooks for CI/CD&lt;/li&gt;
&lt;li&gt;Slack bot commands&lt;/li&gt;
&lt;li&gt;Third-party API integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Chatbots &amp;amp; Voice Assistants&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda powers many conversational interfaces.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Amazon Lex (chatbots)&lt;/li&gt;
&lt;li&gt;Alexa Skills&lt;/li&gt;
&lt;li&gt;Telegram/Discord bots&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. &lt;strong&gt;Authentication &amp;amp; Authorization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Custom auth logic for your applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT token validation&lt;/li&gt;
&lt;li&gt;Custom authorizers for API Gateway&lt;/li&gt;
&lt;li&gt;User signup/signin flows&lt;/li&gt;
&lt;li&gt;OAuth callback handlers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❌ When Should You NOT Use AWS Lambda?
&lt;/h2&gt;

&lt;p&gt;Lambda isn't a silver bullet. Here's when you should consider alternatives:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Long-Running Processes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda has a &lt;strong&gt;15-minute timeout limit&lt;/strong&gt;. If your task takes longer, Lambda isn't the right choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better alternatives:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Fargate&lt;/li&gt;
&lt;li&gt;AWS Batch&lt;/li&gt;
&lt;li&gt;EC2 instances&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Consistent High Traffic&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your application runs 24/7 with consistent high traffic, Lambda might be more expensive than EC2 or containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Calculate your costs. Lambda charges per millisecond, while EC2 is hourly. For always-on workloads, EC2 or Fargate often wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Stateful Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda functions are &lt;strong&gt;stateless&lt;/strong&gt;. Each invocation is independent with no shared memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not ideal for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket connections (though API Gateway helps)&lt;/li&gt;
&lt;li&gt;Session-based applications&lt;/li&gt;
&lt;li&gt;Applications requiring persistent connections&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Latency-Critical Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cold starts&lt;/strong&gt; can add 100ms to several seconds of latency when Lambda spins up a new container.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Provisioned Concurrency (costs extra)&lt;/li&gt;
&lt;li&gt;Keep functions warm with scheduled pings&lt;/li&gt;
&lt;li&gt;Use smaller deployment packages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Heavy Compute Workloads&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda maxes out at &lt;strong&gt;10GB RAM&lt;/strong&gt; and &lt;strong&gt;6 vCPUs&lt;/strong&gt;. For ML training, video rendering, or heavy computation, look elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better alternatives:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 with GPU instances&lt;/li&gt;
&lt;li&gt;AWS SageMaker&lt;/li&gt;
&lt;li&gt;AWS Batch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Complex Monolithic Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda works best with small, focused functions. Migrating a large monolithic app to Lambda is painful and often counterproductive.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Quick Decision Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Use Lambda?&lt;/th&gt;
&lt;th&gt;Alternative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;REST API with variable traffic&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image processing on upload&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cron jobs (&amp;lt; 15 min)&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time webhooks&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24/7 high-traffic web server&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;EC2, Fargate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML model training&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;SageMaker, EC2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running batch jobs&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;AWS Batch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket game server&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;EC2, Fargate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-10ms latency required&lt;/td&gt;
&lt;td&gt;⚠️ Maybe&lt;/td&gt;
&lt;td&gt;EC2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💰 Understanding Lambda Pricing
&lt;/h2&gt;

&lt;p&gt;Lambda's pricing is simple but can surprise you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You pay for:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Number of requests&lt;/strong&gt;: $0.20 per 1 million requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duration&lt;/strong&gt;: Based on memory allocated × execution time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Free tier (every month):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 million requests&lt;/li&gt;
&lt;li&gt;400,000 GB-seconds of compute time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example calculation:&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;Function specs:
- Memory: 512MB
- Avg execution time: 200ms
- Monthly invocations: 5 million

Cost calculation:
- Requests: (5M - 1M free) × $0.20/1M = $0.80
- Compute: 5M × 0.2s × 0.5GB = 500,000 GB-s
- GB-s cost: (500,000 - 400,000 free) × $0.0000166667 = $1.67

Total: ~$2.47/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Getting Started: Your First Lambda Function
&lt;/h2&gt;

&lt;p&gt;Ready to try Lambda? Here's a quick start:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryStringParameters&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &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;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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;h3&gt;
  
  
  Step 2: Deploy Using AWS CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Zip your code&lt;/span&gt;
zip &lt;span class="k"&gt;function&lt;/span&gt;.zip index.js

&lt;span class="c"&gt;# Create the function&lt;/span&gt;
aws lambda create-function &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--function-name&lt;/span&gt; HelloWorld &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--runtime&lt;/span&gt; nodejs20.x &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--handler&lt;/span&gt; index.handler &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt; arn:aws:iam::YOUR_ACCOUNT:role/lambda-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://function.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Test It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--function-name&lt;/span&gt; HelloWorld &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"queryStringParameters": {"name": "Developer"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    response.json

&lt;span class="nb"&gt;cat &lt;/span&gt;response.json
&lt;span class="c"&gt;# Output: {"message":"Hello, Developer!","timestamp":"2024-01-15T10:30:00.000Z"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Best Practices for Beginners
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep functions small and focused&lt;/strong&gt; — One function, one job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize cold starts&lt;/strong&gt; — Use smaller packages, avoid VPC unless necessary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set appropriate timeouts&lt;/strong&gt; — Don't use 15 minutes if 10 seconds is enough&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use environment variables&lt;/strong&gt; — Never hardcode secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor with CloudWatch&lt;/strong&gt; — Set up alarms for errors and duration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Layers for dependencies&lt;/strong&gt; — Share common code across functions&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;AWS Lambda is a powerful tool, but it's not for everything. Use it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Your workload is event-driven&lt;/li&gt;
&lt;li&gt;✅ Traffic is variable or unpredictable&lt;/li&gt;
&lt;li&gt;✅ Tasks complete in under 15 minutes&lt;/li&gt;
&lt;li&gt;✅ You want zero infrastructure management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ You need consistent, high-performance compute&lt;/li&gt;
&lt;li&gt;❌ Tasks run longer than 15 minutes&lt;/li&gt;
&lt;li&gt;❌ Sub-millisecond latency is critical&lt;/li&gt;
&lt;li&gt;❌ You're running a stateful monolith&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start small, experiment, and you'll quickly discover where Lambda fits in your architecture. The serverless journey is worth it!&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/serverless/sam/" rel="noopener noreferrer"&gt;AWS SAM (Serverless Application Model)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning" rel="noopener noreferrer"&gt;Lambda Power Tuning Tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's your experience with AWS Lambda? Drop a comment below!&lt;/strong&gt; 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, please ❤️ and follow for more cloud content!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  aws #lambda #serverless #beginners #cloud
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>beginners</category>
      <category>lambda</category>
    </item>
  </channel>
</rss>
