<?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>Forem: Jeff Yaw</title>
    <description>The latest articles on Forem by Jeff Yaw (@jeffyaw).</description>
    <link>https://forem.com/jeffyaw</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%2F3842374%2F23783062-7561-4fc9-afaa-73df58bdf0bd.png</url>
      <title>Forem: Jeff Yaw</title>
      <link>https://forem.com/jeffyaw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jeffyaw"/>
    <language>en</language>
    <item>
      <title>When an ALB Can Replace Nginx (and When It Can't)</title>
      <dc:creator>Jeff Yaw</dc:creator>
      <pubDate>Tue, 31 Mar 2026 19:26:48 +0000</pubDate>
      <link>https://forem.com/jeffyaw/when-an-alb-can-replace-nginx-and-when-it-cant-85j</link>
      <guid>https://forem.com/jeffyaw/when-an-alb-can-replace-nginx-and-when-it-cant-85j</guid>
      <description>&lt;p&gt;A common pattern in AWS: traffic hits an Application Load Balancer, which forwards to an EC2 instance or container running Nginx, which reverse-proxies to your actual application. The ALB handles the L7 load balancing. Nginx handles... well, what exactly?&lt;/p&gt;

&lt;p&gt;For a lot of teams, the answer is: routing, SSL termination, and maybe a redirect or two. Things the ALB already does. Nginx is sitting in the middle adding latency, operational overhead, and another thing to patch -- without doing anything the ALB can't handle on its own.&lt;/p&gt;

&lt;p&gt;That doesn't mean Nginx is never needed. It absolutely is in some cases. But the line between "need Nginx" and "ALB is enough" is worth understanding clearly, because removing a layer from your stack is one of the most impactful simplifications you can make.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ALB gives you natively
&lt;/h2&gt;

&lt;p&gt;AWS ALBs have quietly accumulated features over the years that cover most of what teams historically used Nginx for. Here's what you get without running a single reverse proxy:&lt;/p&gt;

&lt;h3&gt;
  
  
  Path-based and host-based routing
&lt;/h3&gt;

&lt;p&gt;ALB listener rules can route requests based on the URL path, hostname, HTTP headers, query string parameters, and source IP. You can send &lt;code&gt;/api/*&lt;/code&gt; to one target group and &lt;code&gt;/static/*&lt;/code&gt; to another. You can route &lt;code&gt;api.example.com&lt;/code&gt; to a different backend than &lt;code&gt;app.example.com&lt;/code&gt;. For most microservice routing needs, this is sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSL termination
&lt;/h3&gt;

&lt;p&gt;ALBs integrate with AWS Certificate Manager. You get free, auto-renewing TLS certificates with zero configuration on your application servers. No certbot cron jobs, no Nginx SSL stanzas, no remembering to renew certificates. ACM handles it. Your app receives plain HTTP from the ALB on the internal network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Health checks
&lt;/h3&gt;

&lt;p&gt;ALB health checks hit a configurable endpoint on your targets at a configurable interval. Unhealthy targets get pulled from rotation automatically. This replaces the Nginx upstream health check module and works out of the box with no configuration beyond specifying the health check path.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP-to-HTTPS redirects
&lt;/h3&gt;

&lt;p&gt;A single ALB listener rule can redirect all HTTP traffic to HTTPS. No Nginx &lt;code&gt;return 301&lt;/code&gt; block needed. You can also do domain redirects -- sending &lt;code&gt;www.example.com&lt;/code&gt; to &lt;code&gt;example.com&lt;/code&gt; or vice versa -- directly in the listener rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixed responses
&lt;/h3&gt;

&lt;p&gt;ALB can return a static response body with a configurable status code. This is useful for maintenance pages, custom 404s, or blocking specific paths. Instead of spinning up an Nginx config to serve a "we'll be right back" page, you add a listener rule that returns a fixed 503 with your maintenance HTML.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sticky sessions
&lt;/h3&gt;

&lt;p&gt;ALB supports cookie-based session stickiness at the target group level. If you just need "same user hits the same container," ALB's application cookie or duration-based stickiness works fine. It sets a cookie, and subsequent requests with that cookie go to the same target.&lt;/p&gt;

&lt;h3&gt;
  
  
  Weighted target groups
&lt;/h3&gt;

&lt;p&gt;ALB listener rules support forwarding to multiple target groups with configurable weights. This enables blue/green deployments and canary releases at the load balancer level. Send 95% of traffic to the current version and 5% to the new version, then shift the weight as you gain confidence. No Nginx upstream weighting required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in authentication
&lt;/h3&gt;

&lt;p&gt;This one is underappreciated. ALB can authenticate users directly via Amazon Cognito or any OIDC-compliant identity provider (Google, Okta, Auth0) before the request even reaches your application. The ALB handles the entire OAuth 2.0 flow -- redirect to login, token exchange, session management -- and forwards the authenticated user's claims to your app in HTTP headers. With Nginx, you'd need something like &lt;code&gt;lua-resty-openidc&lt;/code&gt; or a sidecar like oauth2-proxy. ALB does it in a listener rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSocket and gRPC support
&lt;/h3&gt;

&lt;p&gt;ALB handles WebSocket connections natively -- no special Nginx &lt;code&gt;proxy_set_header Upgrade&lt;/code&gt; configuration needed. It also supports gRPC traffic end-to-end, including health checks over gRPC. If your stack uses either protocol, ALB handles it without extra proxy-layer configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access logging
&lt;/h3&gt;

&lt;p&gt;ALB access logs ship directly to S3 with no agents or log shippers to configure. Every request gets logged with latency, status code, target response time, and TLS cipher -- all in a structured format ready for Athena queries. With Nginx, you're managing log rotation, configuring a log shipper (Fluent Bit, Filebeat, CloudWatch agent), and hoping the pipeline doesn't silently break.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection draining
&lt;/h3&gt;

&lt;p&gt;When you deploy a new version of your application, targets need to finish handling in-flight requests before being deregistered. ALB handles this natively with a configurable deregistration delay (default 300 seconds). Active connections are allowed to complete, and new requests go to healthy targets. With Nginx, you'd need to coordinate &lt;code&gt;upstream&lt;/code&gt; changes and graceful reloads yourself -- and getting this wrong drops requests during deploys.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;Here's a CloudFormation snippet that replaces what would typically be 30-40 lines of Nginx config:&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;HttpsListener&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;AWS::ElasticLoadBalancingV2::Listener&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;LoadBalancerArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ALB&lt;/span&gt;
    &lt;span class="na"&gt;Port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
    &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt;
    &lt;span class="na"&gt;SslPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ELBSecurityPolicy-TLS13-1-2-2021-06&lt;/span&gt;
    &lt;span class="na"&gt;Certificates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;CertificateArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Certificate&lt;/span&gt;
    &lt;span class="na"&gt;DefaultActions&lt;/span&gt;&lt;span class="pi"&gt;:&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;forward&lt;/span&gt;
        &lt;span class="na"&gt;TargetGroupArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AppTargetGroup&lt;/span&gt;

&lt;span class="na"&gt;HttpRedirect&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;AWS::ElasticLoadBalancingV2::Listener&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;LoadBalancerArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ALB&lt;/span&gt;
    &lt;span class="na"&gt;Port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP&lt;/span&gt;
    &lt;span class="na"&gt;DefaultActions&lt;/span&gt;&lt;span class="pi"&gt;:&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;redirect&lt;/span&gt;
        &lt;span class="na"&gt;RedirectConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt;
          &lt;span class="na"&gt;Port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443"&lt;/span&gt;
          &lt;span class="na"&gt;StatusCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP_301&lt;/span&gt;

&lt;span class="na"&gt;ApiRoute&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;AWS::ElasticLoadBalancingV2::ListenerRule&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;ListenerArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;HttpsListener&lt;/span&gt;
    &lt;span class="na"&gt;Priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
    &lt;span class="na"&gt;Actions&lt;/span&gt;&lt;span class="pi"&gt;:&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;forward&lt;/span&gt;
        &lt;span class="na"&gt;TargetGroupArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiTargetGroup&lt;/span&gt;
    &lt;span class="na"&gt;Conditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path-pattern&lt;/span&gt;
        &lt;span class="na"&gt;Values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's SSL termination, HTTP-to-HTTPS redirect, and path-based routing -- all version-controlled, all reproducible, no Nginx process to manage. Need a maintenance page? Add a listener rule with a fixed-response action and toggle it with a CloudFormation condition.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you still need Nginx
&lt;/h2&gt;

&lt;p&gt;ALB covers the common cases, but Nginx remains the right tool when your requirements go beyond basic routing and termination:&lt;/p&gt;

&lt;h3&gt;
  
  
  Response caching
&lt;/h3&gt;

&lt;p&gt;ALB doesn't cache responses. If you need to cache upstream responses at the reverse proxy layer to reduce load on your application, you need Nginx (or a CDN like CloudFront). This is a common pattern for APIs that serve the same response to many users -- product catalogs, configuration endpoints, public data feeds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Header rewriting
&lt;/h3&gt;

&lt;p&gt;ALB can add, remove, or modify a few specific headers, but Nginx gives you full control. If you need to rewrite &lt;code&gt;X-Forwarded-*&lt;/code&gt; headers, add custom security headers conditionally, strip response headers from upstream, or transform headers based on request attributes, Nginx's &lt;code&gt;proxy_set_header&lt;/code&gt; and &lt;code&gt;add_header&lt;/code&gt; directives are far more flexible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex URL rewrites
&lt;/h3&gt;

&lt;p&gt;ALB redirects are simple: you can change the protocol, host, port, path, and query string. But you can't do regex-based URL rewriting, path segment manipulation, or conditional rewrites based on cookies or headers. If your application relies on Nginx's &lt;code&gt;rewrite&lt;/code&gt; directive with capture groups and conditionals, ALB can't replace that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Response compression
&lt;/h3&gt;

&lt;p&gt;ALB doesn't compress responses. If your application doesn't handle its own gzip/brotli compression and you're relying on Nginx's &lt;code&gt;gzip on;&lt;/code&gt; to compress responses before they reach the client, removing Nginx means either adding compression to your app or putting CloudFront in front of the ALB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced sticky sessions
&lt;/h3&gt;

&lt;p&gt;ALB's sticky sessions are cookie-based only. If you need to route based on a query parameter (like a session ID in the URL), a custom header, a JWT claim, or anything other than an ALB-managed cookie, you need Nginx's &lt;code&gt;sticky&lt;/code&gt; directive or custom Lua logic. ALB's stickiness is simpler but less flexible -- if "same user hits same container via cookie" is all you need, it works. Anything more granular requires Nginx.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate limiting and request throttling
&lt;/h3&gt;

&lt;p&gt;ALB doesn't do rate limiting. If you need to throttle requests per IP, per user, or per endpoint, you either need Nginx's &lt;code&gt;limit_req&lt;/code&gt; module, AWS WAF (additional cost), or application-level rate limiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request/response body manipulation
&lt;/h3&gt;

&lt;p&gt;If you need to inspect, modify, or filter request or response bodies -- injecting scripts, stripping content, transforming payloads -- that's Nginx with Lua or OpenResty territory. ALB treats the body as an opaque pass-through.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost trade-off
&lt;/h2&gt;

&lt;p&gt;This is the part that gets glossed over in "just use ALB" advice. ALBs are not free, and depending on your traffic profile, they can be meaningfully more expensive than Nginx on an EC2 instance you're already paying for.&lt;/p&gt;

&lt;h3&gt;
  
  
  ALB pricing
&lt;/h3&gt;

&lt;p&gt;An ALB charges in two dimensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fixed hourly cost&lt;/strong&gt; -- ~$0.0225/hour (~$16.20/month) just for the ALB to exist, regardless of traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LCU (Load Balancer Capacity Unit) charges&lt;/strong&gt; -- based on the highest of: new connections/sec, active connections, bandwidth, and rule evaluations. For most workloads this adds $5-30/month, but high-traffic or WebSocket-heavy applications can push this significantly higher&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a baseline ALB costs roughly &lt;strong&gt;$20-50/month&lt;/strong&gt; before you add any features. For a production workload with multiple target groups, SSL, and moderate traffic, $30-40/month is typical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nginx pricing
&lt;/h3&gt;

&lt;p&gt;Nginx open source is free. If it's running on an EC2 instance that already exists for your application, the incremental cost is effectively zero -- you're just using CPU and memory you've already provisioned. Even if you run a dedicated t3.micro for Nginx, that's ~$7.60/month.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the cost is worth it
&lt;/h3&gt;

&lt;p&gt;The ALB cost is worth paying when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're running multiple targets (containers, instances) and need real load balancing -- not just reverse proxying to localhost&lt;/li&gt;
&lt;li&gt;You need high availability -- ALB is multi-AZ by default, Nginx HA requires at least two instances plus a mechanism to fail over&lt;/li&gt;
&lt;li&gt;You value the operational simplicity of a managed service over the cost of managing Nginx yourself&lt;/li&gt;
&lt;li&gt;You're already paying for an ALB (e.g., for ECS or EKS) and would be adding Nginx on top of it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost is harder to justify when you have a single EC2 instance running one application. In that case, Nginx on the instance is essentially free and adding an ALB doubles your infrastructure cost for features you might not need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The operational argument: IaC vs. SSH-and-edit
&lt;/h2&gt;

&lt;p&gt;There's a subtler but important difference between ALB and Nginx that goes beyond features and cost: how changes get made.&lt;/p&gt;

&lt;p&gt;Nginx configuration lives on a server. In a well-run shop, that config is managed by Ansible, Chef, Puppet, or at minimum checked into version control and deployed through a pipeline. But the reality is that Nginx makes it &lt;em&gt;very easy&lt;/em&gt; to SSH into a box, edit &lt;code&gt;/etc/nginx/sites-enabled/app.conf&lt;/code&gt;, run &lt;code&gt;nginx -t &amp;amp;&amp;amp; nginx -s reload&lt;/code&gt;, and walk away. No commit, no review, no record of what changed.&lt;/p&gt;

&lt;p&gt;This happens more than anyone admits. A quick fix at 2 AM during an incident. A redirect someone needed "just for today." A header tweak that never made it back to the config management repo. Over time, the running Nginx config drifts from what's in version control, and nobody notices until the server gets replaced and the new one doesn't behave the same way.&lt;/p&gt;

&lt;p&gt;ALB configuration, by contrast, naturally lives in infrastructure-as-code. Whether you're using CloudFormation, Terraform, or the CDK, ALB listener rules are defined in templates that get committed, reviewed, and applied through a pipeline. You &lt;em&gt;can&lt;/em&gt; make changes through the console, but it's clunky enough that most teams don't make a habit of it. The path of least resistance is the IaC path, which means changes are tracked, reviewable, and reproducible.&lt;/p&gt;

&lt;p&gt;This isn't a technical limitation of Nginx -- it's a human one. ALB's operational model nudges teams toward better practices by making the right thing the easy thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  No CVEs to patch
&lt;/h2&gt;

&lt;p&gt;Nginx has a solid security track record, but it's still software you're running on your servers. When a CVE drops -- and they do, a handful per year -- you need to update the package, test the config, and roll it out to every instance. If you're running Nginx in containers, that means rebuilding and redeploying images. If you're running it on EC2 instances managed by Ansible, that's a playbook run across your fleet.&lt;/p&gt;

&lt;p&gt;ALB is a managed service. AWS patches it. You don't get paged, you don't rebuild images, you don't coordinate rollouts. One less thing in your vulnerability management process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas and limits
&lt;/h2&gt;

&lt;p&gt;ALB isn't without constraints. A few to be aware of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100 listener rules per listener&lt;/strong&gt; (default, can be raised to 200 via support). If you have complex routing with hundreds of path or host rules, you'll hit this. Nginx has no practical limit on routing rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed response body limit is 1,024 bytes.&lt;/strong&gt; Your maintenance page has to be very minimal, or you need to redirect to a static page hosted elsewhere (S3, for example).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No request buffering.&lt;/strong&gt; ALB streams the request to the target. Nginx can buffer the entire request before proxying, which protects slow backends from slow client uploads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header size limits.&lt;/strong&gt; ALB enforces a maximum header size of 16 KB per individual header and 64 KB total. If your app passes large JWTs or cookies, you could hit this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A decision framework
&lt;/h2&gt;

&lt;p&gt;Here's a simple way to think about it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you need...&lt;/th&gt;
&lt;th&gt;ALB alone?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Path/host-based routing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL termination with auto-renewing certs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP -&amp;gt; HTTPS redirects&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domain redirects&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health checks &amp;amp; automatic failover&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cookie-based sticky sessions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blue/green &amp;amp; canary deployments&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance pages / fixed responses&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication (Cognito / OIDC)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket &amp;amp; gRPC&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access logging to S3&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection draining during deploys&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response caching&lt;/td&gt;
&lt;td&gt;No -- need Nginx or CloudFront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header rewriting (complex)&lt;/td&gt;
&lt;td&gt;No -- need Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex URL rewrites&lt;/td&gt;
&lt;td&gt;No -- need Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response compression&lt;/td&gt;
&lt;td&gt;No -- need Nginx or CloudFront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sticky sessions by header/query param&lt;/td&gt;
&lt;td&gt;No -- need Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;No -- need Nginx or WAF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request/response body transforms&lt;/td&gt;
&lt;td&gt;No -- need Nginx + Lua&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request buffering (slow client protection)&lt;/td&gt;
&lt;td&gt;No -- need Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If everything you need is in the "Yes" column, you can drop Nginx and let the ALB handle it. Your stack gets simpler, your deploys get simpler, and you have one less thing to patch and monitor.&lt;/p&gt;

&lt;p&gt;If you need anything from the "No" column, keep Nginx -- but consider whether you really need those features, or whether they're inherited config from a setup that predates your ALB.&lt;/p&gt;

&lt;h2&gt;
  
  
  The double-proxy anti-pattern
&lt;/h2&gt;

&lt;p&gt;If you're running containers on ECS or EKS, you almost certainly already have an ALB. It's how AWS routes traffic to your tasks or pods. If you're &lt;em&gt;also&lt;/em&gt; running Nginx as a sidecar or init container in front of your application container, you have two proxies in series: ALB -&amp;gt; Nginx -&amp;gt; app.&lt;/p&gt;

&lt;p&gt;This is the worst of both worlds. You're paying for the ALB, managing Nginx, and getting the downsides of each (ALB cost + Nginx operational overhead) while the two layers duplicate each other's work. Unless Nginx is doing something the ALB genuinely can't -- caching, compression, complex rewrites -- the sidecar Nginx is dead weight. Remove it and let the ALB talk directly to your application process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;An ALB is not a drop-in Nginx replacement. It's a managed load balancer that happens to cover the subset of Nginx features that most teams actually use. If your Nginx config is a handful of &lt;code&gt;proxy_pass&lt;/code&gt; directives, some &lt;code&gt;return 301&lt;/code&gt; redirects, and an SSL block, the ALB can replace all of that with better availability, simpler operations, and infrastructure-as-code as the default workflow.&lt;/p&gt;

&lt;p&gt;If your Nginx config has Lua blocks, caching rules, complex rewrites, or custom header logic, keep it. That's what Nginx is for.&lt;/p&gt;

&lt;p&gt;The question isn't "is ALB better than Nginx." It's "am I using Nginx for things the ALB already does?" For a surprising number of teams, the answer is yes.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>nginx</category>
      <category>devops</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>How to Use AI CLI Tools: Claude Code, Codex, and Gemini CLI</title>
      <dc:creator>Jeff Yaw</dc:creator>
      <pubDate>Sat, 28 Mar 2026 20:45:44 +0000</pubDate>
      <link>https://forem.com/jeffyaw/how-to-use-ai-cli-tools-claude-code-codex-and-gemini-cli-24d7</link>
      <guid>https://forem.com/jeffyaw/how-to-use-ai-cli-tools-claude-code-codex-and-gemini-cli-24d7</guid>
      <description>&lt;p&gt;AI CLI tools are changing how developers write code. Instead of switching to a browser or IDE extension, you stay in your terminal and work with an AI that can read your codebase, edit files, and run commands. Here is a practical overview of the major options.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All four tools below can be installed from &lt;a href="https://yaw.sh/terminal" rel="noopener noreferrer"&gt;Yaw's&lt;/a&gt; built-in install wizard (Ctrl+Shift+I), which handles dependencies automatically.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Code
&lt;/h2&gt;

&lt;p&gt;Claude Code is Anthropic's AI coding assistant. It runs in your terminal, reads your project files, and can create, edit, and delete files with your approval. It is an agentic tool ΓÇö you give it a task and it works through it step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;macOS / Linux: &lt;code&gt;curl -fsSL https://claude.ai/install.sh | bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Windows: &lt;code&gt;irm https://claude.ai/install.ps1 | iex&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reads and understands your full codebase&lt;/li&gt;
&lt;li&gt;Creates, edits, and deletes files (with approval)&lt;/li&gt;
&lt;li&gt;Runs shell commands&lt;/li&gt;
&lt;li&gt;CLAUDE.md project context files for persistent instructions (reads AGENTS.md as fallback)&lt;/li&gt;
&lt;li&gt;Works with git for safe, reviewable changes&lt;/li&gt;
&lt;li&gt;Hooks system for automating workflows around tool events&lt;/li&gt;
&lt;li&gt;Subagents for parallelizing complex tasks&lt;/li&gt;
&lt;li&gt;MCP (Model Context Protocol) support for external tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best For
&lt;/h3&gt;

&lt;p&gt;Multi-file changes, refactoring, building new features, and understanding unfamiliar codebases. Claude Code excels at tasks that require reading many files and making coordinated changes. Its CLAUDE.md context system lets you encode project conventions once and have them applied consistently across sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Codex
&lt;/h2&gt;

&lt;p&gt;Codex is OpenAI's CLI agent. Like Claude Code, it operates directly in your terminal and can read, write, and execute within your project. It is built in Rust and uses OS-level sandboxing to isolate command execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm install -g @openai/codex&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OS-level sandboxing ΓÇö read-only, workspace-write, or full-access modes enforced via macOS Seatbelt and Linux Landlock&lt;/li&gt;
&lt;li&gt;AGENTS.md context files ΓÇö an open standard under the Linux Foundation, also read by Cursor, Copilot, Vibe CLI, and Devin&lt;/li&gt;
&lt;li&gt;Image input ΓÇö attach screenshots or design specs alongside prompts&lt;/li&gt;
&lt;li&gt;Subagents for parallelizing complex tasks&lt;/li&gt;
&lt;li&gt;Built-in web search for up-to-date information&lt;/li&gt;
&lt;li&gt;Multiple approval modes (suggest, auto-edit, full-auto)&lt;/li&gt;
&lt;li&gt;MCP support for external tool integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best For
&lt;/h3&gt;

&lt;p&gt;Security-conscious workflows where you want OS-enforced boundaries on what the AI can access. The sandbox modes are the most granular of any AI CLI tool ΓÇö you can give Codex full autonomy within your workspace while blocking network access and protecting .git. The AGENTS.md standard also means your project context works across multiple AI tools, not just Codex.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gemini CLI
&lt;/h2&gt;

&lt;p&gt;Gemini CLI is Google's open-source (Apache 2.0) AI coding agent. It is free to use with a Google account ΓÇö no paid API key required for Gemini Flash models, which include a 1 million token context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm install -g @google/gemini-cli&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Free tier with 1M token context window (Gemini Flash models)&lt;/li&gt;
&lt;li&gt;Open source (Apache 2.0)&lt;/li&gt;
&lt;li&gt;Extension ecosystem ΓÇö browse and install community extensions with agent skills&lt;/li&gt;
&lt;li&gt;GEMINI.md context files with hierarchical discovery across directories&lt;/li&gt;
&lt;li&gt;Google Search grounding built in&lt;/li&gt;
&lt;li&gt;MCP support for external tools and services&lt;/li&gt;
&lt;li&gt;Can be configured to read AGENTS.md for cross-tool compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best For
&lt;/h3&gt;

&lt;p&gt;Developers who want a free, open-source AI CLI tool with no API costs to get started. The free tier is genuinely usable ΓÇö 250 requests per day with Flash models and a 1M token context window. The extension ecosystem and Google Search grounding also make it strong for tasks that need access to current information or specialized tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vibe CLI (Mistral)
&lt;/h2&gt;

&lt;p&gt;Vibe CLI is Mistral's command-line coding assistant. It takes a different approach from the task-oriented agents above ΓÇö instead of working through multi-step plans autonomously, it focuses on rapid, conversational iteration. You describe what you want, it generates code, and you refine together in tight loops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;macOS / Linux: &lt;code&gt;curl -LsSf https://mistral.ai/vibe/install.sh | bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Windows: &lt;code&gt;pip install mistral-vibe&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Free to use ΓÇö Mistral's Experiment tier includes all models with no credit card required (note: free tier requests may be used for model training)&lt;/li&gt;
&lt;li&gt;Powered by Mistral's models (Devstral 2, Codestral)&lt;/li&gt;
&lt;li&gt;Conversational, iterative workflow rather than autonomous execution&lt;/li&gt;
&lt;li&gt;File reading, writing, and patching&lt;/li&gt;
&lt;li&gt;Shell command execution in a stateful terminal&lt;/li&gt;
&lt;li&gt;Recursive code search with grep (ripgrep support)&lt;/li&gt;
&lt;li&gt;AGENTS.md support for cross-tool project context&lt;/li&gt;
&lt;li&gt;MCP support for external tools, databases, and APIs (added in v2.0)&lt;/li&gt;
&lt;li&gt;Custom subagents and slash-command skills&lt;/li&gt;
&lt;li&gt;Clarification system ΓÇö asks before acting when intent is ambiguous&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best For
&lt;/h3&gt;

&lt;p&gt;Quick prototyping, creative exploration, and developers who want a back-and-forth conversation with the AI rather than handing off a task. The clarification system makes it more cautious than fully autonomous agents ΓÇö good for situations where you want the AI to check before making changes. Also the only tool here that runs entirely on European infrastructure (Mistral is based in Paris).&lt;/p&gt;




&lt;h2&gt;
  
  
  Terminal Setup
&lt;/h2&gt;

&lt;p&gt;Regardless of which tool you use, your terminal setup matters:&lt;/p&gt;

&lt;h3&gt;
  
  
  Split Panes
&lt;/h3&gt;

&lt;p&gt;The most important setup. Run the AI tool in one pane and keep a regular shell in the other. You can check files, run tests, and verify changes while the AI works. Most terminals support this ΓÇö split vertically so the AI gets the wider pane.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scrollback
&lt;/h3&gt;

&lt;p&gt;AI CLI tools produce a lot of output. Make sure your terminal has enough scrollback to review what the AI generated. At least a few thousand lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Context
&lt;/h3&gt;

&lt;p&gt;Each tool reads its own context file: Claude Code uses CLAUDE.md (falls back to AGENTS.md), Codex uses AGENTS.md (with configurable fallbacks), Gemini CLI uses GEMINI.md (can be configured to also read AGENTS.md), and Vibe CLI reads AGENTS.md. These files let you encode project conventions, tech stack details, and coding standards so the AI follows them consistently. AGENTS.md is an open standard under the Linux Foundation, adopted by 60,000+ projects ΓÇö if you maintain one, it works across most tools out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-Detection
&lt;/h3&gt;

&lt;p&gt;Most terminals treat AI CLI tools as ordinary processes ΓÇö they run, but the terminal doesn't know what they are. &lt;a href="https://yaw.sh/terminal" rel="noopener noreferrer"&gt;Yaw&lt;/a&gt; handles this automatically ΓÇö it recognizes when you start any of these tools and splits a companion shell in the same working directory. It also has a &lt;a href="https://yaw.sh/terminal#editor" rel="noopener noreferrer"&gt;built-in file editor&lt;/a&gt; for reviewing AI-generated changes, inline image rendering for viewing screenshots and diagrams alongside your sessions, and context-menu actions (Explain This, Fix This) that send errors directly to an AI provider for help.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which Should You Try First?
&lt;/h2&gt;

&lt;p&gt;If you're new to AI CLI tools, start with whichever matches the AI provider you already use. If you have an Anthropic API key, start with Claude Code. OpenAI key, start with Codex. If you want to try one without paying for an API key, Gemini CLI is free with a Google account and Vibe CLI is free with Mistral's Experiment tier. If you're not sure, Claude Code and Codex are the most mature and widely used.&lt;/p&gt;

&lt;p&gt;You can use more than one ΓÇö they all install independently and work in any terminal. Try running a real task (not a toy example) to see how each one handles your actual codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;File Editing&lt;/th&gt;
&lt;th&gt;Command Execution&lt;/th&gt;
&lt;th&gt;Context Files&lt;/th&gt;
&lt;th&gt;MCP&lt;/th&gt;
&lt;th&gt;Open Source&lt;/th&gt;
&lt;th&gt;Free Tier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CLAUDE.md, AGENTS.md&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (sandboxed)&lt;/td&gt;
&lt;td&gt;AGENTS.md&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini CLI&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;GEMINI.md&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vibe CLI&lt;/td&gt;
&lt;td&gt;Mistral&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;AGENTS.md&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://yaw.sh/blog/ai-cli-tools-claude-code-codex-gemini" rel="noopener noreferrer"&gt;yaw.sh/blog/ai-cli-tools-claude-code-codex-gemini&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cli</category>
      <category>code</category>
      <category>gemini</category>
    </item>
    <item>
      <title>The Terminal I Wished Existed, So I Built It</title>
      <dc:creator>Jeff Yaw</dc:creator>
      <pubDate>Wed, 25 Mar 2026 01:59:10 +0000</pubDate>
      <link>https://forem.com/jeffyaw/the-terminal-i-wished-existed-so-i-built-it-4amf</link>
      <guid>https://forem.com/jeffyaw/the-terminal-i-wished-existed-so-i-built-it-4amf</guid>
      <description>&lt;h2&gt;
  
  
  The Terminal App I Wished Existed, So I Built It
&lt;/h2&gt;

&lt;p&gt;I've spent the better part of a decade living inside terminals. SSH sessions into production boxes at 2am. Tailing logs across a dozen services. Bouncing between databases trying to figure out why something that worked yesterday doesn't work today. Terminals are where I live.&lt;/p&gt;

&lt;p&gt;And almost all of them feel a little buggy. iTerm2 is the exception, but it's Mac-only. On Windows, every terminal I've tried has weird copy/paste quirks or downright bizarre usability issues that make you wonder if anyone on the team actually uses it daily.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Here's the thing about terminal apps in 2026: they all make you choose. You want AI? Cool, sign into Warp with your email and let them phone home with telemetry. You want something that works properly on Windows? Good luck — most terminal developers treat Windows like an afterthought, if they think about it at all. You want database connections built in? That's a separate app. SSH management? Another app. Redis? Another one.&lt;/p&gt;

&lt;p&gt;I had six tools open to do what should be one workflow. A terminal. A database GUI. An SSH manager. A Redis client. An AI chat window. A text editor.&lt;/p&gt;

&lt;p&gt;That's insane.&lt;/p&gt;

&lt;p&gt;I wanted a terminal that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't require me to create an account just to type commands&lt;/li&gt;
&lt;li&gt;Doesn't send my data anywhere&lt;/li&gt;
&lt;li&gt;Has AI built in, but lets me bring my own API keys to whatever provider I want&lt;/li&gt;
&lt;li&gt;Connects to Postgres, MySQL, SQL Server, MongoDB, Redis, and SSH natively — with Tailscale integration&lt;/li&gt;
&lt;li&gt;Has a built-in file editor so I never have to leave the terminal&lt;/li&gt;
&lt;li&gt;Works on Windows without feeling like a second-class citizen&lt;/li&gt;
&lt;li&gt;Doesn't feel like it was designed by a committee&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing like this existed. So I built it. I called it Yaw.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Decade of Frustration, Compressed
&lt;/h2&gt;

&lt;p&gt;I'm not a junior developer who got lucky with a weekend project. I've been doing DevOps and SRE work for years — the kind of work where you learn exactly what terminal workflows actually look like in practice, not what some product manager imagines they look like. Every architectural decision I made came from years of being the end user.&lt;/p&gt;

&lt;p&gt;That domain knowledge is the part that took a decade to accumulate. It's also the part AI can't replace. But AI absolutely changed the math on everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code Changed How I Work
&lt;/h2&gt;

&lt;p&gt;I need to be honest about this because the internet has a bad habit of either overhyping AI ("I built a SaaS in 20 minutes!") or dismissing it ("it just writes bugs"). The reality is somewhere in the middle, and the middle is still transformative.&lt;/p&gt;

&lt;p&gt;I used Claude Code throughout the entire build. Here's what that actually looked like day to day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it was incredible:&lt;/strong&gt; Scaffolding. When I needed to wire up a new database connection type, I didn't have to go read driver documentation for two hours. I described what I needed, Claude Code generated the integration, and I refined it. The boilerplate-to-thinking ratio shifted dramatically. Instead of spending 70% of my time on plumbing and 30% on decisions, it flipped. I spent most of my time making decisions and Claude handled the wiring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CLAUDE.md trick:&lt;/strong&gt; Early on I started maintaining a CLAUDE.md file in my repo — basically a living document that describes the project's architecture, conventions, patterns, and decisions. Every time I'd work with Claude Code, it had this context. And it got better over time. Eventually Claude Code understood my codebase's patterns well enough that its suggestions actually matched how I'd write the code myself. This file became a competitive moat I didn't expect. The more I built, the smarter the AI assistance got, the faster I could build. A compounding loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it fell short:&lt;/strong&gt; The decisions that mattered most. Which features to include and which to cut. Whether to require sign-in or stay anonymous. How the AI integration should feel — not just work, but feel right in a terminal workflow. These aren't code problems, they're product problems, and they come from years of being the frustrated user. No AI has that scar tissue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack (And Why Electron Isn't the Enemy)
&lt;/h2&gt;

&lt;p&gt;Yeah, I used Electron. I can already hear the comments.&lt;/p&gt;

&lt;p&gt;Here's why: xterm.js is the most battle-tested terminal emulator on the planet. VS Code uses it. Millions of developers use it daily without knowing. It handles Unicode, ligatures, truecolor, mouse events — all the stuff that takes years to get right in a native implementation.&lt;/p&gt;

&lt;p&gt;And node-pty with ConPTY gives you real, native terminal process management on Windows. Not a shim. Not WSL pretending to be a terminal. Actual ConPTY support that makes Windows feel like a first-class platform. Copy and paste just works — which sounds ridiculous to brag about, but if you've used terminals on Windows, you know.&lt;/p&gt;

&lt;p&gt;Could I have gone native? Sure. I'd also still be working on it months from now, fighting platform-specific rendering bugs instead of shipping features that matter.&lt;/p&gt;

&lt;p&gt;The rest of the stack is React, Zustand for state management, Vite for builds, and Electron Forge for packaging. Nothing exotic. I didn't need exotic. I needed fast, reliable, and cross-platform on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "No Sign-In, No Telemetry" Actually Means
&lt;/h2&gt;

&lt;p&gt;This isn't a marketing slogan. It's an architectural decision.&lt;/p&gt;

&lt;p&gt;When you use AI in Yaw, the API calls go directly from your machine to your AI provider. Claude, ChatGPT, Gemini, Grok, Mistral, Bedrock, OpenRouter, HuggingFace — nine providers, you bring your own keys. Or run models locally with Ollama where nothing leaves your machine at all. Either way, the request never touches my server. I never see your prompts. I never see your responses. I have no infrastructure to log them even if I wanted to.&lt;/p&gt;

&lt;p&gt;There is no account system. There is no user database with your email in it. There is no analytics pipeline watching what commands you type. This isn't because I'm too lazy to build those things. It's because I think the current model of "give us all your data in exchange for using our app" is broken, and developer tools especially should not work that way.&lt;/p&gt;

&lt;p&gt;Your terminal sees everything. Every command, every password you type, every SSH session, every database query. A terminal app that phones home with telemetry is a terminal app you shouldn't trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Parts AI Couldn't Solve
&lt;/h2&gt;

&lt;p&gt;Not everything was smooth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updates.&lt;/strong&gt; Getting the update process dialed in across three platforms was a surprisingly deep rabbit hole. It sounds simple — check for new version, download, install. In practice, every platform has different expectations for how apps update, and getting it to feel seamless instead of janky took more iteration than I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software detection.&lt;/strong&gt; Yaw detects installed software and makes it easy to install tools like AI CLI utilities across all platforms. Sounds straightforward until you realize every tool can be installed five different ways into five different directories depending on the platform and package manager. Accounting for all of that without breaking on edge cases was tedious, unglamorous work that AI couldn't shortcut.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Organizing the chaos.&lt;/strong&gt; Where does every feature live? What keyboard shortcuts make sense? When you're building a tool that combines a terminal, database client, SSH manager, Redis client, and AI — the UX decisions multiply fast. There's no right answer, just a lot of "try it, use it, realize it's wrong, move it."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowing what NOT to build&lt;/strong&gt; was harder than building. I shipped at least five features that I ended up ripping out entirely. AI makes it dangerously easy to add things. You describe a feature, it materializes in an hour, and suddenly you've got scope creep disguised as productivity. The discipline to kill your own work is entirely human.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping Fast Isn't Just About the First Version
&lt;/h2&gt;

&lt;p&gt;One thing I didn't anticipate: the speed advantage doesn't stop at launch. An early user asked if I could add AWS Bedrock as an AI provider. I shipped it in three hours. Not three days. Not "we'll add it to the roadmap." Three hours from request to release.&lt;/p&gt;

&lt;p&gt;That's what happens when you have deep knowledge of your own codebase, a well-maintained CLAUDE.md, and no layers of process between a user request and a deploy. The early feedback from DevOps engineers and SREs has been exactly what I hoped for — people who get it, who've been waiting for a tool that respects how they actually work.&lt;/p&gt;

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

&lt;p&gt;A solo developer with deep domain knowledge and AI tools can now ship what used to require a team and months of runway. That's not hype. I did it.&lt;/p&gt;

&lt;p&gt;But the takeaway isn't "AI replaces developers." The takeaway is that the bottleneck has shifted. It used to be "can you write the code?" Now it's "do you know what to build and why?" Domain expertise, taste, and architectural judgment are the scarce resources now. The typing is the easy part.&lt;/p&gt;

&lt;p&gt;If you've been sitting on an idea for a tool you wish existed, the calculus has changed. The gap between "I wish this existed" and "I built it" has never been smaller. And once you ship, you can iterate at a pace that keeps up with your users instead of making them wait.&lt;/p&gt;

&lt;p&gt;That's what I built Yaw for. Not to prove AI can write code. To prove that one person who knows the problem deeply enough can finally solve it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Yaw is free to download at &lt;a href="https://yaw.sh" rel="noopener noreferrer"&gt;yaw.sh&lt;/a&gt;. No sign-in required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>terminal</category>
      <category>devops</category>
      <category>ai</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
