<?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: Alex Dev</title>
    <description>The latest articles on DEV Community by Alex Dev (@alex_dev_523ee1a46fb36a13).</description>
    <link>https://dev.to/alex_dev_523ee1a46fb36a13</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1788946%2Fba3b8073-cc7b-4fcb-b242-00e637383141.jpg</url>
      <title>DEV Community: Alex Dev</title>
      <link>https://dev.to/alex_dev_523ee1a46fb36a13</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alex_dev_523ee1a46fb36a13"/>
    <language>en</language>
    <item>
      <title>Why I Built My Own AWS Deployment Tool</title>
      <dc:creator>Alex Dev</dc:creator>
      <pubDate>Tue, 17 Feb 2026 17:40:03 +0000</pubDate>
      <link>https://dev.to/alex_dev_523ee1a46fb36a13/why-i-built-my-own-aws-deployment-tool-og7</link>
      <guid>https://dev.to/alex_dev_523ee1a46fb36a13/why-i-built-my-own-aws-deployment-tool-og7</guid>
      <description>&lt;p&gt;I'm a software engineer with over 12 years of experience across different languages, teams, and dozens of projects. I love what I do, and I'm drawn to solving real problems with clean tools.&lt;/p&gt;

&lt;p&gt;I've been building serverless applications on AWS for years. Across multiple jobs and projects, I've used &lt;strong&gt;Serverless Framework&lt;/strong&gt; and &lt;strong&gt;AWS CDK&lt;/strong&gt;. And there was always this friction — the gap between having an idea and getting it running in the cloud felt wider than it should be.&lt;/p&gt;

&lt;p&gt;This is the story of why I finally snapped and built my own deployment tool — one that skips &lt;strong&gt;CloudFormation&lt;/strong&gt; entirely and deploys AWS resources with &lt;strong&gt;direct API calls&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Background
&lt;/h2&gt;

&lt;p&gt;My serverless journey started with &lt;strong&gt;Serverless Framework&lt;/strong&gt; — it was the go-to tool at the time, and it worked. Then &lt;strong&gt;AWS CDK&lt;/strong&gt; came along promising infrastructure as "real code," and I jumped in. I spent a lot of time with &lt;strong&gt;CDK&lt;/strong&gt; commercially, building stacks, configuring resources, trying to get everything just right.&lt;/p&gt;

&lt;p&gt;I've also worked with other clouds — &lt;strong&gt;Google Cloud&lt;/strong&gt; and &lt;strong&gt;Firebase&lt;/strong&gt; in particular. And honestly, &lt;strong&gt;Firebase&lt;/strong&gt; left an impression on me. The way you could describe your functions in code, run a single CLI command, and have everything deployed — that felt like how things should work.&lt;/p&gt;

&lt;p&gt;That said, I always preferred AWS itself. &lt;strong&gt;Google Cloud&lt;/strong&gt; felt overcomplicated — containers everywhere, things weren't obvious. &lt;strong&gt;AWS Lambda&lt;/strong&gt; was simpler: no container builds, functions spun up fast, and the whole model just made more sense to me. So I loved &lt;strong&gt;Firebase's&lt;/strong&gt; developer experience, but I wanted it on AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pain
&lt;/h2&gt;

&lt;p&gt;With &lt;strong&gt;AWS CDK&lt;/strong&gt;, I constantly found myself solving infrastructure puzzles instead of shipping features. How do I split stacks so that Lambda functions deploy faster, separate from the databases? How do I configure bundling — should I use tsup externally, or let CDK handle it? Every project started with the same yak-shaving.&lt;/p&gt;

&lt;p&gt;And then there was &lt;strong&gt;CloudFormation&lt;/strong&gt;. Every deployment meant waiting for &lt;strong&gt;CloudFormation&lt;/strong&gt; to diff, plan, and roll out changes — even for a one-line code fix. For large projects with hundreds of resources, maybe that's an acceptable trade-off. But for serverless, where the whole point is agility, it felt like dragging an anchor.&lt;/p&gt;

&lt;p&gt;I wanted a &lt;em&gt;fast loop&lt;/em&gt;: change code, deploy, see results. Instead, I was spending my time debugging stack configurations and staring at &lt;code&gt;UPDATE_IN_PROGRESS&lt;/code&gt; for minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Inspiration
&lt;/h2&gt;

&lt;p&gt;At some point I discovered something on my own: you can create AWS resources directly through the &lt;strong&gt;AWS SDK&lt;/strong&gt;. No &lt;strong&gt;CloudFormation&lt;/strong&gt; needed. And it's &lt;em&gt;much&lt;/em&gt; faster — the end result is exactly the same, but you skip the entire provisioning engine. That was the moment it clicked: combine &lt;strong&gt;Firebase's&lt;/strong&gt; code-as-config model with direct &lt;strong&gt;AWS SDK&lt;/strong&gt; calls, and you get the best of both worlds.&lt;/p&gt;

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

