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]