sam

7.4K posts

sam

sam

@samgoodwin89

Type-checking the cloud @alchemy_run

Seattle Katılım Ağustos 2014
1.2K Takip Edilen3.8K Takipçiler
Sabitlenmiş Tweet
sam
sam@samgoodwin89·
A post on my decade long journey with Infrastructure-as-Code, why I built my own minimal library in pure TypeScript and why I think you should at least entertain the idea of "un-bundling IaC" and returning to simplicity. I built alchemy after years of working with every other option, from CloudFormation, CDK, to Pulumi, Terraform and Kubernetes. IaC is non-negotiable in my opinion, and is one of my favorite technologies as a developer. I started with CloudFormation since I worked at Amazon and really hated that. JSON is just not expressive enough for me and debugging broken stacks was was always something I dreaded. I'm pretty sure the CFN website still squashes the whole error message into a 10 pixel wide box 😅. The CDK is an upgrade on that (because yay for TypeScript) but it is still always going to be limited by the CloudFormation service, which is very slow to change and use. Being developed by corporate giant Amazon and with the founder moved on to new ideas, you just have to accept that things will move more slowly and you have no say or control over that. It's how things are in a big centralized company. One mega wart with the CDK technically is its awful coupling to synchronous I/O, making it damn near impossible to do anything async. Every user inevitably runs into this, googles it and is met with "fuck off" in the GitHub issues. Then we're forced to hack around it, which is not pretty. Not allowing is async is basically not allowing JavaScript. I doubt it'll ever be fixed. They're also stuck on CJS. Taking a dependency on the CDK and using it anywhere other than locally will 10x the size of your app and destroy your DX. It's a tangled mess of of complicated, legacy TypeScript code generating complicated, proprietary JSON files uploaded to the complicated, opaque, proprietary CloudFormation service 🫠 Why? How does this serve the user? Later, I moved on to Pulumi, which was a nice change of pace since it runs locally and is much faster. You can cancel a deployment by hitting Ctrl+C (hallelujah!) and it also supports more than just AWS, which is great because everything should be managed sensibly in code. However, Pulumi is largely a wrapper around Terraform. If you thought you were going to escape the layers, you were sorely mistaken. That JavaScript is just lipstick on the pig that are clunky "providers" implemented with Go, running in a separate process. You can't just run Pulumi everywhere (like the browser) and it even struggles to run in AWS Lambda if you're using ESM. Pulumi Custom Resources are possible but more of an afterthought and a PITA to implement. They have many sharp edges and gotchas, like the resource code having to be serialized and their coupling to CJS - so if you import the wrong thing with ESM, it just breaks. I couldn't even use AWS SDK v3 in a simple custom resource because of how leaky these hacks are. If Pulumi bricks itself by getting into some weird state, good luck fixing it. Their state files are complicated and their providers are opaque, so you're really just stuck running `pulumi refresh` and praying, or surgically removing and restoring resources one by one. It's not transparent. I've used Terraform when I've been forced into it. It does exactly what it claims to do, but I don't love it because it's a custom DSL and a heavy toolchain. And for what? Calling a few CRUD APIs? Way overkill. If it doesn't already exist in Terraform, then just forget it. Every time I think about implementing a custom resource for Terraform, I just can't bring myself to do it. Let me just write a function in my language, please! Lately, I've been using SST because I've been doing a ton of web development. At first, I really liked SST because of its local development experience. `sst dev` gives you live deploy, a TUI multiplexer and a proxy from the cloud to your local code. This is great for building web apps in AWS. But, SST is a wrapper around Pulumi!! (which is a wrapper around terraform (which is a wrapper around the underlying SDK ( ... ( ... ( ... )))) ... Layers. Layers. Layers. When will it ever end? As my app grew, SST's bugs and opinions ate away at me. I got blocked by broken resources that have race conditions and it was impossible to work around (still is, by the way). I also wanted to deploy a nested app using another `sst.config.ts` but it conflicted with their assumptions around generated sst-env.d.ts files. Again, I was let down by the complexity and opinions(!) of my chosen IaC framework. And for what? To help me call a few CRUD APIs and track my Resources? Honestly, it just seems insane how far we've drifted away from simplicity in this area. This became even more apparent as I started using Cursor more and more to write code. You see, I've found myself doing more frontend than I've ever done before, and I'm not a good frontend developer. So, I relied on Cursor to write most of the code for me and (as many others have experienced) it totally blew my mind 🤯 Cursor is just really, really, really good at TypeScript, React and Tailwind. I've built a functioning and (if i don't say so myself) good looking SPA. It's been a blast. As a "backend guy", i feel my world has opened up. But, this got me thinking ... you know what else Cursor is really great at? Perhaps even better at? 👉 Interacting with CRUD APIs. While there's a ton of frontend training data for LLMs, there's just as much (if not more) training data for CRUD lifecycle operations. They've also ingested all of Kubernetes, Terraform, Pulumi, SST and the AWS CDK's training data. Modern LLMs know it very well. Long story short, I discovered that Cursor can pretty much one-shot the implementation of Resources. All of the resources in Alchemy are entirely generated on-demand (5 minute time investment, tops). When I run into a bug, I just explain it to Cursor who fixes it immediately. Working this way may seem like more work at first glance, but in practice I think it's not. I will never get blocked by working this way. I will never have to wait for another person to prioritize my problem, merge a fix and release the change. I will never be confused about what's actually happening under the covers. This is a game changer for me. It means we don't need tools like Terraform to build layers of "provider" suites for us. We just need the engine - the bit that tracks state and decides what to create/update/delete. The rest can be generated at near zero cost. I suspect a lot of people gripe with me on this claim of "zero cost" because they have not yet embraced the AI-generation workflow. Their instinct is right, but outdated. Before LLMs, I would agree that this was infeasible, but I think we've finally crossed the threshold where LLMs can do this work trivially and respond in real-time to your requirements. If this is not obvious to you, then I suggest forcing yourself to practice with AI code generation. When it fucks up, try blaming yourself instead of blaming and discarding the LLM. I think you're missing a skill. This is why adoption curves are a thing, do you want to be a laggard? At the end of the day, you need to realize that agency is going to matter more and more as time progresses and that shackling yourself to a mega-provider who doesn't even know your name is a ticket to inefficiency. I think we've mostly been hoodwinked into thinking that because managing infrastructure is complicated, we need complicated solutions. Now is the time for that pendulum to swing back in the other direction and "un-bundle" IaC.
English
9
8
125
20.8K
agusti
agusti@bleuonbase·
I agree with this take but the problem is it's only beautiful once you understand it, I guess its a bit like real languages, they look weird and obscure if you are not fluent in them but click and are beautiful when you do, or something along these lines. kit makes a great job describing the abstractions and recursiveness of them and how it makes effect really click he's a great teacher wish he did more content
English
1
0
1
23
sam
sam@samgoodwin89·
@bleuonbase @vladlykhonis @dillon_mulroy I actually find effect code beautiful tbh. Once you understand its patterns, the whole codebase reduces into this one, simple, composable function.
English
1
0
1
31
agusti
agusti@bleuonbase·
@samgoodwin89 @vladlykhonis @dillon_mulroy But actually having controlled errors, real types, and avoiding all the footguns regular js/ts give you to shoot yourself in the foot by adding some structure and new concepts isn't that bad, more so if you're a team shipping at scale, imho
English
1
0
0
32
sam
sam@samgoodwin89·
You really need to try it instead of thinking it’s like Rx. That’s a classic mistake. I understand your reaction, but Effect is the most scalable and reliable way to build services with TypeScript. Everything I build with effect is built quicker, works more reliably, is faster, easier to test, instrument and portable across all JavaScript runtimes. That HttpServerRequest is an HttpEffect. It’s a bit odd that it isn’t passed in as an argument, I’ll give you that, but Effect is the contract for any effect based http server. Effect’s http router builds one, so you can easily swap it in/out. It is “an effect that returns a http response or error, given an http request”. I don’t try to convince people of Effect on Twitter anymore. Your reaction is a common one. I’d encourage you to try it instead of judging without experience and comparing it to Rx. Rx and Effect are not in the same category.
English
1
0
1
39
Vlad Lykhonis
Vlad Lykhonis@vladlykhonis·
For example take http request = yield* HttpRequest. Like what’s even going on here. This first impression of confusion is a red flag. I might be wrong, but experience with reactive extensions (Rx) back in time taught me this. It starts to failing with growing team and exposure of code base to more people. I was huge proponent of Rx, but then saw myself how it fell short. This code above gives me the same vibes each time it shows up on my timeline.
English
1
0
1
40
sam
sam@samgoodwin89·
@threepointone Calling this shell confused me for a bit.
sam tweet media
English
1
0
9
1.1K
sam
sam@samgoodwin89·
Those are Effect’s platform abstractions. It’s called an HttpEffect: Effect “An effect that returns an http response or error, given a request”. It’s basically your request handler. All of Effect’s http stuff boils down to building that effect. In alchemy, we have you return that. Think of it like export default { fetch } if you’re familiar with CloudFlare. The pattern is to have an Effect return a HttpEffect. The first Effect declares all the infrastructure dependencies it needs and then returns the implementation that uses that infrastructure). Alchemy evaluates all the outer effects to build your infrastructure graph and deploy it. Then at runtime, all the outer effects are evaluated when the Worker/Container initializes, and then requests start being served.
English
0
0
0
73
Cooper Corbett
Cooper Corbett@flooperflorbett·
@samgoodwin89 @dillon_mulroy is HTTPServerRequest/Response something available and required within Cloudflare.Container? any type safety requiring these to be used inside the effect if so?
English
1
0
0
159
sam
sam@samgoodwin89·
@vladlykhonis @dillon_mulroy The answer to your question depends on what you mean. Are you asking why Effect or why Alchemy’s abstraction on top of Effect?
English
1
0
0
127
Vlad Lykhonis
Vlad Lykhonis@vladlykhonis·
The is code looks so weird. I get the “ease” point, but it does remind me of a wave of reactive extensions. I did get use of them back in 2016, but it becomes very specific and fragile at scale of a product and a team. Why simple async function calls and composition of such wouldn’t work here? Why simpler solution wouldn’t suffice?
English
1
0
1
147
sam
sam@samgoodwin89·
@refactorordie I just prompt it. “Use bun -e to analyze this spec and …” “Use bun -e to explore this api and …” Playing with ideas beyond that but nothing to report on.
English
0
0
1
36
Cole Lawrence
Cole Lawrence@refactorordie·
@samgoodwin89 Do you use bun -e plus some common way of storing and retrieving state across calls? I keep trying to make this idea of a tool for ipython-like REPL with TypeScript make sense in my head
English
1
0
0
39
sam
sam@samgoodwin89·
@skootat A problem to be solved.
English
0
0
0
283
skootat
skootat@skootat·
@samgoodwin89 The Effect story within workers or DO still isn’t great though right? mostly unsupported, maybe a cart before the horse situation here
English
1
0
0
331
sam
sam@samgoodwin89·
On the left is a Worker. On the right is a Durable Object "yield* User" binds the DO into the Worker where you can interact with it via fetch or RPC.
sam tweet media
English
4
6
120
13.8K
sam
sam@samgoodwin89·
@MatthewZ78203 I’d rather use a fax machine than CloudFront
English
1
1
3
222
Matthew Zhao
Matthew Zhao@MatthewZ78203·
@samgoodwin89 We use cloudflare workers proxying S3 with live route rewrites and that is heavenly compared to cloud front and cf functions
English
1
0
3
221
sam
sam@samgoodwin89·
@Alphonste You can use tunnels or whitelist cloudflare’s IP range. Those are both less than ideal, that’s for sure.
English
1
0
1
149
Alphonse
Alphonse@Alphonste·
@samgoodwin89 For cloudflare to connect to RDS inside a VPC, it must be accessible publicly or else hyperdrive can't connect to it. Maybe this doesn't matter practically but I like the idea of the database being isolated and I'm sure other aws users do too.
English
1
0
0
153
sam
sam@samgoodwin89·
Have been building resources for deploying websites to AWS and OH MY GOD CloudFront is hot garbage compared to Cloudflare. 10+ minutes to deploy vs ~1s. Do people really want to deploy websites to AWS?
English
9
3
77
16.9K
sam
sam@samgoodwin89·
@bleuonbase A lot has changed since I built this. We now instead have coding agents implement tests for each error case, run them, detect undocumented errors, patch the spec and iterate until a service is covered. See: github.com/alchemy-run/di…
English
1
0
1
18
sam
sam@samgoodwin89·
It turns out that even AWS doesn't properly document all of their errors. So I built an Agent with Effect/AI to discover missing error tags by invoking the API. Our AWS SDK is Effect-native, so it was trivial to build. This is not sped up:
English
8
7
160
34K
sam
sam@samgoodwin89·
@jake_researcher I am building a competitor to SST :) I just built my own variants of their resources. Not impressed after coming from Cloudflare
English
0
0
10
699
Jake
Jake@jake_researcher·
@samgoodwin89 Totally agree - the deploy times are brutal. Have you tried the SST framework? It uses CloudFront under the hood but handles invalidation automatically, so you don't notice the wait. Still would rather just use Vercel or Cloudflare Pages though
English
2
0
4
721