&lt;p&gt;What if you could just write your Lambda handler, declare its configuration right next to the code, and run a single command to deploy everything? &lt;strong&gt;No CloudFormation templates. No stack definitions. No bundler config. No IAM boilerplate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest challenge was clear from the start: I'd need to do what &lt;strong&gt;CloudFormation&lt;/strong&gt; does — compare current state with desired state and sync the difference — but without &lt;strong&gt;CloudFormation&lt;/strong&gt;, using direct &lt;strong&gt;AWS SDK&lt;/strong&gt; calls. That's not a trivial problem.&lt;/p&gt;

&lt;p&gt;Three libraries gave me the confidence to try.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ts-morph&lt;/strong&gt; — a fantastic library for analyzing TypeScript AST. It's essentially &lt;em&gt;metaprogramming&lt;/em&gt;: I could extract all the infrastructure information directly from handler code instead of keeping it in separate YAML or JSON files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Effect-TS&lt;/strong&gt; — the project was going to be complex, orchestrating API calls, managing errors, handling concurrency. &lt;strong&gt;Effect&lt;/strong&gt; gave me a way to write that kind of code without the cognitive load spiraling out of control as the codebase grew.&lt;/p&gt;

&lt;p&gt;And a &lt;strong&gt;typed AWS SDK wrapper&lt;/strong&gt; I had built a couple of years ago — a generator that reads JSDoc from &lt;strong&gt;AWS SDK&lt;/strong&gt; source, extracts all possible error types, and produces &lt;strong&gt;Effect&lt;/strong&gt; wrappers where every error is known at the type level.&lt;/p&gt;

&lt;p&gt;With these tools in hand, I set out to build &lt;strong&gt;effortless-aws&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finding the right API took time. &lt;strong&gt;Firebase&lt;/strong&gt; inspired the direction, but I didn't love everything about it. I wanted something where every handler is a single function call that takes one options object — path, method, handler logic, dependencies, parameters, everything in one place. No curried functions, no builder chains. Just one object that's easy to read and extend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Effect-TS&lt;/strong&gt; inspired me here too — specifically its approach to context and built-in &lt;em&gt;dependency injection&lt;/em&gt;. In &lt;strong&gt;Effect&lt;/strong&gt;, you can verify at the type level that all dependencies are satisfied before running anything. I wanted the same for my handlers: you declare a context factory at the Lambda level, and it's injected into every request handler with full type safety. No runtime surprises where something is undefined because you forgot to wire it up.&lt;/p&gt;

&lt;p&gt;This matters because some handlers need references to other resources — a Lambda that writes to a DynamoDB table, for example. A single config object makes that natural: you just add a &lt;code&gt;deps&lt;/code&gt; field. Everything — context, deps, params — is &lt;em&gt;type-checked&lt;/em&gt; and injected automatically.&lt;/p&gt;

&lt;p&gt;Here's what the simplest handler looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineHttp&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;effortless-aws&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;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineHttp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onRequest&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="o"&gt;=&amp;gt;&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="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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World!&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's your &lt;strong&gt;Lambda&lt;/strong&gt; function, its &lt;strong&gt;API Gateway&lt;/strong&gt; route, and its &lt;strong&gt;IAM&lt;/strong&gt; role — all in one file. Run &lt;code&gt;eff deploy&lt;/code&gt; and it's live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and After
&lt;/h2&gt;

