Explore how software engineers use Effect to build reliable, production-ready software in TypeScript.
If I haven't expressed it, I want to express
one last time. Like effect systems are [ __ ]
perfect. Promises are junk. They're straight
hot garbage. Programming is in this sort of
a this this local maximum that it's been
is hanging around in. And there have been
better ways of programming that have been
sort of embraced in these niche languages
which were too weird to sort of penetrate
the mainstream. And bless Michael Arnaldi
for bringing effect into Typescript because
it has allowed it to be viable. every language
where an effect system was introduced in, it
did become the dominant way of writing in that
language. Never before has it been introduced
into something as ginormous as as TypeScript,
but I feel like we're on that
hockey stick growth curve. [music]
Hey Kit, I'm so excited to finally have you on
the podcast. We've been planning this for quite
a while. Finally happening. Welcome. You finally
caught me. I guess I have to be here now. What an
awful day in my life. So, for folks who probably
have seen one of your videos, uh, by now they have
recognized your voice and you're you're finally
here. For those of you who don't know who you are,
would you mind giving a brief introduction who you
are, what you have to do with effect, and what you
currently do. Yeah. Yeah. Yeah. Hi, everyone. I'm
I'm Kit. I don't always sound like this, but I'm
very close to my microphone. That's the trick
to ASMR. You just get really close to your mic,
by the way. And if you go back, you kind of sound
normal. It's my normal voice. But yeah. Um, just
get back just get close to the mic and it'll all
work out for you. Um, yeah. Uh, my name is Kit. I
was uh I've been a longtime effect system fanatic.
Uh, it it bit me around I don't know, eight years
ago. And ever since I've been doomed career-wise
uh to to live in a increasingly diminishing niche
of irrelevancy and and functional perfection to
sort of wrap build a cocoon around myself and die
in it was my fate. But then the robots came and
I got scared. So I decided to flee to the very
uh welcoming giant uh tower of of Typescript.
And uh turns out there's like nice people
there and there are the jobs and stuff. So,
luckily it's been saved from my doom fate. Um,
so I'm a big effect systems fan, big effect fellow
like effect like effect systems of all kinds, but
um, effect is the first one that seems to have
a shot at mainstream relevancy and I want to,
you know, I want to curse the world with
my same disease. Yes, that's it. Yeah. So,
if if anyone hasn't checked out Kit's videos yet
on on YouTube, definitely check those out. We're
going to put those links in in the description.
But aside for your interest in effect systems,
you're also currently working on OpenCode. So, uh,
how did that come to be? So, that I think that was
a direct I'll have to ask Dax. Uh, you'll have
to have Dax on at some point. He's been, I think,
at this point, uh, thoroughly effectilled, as they
say, by himself, too. I I I've learned my lesson.
I think I might have the record for rewriting is
the most systems in in the world into some form
of effect system just because it's niche enough
of a technology and I've done it at like five
jobs now. So I've had enough experience trying to
convince people and you can lead a horse to water
etc. I mean I mean you can really just sort of
dump water all around them and make it so that
they can only step in water. That's kind of
my technique. And just like drink the water
in front of them and say m water so good. Uh this
water is pure. But you can't make them drink. So
that technique seems to have worked with Dax even
though he I think he he wanted me to join because
of uh he knew what I was get he was getting rather
um I wasn't hiding my effect passion on the on
the timeline as it were and so I think I I came
across his timeline with um uh I made this thing
called effect.institute which is sort of a an ASMR
effect tutorial interactive tutorial thing that's
kind of weird. Yeah, we can I know we wanted
to make this interactive so I could show off
some of these things as we talk about them, but
I mean you you've you've now mentioned it a few
times. Let's take a look. And for for those of you
who are just listening, uh definitely check out
the the link later is definitely worthwhile your
time, particularly if you're rather new to effect.
Effect has always been like mindblowing, but it
took a little while until it kind of clicked if
you're new to effect. And effect institute
lowers the bar so much by like just making
it much more accessible. It reminds me to like
some of the feelings I had when I tried out like
um Rails for Zombies uh back in the days and I
was always hoping there would be something like
that for for effects and then Kit came along and
did it. Yeah. Yeah. Yeah. I I do like I did love
Rails for zombies. Yeah. The goal is to make it
such that you would want to go through it even
if you don't know what effect is. Like even if
you hate effect, you'll want to still interact
with the website. Um, so yeah, this is this is my
newest effect tutorial. I have some real feedback
here. I'm a wizard. That's Wow, it's really great.
I have the man I have the mandate. Yeah, I have
the mandate. That's that's my goal is to have
the mandate. As you can see, there are lots of
uh many pending lessons here. little effect or die
reference at the bottom. But yeah, I'll I'll just
click on this thing. It's [laughter] so I made
my screen ginormous or my my display resolution
very low. Uh but yeah, it's a sort of interactive
thing. I'll just click through this. You see, use
the arrow keys to navigate between these different
lessons. And when you hit space, you will hear my
voice speaking to you in maximal uh narration
mode. Welcome to the effect institute. I'm Kit
and I want to teach you effect. But first, the
briefest of tutorials. Pressing space, as you may
have discovered, will play or pause the See, it
just paused it. So, you can see that there's some
animations that sync to the narration, and it's
more interesting. I can you can also command click
between sections. When we call it, a promise has
but one failure. Check out fail. And if it does,
so I'll pause it there. Can you hear that, by the
way? Is that too loud? This is uh Yeah, perfectly
audible. And I would encourage everyone to check
it out by themselves. Probably even better audio
quality. You can play around, see those like
amazing detail, both not just visual details,
but actually also audio details. Those [clears
throat] just uh craftsmanships at its finest. So,
uh, it is it's almost so good that it distracts
you from learning it. [laughter] That's the good
that was the goal is to find that that boundary.
Um, yeah, and there's some nice animated effects.
It's the last thing I'll show off is that oh,
there's some like uh nice little pretty things
that are animated with W web GPU, which I'm pretty
happy with. Uh, anyway, yeah, I'll I'll leave that
for the uh the viewers to indulge in. and and
so effect institute is built uh sort of like
as a sequential project for some of your your
previous related work. Let's let's just dive in
and we'll talk about OpenCode in a moment. But
effect institute is sort of like a successor
project that builds on top of like visual effect
visual types etc. Is that the the way how I should
think about it? Yeah. Yeah. Yeah. I've basically
been building weird visual we can tie this in I
guess to my my weird history too but um my first
I joined a bootcamp that was my entry entry point
into programming and then they hired me. So from
day one at my job I was always doing some amount
of teaching and creating visualizations cuz I
found that to be fun. Like I I went to school for
illustration and so I I like to keep some sort of
visual elements and build full stacky things. I
like it all. I just want to do all of it and and
a good way to force everything into every project
is to build, you know, some kind of full stack
animated application which kind of lets me shove
all my interests in there uh as best I can. So
that's kind of why everything takes on this kind
of shape. I actually back in the day when I um
we'll get to this a while ago, but I started my
effect system journey in Haskell and then quickly
ended up in Scala and there's this library called
Zo. You can see right away that this looks
terribly ugly, but there's this thing I made,
which is this visual Zio animation. It tries
to teach you Zio and give you a sense of how
things work, help you build an intuition with some
incredible animations. Obviously, this looks like
hot garbage because I made it in 2019. But, you
know, it is working and it's using Scolletjs. So,
it's actually running this effect system called
Zio in the browser and letting you visualize
various things. There's some things at the bottom
here that don't look as bad. So there's this,
you know, visual stream uh implementation. So if
you uh map over a stream, multiplying it by two,
you'll see sort of the resultant stream is double
the first stream. If you zip a stream with an
index, you'll see it uh zipped with its index in
the list. So there's some some ideas here that are
not too different than what I end up doing later
on. It's just I think generally much uglier. Got
a little It turns out if you make a bunch of
projects, you get a little better at visual
design. So I made that about yeah a long time
ago this library that effect was based on called
uh Zio and then about last I think around almost
exactly a year ago uh effect became impossible to
ignore and Scala became increasingly to me felt
like I was increasingly irrelevant in the world
at large. So I decided to jettison and and and
at least try actually it was simpler than that.
I just wanted to try out effect and I thought
I could remake um this project basically an
effect. So I made a visual effect. So if you
go to effect.kitlangton.com and you'll see it's
uh it's prettier and you get this little spinning
uh orb up there. It's pretty nice. And basically
you click and you see a visualization of
different uh effect syntax. So these are
sort of the basic constructors. You can effect
can succeed. It's green. They're sound effects.
They can fail. It's red. They're sound effects.
And then it can die. This one's fun. So yeah,
I definitely got carried away with everything. Um
I'll I'll skip off to just for for time reference.
I think you built this one pre- AI uh or like
th this one was probably like Claude Code or
whatever was not as widely available. It was this
was a year ago. It was definitely AI helped. Um,
but also this is like the architecture I kind of g
I've just been pulling code forward from all those
past projects. So you could certainly not oneshot
this. And so there's like real artisan handcrafted
love going into this. There is some handcrafted
love and and I think you can also bring as a as
a very brief tangent I'm sure everyone's seen on
the timeline uh much slop in the world. People
use AI to just build more and that's great. But I
feel like to really take the most advantage of AI,
you should use it to iterate even more and to push
things further instead of just letting you sort
of poop out more content that just sort of meets
the minimum bar of legibility of of sort of okay,
this vaguely feels like a blog post that
might have been published 10 years ago. Well,
now everyone can do that. So there's all this
slop and I think people are getting a little
more perceptive around identifying it. There was
definitely a time where you would be rewarded for
po publishing slo blog posts but I think that
the sort of the telltale indicators of AI I
mean it's always changing right purple gradients
different fonts like it's adapting but if if you
if you view a lot of stuff I think I think the
general consuming public my hope is that they
become perceptive to that and seek sort of higher
taste content higher quality content and you as
a person who's making stuff has more opportunity
to iterate further and further so even if you're
using AI to do things just like raise your
bar a lot uh and and really cuz if anything
uh looks off to you just just you know shoot off
another prompt and and make it better. Isn't it
amazing that we can now go the extra mile with
like so little energy we need to put into it?
Uh I think for for perfectionists is like the
the best time ever. Yeah. And I think maybe the
only counterveailing force there is that there's
also this newfound pressure to produce more. So
it's like okay I could perfect one thing or I can
produce 100 awful things. So I mean it's the same
balance that's always been there of like explore
and exploit uh except maybe there's increasing
pressure from the outside. But yeah I'd say it's
more satisfying like do you can explore more but
when you finally do find something that uh you
know interests you and titilates you that you
feel like there's some promise there definitely
go deep on exploitation. So speaking of promises,
let's zoom out a little bit. Like highly recommend
to anyone like check out those various projects
that you've just just demoed in case you're
just listening to this right now. But you've
um about when was it like half a year ago or so
and we're recording this at the end of April.
You've joined the uh folks who are working
at OpenCode or on OpenCode. I think Anomaly
is the is the company behind it. Yes. Yes. Yes.
It's not just called OpenCode but yes I I think
you've joined them with the goal to free them of
the the promises [laughter] and bring in effect
to the OpenCode codebase. So, uh, I think there's
a fascinating story behind this that I would love
to unpack and learn more about. Like, OpenCode is
probably an pretty sizable codebase, probably even
larger so by now, but can you share how big it was
when you when you joined and what the situation
was like? So, actually, I I don't know. I should
know the number of of lines of code, especially
because I'm sure I've added to it with my various
tests and parallel implementations. uh of things.
But actually, it can kind of be nice when you're
taking off maybe a potentially infinitelysized uh
task to to not look ahead and just sort of let the
uh the passion of the moment. If you're going to,
let's say, run across Antarctica, you might just
want to focus on the next 10 footfalls instead of
the the entire breadth of of cold, harsh alien
landscape ahead of you. So I I I didn't really
consider just how much once I started converting
it to effect uh how large that task would be.
I actually I joined I think in February so it's
only been a few months but it's felt it feels like
uh a few years for various reasons. On a high
level how concluded is the effect migration the
effectification of OpenCode at this point? Uh
it's getting really really close. I currently
have a sort of a parallel implementation of all
the routers. So a little bit about OpenCode's
architecture is that for anyone who doesn't know
it's it's basically a Claude Code like except it's
all open source and it works with all different
models. You can use it with your uh ChatGPT
subscription, your pro subscription. You can just
ooth in there. You used to be able to use it with
your uh Anthropic subscription but uh they took
that away. So they've they've been reducing what
you can use that for but nonetheless and we have
some subscriptions etc. But generally it's an open
source uh agent which you know you you ask it code
queries and it runs in a loop and can call various
tool calls and work with all these different
providers and it's open source and the I think
the the distinguishing mark architecturally is
that it it sort of is this client server model
so that you can have multiple sessions uh you can
have multiple clients all connected to the same
server that runs the sessions and there's some
been some recent work now there's we're sort of
working on this control plane so you can have one
running control plane and then possibly different
workspaces running in different sandboxes etc.
Nonetheless, client server architecture. And so
there's a server is the point is what I'm trying
to get to. And initially this was written with
Hono which is uh a nice server library very
efficient but it's not written in effect. And
effect has its own uh server library. And one of
the beautiful things about effect is that it is
this sort of all-encompassing ecosystem uh that's
all very high quality and interoperates with
itself uh obviously more smoothly than another
non-effect library will. And that there's a very
a couple of reasons for this and and why you might
want effect to wash over everything slowly. Not
that it can't interoperate, but just that the more
land mass it it claims, the happier you become.
um because you get to preserve its those
invariants that it has throughout larger
swaths of your codebase. And another sort of piece
of glue in in effect is the schema library which
is if you're familiar with Zod, it's sort of the
Zod of effect. So you design your data as these
descriptions, these specifications and you get
serialization and deserialization for free and
other various things because uh that your types
are sort of described as data very very clearly.
It can also be used to generate uh many things
like open API specifications uh servers clients
etc. And it is all very type- safe. So once you
switch over to effect, you probably are also
going to want to design all of your data models uh
using schema and do all of your data modeling with
schema and also use it to generate your server and
and open API docs. So some of that was done with
Zod and Hono. So, I've just gotten the point where
there's a parallel implementation of everything
using effect server uh the the effect server um
setup the HTTP API uh in parallel with Hono and
I have a feature flag that either routes to the
Hono implementation or the effect implementation.
And if this works well for the next few days, I'm
going to delete the Hono stuff. This is actually
definitely a worthwhile uh tangent, which is how
do you approach even organizing such a migration?
So I I started off really timidly. Um I mean Dax
did mostly give me the thumbs up. I guess my first
I've been working on also in parallel this other
project which will probably be revealed in time.
So that's kind of what I spent my first month
working on with some parallel uh uh commits to
uh the main OpenCode codebase. And the original
intention was actually to not necessarily involve
effect there because it's an open source library
because maybe that would alienate certain people
just keep it basic TypeScript. But um I didn't
even have to convince Dax. As I mentioned, I just
kind of tried to really make a show of how much I
enjoyed working with effect. And I wanted to make
this other project a sort of a shining exemplar
of effect and and let the quality of that uh sort
of incept the idea of using effect everywhere.
But he he we was I guess doing some parallel
exploration and using it in some side projects and
ended up convincing himself before I ever had to
uh to to try too hard. So before long he was like
you know what we'll just effectify everything. I
know how these things go. It it'll it'll uh it'll
come here eventually. We we want it too much. So
once I was given the green light I started very
timidly by uh just um boarding some identifiers.
So one thing that effect has is with schema is the
ability to have branded data types and that's very
useful in in a codebased like effect for instance
and sorry like OpenCode you have many identifiers
for sessions for messages for um workspaces etc.
And many of these are strings. They're like random
UUID strings, but the type system says they're all
strings. And these are passed around everywhere.
And the trouble with just using raw strings is
that you can easily sort of mistranspose them and
and pass in a name like the model name instead of
the model ID. And TypeScript won't care. And maybe
this will even get serialized uh and saved to your
your database and things will just kind of break
in in weird ways. So, I've always been a fan of
using the type system to help reduce the burden
of and the possibility of of having these kinds
of simple errors. Like types are really good at
tracking sort of coloring data in various ways and
making sure you don't use the wrong put the the
square peg in the round hole. Simple stuff like
this. to to add one more tangent to the stack is
like my my meta thought about AI and what's good
for agents is uh and maybe this is just selfish in
my own own own worldview that I had previously but
I think it applies is that what is good for humans
is good for agents generally what is good for a
hardworking engineer is also good for an agent
because we're both context constrained agents are
obviously context constrained in their windows
nowadays they have like million context windows
but still there's some kind of degradation that
happens beyond 300,000 tokens in a lot of them
and I don't know it's all it's is I'm sure it'll
get better, but for the time being and maybe for
the until the singularity in the event horizon,
I think that will hold for a while. Why not be
more context efficient? We obviously as humans
are even more limited with our primate minds.
There's this paper which is like the magic number
seven plus or minus two, which is like we can
hold basically five to nine discrete ideas in
our heads. And we have all sorts of clever tricks
of like chunking over time. Five discrete ideas
collapse into like one idea. uh you know, driving
a car at the beginning. It's like, "Oh my god,
I've got to articulate each of my limbs in this
particular position and put the pedal on the gas
to accelerate and that I feel like I'm doing like
quap." If you've ever played Qwop, everything is
everything starts as quap basically. Uh let's find
quap. Uh oh, I'll just play. It's an athletics
game. So, you have different buttons. Qw controls
the thighs and the calves. So, your goal is to
start a And then I died. So, I made it 1.3 meters.
And like we could just do this for the rest of the
thing. I used to I I used to be okay at this game,
but I haven't played this in decades. I'm going
backwards. Uh so you can figure out different
techniques, but uh yeah, I'm going the wrong way.
Let's just Can I even kill myself here? Anyway, uh
so this is how everything feels at the beginning.
Um all skills to me, but it's eventually you
just it it sort of collapses. It chunkifies and
becomes walking. I don't know why I'm going
into this learning psychology stuff. I was
interested in this in the past when I was doing
teaching. Nonetheless, we still are constrained
dramatically. So what it what helped me in and
why I was so drawn to um type systems and hasll
originally was I started with Ruby on Rails which
is great and fun but I felt this like deep anxiety
uh building every time I wrote any code. It just
felt wrong and scary. Basically you have to keep
everything in your head. There are these implicit
contracts that you're dealing with otherwise your
code will just break at runtime. So there are
always types. It's just whether or not you encode
them in the type system or you just kind of use
conventions and try to keep them in your head.
I don't know how I ever kept it in my head. It's
impressive and for people that can use dynamically
typeyped systems like I think maybe there's
some kind of brain atrophy that occurs when
you go over to a static type system uh because it
just feels like so impossible once you've embraced
that crutch or exoskeleton or whatever that Gundam
uh whatever neon evangelon suit or whatever of the
mind it's it's super addicting and super fun.
So I just kind of got addicting addicted to
expressing more and more in the type system with
the goal of enabling local reasoning which is
uh basically to me what all of functional
programming is for. There's lots of people
who like to get a little onanistic about it and I
think a lot of people like words like endofunctor
and monad and profunctor optics and they can use
these words to confuse and insultify people. We
are not going to use the M word um because it's
actually kind of simple really and that word does
not help like you don't need it to teach people
obviously. In fact, removing that from effect,
people are learning way more effect than they
ever learned the Haskell effect system or or
some of the other uh more category theoretical
embracing uh effect systems of the past. Not
necessary. And in fact, it can be an obstacle.
I think people can fall in love with that and
they it's kind of arbitrary and anyway that's a
whole other talk. I forget which stack I'm on now,
but uh oh yes, why context? Like it allows you
to forget more and more incidental complexity.
The reason why we like pure functions is because
it's input mapped to output. You can forget about
global state. The second you have global mutable
state or even a mutable input, you have to be you
have to know now in your head, you have to load
into your local RAM how everything is called, how
instead of just being able to sort of zoom in on
this one function, clear your mind and just solve
the hard problem of what you're dealing with. But
yeah, I mean there's so many related ideas there.
But generally they uh to close one of those loops
is that we're context constrained. The agents are
context constrained. Type systems are are great
for both of us. Um I'll trust an agent's code if
it type checks afterwards, just as I would trust
my own code more if it type checks afterwards. Um
if it were a dynamically typed system and the
agent made a bunch of changes, well, I would
be very afraid. Uh just as I am with myself. I I
couldn't work on just JavaScript codebases because
it you don't refactor a JavaScript codebase. Like
that's a horrifying proposition. But if you have
types constraining you, you kind of can get to
the point I know it's been a meme and people
make fun of like if it compiles then it works.
Not necessarily. There's still logic you can get
wrong of course. But if you're just doing a pure
refactoring, I'm much more confident that it does
work if it compiles afterwards on either side.
And I you still want to double check everything
of course, but you can express a lot in the type
system. So that that's useful. And even further,
effect allows you to express even more in the type
system and have more constraints and a whole bunch
of other features, but basically it it allows me
to use agents a little more uh maximally than I
would otherwise to embrace them more and to be
less afraid. And and also like there's two sides
of it too. Like there's there's the there's the
fact that you know if it makes a bunch of changes,
it probably won't break anything if it if it does
compile at the end. But also once you're you have
a type you know you know certain things about it
especially with with schema like there's an aspect
of uh there's a principle or property known as
being correct by construction. If you only expose
a limited ways of constructing a type that might
fail it's sort of like if your types are basically
parsed you know that once you have an instance of
that type certain properties are fulfilled which
means I don't need to if I have just a string I
don't need to peruse the rest of my codebase to
know like what kind of string is this? what kind
of properties does the string hold? I'll say, "Oh,
it's this type. Therefore, I know it's a a valid
UUID or I know it's a a positive integer." So,
you just have to search less. You sort of there's
more semantic meaning in a very specific type than
there is string, which can really be anything,
which means less context for me. Also, less like
AIs know how uh hasll works and how type systems
works, how effects work. So, they can also trust
the type system. So it also helps them not have
to once again like look at the whole codebase to
understand any one aspect of it if it is if if it
is sort of pure and self-contained. Okay. So we're
basically just exploring how constraints help uh
overall and how this has this like almost emergent
positive effect for any codebase. So that's how
you what you've used to um to start the migration.
It's not necessarily that this is the core of
effect yet. It's just that this is a primitive
that the effect ecosystem provides. So you've used
that to kick off the effect migration. What was
the the next step after that? Yeah. Yes. Yes.
So I I got increasingly gluttonous with my PRs
once I sort of got enough confidence to that that
I wouldn't break everything. [snorts] So I made a
full few PRs. You know, the first two I was like,
"Oh, please review this everyone. Review this one
line commit." In short order, I was merging, you
know, 20 commits myself. uh 20 PRs myself um at
the same time. So all all the guardrails kind of
flew off once I I was not breaking production for
a few weeks. So yeah, I I sort of started with
one service after that. So I did some some IDs
fine that was good to have changed throughout
the codebase pretty minimal just I think an
increase in code quality but after that it was
time to actually sort of make an effect service.
I think that's a reasonable boundary. One of the
reasons I like effect is because of I I wrote an
article about this at some point. I think I just
posted it on Twitter, my only Twitter article. I
was trying to win a million dollars. Um but I
I didn't. No one not enough people care about
layers. But uh just kidding. Services are
beautiful because they are very reg regular.
They're they're fractal. They can express many
things. It's it's a very reasonable module of
a codebase. And and ideally why I was first
drawn to effect systems particularly in Scala
with the zeal library is because there was a way
of writing code where every piece of your code
was sort of self similar to every other piece of
your code. Services were composed of services.
It was turtles all the way down and back up and
and all the files were were easy to navigate like
it just felt clean and organized and uh whatever
kind of OCD I have that only applies to code was
deeply satisfied by that particular architecture.
And so an analogy here might be for for those the
most of us I guess who haven't done a lot of Scala
in the in in the past but uh I think a more common
analogy would be maybe react components where
you've used the word like fractal or self similar
uh when you look across like a large react
codebase like it's everything is a react
component and then you compose them together and
like whatever react component you look at you have
an intuition for like how the entire thing works
and I think the same can't really be said for like
free form TypeScript business logic particularly
when it's async etc and I think what you're
describing with a with a service is uh there we
we get that sense of familiarity and composability
etc is that right yeah yeah especially contrasting
it with just vanilla TypeScript I wasn't I'm not
too experienced in vanilla TypeScript codebases
I've sort of avoided that uh like the plague
for a long time like that's why I was a Scala
engineer so I tried to you know live in my own
little perfect bubble as long as possible but I
have some you know I read about it in books what
the real world is like and how it's very messy and
I certainly peaked at some codebases and and was
sort of mortified and horrified and disgusted
and repelled by the standards that exists out
there because basically without effect you have
a few ways of doing things and and none of them
are very good especially if you want to test your
code. I I think the number one the obvious way is
just to have okay have a file where you implement
your service as a bunch of exported functions and
if you have some shared state just put that in
the file if you start up some connections just
uh to to external resources uh whatever open up
a websocket connection just do that in the file
when the file loads I don't know and if you want
to test it basically uh I think if either don't
test it or use monkey patching like if you want
to depend on a service that depends on something
else well it's just it's importing it directly.
So you the only way to really test it is to do
some kind of monkey patching thing uh some kind
of mock which is very brittle and not type safe
at all as far as I can tell with the tools that I
was using or exploring briefly and so you have to
just make sure if you change the underlying logic
of that dependency that you also update your tests
and it's easy for these things to drift and fall
out fall apart which once again like the anxiety
comes flooding back in the second like if I on
my tombstone uh maybe the word single source of
truth is what I'll get as my my epitap. Not a good
epitap actually, but it sounds sounds deep. Um,
but that's what I find myself. It's like that's
my northstar design principle because yes,
there's just one way of expressing things. It's,
you know, and once again, dynamic types fails
that test because there are no contracts. They're
implicit. So every time you if you try to do duct
typing, there is no single source of truth of that
contact contract every type. You just need to sort
of manually make sure that you don't [ __ ] that
implicit contract up. With a type system, you can
have a trait, you can have an interface. That's
the single source of truth for the structure of
that type. Similarly, the reason I like scheas is
there's a single source of truth for the structure
of so many things. Everything is derived from
that initial specification, servers, clients,
etc. And so with services, you have an interface
that represents that service and you can have
multiple implementations of that uh which are
described by this uh data type known as a layer,
which is a sort of effectful constructor for a a
service interface. And so you could easily make a
test fake, a high quality test fake that you know
does everything you could possibly want it to do
per data type. Sometimes they're very simple.
Sometimes you can do more complicated things in
the effect codebase. One of our services, sorry,
in the OpenCode codebase, one of our services
is obviously the LLM service which wraps at the
current moment um AI SDK, but that's we don't want
to test necessarily against real LLM providers and
and you know every time we run our tests we lose
$100. That would be bad. So, one of the things I
did is built a really nice test fake library where
uh the rest of you could just run the main sort of
session service and and call the prompt method on
that and it'll run all the real production code,
but when it gets to that LLM service, uh it it's
feeding back pre sort of predetermined data that
you determine in the test. So, I made a really
nice test mock. So, first you can make prompt.
It'll obviously block waiting for what would
have been HTTP responses to come back in from say
ChatGPT's uh model but instead on the next line I
can say you know llm.text what's up and then that
will get fed back to it it'll sort of be streamed
in as the response uh to the the session service.
So you can do all sorts of really beautiful things
that I don't even know my theory is just people
just don't really do it at all. They just don't
test it. That seems to be the case. That
seems to be why so much software is just
um I'm I'm trying to think of a better word than
dog [ __ ] but that's the one that comes to mind.
So, [laughter] I don't know. Have you felt this?
I've noticed that like catchy.com for instance, so
often I'll type a message, I want to type a second
message, I can't hit the submit button anymore.
Some stream has gotten lost. And with my effect
oriented mind, I just see like a leaked resource
or an unclosed stream. An abort signal that
wasn't propagated as most likely uh the culprit
cuz that's hard to do right and you have to do it
all the time everywhere and be very careful about
it. And there's so many sort of foot guns. It's
just a sort of a golem consed entirely out of foot
guns. [laughter] So just to bring this back, we're
basically uh walking through the the the migration
from uh the OpenCode codebase with like pre-effect
to increasingly effectified where you've mentioned
that by now you basically have a parallel
implementation of pretty much everything that's
fully effectified and you're about to flip this
over completely. But I think you're still walking
through the early stages of like how did you kick
things off? Also, how did you uh win the trust of
the of the rest of the the OpenCode team to like
get get on board with this to change the way the
at that point status quo way of writing TypeScript
to now embracing this. Yes. Okay. Thank you. Yeah,
I'm sort of turning this accidentally into Zeno's
podcast or Zeno's Paradox podcast where Oh, I'm
We'll never get through the first question.
We're going to keep getting halfway there and
going off on tangents, but hopefully it'll be a
good uh journey to nowhere. Yeah. So, services,
I I implemented a service. So, uh some tangential
service, a new service that involved this other
project that I was working on, some kind of uh
service that was logging into that. Dax wrote
the original version uh had a PR. I knew it wasn't
connected to the rest of the infrastructure. So,
I basically effectified that, turned it into
a service. It went well and from there I tried
to just hunt through the rest of the codebase.
I mean many codebases are oriented as services
even if they're not effect services even if
they don't make their boundaries too clear.
Every file perhaps is a module. There's some
kind of internal dependency graph that you can
track. So I try to find a good inlet there and
I think you know generally starting at either
the bottom or the top is is good. Um burning the
candle at at both ends can work but but also you
can start really anywhere. I tried to find some
simple services. I think I can show off some
code actually at this point. Um, not that it's
going to see if it if it's easy enough to to to
understand. Yeah. Yeah. Yeah. It's not Mhm. While
we're pulling this up, I'm curious like how did
you choose that service? Um like what I've seen a
lot is that people adopt effect really motivated
um centered around a particularly burning
problem and like often it has to do something
uh with testing. Um, so I'm curious what was that
burning problem that you were reaching for like a
services abstraction right away and uh that is
probably also buying you some some trust and
goodwill from new colleagues who um who are well
aware of this problem and might be skeptical of
like new technologies. and seeing it like solve a
real problem probably goes a long way to to build
up a lot of trust and to to get the goodwill to
to do the effect migration. Yeah, luckily I had
some cover because well I was hired knowing that I
was uh totally deranged by effect and that I there
would be no stopping me. So I took I took that uh
as as some encouragement. And then yeah, I I was
mostly optimizing for something that wouldn't
break everything. I think I was more afraid of
uh making like I already had enough goodwill and
trust to do this. They were already sort of sold
in effect. The only thing I I could see sort
of undoing that was if I destroyed OpenCode for
millions of users. So it was more like minimizing
the impact at least at the beginning. I knew the
benefits would come and things would become more
testable. I think one service I started with
uh I might be wrong on the order the PR it's all
open source so you can track all the PRs if you
want and go through the history of whatever 400
PRs I opened in the last couple months but I think
there was these there's permission and question
uh services so I'll just start with uh question
so this is like the ask user question tool right
so I can say something just for the the folks who
are just only listening uh we're basically just
navigating through the OpenCode uh codebase at
this point and We're looking at a few different
modules. I think you're you're using zed here and
uh obviously also running OpenCode uh on on the
side to get some some more information about
uh the the codebase as uh as we do these days. We
don't like look things up ourselves anymore, but
we get it explained. Um, and so yeah, we're we're
looking here at a few schema definitions, which I
suppose is related to some of the the early work
you've been doing. Yep, exactly. Uh, so we're
looking at the question service. I'm sorry for the
audio people. This might be a little weird. Uh but
we I'm sure we'll go on many tangents that are
more amendable to just the spoken word. But um
the question service is basically there's this ask
user question tool that I think Claude Code first
had where the agent has a tool call available to
you where it can submit questions and then you
see in your UI a in this case a numbered list
of of possible answers that you as the user uh
can can answer or you can form in as a tuy. Yes, a
little TUI form here. So, uh I I asked OpenCode to
ask me a bunch of questions. It's obviously pretty
random questions because I it has no context. It's
a new chat. So, it asks me for my current goal.
What would you like me to help with right now? I
don't know. Let's say review some code. So, I can
hit three there. Uh how much detail? Detailed.
Okay. Um whatever. And then research only. Fine.
And then I can confirm these results and it'll
get those results and continue looping with that.
I'll interrupt it here. But the question service
is the one that tracks which questions have been
answered um the user's responses and when it's all
all done. So there's a bit of asynchronicity here
which is always a good place to to use effect. Um
once again that wasn't the primary service. I
wanted to start by not destroying the codebase
and ruining my credibility but I I there there
is also a little bit of extra benefit here.
um something that there's some somewhat meaty
uh immorsely uh about about this with regards
to concurrency and asynchronicity. So at the top
I do have some schemas. So the the optic question
option uh there's like a label and a description.
So I I guess that's sort of what is is actually
I forget what what is indeed what here. Um I
should look at this exactly again. I think yeah
the answer is is what I'm offered. So we're we're
effectively modeling our situation here. So if you
think about this form, we have questions, we have
like different options to answer that question and
then we get answers hopefully. Exactly. Yeah.
And and the nice thing is that some of this was
already designed albeit in Zod and raw promise
raw promises. So I I didn't have to think about
the design of the system just yet. I'm excited
for multiple passes where I can improve things
uh or or sort of apply my taste and opinions
to the overall uh data modeling. But my goal
originally was like don't do two things at
once. If you're going to be refactoring a
giant hundreds hundreds of thousands of lines of
code into a new paradigm, don't also inter interle
uh sort of semantic logic changes because if
something breaks, I didn't want to like not know,
okay, was it the effect migration that broke it or
was it my other uh logical change that I couldn't
resist? Um don't change too many variables all
at once. Yeah, that would be very frightening.
Uh need to isolate the variables as you as you'll
see here. I also on a lot of these schemas I tend
to use schema.class just I don't know I like the
way that that reads. One of the the downsides of
Typescript is sort of its general verbosity.
This is why I was sort of stuck in Scala for
so long is that you do have to sort of repeat
uh various identifiers multiple times for for
various reasons. Luckily um and this is something
another tangent we can go on down the line is like
AI I think not only helped affect adoption
but also well yeah AI for me personally made
effect [clears throat] more feasible. I was such a
chromogen uh that I I loved Scala's terseness and
and expressivity as as they like to say which
basically just means you can you don't have to
write a lot of redundant code. It's kind of that
single source of truth principle except even at
the syntactic level just like you write the bare
minimum that expresses the idea with TypeScript.
Sometimes you have to uh duplicate yourself.
However, agents make this painful. Exactly.
Like now it basically you no longer need to write
it yourself. So the when you had to suffer twice
before where you had to like by hand write out
the duplication then still read the duplication.
Now we only have to bear with the uh reading
the duplication which is more tolerable. It's
more tolerable and also the brain's really good at
pattern matching. So once this you know your brain
recogn I like all I see now is just okay there's
an option thing there's opt option everything else
kind of blurs out in the background and and of
course maybe one day we'll have uh in our idees
sort of projectors that can uh instead of just
syntax highlighting we can sort of restructure the
syntax because if we're not editing the code we
can just sort of read a simplified projection. We
could project this into another language that we
like. So I could read this as H hasll or whatever.
The Unison programming language kind of had
support for this, but but anyway, that's not here
yet. Maybe that'll never get here. It probably
doesn't matter because this this isn't really
a pain in reality. I'm I get better at reading
TypeScript. But you'll see that each of these
does have a static readonly Zod property because I
have a I should probably open source this. I mean,
it is open source. It's just all in line, but
there's a there's an effect to Zod converter.
So there's the schema to odd converter because we
need this at the boundary for the Hono uh server
so it can generate its API documentation and parse
things. That's how you basically created this like
still lasting bridge between the old world and the
new world where you didn't have to do a big bang
migration, but you could essentially still have a
single source of truth throughout the migration,
but still like feed it into the old system and
use it in the new system or vice versa. Exactly.
So yeah, we have this this boundary problem
and and generally I mean effect makes this
fairly painless, right? So you need a way from
going from the promise boundary to the effect
boundary and then from the effect boundary back
to the promise boundary. So you can run effects
as promises and you can wrap promises as effects.
So effect.promise can uh it actually gives you an
abort signal which is really nice even though most
people don't use that in their promise code. And
then you can call out to some other you know AI
generate text and you can pass in the uh signal
the abort signal as as an option or something
and that'll turn that into a promise. And of
course you can also do Effect.runPromise and take
some effect and get back a promise of its uh its
result or whatever. So you have to do these at the
boundaries and there's some tricks that I that you
might have to do in in in a large codebase like
this and we can get to that later on. I mean
it's all open source once again. So there's some
details there if if there is some complexity which
this codebase does definitely have. Uh and then
I had that same problem with Zod and schema. So
I don't think I ever go from Zod to schema. Uh I
just because you have less of these stacks, right?
So I just uh defined the base implementation. I
just started at the leave nodes instead of like
having Zod then schema then Zod. I started the
leaf nodes and built myself back up converting
to Zod at the boundary so that we could use it in
the uh the Hono uh server. And that's basically so
earlier you asked before about the Hono server. So
I do these days have an HTTP uh API but basically
um both are pointing to the same underlying
at this point effectified services. This one
just calls uh Effect.runPromise basically before
calling these things. Uh or I have a I have an app
runtime.runPromise on the uh the effect services.
Whereas the HTTP API can just sort of yield
whatever kind of uh service I'm usingdo thing
directly instead of having to run to a promise.
And I think that's like really highlighting one of
the the most important things about using effect
not in a vacuum where like you like one Sunday
you start a new project and you decide to write
the first line of code in effect and like you're
you're already in that universe. But uh when you
have a codebase that probably has like hundreds
of thousands of lines of code with like that has
real complexity like everything kind of interwoven
with each other. Now you want to like you maybe
you have all the agreement that in a year from
now everything should be effect but how do you
go from like today to there in a way where
you don't like freeze everything now you need
incremental adoption and I think this is where
there's like multiple ways to to go about this
but interoperability is like helping so much this
is why I think converting a code ba an existing
TypeScript codebase to effect is just making
that's so easy is like very far away from let's
say porting something from Go or Java into effect
because you can still like reuse all the chunky
bits of your code as little or as much as you want
or or say like to use a TypeScript uh library as
well like to to make fun of RxJS more like if I
were trying to do this I don't think this would
work very well like going from promise to RxJS
that might be possible but it's it's a it's a
very different paradigm right this always operates
the level of streams. Everything is going to be
a stream. Not all promise code fits into these
oneshot streams very well. It's it's a weird uh
impedance mismatch to fill out your bingo card. Um
yeah, whereas effect uh is represents basically a
computation that might succeed or fail. It's it's
strictly a supererset of of a promise basically.
And then you have a separate streaming abstraction
for when you want streams. So yeah, it's pretty
painless. Uh overall, there are a few nuances.
They'd probably be boring in this context,
but I definitely will write something up um at
some point or make or add a add a chapter to my
effect institute on some of the more uh low-level
gotchas or details. But uh I think most of that is
only because of the some particular patterns that
we were using in OpenCode that aren't necessarily
even very common. So, so if we zoom out a little
bit, so this this questions module has been like
one of the the first targets that you've converted
and sort of like introduced the effect patterns
and prove that this is working. Um, if we zoom
out a little bit and like over the course of now
a few weeks and months, how did the migration
progress? Which sort of issues did like at some
point you probably uh started looking into more
thorny areas of the the codebase? I think you've
mentioned something that uh before that there were
like pre-existing abstractions that you had to
like translate a little bit uh a little bit more.
Um so which sort of challenges were you facing and
how did you overcome them? Yeah. Yeah. Yeah. I'm
actually going to have OpenCode here uh render a
little service tree of of uh all the services that
I've effectified. Hopefully that will help because
this is just one note of of many. uh the sort of
the core of it is definitely the the core session
service that ends up transitively depending on all
all of these other ones and just to sort of round
off like so at the top I had the schemas which is
is great but then we we try to make all the files
there are many ways of doing this but um once
again self similar and consistent so that if I
have an agent port another file it's a fairly
mechanical process so at the beginning I would
recommend doing this is the approach that worked
at least for me when migrating a large codebase
or doing any kind of repetitive task is like walk
walk through the very first couple of iterations,
the example set very carefully, maybe handcode
them or at least go through a bunch of iterations
until you're very happy with the way everything
looks and then use that as sort of a seed as a
reference point for the agent as you tell it to
do other ones and eventually you can see that it
gets to a point where it almost does what you need
on the first shot or the second shot. And so yeah,
really paying attention to those first few
examples because it agents are pretty good
at copying and extrapolating existing patterns.
So you want to make sure that the the data set
you're feeding it is is really high quality. So
if someone doesn't have that in their own codebase
yet, would you uh have any advice for references?
What people should check out as a maybe pointing
their agent to as a reference? Should should
they just look at the OpenCode codebase or are
there other simpler reference apps that that you
would point people to? So I did I did make that
effect.solutions website for sort of that purpose.
I could briefly share that again. I actually don't
think I shared this the first time. So let me
just share it. Um I do need to update this.
I think I just did update it for the newest
effect v4. But basically this is just kind of
um yeah I was trying to with all my effect work
uh effect tutorialization work. I was trying to
hit a couple of different um what I saw thought
of as GA gaps. One was just to as I was mentioning
before just like highquality uh content that
would have been enticing even for its own sake
regardless of effect just to expose it to more
people so that they would share it and say like
I don't even know care about effect but here mom
watch this uh this this will be interesting this
will make your brain tickle or something. And the
other one was, so the existing docs are great,
but they're very encyclopedic and they kind of
start from first principles and it just gets
into the details really quickly. I wanted a much
higher level view almost like an almanac or a a
best practice guide of just like don't worry about
how this works. Uh you can read the docs if you
want to know that or you can ask your agents for
this is sort of low-level toutelage, but why don't
I just tell you what to do. Yeah. And obviously
that's not going to apply to every situation,
but if you're a newcomer, like what what fulfills
the paro principle of like here's 20% of the sides
of the documentation that satisfies most of
what you need. So like here's basically sort of
very prescriptive organize your stuff like this.
And there are many ways of doing this. In fact,
in OpenCode, I I've changed this. I pull
this out into a separate interface. But
it's really that that's a matter of taste. This
is perfectly viable. And I would just recommend
being consistent picking something that you that
you want. And there's a whole bunch of examples
in here. So effect.solutions has one take of this.
Uh and it also of course because everything does
there's this copy agent instructions button. So
if you click this and paste it into whatever agent
you want, it basically tells the agent how to um
install a CLI and set up your codebase with like
the effect LSP server and the right TypeScript
settings and how to basically read these docs.
That seemed to be the lowest friction way of doing
it. just like a copy paste into your agent instead
of an MCP or something which is just more friction
at the end of the day just like click copy pasta
people can copy pasta since the beginning of
time so I trust them to continue doing that
um got it and so that basically contains all the
patterns that are like yeah parto principle style
that you might encounter as a foundation uh
just to to get things going and at that point
you probably develop your own tastes and your own
like preferences how what is like your Acme Corp
uh way of doing things and then you can mold it
and like tell the agent like maybe create your own
skill file or something. Yeah, that's basically
the equivalent of my skill file except as a as a
website and not as a skill because I think at
the time I made it skills weren't universally
adopted. I think we were still unskilled. We
were unskilled. Uh but yeah, it's basically
I was I was constantly sending paths to my agents
of other codebases where I done this. I was like,
let me just uh sort of collect this into some a
useful thing that I can distribute both to future
versions of myself as well as uh the general
public. Um and yeah, in there I I basically
recommend okay, always use schema for all your
data modeling because yeah, it's principled it
works well. That's just something I was missing
from Scala is just a you could basically define
any kind of data in the world as a combination
as a sort of a composition of what the functional
programming language people called product and sum
types. But it's like and types and or types like
uh for instance uh you can think of a boolean
that's that's sort of the canonical most simple or
type. It's either it has two values false or true.
But a person might be a composition of uh you know
um an uh is alive which is a boolean and their
name which is a string anyway. And you can have
infinite types and all that stuff. But basically
that that's uh these two very simple composable
primitives are enough to basically define any kind
of data type you want in the world. Like bits are
essentially booleans. All right. On or off, true
or false. And then you just have a bunch of them
anded together, right? A bite is is whatever
eight of them. Um and out of that, of course,
everything uh computable calls. So it's definitely
powerful enough and and simple enough. Um yeah,
it's orthogonal. It's it's got everything I like
about um API design like maybe one of the top
principles of good APIs in my in my estimation is
orthogonality that you don't have too many things
doing the same thing. You have you can achieve
a lot of power but through compositionality and
orthogonality and I think just product and
subtypes are a good example of that. We're
not going to go in a in a separate tangent
of this, but just a call out also for like
your visual types project, which like beautifully
also like highlights the the the various goodies
of Typescript on a on a type level. I I think we
we're going to like see a a brief demonstration of
that, but like please go check it out like in
your own time. This is uh like I think mostly
decoupled from effect but if you uh I think
it's gives you like a deeper understanding of
um like effect by just knowing the basics
of it. Yeah. Yeah. Yeah. So this is just
another fun thing. I was as I mentioned newer to
typescript and there are some details to the type
system. So I kind of did this as a little um
both to have fun with animations but also to
uh teach myself um type. So yeah, you can hit the
arrow keys again to like navigate between. Okay,
this is the concept of types are sets, which
is kind of what I was alluding to. So boolean
is a set of true or false. Direction, northeast,
south, west. So these are sort of these sum types,
these ore types. Uh and number you can really
think of as an infinite set of well of ores. Um
so it still it still works there. Anyway, there's
a whole bunch of fancy animations and where's the
vin diagram? This is my favorite one. Um,
but yeah, I'll stop sharing that one. Um,
check it out. It's too too good to ignore.
[laughter] I got going going back to to the to
the migration and I think you're by now um GP 5.5
has given us a good list of like all the modules
we need to scroll quite a bit. So this is the the
domain of uh of OpenCode. Yeah. Yeah. It's kind of
is it's sort of uh it's not done it in the nicely
nested way that I would have wanted. It's kind of
showing each we have to reconstruct and traverse
the graph here. It's just giving us a bunch of
edges basically like okay agent define depends on
plugin but we would have to find plugin. I'm going
to give it one more chance and plugin depends
on bus and config etc. I want to actually here
we go. So okay so there's the the main app layer.
Uh let me go to where's the the session and and I
think just like looking at this ignoring which
technology this is we might be writing rust we
might be writing something else but I think this
gives you OpenCode is like a complex product
um and a complex codebase project etc and this
gives us a good semanticformational overview over
like the involved concepts almost like a glossery
page that explains the the various concepts
and doing that in a hierarchical way. And like
the cool thing is like those those concepts now
correlate very elegantly, very concisely with
effect services that actually give you the
implementation for that concept. Yeah, basically
each of these is an effect service. So every file
is laid out the same way. And once again I you
know if I do come up with a better pattern I
could this is a very parallelizable agent happy
refactoring where it's like oh let's rename
all interfaces to something else like okay I'm
exporting the interface as interface that's kind
of silly but why not? I was trying to think should
I come up with a better word like what should I
call this? Well it is an interface so why don't I
just call it the interface [snorts] and it's per
file so it it ends up working out just fine.
And so yeah there's there's always a service
context.service service um that has this interface
and then there if there is a main layer sort of a
main implementation which most of them have just
one it's defined here as as layer which uh all
like the way you construct these things or the way
I always do is at at the top you always uh import
transitive uh dependencies or sort of you I guess
yield your transitive dependencies so these are
other services so by yielding this we're going to
get a a dependency on bus service so we'll need to
come up with an implementation of that to provide
later. But of course, bus service has its own
layer. So, we can just to linger on this and like
if this is the first time someone is seeing this
and highly recommend maybe switching to a video
at this point if you're if you're still just
listening. The the magic that we're seeing here
and I think that's really novel about effects,
at least when effects started uh coming out. No
other technology that I was aware of in Typescript
did this is that if you're using one thing um
like here in like we're we're using the this
service for our thing that we're building now
we know like A requires B to to B and like this
um transitive dependency like and u that that just
like builds up uh implicitly without you having to
write down like be disciplined and write all of
this down like this just composes automatically.
Uh I think that's like one of the most beautiful
things about effect and that's really like through
composition you can break down the complexity
of your of your codebase. I mean yeah the agent
was fairly uh quickly able to come up with this
outline all by itself. I wonder if this were in
the old style if it would have been able to figure
out this exact structure because it's able to just
look at these values and these types directly
because basically every file also has I kind of
stole this pattern from effect itself the previous
iteration just always also exporting a default
layer that takes the layer and just provides a
good reasonable default. So you'll see that if
this is imported anywhere, which I'm sure it
is. Okay, so the server like the main server
just imports the top level of default layers
for everything and if and if these have other
dependencies, they're going to depend on their
default layers and so on transitively. So you
don't have to provide everything. Everything
defines a default layer and you basically just
use those at the top of your application. And
just to make this a bit more concrete, if you
can go back to the place where you're constructing
all of the the layers and compose them together,
this is like this is probably the like routes you
you probably instantiate somewhere and then you
run the real thing. But let's say we're in a test
now. You might not want to maybe use like the real
LLM layer, but now you might want to use the the
mock in memory LLM layer. And just by switching
this out, like you're still satisfying that you
need an LLM service, but now you're providing one
that doesn't cost a gazillion dollars per run,
but like one that you can run instantly uh while
being offline, maybe. And this just replacing the
need for like crazy monkey patching, etc., is like
one of the the many killer features here. these
data stripes I recently added to Zed uh if you
launch OpenCode within Zed you see the selected
line ranges down here which is kind of useful so
you get that as context in the thing and I just
wanted to use that new feature show it off uh
because I'm happy about it because I use Zed and
OpenCode all the time so I was missing this sort
of explain the data types and how they interrelate
um is nice because now it has the context sent
there and it knows what I'm talking about yeah so
now if I was confused about exactly what it would
Okay. Uh whatever. Um it's nice. Okay. So, uh
but yeah, so the structure of these layers is you
yield to transitive dependencies. Then you have
some internal helpers potentially. These are not
part of the public API. And then I always sort of
define the public API as Effect.fn calls. And the
reason for this is that well you give it a nice
little label here. This is used as a telemetry
span. So, one of the cool things we got working in
in OpenCode is if I go to this little local thing
I have is I made my own little TUI for looking at
traces called motel. Huh. Um, and so I can make
this larger or smaller. Uh, let me make it larger.
Yeah, let's look into this. And like before we go
uh more into this uh just for for those who've
like hearing about hotel or open telemetry the
the first time it's basically if you're still
just console logging and like you're you're tired
of just like looking through like ton pages and
pages of like console log like and you want a more
structured way like typically your code runs in a
hierarchical way and this is probably also the way
how you're thinking about this. Uh, open telemetry
is a like by at this point like a pretty long
established common standard that allows you to
define things called traces um, logs and metrics.
Effect natively integrates with all of this and
uh, it can be emitted anywhere. It can be uh,
emitted into data dog or sentry or wherever like
all of those technologies support that. But what
Kit has built here is a version that runs
completely locally and that gives you a much
more intuitive way to see what has your complex
program actually done and why is it so slow. Yeah.
Yeah. And so one of the things obviously before
effect that we did have some log files but lots
of things weren't logged and you don't get spans.
It's actually really difficult to do I think open
telemetry integration without effect. I actually
I guess I've never tried it before because I'd
imagine not just only difficult it's very gross
because you you have like the happy path. So like
the the thing you need to care about with open
telemetry when you want traces is you need to wrap
everything in little things called spans and that
then you can have like in the same way as you have
a function and in that function you call other
functions etc. And sometimes they run in parallel.
Now you have a little wrapper thingy that is
language independent that's called a span and
where you have subspans etc. Now you need to
basically wrap and instrument your entire code
with like spans and that's okay for the happy path
but you also need to consider things like error
handling or you need to consider things such as
like interruption etc. And this is where you just
like keep wrapping and wrapping and wrapping your
code until from like single beautiful functions
you have now four layers of uh brace indentation
and you just uh like either you regret doing this
uh like hotel instrumentation because your code
is like no longer readable or or you do it and
you like you hate you hate this entire thing uh
either way and with effect you basically get it
for free. Yeah, all you have to do is just wrap
your functions with Effect.fn, which is nice to do
anyway because it it sort of is an alternative to
the other way you would have to wrap it. I mean,
you have to use generator functions to compose
sequential effects anyway. So, all you have to
do in addition to that is basically just add a
little label here and that becomes a telemetry
spin. You can see that they're nested because if a
function is called if a if a annotated function is
called within another function, they get nested
to their current parent and so on and so forth.
And you can see how long these uh things take.
The prompt takes 23 seconds. Most of this time is
waiting for the the agent to to run and therefore
most of the other little things are done at the
beginning and at the end of that of that run.
So it's not terribly interesting in this case.
Uh but it can be useful to see okay what takes
the longest. All right snapshot our snapshotting
takes 102 milliseconds. Is that worth looking into
more? Like is there something that's really slow?
I can here sort by the slowest call. So, okay.
Why does O all take uh 800? Clearly, there's some
kind of issue with this this uh this one because
it takes 8,000 seconds. That's not exactly true.
I'm sure uh span must be getting left open. But
one cool thing is I'm I'm using this to debug
uh OpenCode itself. So, every instance I start
locally connects to that local telemetry store
which is this is like totally written in effect
in Typescript and is all local data. uh and
it's using open tui which uh is the ti framework
written in typescript that OpenCode uses lots of
dog fooding going on so honeym uh a great codebase
to also check out if you want to build your own
like effectbased CLI effect has a great way to to
write CLIs integrating that with a complex system
like uh open toy uh open toy open ti open that
works yeah open whatever and then also connecting
it with like the way how you do state management
uh in in effect apps which I suppose you're using
effect atom for this uh for for this I am yeah
I've yet to get to the front end the TUI front
end in OpenCode but maybe but one day I'd like to
do this once the back end is all clean up but yeah
I think this is using effect atom yeah um it is
partially vivecoded this of course so I wouldn't
necessarily use this as a a shining example but
um it does work which is nice so one of the Cool
things as well is that if you're running locally,
so not in production, you get this uh this session
ID. So if I want to debug this very session, I
can go here and filter by any sort of attribute
key. So I can find session ID and paste this in.
And we see there there are five spans for it. And
then theoretically in here I might I might be able
to find something like a question like let me look
for ask eight matches. Oh yeah, there's question
ask. So I did ask twice. Where did ask? And just
to uh explain uh what what's going on here, we we
were looking at that like effect code for that ask
questions form module in OpenCode and we've used
it before. We filled out like some example data
for for that and uh that code has run and like in
traditional system you might have like maybe some
log output in a in a console somewhere uh but like
then that quickly turns into hundreds t thousands
tens of thousands of of lines and you're just
maybe command effing through that but that gets uh
very boring very quickly. you probably don't have
like timing information in there. And here we're
like looking at something that is like immediately
intuitive. Like we immediately understand where
uh like time was spent and at every span that
we focus on, we now have like some contextually
enriched data called span attributes that is
relevant for for this particular span if we
choose to instrument it like this. Yeah, I should
probably add some more attributes for question
ask because we don't see the questions here, but
that's that's that's trivially uh addressable. Um,
one of one thing that obviously might be striking
is that this took 228 seconds, but that's not
a problem actually because if you look at
the implementation here, what ask is called
being slow. Yeah, it was me. It was me taking 228
seconds to finally answer the these questions. Um,
which is interesting because it it shows off a
little bit of the effect concurrency primitives
that we're using in here. uh like this isn't
problem. This isn't blocking anybody. This is
just me semantically blocking uh and and not and
taking a little while to resolve this uh deferred
um that was going to be done by me finally hitting
submit on those three questions. We'll see that
it's called question.ask is called inside of tool
execute which is called in session prompt resolve
tools which is eventually called in session prompt
uh run. So if I just close this and we we can just
briefly look at the implementation here. I'll try
to do my best to paint a picture. So we have this
question.ask function which is annotated labeled
which is sort of trivially becoming that span
which just works with any open telemetry provider
including my vibecoded semi vibecoded uh TUI
version. Then uh this takes session ID a set of
questions and a tool. So these are basically the
questions that the agent asks. So this was parsed
from the agent's JSON response and it's passed
into this function. And then there's this tool ID
so we know what tool it it resolved to. And then
uh pending pending pending. So yeah, we have this
it's a little interesting. I made this instance
state abstraction. This is probably one of the
more complicated bespoke abstractions used inside
of the OpenCode codebase because OpenCode is
that server and there might be multiple parallel
sessions going on uh in multiple projects in
multiple folders and we wouldn't for various
reasons we want each sort of folder to manage its
own state. So when you kill that session we can
release all of that state. So this instance state
thing is basically keyed by the current implicit
session. So each session can store its own list
of pending questions basically and the pending
entry is this. It's a particular request and
there's this deferred abstraction and a deferred
abstraction is part of effect. It's very nice uh
very useful for these kinds of interactions which
it's something that you can create and it's
a value that you can manually either cause to
succeed or cause to fail later on. It's kind of
similar to how a promise works actually um except
promise doesn't by default let you outside of
the thing complete it or resolve it. But anyway,
so it's this value you get that you could store
off and later on complete it with a value. So it's
really good for these kind of messaging systems
where the agent wants to ask the user a question
and then it wants to wait for the user to answer
it. So where this is called it's basically we call
the tool execute calls ask and then it it makes
this new deferred which is either going to succeed
with a list of answers that the user selected or
it's going to fail with a rejection error. So this
already like I kind of was forgetting what this
did because I worked on this forever ago. But once
again nice types. These aren't just strings. These
are nicely named types. It's like a read only
array of answers or it fails. And the other thing
is that effect obviously lets you signify how
something might fail which is not sort of implicit
in promised land. You don't know. You have to look
at the values. Here I can just look at the types
and say oh yeah this makes a lot of sense. The
user either responds or they reject they hit
escape and they cancel the question and they're
like I don't want to answer any of your questions
robot. So this is really good as documentation.
And then we basically publish this uh event asked
info question which will get sent to the TUI uh
so it knows that it's waiting on these questions
and we update this local state um basically saying
that question ID is is has this deferred in it
and when the user eventually replies to a question
with a particular question ID here we can get
that state for this instance get get the existing
question and if there isn't one it'll it'll just
fail uh it'll it'll short circuit but if there is
a question that it found, it'll delete it from
the pending set and it will publish the set of
answers and then it will succeed the deferred.
And by succeeding this deferred value, it allows
this bit which was waiting on for 228 seconds or
something to finally resolve with that success.
So it depends on how this def it's awaiting this
deferred. And so uh it's going to semantically
block here whatever code is calling this will
semantically wait until this deferred is either
completed or or rejected. And I think there's a
reject method as well which will find that same
question and then call fail with a rejected error.
And of course this is type safe like I cannot just
fail with any error here because if I change this
I believe this will cause yeah an error because uh
this deferred has to fail with rejected error not
with error. So it's it's all really damn nice. You
can't like you can't get it wrong. Uh it's nice to
be constrained in these ways. I can come back and
look at this after I think I did this three months
ago and sort of piece together what it does again
uh live. And I think what is particularly nice
is how it's all scoped locally. Like this is not
too large that uh we could still wrap our head
around now walking through this as most of us
are not working on the OpenCode codebase and are
not familiar with it. Yet looking at this like we
we can grasp it. It fits in our in into our head
and yet it's like a a non-trivial um complexity
amount with like all those different cases but it
is all locally scoped and like we can just compose
it like in the in the overall application and it
doesn't leak out that complexity throughout the
entire codebase presumably but it's all contained
here in the same way as if I'm thinking on a on a
high level building something like OpenCode
there's at some point there's a little thing
that's responsible for for questions and then
that's resolved and it get that gets out of the
way and I think that maps really nicely to to
the code where I like this distinction between
existential complexity and exist existential
complexity where existential complexity is
like all the things that you actually need to
build your app that's the good stuff accidental
complexity is like that everything additionally
gets more complex without adding any benefits to
your to your app and like that you want to have as
little as possible and I think effect allows us to
to express that at the at like just the right uh
cut off point. I certainly think so. I would like
to shift the topic slightly from like so far we've
been looking at code kind of like the the old way
uh as we've been writing and reading code etc.
And I think that's still important like whatever
um someone else or an agent etc is writing I think
us as engineers we should still be able to somehow
load that in our head and make sense of it and
ideally even be able to judge it whether can it
be improved is it good enough does it solve the
problem etc. Um and uh Kit is having fun over
here with like uh with a vim mode in in Zed. Um
and I think it's certainly still worthwhile today
to to learn vim as that as that's that little
uh anecdote. But um what I'm curious about is
if we're now allowing ourselves to switch into
like a fully AI pilled perspective where truth
be told I have written the last line of code uh
me personally in October last year 2025. So since
then every line of code that uh I've shipped
um I have had done by an agent. So, uh, I've
made the full hard switch and it's been great.
It doesn't mean that I don't read any lines of
code anymore. Quite the opposite. Uh, and I'm more
thankful than ever for effect because it allows me
to review in a much higher level whether whatever
was produced here is actually good or not. And I'm
also very glad that I've had like years and years
of prior hard work by hand experience uh writing
all of this by myself. So I know what uh good or
worse looks like at least judging based on my own
standards. Um and uh I think that is just becoming
increasingly the the new reality. like not
everyone has has to go like all the way over there
yet. But I do think it's the new reality that
it's not just effect being an important thing for
humans to decide for like hey are we using this as
a team or not but it also plays a big role where
the agents are uh successful with it or not. So I
would like to hand back two questions to you like
how much code are you still writing by yourself by
hand versus with coding agents working on a coding
agent? Um and what is your perspective on whether
effect is an advantage or a disadvantage for
coding agents? Yeah. Yeah. Um yeah it's a tr it's
a tricky question but I'll start by just answering
it honestly which is that uh yeah mostly dictating
to the agent to write the code for me even in
such uh sort of um embarrassing circumstances
as like you know rename that variable or add a
parenthesy to line you know column 18 on line
149 or something like this. Uh so one thing I do
all the time is I I made my own open source uh
hex.kitlangton.com. Uh what is this? Uh uh I type
that out though. Um it is sad to see my skills
slightly slightly. It like I miss it especially in
these contexts. I I used to do these like weekly
when I worked in the Zo world, these weekly videos
where I do a bunch of live coding and it was super
fun and I knew all the tricks and JetBrains
and all the refactoring shortcuts uh and and
took great pride in my Vim skills and and I I you
know have a Dvorak keyboard layout. I went fully
uh you know keyboard lifestyle, but of course a
split keyboard these days. Yeah. I got my weird
my weird uh double halved uh broken in half. Uh it
doesn't even have labels on the keys which is Oh
yeah. Uh which uh impresses no one because no one
sees it except for my wife who's not impressed by.
And also once in a while if I like need to figure
out how to do something uh with the keyboard like
it doesn't pair with Bluetooth, I need to look at
the manual and it's like oh just hit command shift
option P. I'm like okay but where's the where's
which one's the command key or which one's the
meta key? So I have to pull up the the di anyway.
It's very embarrassing in those moments. Um what
was I saying? Oh yeah. So I I'm very sad to admit
that most of the time I dictate. So I mean there's
tons of apps that do this. There's another good
open source one called Handy. I made one called
hex just to fulfill my own specific needs and
desires where I just like basically let's say
hey can you delete all the comments I added in
this file please uh I want to restore this file
and so I'll just do that yeah yeah it's super fast
it use it's not me it's it uses a parakeet v2 from
it's an Nvidia text to speech to text model and
there it goes it left some new lines in whatever
you know I'll and and with this newfound ability
to know what line I'm selecting I could just sort
of purely with Vim I don't know why I clicked I
can just navigate between these windows, go to
line 135, select this, go back here, hit option
and say, "Hey, let's rename the bus variable to
uh vehicle and it'll do that for me." And then
while it's doing that, I'll, you know, be able to
look around at some other things, and it'll make
the change. Uh, so yeah, like, okay, maybe I could
have actually sometimes for renames I will use my
let me make that back to bus using this trick. Uh
but really if if sometimes there are consequences
of renaming things where you have to also update
it in a comment that the renamer doesn't catch.
So yeah for efficiency sake I end up do just
dictating everything. If if you do not yet dictate
I highly recommend dictating because there is some
friction with typing like I did my I did my monkey
type. I have at my peak I had a pretty good words
per minute. I'm sure that's atrophied somewhat
embarrassingly now and I have more typos. Luckily,
agents don't really care about typos. So, you
can type like an idiot and it'll figure out
what you're saying. Uh, what did I just say? Um,
and so like you can just degrade even more even by
typing yourself. But there's there's a bit of
a friction there to just typing. Like it took
me way slower to type that. I didn't actually
Oh, I did say it still understands I think. Um,
I don't even how did it know that that I didn't
Anyway, I thought it was just devolved into just
random letters at that point, but maybe it
just autocompleted the obvious intention of
it. [laughter] Uh, and nonetheless, agents don't
really care about typos. They can so you can type
like an idiot. It still understands. Uh, yes.
Um, and even there even some dictation typos,
if you will, uh, misunderstandings, but it doesn't
matter. The agents get it. Even if you dictate one
thing and it it doesn't understand how to say,
you know, two, okay, you know, two weeks. It's
not two weeks instead of two, but if I say I'm
working on an agentic 2. What do you think? Um,
okay. Agentic UI. Okay, fine. So, it'll figure
it out. And if you're in the right context,
it doesn't even really matter. So, you can
just quickly brain dump so much more context
verbally than you could u bottleneck through,
you know, your your digits. As fun as typing is,
as much as I miss the experience and the time
where that was a sort of a differentiator, I I
can just brain dump and and and it I tend to have
better results. I I will sort of secretly subtly
uh not encode as much of what I'm thinking if I'm
typing because I'm I'm it takes too long and I'm
lazy. So, it might be it'll it'll be misspelled
anyway because my typing is atrophied. So, highly
recommend dictating. That's so that's kind of my
answer. I mostly dictating to an OpenCode agent
or multiple OpenCode agents. I I like to use tabs
and have multiple in this project. Another thing
I've been doing lately um is having it create work
trees for different uh right lines of work. Like
if I have a refactoring that spans multiple files,
I'll just tell OpenCode to create a bunch of work
trees. And it doesn't need any special feature
for that. It can just create a git work tree,
CD into that directory, and then work there.
And then I have it open up a pull request and
then I look at that in the browser. So you know
make a new work tree where you rename the const
bus. Select this line here to vehicle and open
up a pull request. Yeah. And then open that pull
request in my browser. So maybe I'll do something
like that except with an actual uh work task. And
then we'll see that it is going to you know make
some to-dos. Okay. So do some [ __ ] So that
that gives us a pretty good sense of your current
workflow. um as it relates to effect, how do you
think using coding agents etc. um how does that
compare to working in a completely uneffectified
codebase? Do you have any comparison there uh or
any intuition? I mean unfortunately or fortunately
for myself I've been so thoroughly effectilled
for so long that I haven't really worked on a
non-effect codebase in a while. But I I mean I was
already once again like horrified and recoiled and
in terror to work on them before agents just for
my own productivity. Oh, something that I I that
slipped my mind earlier which uh when talking
about the nice constraints of these things is
that if you do not have the constraints of of a
type system just like sort of that sort of subtle
uh pernitious tendency to when typing anytimes
there's friction like we're really lazy creatures
right we're trying to preserve energy all the
time it takes a lot of effort to to to be less
lazy um convenience sort of trumps everything we
we will express less now okay just open up the PR
for the bus rename there it's very bus binding
is open. So I then would like open up the tab
here and review it. Maybe I can leave comments and
then have the agent look at the comments. So I'll
say in here I'll dictate what have you done? Why
did you rename this? I don't want you to do this
at all. Actually close everything. Um so I'll add
that comment and then I will say read the comment
I left and uh fulfill it. Anyway, it'll do that in
the background. So just as uh if by dictating I'm
able to uh I just naturally end up being more
encompassing with all of my thoughts and I and
I don't sort of drop them on the floor because
I'm bottlenecked by my typing speed. Similarly,
if it is difficult to refactor a codebase, which
it is if it's dynamically typed, you will just
simply refactor less. You might not think that
you're avoiding refactoring, but it's it's a pain
in the butt and it's very scary. At least speaking
for me, maybe some people do love it. Obviously,
DHH loves Ruby on Rails and I I get it. Like
there's some beauty there, but for me personally,
that scares the big Jesus out of me. I just simply
wouldn't refactor it very much. I'd maybe like
create a whole new codebase. And if you're not
refactoring, like why do I refactor? I refactor
because I learned something about the code, about
the problem space while I'm working on it. And
I want to encode that. I want to encode that
constraint in the type system and the structure
of my code. I want to delete redundancies, sort of
collapse duplication into better abstractions. And
I'm able to do that fearlessly with the backing
of a type system doubly triply so with a library
like effect which allows me to encode even more in
the type system and have even more constraints. So
I I end up refactoring more and I end up making
more beautiful codebases. So I think that just
becomes easier to work on these codebases anyway
because they're better factored and they better
express the domain and I can make them regular.
I can refactor so I can shape things similarly
without fear. And the more things are consistent,
the easier it is for an agent to extrapolate on
those patterns just as it was easier for me to
extrapolate on patterns or like a newcomer like
because you can also think of a of an agent as a
fresh really smart fresh hire who already knows
about you know every library in existence. But if
your codebase is a mishmash of different patterns,
how would a new how would like a day one like
they're always sort of spawned into existence the
second you have a fresh session. I mean whatever
module your agents.md files uh how well are they
going to be able to perform the implicit
context of your entire codebase will guide
them even more than the agents files. So yeah,
it's it's easier to in a library like effect,
it's easier to refactor. Therefore, it's easier
to maintain a higher quality codebase that is sort
of made consistent and then agents will sort of
extrapolate on that better. And because of this,
the regularity, the self similarity, the fact that
each of these files look the same, I could open up
five parallel PRs, something basic obviously,
and I could sort of scan over it and just the
patterns if there is an aberration, an anomaly if
you will, that'll become that'll be immediately
evident just by almost the structure of the
code itself. Like one thing I can do is let's
say in here like I I can start from the interface
basically. I can have a uh I can make it sort of
a to-do interface and and I can I can hand type
this if I feel like hand typing it. I could I'm
at this point sometimes I actually do mistyping
because uh and I try to do a little bit to b make
asy diagrams. You saw me do a little bit of that
during this this uh this this talk. I used to do
this a lot when I was doing live coding. it it can
be even more compact sometimes to express things
diagrammatically in in you know ASCII. So I might
just be able to say like okay what what do I want?
I want a create to-do I want a list and I want a
toggle to-do method. So I could do that here or I
could describe it as the interface. Sorry. No, the
way how I think about it is like whatever is like
the highest signal noise ratio that you can sort
of like give as intent to the agent like just use
that. Sometimes it is like you copy pasting like
the old code and the new code you know exactly
how you want the new code to be then just write
it out paste it in say like hey let's make this
happen everywhere consistently. Uh, and sometimes
it's like a little uh like a little scribble on
like a piece of paper and like I airdrop the the
picture into the coding agent and say like, "Hey,
have this idea for this better architecture. What
do you think?" And like whatever is like the the
best signal like I I think the the mental model
I like is what if you would uh show something to
your like favorite colleague who's like very
experienced like immediately gets everything
without you having to explain everything. and you
what is the thing you tell them and I think that
works pretty well for for coding agents and uh
that I I love the the idea of like doing something
at a speed of thought and I think now we now we
can yeah it's it's uh it's pretty fun obviously
they can go off the rails and do all sorts of
crazy things so like I don't foresee myself being
taken out of a job anytime soon but I can I can
definitely by using them often you get a sense of
what their limits are and those bound boundaries
are always shifting. I mean, I've been using
them maximally since they first came out and you
know, the first version of it was called Codeex,
right? The autocomplete model and I ran it in
Jetbrains. I was doing some pretty complicated
type level stuff in Scala and it it it was able
to extrapolate and like auto I could tab complete
and it would do the next arity of the the method
or whatever or it would it would it would fulfill
some pretty interesting patterns that weren't
just boilerplate. So I think I think there is
something about the models where they are really
good at logic programming and and these kinds of
sort of mathematical patterns and they deal well
with types right I mean because whatever the curry
Howard isomeorphism types are logic etc. They seem
to be pretty good at this at this domain. Uh and
my experience has borne that out. So so yeah what
I might do in this case is just yeah let me let me
uh maybe I would have just dictated this to make a
new effect to do interface. But sometimes it could
be fun if I do want to be very specific. I could
do this iteratively like now that I have this
file I could say um just turn these into actual
functions on the interface please uh effectful
functions and we'll see what it does. Uh it should
be able to do this pretty quickly and then once it
does uh yeah maybe it'll look at other files. Oh
it's it's loading my whole effect skill because I
do have an effect skill that tells it to look at
the effect. Speaking of that effect skill is that
public? So this there is one in the codebase
that it found actually if I go to effect seal
and it's very basic and it just basically says do
clone. So obviously I'm sure you've mentioned this
trick before in this podcast but if not clone
the effect v4 I'm using effect v4 in here. So
that's effect small uh under the effect-ts uh or
clone that somewhere and just tell the agent to
look at it. Right. Yeah. I I [clears throat] think
that's one of the most reliable patterns not just
for effect but for like any uh any technology that
the coding agents might not be like born with yet.
And I I think that's that's a great uh a great
concept or a great method to embrace. However,
I do think that it's still worthwhile pairing that
with a skill um particularly to show very specific
usage oriented patterns and maybe your preferences
since like effect is uh such a wide ecosystem.
Sometimes there's multiple ways to do the same
thing. So for example, if you want to build an
HTTP API, you could do it over RP over effect RPC.
You could do it over the effect HTTP API module.
You can also do it like more lowle. And maybe you
have certain preferences for your codebase. So I
think being in the effect skill being it making
it more yours being more specific about like
hey other people might be doing this but we we're
doing this here like writing this out concisely.
I've had good success with in for for the for
my own effect skill. Yeah it's not it's not
we don't do anything too complicated. It's mostly
like agents files. I think the skill was recently
added. Um in the agents file I do have some
basic things like use Effect.gen use Effect.fn
etc. Like use date time instead of new date.
There's some basic things, right? If if you see
the agent making a mistake a bunch of times, just
throw it in your agents file or ask an agent to
make that change. May maybe one other topic before
closing out. You've mentioned that you're using
uh effect 4 or effect small as it started and
still called this way at least of as of today. Um,
did you start the migration with effect 4 right
away or did you start with effect three and like
upgrade it to effect 4 at some point? No, I I you
know laser less rule. I just I just went with it
um the the beta version. I think I asked Dax, oh,
you know, it was it was still pretty early on, so
I was afraid. I'm like, uh, there's a beta version
that just came out. Should uh I can use the old
version. What what would make you feel the safest?
And Dax did not uh did not flinch. was like,
"Yeah, just use the use the beta version." What's
your experience with Effect 4 at this point? Or
do don't you really have like too many reference
points to to the old version? It's it's I mean,
the agents do great. Um I think there were a
couple of naming flips that went back and forth.
The like everything just works so well. I forget
what really ch it's pretty seamless. Obviously,
the bundle size has been improved. Tree shaking
is improved. Uh schema is improved. But so so
far pretty seamless. I like Context.Service.
I use that all the time. Uh it's better than
what was it before? Context.key, I think, or
context. I think it was Context.Tag was before
and effect. Yeah. Finally, we're we're we're
going to simplify things. Yeah. I think this
is temporarily service map, but got shifted back
to context, which is great because that's that's
a good metaphor from the React world for how these
things propagate. Um I wouldn't even mind somehow
magically having it be service, but this is fine
or effect. Uh yeah. No, I mean it's barely noticed
that it's a beta. There were a couple of those
naming changes, but I think those have mostly
uh landed. It's great. I mean, it it's mostly
the same API as as V3, just better internals and
uh yeah, a whole bunch of small breaking changes,
but for the those those methods that were being
hit all the time or whatever, the paro principle
methods, it's not too different of an interface.
I can't really recall anything off the top
of my head that's dramatically different.
So you've been showing off uh a bit of OpenCode
here during this demo and I think it it really uh
showed off like what an amazing workflow can look
like particularly when paired with an open editor
that's integrated now with said and maybe also for
for other editors um also what you've shown with
voice dictation which I also use heavily uh I'm
curious what is coming up for for OpenCode as it
relates to effect or maybe as it doesn't relate to
effect. What are what are you most excited about?
Yeah. Yeah. So, definitely a thousand different
things. Uh yeah, I've been mostly working on this
effect migration. I did some other fancy uh
things like on the weekends. Now, if you hold
click and hold on the OpenCode logo, [laughter]
boom. Uh make some different fun effects. Uh
there's some other animations I snuck in there
on secret screens. Uh, we have the desktop app
that I added some animations to, but that's kind
of being refactored at the moment as well. So, I'm
excited to get back to some more product work. I
did just recently add this Zed integration uh and
and my co colleague uh James Long, who is known
as the uh well, I don't know if he's known as it,
but I'll tell you he's the he created prettier.
He's sort of working on some stuff right now with
regards to work trees and workspaces. So, you can
I don't think I have this set up locally. It's
still I think I've seen some demos uh on on X and
like yeah, I'm a big fan of of James' work. I've
had him on my other local first podcast uh quite a
while back and I'm very excited for you all to to
have a chance working with working with James. Oh
yeah, he's great. We just we just hung out for a
week in in Miami uh the whole team or most of the
team which has been was just really fun. But yeah,
he's great. You should definitely have him on
at some point. He's you could learn about his
effect journey because he's kind of new to
it. But I think he's he the pills have taken
hold after some time. So he's working on a bunch
of stuff with regard to syncing and workspaces
that I think it'll make it really nice because
generally each OpenCode instance right now is
while there is that server client architecture
if you just run it naively like this it's kind
of all encapsulated. It's it's serving it's not
even exposed. It's internal to the process. So
there is the server server client architecture
but it's not necessarily used in this case.
You can start it as a you can do OC serve and just
start it as a server and then connect to this from
other clients. Some companies like I think Ramp
famously has built a whole bunch of its internal
uh agentic tooling off of OpenCode using this this
feature. Uh one thing that I really want to have
that James' work is going to be a foundation for
is that like basically OpenCode server can be this
control plane can be like a demon process in your
computer and all the clients just go into that and
that'll help with things like memory usage because
instead of having multiple servers it'll just be
one. Similarly whenever you run Claude Code like
each time it's starting a uh it's starting its own
server process and you you see the screenshots
on Twitter whatever of like 27 different Claude
Code or OpenCode processes. Um so that that'll be
really nice as a user experience and to be able
to sort of switch to different uh sessions. Yeah,
I just want to get back to product work. I have a
thousand ideas. I wanted to first get this effect
infrastructure like I don't like refactor as as
I sort of I think I've expressed a few times I'm
terrified of refactoring uh non-effectful code or
making dramatic changes to it. So once everything
is beautiful perfect self-similar fractal effect
perfection then I can I'll probably make a second
pass sort of embracing uh pushing it even further
really making the most out of typed error messages
etc. And then once I'm super happy I will add a
bunch of features. So something I I really want
to add that I have a bit of a few spikes exploring
is background sessions. Sorry, background agents
and background bash tools. Vision's pretty good
at dealing with that anyway, but right now if I
have if I have it spawn multiple sub aents, it it
blocks the main session. You can get around this
with plugins, but yeah, basically making the the
SDK nicer and also uh some of those some of those
little uh paper cuts like a really nice experience
for for that. There's there's a thousand other
things that everybody else is working on, but uh
those were sort of my own um little pain points
that I want to address after the architecture is
uh crystalline uh scintillating apogee of of
types. Yeah, I'm I'm particularly excited for
OpenCode to be open like that other people can
learn from it like follow the journey. Now we
probably OpenCode is probably like one of the
largest codebases that has migrated to effect
or is in the process of migrating to effect and I
think that gives you a really great like reference
how you can do the migration what a full fully
effectified codebase looks like and thank you
definitely also for that. Yeah I know leaving
off here on a on a great cute demo. What are we
looking at here? So, so [laughter] uh um my other
colleague uh Sebastian known as KMDR commander
I call him because I call everyone by their uh
their I guess Twitter handles because that's what
my brain sort of brings into my uh awareness uh
primarily is is he added this plug-in capability
to OpenCode. I mean it's been there always but
TUI plugins that can also interoperate with server
plugins. So uh when I when I now prompt it will
its eyes will glow. This is goblin mode. Sort of,
you know, I decided to make a novelty plugin based
on the uh the trending meme of of GPT's goblin
issue. So, this introduced obviously I said tell
us tell me a story. You know, it injects something
into system prompt to to tell it to, you know, not
shy away from its uh innate love of goblins and
raccoons and such. And then when you're typing,
it'll add this little beautiful it looks like a
cat, but a goblin animation here. Uh so, pretty
stupid, but you know, it's it's easy. I prompted
this with like three pro prompts and it made a 2
plugin for me. So I can I think there's a way to
get um yeah plugins here and I can disable or
enable a lot of the APIs. The UI is actually
built on top of uh plugins now. So we're trying
to make it even more pluggable which is a fun
thing to have. But anyway, that was I just thought
I'd throw that up in the background for silliness
as we as we leave. But yeah, it's an honor to
uh be able to contribute back to effect and to
be part of this thing. I mean it's if I haven't
expressed it I want to express one last time like
effect systems are [ __ ] perfect and and there
it fulfills the whatever the the the criterion of
being 10 times more uh powerful and just better
than the competition. Like promises are junk.
They're straight hot garbage. Programming is in
this sort of uh it's this this local maximum that
it's been is hanging around in. And there have
been better ways of programming that have been
sort of embraced in these niche languages which
were too weird to to sort of uh penetrate the
mainstream. And bless uh the Italian uh vampire
Michael Arnaldi, the true creator of effect,
I I'll say it uh for for bringing effect into
Typescript because it it has allowed it to be
viable. Um and one thing I like to think a little
thought experiment is that every language where a
type system an effect system was introduced in, it
did become the dominant uh way of writing in that
language. Um, never before has it been introduced
into something as ginormous as as TypeScript,
but I I I feel like we're on that hockey uh stick
growth curve. And it's it's just better. Like I
don't we don't need to really pitch to anybody. We
need to like tantalize them with some ASMR [ __ ]
But they'll realize uh either by the quality I
hope to make OpenCode so much better and so much
more reliable and and memory efficient and fast
and delightful to iterate on as everybody else
sort of just uses agents to write JavaScript slop.
they'll become uh they'll be weltering in their
own slop and we will be uh you know iterating in
our golden crystalline palaces in the sky made of
pure uh thought. Uh that's that's the idea that
we can like really show the proof in the pudding
uh by covering ourselves in pudding and running
out into the streets naked and then all of us
everyone will join in in the pudding party.
Beautifully said. Well, Kit, it's been a true
pleasure uh having you on the show. We've spent
quite a bit of time together. So thank you so
much for for giving that to to me and to all of
us here and sharing that journey how you came
to effect how you're effectifying OpenCode and
can't see to to see where this goes. [music] I'm
I'm super excited. Thanks for having me on. Thank
you so much. Take care. Bye. Peace. Thank you for
listening to the cause and effect podcast. [music]
If you've enjoyed this episode, please subscribe,
leave a review, and share it with your friends.
If you haven't done so already, you can join our
Discord community. And if you have any questions,
feedback, or suggestions [music] about this
episode or about effect in general, don't hesitate
to get in touch. See you in the next episode.
[music]
[music]