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