&lt;p&gt;To appreciate the difference, here's what it takes to create a simple HTTP endpoint with a DynamoDB table in CDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CDK: stack definition (separate file)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&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;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;billingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BillingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAY_PER_REQUEST&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;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;nodejs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NodejsFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GetOrders&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="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/handlers/get-orders.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_20_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;bundling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sourceMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantReadData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addRoutes&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&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="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;integration&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;HttpLambdaIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GetOrdersIntegration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&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;That's just the infrastructure. You still need the handler file, the stack wiring, the app entry point, and &lt;code&gt;cdk deploy&lt;/code&gt; with &lt;strong&gt;CloudFormation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;effortless-aws&lt;/strong&gt;, the same thing is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// That's it. One file.&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;defineHttp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineTable&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;effortless-aws&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;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineHttp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;path&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="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onRequest&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;deps&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;items&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;deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&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;status&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;items&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;Run &lt;code&gt;eff deploy&lt;/code&gt;. Done. The table, the function, the route, the IAM permissions — all created in &lt;strong&gt;seconds&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;Under the hood, &lt;code&gt;eff deploy&lt;/code&gt; goes through four stages. Orchestrating them was the hardest part of the project — each stage feeds into the next, errors can happen anywhere, and resources depend on each other. &lt;strong&gt;Effect-TS&lt;/strong&gt; made this manageable: the whole pipeline is composable, each step is an &lt;strong&gt;Effect&lt;/strong&gt; you can reason about independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Scan
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ts-morph&lt;/strong&gt; reads your TypeScript source and extracts every &lt;code&gt;defineHttp&lt;/code&gt; / &lt;code&gt;defineTable&lt;/code&gt; call — method, path, deps, params, static files — straight from the AST. This was the part where I realized &lt;em&gt;metaprogramming&lt;/em&gt; in TypeScript is actually viable. Instead of maintaining separate YAML or JSON config files, &lt;strong&gt;the code itself is the source of truth&lt;/strong&gt;. &lt;strong&gt;ts-morph&lt;/strong&gt; made it surprisingly elegant.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Bundle
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;esbuild&lt;/strong&gt; compiles each handler into a single ESM file. I had experience with &lt;strong&gt;esbuild&lt;/strong&gt; before — it's fast and does its job well. But I also wanted to solve the dependency problem: in a project with many Lambda functions, they usually share the same production dependencies. Bundling &lt;code&gt;node_modules&lt;/code&gt; into every handler ZIP felt wasteful. So I put shared dependencies into a &lt;strong&gt;Lambda Layer&lt;/strong&gt; — one layer for the whole project. Each handler bundles into a clean single JS file, and the layer provides &lt;code&gt;node_modules&lt;/code&gt; at runtime. This was its own challenge to get right, but it works reliably — at least with &lt;code&gt;pnpm&lt;/code&gt; projects so far.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Diff
&lt;/h3&gt;

&lt;p&gt;The tool checks what already exists in AWS and compares it with what your code declares. Only the differences get applied. This is essentially what &lt;strong&gt;CloudFormation&lt;/strong&gt; does — syncing desired state with current state — but without the provisioning engine overhead. Getting this right was the biggest conceptual challenge: figuring out the right granularity of comparison and making sure updates are idempotent.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Deploy
&lt;/h3&gt;

&lt;p&gt;Direct &lt;strong&gt;AWS SDK&lt;/strong&gt; calls create, update, or reconfigure resources — &lt;strong&gt;Lambda&lt;/strong&gt; functions, &lt;strong&gt;DynamoDB&lt;/strong&gt; tables, &lt;strong&gt;API Gateway&lt;/strong&gt; routes, &lt;strong&gt;IAM&lt;/strong&gt; policies. Because every AWS call goes through the typed &lt;strong&gt;Effect&lt;/strong&gt; wrappers, every possible error is known. If a function already exists — I log it and move on. If there's an internal AWS error — I stop the deploy. No guesswork, no catch-all try/catch blocks.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;effortless-aws&lt;/strong&gt; is currently in alpha. It works — I'm using it myself to deploy real projects: the documentation website, and a couple of personal projects that use Lambda functions, DynamoDB tables, streams, and triggers. Everything deploys and runs. But it's still in active development: there may be rough edges, and I'm constantly testing and fixing things.&lt;/p&gt;

&lt;p&gt;The roadmap is full of features I want to add, and the project is evolving fast. I've put together a website with documentation that covers what's available today — handlers for Lambda functions, DynamoDB tables, static websites, and more.&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;effortless-aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/effect-ak/effortless" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://effortless-aws.website" rel="noopener noreferrer"&gt;Website &amp;amp; Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools I used to build this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://effect.website" rel="noopener noreferrer"&gt;Effect-TS&lt;/a&gt; — typed functional programming for TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dsherret/ts-morph" rel="noopener noreferrer"&gt;ts-morph&lt;/a&gt; — TypeScript AST analysis and manipulation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://esbuild.github.io" rel="noopener noreferrer"&gt;esbuild&lt;/a&gt; — fast JavaScript/TypeScript bundler&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws/aws-sdk-js-v3" rel="noopener noreferrer"&gt;AWS SDK for JavaScript&lt;/a&gt; — official AWS SDK v3&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kondaurovDev/aws-sdk" rel="noopener noreferrer"&gt;Typed AWS SDK wrapper&lt;/a&gt; — Effect wrappers with typed errors for AWS SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm confident in the ideas behind this project and I'll keep building it. If you're a developer who wants to ship faster — to spend your time solving real problems instead of writing infrastructure config — give it a look. I'd love your feedback.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>typescript</category>
      <category>serverless</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
