Effect TS

Learning Effect -- First impressions


At some point, software developers have to come to terms with the question of how we deal with complexity in a way that allows us to move fast with few bugs. For me, ML (Meta Languages) played an important role in maturing me as a software developer — I stumbled on this style of programming languages by accident when I picked up Rust for the challenge of learning something new. The language features that made me love Rust were things like Option and Result types, pattern matching, and immutability by default. These features are the reason that Rust is still my favorite language to do side projects with. I love that errors and None types are represented in my type system, sum types spark joy, and the language is extremely well thought out with a rich standard library. After Rust, I tinkered with OCaml, did quite a decent amount of front end with ReScript, and introduced neverthrow and ts-pattern at work.

Now enter Effect. About a week ago, I finally decided to stop being lazy and learn at least the basics of what Effect has to offer since several software engineers I respect on X seem to swear by it. So here are my thoughts a week in. Excuse my ignorance, as I’m sure I’ll have a more nuanced view when I’ve done more with the library.

I’ve really enjoyed Effect and have good and bad things to say about it. Let’s just start with the bad of Effect — I anticipate that some of the issues I’m about to describe will go away as I learn Effect, but there have been several pain points.

For starters, the learning curve is tough. The conceptual buy in with Effect actually isn’t too bad, but it takes some time to get used to. Once I understood things like the Effect type and Layers, it took quite a bit of practice to acquire a productive intuition for writing code in this way — and this comes from someone that’s already pretty familiar with things like mapping/flat mapping over functors. If you haven’t done functional coding before, Effect will be even harder for you to grasp.

A second negative is that I had to acknowledge to myself that I’d have a really hard time suggesting this technology at work. While some teams actually do use Effect in professional code, I personally would not be the person to make the recommendation even though I really do believe you can build some really robust, safe apps with the library. The reason for this is that this style of programming is inaccessible to many TypeScript developers. Where I work, our TypeScript developers generally fall into two kinds: React developers, and Java engineers working on Cloudflare workers — both camps would struggle for a long time to pick up Effect, and would hate the strictness of the library.

So in short, the down sides of Effect are that its hard, and that I’ll likely never get paid to use it.

With the bad out of the way, let’s talk about the positives.

First, I’ve really enjoyed the challenge of learning Effect. The challenge initially is what kept me away, but the learning curve has actually been part of the fun. Effect is fun to dig into the docs, and I love the lightbulb moments when Claude explains or fixes something that I didn’t understand before.

Next, good dependency injection was a nice surprise. Before digging into the docs, I didn’t realize what a central role dependency injection played. An Effect itself has three generics parameters — the first two are success and failures, but the third is required dependencies which becomes useful for dependency injection. Once you get into Layer, you can fully decouple your business logic from its dependencies and let layers handle wiring everything together. If you can get past the learning curve, it’s a really cool experience.

Third, Effect brings safety and explicitness to your programs. If you like what TypeScript has been able to do for your programs by creating type-level safety nets, you might be intrigued by how Effect takes that to the next level. With Effect, not only are your values and function signatures typed, but side effects, errors, resource lifecycles, and concurrency patterns are all modeled at the type level. This means you can statically guarantee things like which parts of your code might fail, what resources they require, and whether they are safe to run concurrently. The result is a programming model that feels deeply intentional—where nothing happens by accident, and many classes of runtime bugs are ruled out entirely by the compiler.

Finally, I’m excited to learn about some of Effect’s other modules for things like concurrency and streams. The concurrency model in Effect gives you fine-grained control over fibers—lightweight, interruptible threads of execution—with built-in tools for racing, parallelism, supervision, and cancellation, all fully typed. Meanwhile, the Stream module brings powerful, backpressure-aware data pipelines to the table, allowing you to work with potentially infinite data sources in a resource-safe and declarative way. These modules open up a whole new dimension of expressiveness, especially for building highly responsive, concurrent systems without sacrificing clarity or type safety.

All in all, I’m going to keep hacking away at Effect and see where I end up with it. I’ve enjoyed working with Effect, and want to have a working knowledge of the basics in my toolbelt.