Zencastr
00:00:00
00:00:01
Speed1x
Format▸
Share
Embed
Report

Why Lisp Thrives In The Age Of AI (with Cameron Barré)

Developer Voices
Developer Voices

0 plays · Nov 11, 2026

Transcript

Speaker: there was a period earlier this year where i started to fear that programming language innovation was going to die. Killed off, of course, by AI, the monster slash spirit of the age.

Speaker: It seemed reasonable. My thinking at the start of the year was this. AI is much better when it's got lots of training data. It's much better at Python and JavaScript and Java and those mainstream languages that have maybe 20 years worth of publicly accessible repos to train on, which is great if you're using a mainstream language.

Speaker: And you've got that much more of a competitive advantage if you are a mainstream language. Unfortunately, if you're not, the barrier to adoption just got that much larger. It's no longer enough to be a great language with great implementation and great design and a library ecosystem and a really nice community and great developer tooling.

Speaker: You've also got to have 10 years worth of publicly accessible training data just to be considered. That's depressing thought that AI might spell the end of language innovation.

Speaker: And I think it was true for a brief while, but I think it's changed because at some point they got that much better. They leapfrogged over that problem. And with the right skills files set up, I find now that if I'm using an agent to do my coding, it's as good at writing Haskell as it is at writing Java.

Speaker: It's as good at architecting Gleam as it is architecting an OO app written in TypeScript. Skills files seem to provide the post-training that an AI needs to be just as productive.

Speaker: And that flips the whole situation on its head. Because suddenly, if you want to try out a new language, you can be productive in hours. And you don't need to know everything to get started because you can rely on the AI helping you out.

Speaker: If you want to try new architecture, it's never been a cheaper time to you take it for a spin. If you want to persuade your boss that we wouldn't have these problems if we'd used an actor model with Erlang, well, you know, overnight you can rewrite a chunk of the code base into exactly that shape for very little cost and very little time and no risk.

Speaker: And you can actually see if that claim holds up and come to your boss with evidence. This actually might be a fantastic time for innovation in languages, in architectures, in experiments with different ways of doing things.

Speaker: All we need to do is find the exciting ideas and take them for a test drive. And with that in mind, in today's episode, I have an enthusiastic proponent of a language and an architecture and a library that you should try.

Speaker: I'm joined this week by Cameron Barre, we're going to talk about what makes a simple system or a complex one, where the differences are, why he thinks Lisp is an excellent choice for expressing code and expressing constraints,

Speaker: why event-driven architectures are so simple to understand that even a clanker can do it right. And why what we really need today it is a language and an architecture that both provides power where we need it and constraints so that you can narrow down the ways and an AI agent will try and solve the problem.

Speaker: We need to be less hands-on and more driving large channels with our tools. And event-driven architectures, grain, the library, and closure, the language, might just be the way to do it.

Speaker: So, for a tour of a different way of thinking that you might find very cheap and easy to experiment with, join me this week. I'm your host, Chris Jenkins. This is Developer Voices.

Speaker: And today's voice is Cameron Barret.

Speaker: Cameron Barre. Cameron, how are you? Good, Chris. How are you? I'm good. I'm good. i'm um I am keen to pick your brains about, and ah without wishing to scare you, what's the right way to write software now?

Speaker: Yeah. we've got a We've got a tall order ahead of us.

Speaker: Yep, but ready to get into it for sure. Okay, so I know you have some strong opinions on this, and strong opinions generally on the right way to write applications, but like it's all in flux right now, isn't it? it's there is Firstly, there's a lot of change because we're not often writing applications directly by hand for every single line of code.

Speaker: And because the cost of making new decisions is a lot lower, you can try out a new language and new framework a lot more easily than you could. So i'm going to have to get you to back up some of your decisions generally and in the new world. Let's start with an old favorite of mine. You're a Clojure user, right? That's right.

Speaker: Give me, give me you from your perspective, what Clojure is and why it's a good choice. Clojure, it's a general purpose programming language, of course, so um anything that it ah fits for, you it's great to use it for.

Speaker: um It has the added benefit of having been really well designed to solve problems in the space that software engineers work in ah by Rich Hickey, the creator.

Speaker: And there's so many good resources that people can go and check out um information that is released over the years. But essentially, if I had to boil it down, um it comes down to the distinction between accidental complexity and essential complexity.

Speaker: And Clojure as a tool helps reduce the accidental complexity in our work down to the minimum so that we can actually focus on the essential problem complexity that we're trying to solve anytime we're building software, no matter what kind of software it is.

Speaker: Okay, you're going have to break that down a bit ah because I think you're alluding to a paper called Out of the Tar Pit here. Yeah, Out of the Tar Pit or even Fred Brooks' ah Mythical Man Month. um These are both places where ah this idea of incidental or accidental and essential complexity are kind of introduced to people.

Speaker: Define them for me because not everyone will know these terms. Sure. Yeah. I mean, that's actually a huge problem in our industry, I think, is ah our language that we use is so overloaded.

Speaker: And a lot of the times i notice engineers using the words simple and complex in a different way than I'm familiar with at this point. And a lot of the times people are using simple as if they mean naive or primitive.

Speaker: But actually, um Simple just means taken apart and complex means tangled together. Within complexity, there's two different kinds at least, um incidental and essential.

Speaker: Incidental essentially just means that ah you've made choices that have made your life harder. um They are complications that occur in your workflow or your tool chain or your paradigm that cause you to spend more time working on sort of the meta aspect of making your program function and build correctly ah versus essential complexity, which is like,

Speaker: um Let's say you're building accounting software. Well, accountants have a lot of interesting things they like to do, or in the finance world in general, there's a lot of essential problem complexity that you have to manage to really build software for a particular domain.

Speaker: And so the the accidental stuff is, kind of the code word for your fault, you shot yourself in the foot. And essential means that ah you actually have to manage this. And so you want to pick tools that let you get straight to the essential problem complexity and avoid the incidental complexity so that you can actually um not get bogged down while you're working.

Speaker: Right. Yeah. this is ah if you' If you find yourself building an accounting system and you're wrestling with tax rules, there's no way out of that. If you're wrestling with Kubernetes configurations, actually, that really is your problem.

Speaker: Yes, and maybe at some level of scale, you actually need the Kubernetes. ah like You need that to do your thing. But I think we've kind of found over the years that like most small startups and companies don't actually need that. ah Those systems came out of massive companies like Google or Yahoo, etc., and they probably need that. They have like...

Speaker: you know thousands and thousands of machines to orchestrate. Whereas if you're a startup, um you probably don't need that. Maybe you can get by if you use like a managed solution or something to reduce your actual ah incidental burden. But yeah, that's a great point.

Speaker: Okay. Well, how does that play out at a language level then? Can you give me an example of something in a language that is accidental complexity rather than essential to solving the problem at hand?

Speaker: Yeah, I would say um just the fact that if you choose to be an object-oriented programmer, you have to learn like 23 different design patterns that at the end of the day are all kind of focused around controlling code execution.

Speaker: And... um That's one example. And if you compare that to, let's say, functional programming where functions are first class, um we just pass functions around to manage all sorts of different kinds of code execution. And especially back to the OOP case, if you take all of the data in your system and wrap it in ah classes and create types for them and couple behavior and state, then you're making a bunch of small incompatible surface areas versus looking more at, say, the Clojure core library where we have you know a few core data structures that we can do everything with and we have a large ah core library that operates on all of them. So um in the OOP case, you're kind of like,

Speaker: making it harder for yourself for the given type of programming you're doing functional case, you're eliminating all of that. You can actually shed that knowledge and just get straight to, I'm going to go implement my tax rules or whatever.

Speaker: This is, I think one of the great divides between, um, object orientation and functional programming, which is, is it normal to look at data directly or must it be all packaged behind an API?

Speaker: Right. Um, And I'm not even knocking like OOP as a whole. ah i think that there's situations where it makes sense, but the the domain I work in is information systems. And often we're just moving data from point A to point B, managing that data, transforming it, et cetera.

Speaker: OOP seems to be more useful and applicable back to where it came from, which is like starting out as like modeling hardware device drivers and and things like that, that don't have like long lived sort of information management responsibilities. And so i think in a functional paradigm, um you can still do all of the encapsulation that you want to do. It's just possible to ah to look at it from a different perspective that that sheds a lot of the sort of mechanical necessities that come with object oriented programming.

Speaker: yeah Yeah, you're making me think of one place I think object orientation is great is if you want to simulate like a device driver, you want that to have opaque state that you can't see directly and could get out of sync.

Speaker: right When you're simulating other systems, maybe object simulation is a good idea, but it's not always a good idea internally, is my feeling. Right. um It just depends. like Everything just depends. um and and What's interesting about even Clojure is just that ah you You could do all that stuff, too. We have protocols and records, and it's basically you know interfaces in object-oriented programming, and you know you choose what state you want to put behind those. um So it's not like it's either-or situation either, but it's just you know there's there's cases where OOP stands strong, and there's cases where it adds a lot of incidental complexity, which over time in larger systems becomes distracting and chaotic and results in

Speaker: you know, the, the feeling of sort of, man, I wish we hadn't done that particular pattern. Uh, let's start doing a new pattern over here, but we're never going to have time to refactor that old one. And then, yeah, it's, you just end up in in a lot of ah sort of deadlock situations where you're like oh man, I wish I hadn't done it that way. yeah Yeah, I think eventually all systems suffer from that, but maybe I've seen it a bit more often in Java.

Speaker: I'm not sure I want to put my stake in the ground on that argument, but it doesn't sound unfamiliar, I'll put it that way. um so Okay, so if we're if we're making an argument for functional programming, you're still not making an argument for Clojure itself.

Speaker: because ah i'm um I'm an FP nerd. I like data manipulation as the primary way you think about the world. But that still leaves me a lot of options. right why Why would you pick Clojure over Haskell, let's say?

Speaker: Yeah, um that's a great ah point. and Functional programming is not a monolithic thing. um there's a lot of different flavors of it and and i'm not an expert on all kinds of the functional programming uh you know techniques that are out there Closure has a very specific, Closure is like an ideology for me. It's like a philosophy.

Speaker: It's like a philosophy that comes with an executable tool that lets you carry out that philosophy. And in the Closure community, we have this sort of shared set of ideals that anyone can go and learn about. You can just go and...

Speaker: ah there's a repository on GitHub called the Bridge Hickey Fan Club, and you can go and get links to all the best talks and watch them. And that's where, at least for me, my, that's where I sort of drank the Kool-Aid.

Speaker: And, When you sit and simmer with those ideas for a while, you start to realize that closure is a tool designed to carry out that philosophy and that that philosophy is not necessarily shared across all functional programming languages or communities. and So everyone does it a little bit differently.

Speaker: um And so I think even in the functional community, closure is um

Speaker: even even even a more niche subset of sort of how things can be done and how we would approach programming problems. And so for something like Haskell, um I'm not a Haskell expert, but my interpretation of it is like,

Speaker: This looks like I need to like be ah an expert at things that aren't necessarily relevant to to the programs that I'm trying to build. I have to be an expert at you know like functional type systems, and it sort of has a more sort of formal flavor to it. And in addition to that, I don't believe Haskell has a REPL kind of workflow, does it?

Speaker: No, um sort of. i wouldn't I wouldn't come out batting for it though. Yeah. so i mean even beyond the functional ah sort of the the code style and the approach, I think Haskell is going to tend to be lean more towards like, oh, I can formalize everything um versus just having like a program that works and having the tools to deal with any issues that come up.

Speaker: and ah ah And just, you know, functional programming with a REPL is incredible. And this is one of those things that it's basically, in my opinion, impossible to just explain to someone why Clojure is good.

Speaker: um they have to be like guided to have an experience that's like this aha moment. And I think if you if we guide people to ah experiencing the REPL in like a real actual workflow,

Speaker: Just that alone is is really cool over other um you know other general purpose functional programming languages. like there There's so many dimensions to Clojure that make it awesome, actually, that it's hard to actually express in any one angle.

Speaker: the One thing I remember on that specific topic, because i I'm not really a Clojure programmer anymore, I used to be. um One thing I really liked about the REPL is you can kind of live interact with your programs.

Speaker: live update them, change them, inspect them, and you're expected to be interacting with the program as you build it. And it becomes very conversational. And I remember really liking that. You felt like you were having a conversation with your program rather than just typing it out and making it happen.

Speaker: I wonder specifically, is that enhanced or made obsolete by the fact that more and more our programming loops are conversations with an agent who then writes the code?

Speaker: Because there's ah there's an overlap in style, but what's the result? The result is pretty great. ah I mean, just just on the subject of the REPL, so I started out ah my sort of programmer life as like I was going to be a C-sharp developer, and there's no workflow.

Speaker: there's no like ripple workflow um not equivalent to Clojure, like in the C-sharp.net world. And so switching from sort of having to simulate the computer in my head and like use debuggers all the time, the idea, like when I think about the REPL, I think about like someone who's like ah like a woodworker who has a shop and they go into the shop and all their tools are like on the corkboard, like hanging on the wall and they pick them up and they apply them to the problem they're working on. And it's just a live...

Speaker: reactive environment. um It's really cool, the idea of working in your program while it's running on the program. And then if you're like a if you really pay attention to your your application ah sort of set up an architecture, you can make it to where, like theoretically, you could start the program start your session on the, your REPL session on the first day of the month and never restart the REPL for 30 days later. You could be using the same like session and you've just been getting work done, evaluating code, testing it out yourself so you get firsthand experience of how it works, writing unit tests, of course, um and just like committing the source as you go. And that feedback loop, even just as a human developer, is fantastic. it makes it it It changes the whole experience of programming. I know it did for me.

Speaker: And then when it comes to AI coding, one of the most important tricks that there are for this new kind of paradigm is that if you can get the the coding agent into a feedback loop with itself, you can have it automatically debug, verify to the extent that I can verify et cetera. So being able to, so the classic setup that I use at this point is I always have in rebel going for AI to connect to using an MCP server.

Speaker: And I have playwright going as well. And so I'll have the AI do a thing and I say, okay, go make sure it at least works to the, to what, like to the extent that I asked for it to, to be working. And it can actually go and interpret and interrogate the backend of the program via in rebel restart, stop the system, and etc. And then it can use Playwright to go and actually visually um ensure that the front the app is working. So the REPL is like the second side of the coin to the full end-to-end verification feedback cycle for a coding agent. and So it's very important. And there has to be a material difference between using a language that doesn't have a REPL and one that does.

Speaker: yeah yeah Yeah, absolutely. That's a good point because um like an agent's ability to write front-end stuff absolutely transforms when you give it playwright so it can inspect the thing it's just built.

Speaker: We don't really have that analog at the back end. We don't give the agent the tools to inspect the live running program in the same way. yeah certainly And certainly not like like a debugger will let you look at it, but it usually won't let you change it and then try again.

Speaker: not in the same way. Yeah, exactly. um And it leads to other interesting implications. um Let's just pretend for a sec that the the your back-end implementation is one that doesn't throw away data. So an immutable database paradigm where everything that the system does is recorded.

Speaker: ah we ah I work with a developer kind of daily right now, rebuilding customer's sort of enterprise app. And we have some like essential finance domain complexity. And we were working on this kind of complicated feature. And it had some numbers that, you know, should be correct. And it's, you know, you can prove whether they're correct or not.

Speaker: And she did like the by hand calculation and figured out like the exact sort of like what it was off by. and i said okay wait and then i said hey uh we're you we probably be either using cloud code or codex i said let me like like let's see what happens and i said go and and so so we're building an event sourced application so he said go and i said look at the data

Speaker: and look at the implementation and consider what we have what we're trying to achieve and determine if there's anything wrong. And so it was able to basically use the in-reple to kind of triangulate all of that, go through the event source ah ledger, ah build the projection, and then it came back with like the exact number that she had calculated.

Speaker: And so the debugging capability is incredible when you have a repl and you can actually give these harnesses the ability to navigate around this like program space.

Speaker: yeah Yeah, we are going to reinvent ways to look at and manipulate a live running program because most languages don't have that capability.

Speaker: Yeah, and I think it's going to catch on because there's some research ah called RLM, Recursive Language Model, and it basically suggests that, ah and the the the driving force behind it was like, I have giant documents that I don't want to put at all in ah the agent's context window because that will cause degradation of the service quality.

Speaker: ah What can we do? And the answer is basically provide a dynamic symbolic environment for the agent to load up data and then explore it via a REPL essentially.

Speaker: um And then it can actually profile, pick and choose what parts of that content it wants to load into its context window. So there's going to be like a bit of a renaissance for the idea of rebels anyway, because that's a it's turning out to be a very strong paradigm in AI. And so it's like, OK, it was probably a good idea to use a language that just has a first class and like excellent rebel experience to begin with. And then that way you're kind of infusing that same paradigm throughout sort of all of what you're doing.

Speaker: Yeah, yeah, yeah. i can I can see the usefulness of that that I've experienced as a human programmer being amplified in the agent world just because they're so much faster if they've got the same feedback loops that we enjoy. right yeah I want to hold on to that idea of inspectability.

Speaker: ah because we'll talk about event systems in a minute. But before we leave the topic of Clojure specifically, I've got to pull up a couple of things and get your take on them. So where do you stand on the syntax of Clojure? Because Lisps... you One could make the argument that Lisps have never caught on because their syntax is weird.

Speaker: Yeah, one could make that argument, I guess, if they were if they were so inclined. ah you know I couldn't imagine anyone actually doing that, though. I'm legally bound to play devil's advocate sometimes. so Of course. um you know When you say syntax, I say what syntax? um Lisp really doesn't have a lot of syntax, which is awesome because it reduces the cognitive burden on the user after they get into the flow of things.

Speaker: You don't have to remember as many things. That's another example of eliminating incidental complexity. It's incidental and I'm not but i'm not knocking C++. plus plus I watched the recent C++ plus plus documentary. It's awesome.

Speaker: But as a language, you have to admit it has a lot of syntactic elements and it's a lot to remember and it's a lot to master. And you have to know what all of these things do. And some of them are overloaded versus a language that can really honestly say, i don't really have much syntax. um All I have is expressions and expressions.

Speaker: I think that when it comes to the syntax, that is a superpower that anyone who sits with the anyone who gets over the hurdle and stays with it long enough eventually comes to recognize that that the parentheses are a superpower because they enable the code to be data and to program in data has a lot of awesome capabilities.

Speaker: And honestly, there's editors at this point that control and help with ah the parentheses. Like you just, they bleed into the background after a certain point. And after a certain point, you start to actually recognize the the shapes of things and the parentheses no longer become an issue.

Speaker: This is kind of like in Rich's talk, um and design composition and performance. He kind of makes the case that, ah You know, not every tool is easy to pick up. Like it's difficult to learn to play piano or learn to play guitar because you have to learn an interface to something. And so Lisp's interface is it's the way that you code in it.

Speaker: And someone who's unfamiliar with that can't expect to get it right away. And so the best thing to do is just to accept, Hey, this is kind of hard right now because it's unfamiliar.

Speaker: But if I can just get past this parentheses thing, uh, which is kind of like the, the rite of passage anyway, for anyone who wants to learn Lisp, then you'll start to see like, Oh, this is actually less complicated than other programming languages. I have to remember fewer things. And I'm always looking at, um,

Speaker: I'm always looking at the same like sort of model or interface on my screen at this point. There is not a bunch of ah trivia that I need to know to use this programming language. And I think that anyone would benefit from just kind of ignoring that feeling of, oh, this looks weird, and just kind of like stick with it long enough, find an editor. So if you're interested in Clojure, you can download ah Calva, C-A-L-V-A.

Speaker: It's one of the best Clojure integrations for Visual Studio Code. okay And it helps manage the parentheses for you. So, you know, it's not really something that, you know, you have to deal with like before the days of smart editors where you had to like type all the matching parentheses in a notepad or something like that. Yeah.

Speaker: Like we have tools for it. Yeah, yeah. These kinds of things can be automated away. i realized you while you were saying that, you're giving me flashbacks to my very first job interview where I failed the job interview because I didn't know some really obscure C syntax that frankly shouldn't have existed. That's my argument. I should have got the job because that syntax was wrong to exist. Simple languages are better.

Speaker: and but Okay, so are we saying that the reason you like Clojure is a mixture of community? I'm getting that from you. um a simple data-centric language with a reduced syntax and live inspectability.

Speaker: Certainly. um It's really weird. I have not... like when I started going to the conch, the like kind of flagship Clojure conference a few years ago, despite having been Clojure programmer since like 2015.

Speaker: And I realized that I'm part of a sort of a subset of people that if you ask them, like if they had to like go on programming without Clojure, like,

Speaker: what would happen. And but our kind will say, i don't know if I want to like even do that anymore. um i don't, I like, I think I would actually get like sad and depressed if I had to work without it because, know, There's that like XKCD, like a couple memes where it's like, it kind of like mystifies the experience of using Lisp. And kind of suggests that if you go far enough with it, it changes how you think about programming forever. And for me, that's true. And so anytime I go and use a language that is not Clojure, I notice all the deficiencies.

Speaker: And the deficiencies come from the fact that most languages have not been designed with the same language. set of principles and intentions as Clojure has. And so it's really weird as a programmer.

Speaker: i like I literally love it. it's It's crazy. Like, I love the programming language. It maps to how I think now. And anytime I'm dealing with like incidental complexity from another language, I'm just like, why can't I i just wish I was using Clojure because i I know that I could just go and solve this. um And so it's a really complex set of preferences that come from so many aspects of what using the language feels like um and what it lets you do.

Speaker: And there's like no problem I feel like I can't solve with it, except for like, you know, if there's literally like a legitimate case where it's not the right language for the job because you can't easily solve the problem with it, that's different.

Speaker: But it's a general purpose programming language, so most problems can be solved with it. um So in other words, I don't know, it's complicated. Like I basically ah love the language. And if it was up to me, I would use it basically all the time and not use anything else.

Speaker: Fair enough. Yeah, it's ah I mean, there are certainly places. good One thing we haven't mentioned, Clojure being a language that runs on the JVM. yeah Right. So well, one you flavor of Clojure.

Speaker: oh how many? other Oh, yes. There's Clojure script, isn't there? Well, actually... Give me all the flavors. I'm not up to date here. We're experiencing a bit of a renaissance, which is incredible. I find it incredible. um So, apparently, and I'm not... So, apparently, in the wild, there's like 47 different dialects.

Speaker: I don't think most of those are like something you would go and pick up, but so there's closure for the JVM. That's the classic there's closure script for the browser. Um, it's hosted on sort of the JavaScript runtime. I think you can also do back end stuff with it.

Speaker: There's closure dart for the Dart platform, and it's mainly for producing Flutter apps. And you could do other stuff with it, but you can produce Flutter apps, so mobile, desktop, web.

Speaker: And i know the guys that make it, Christophe and Baptiste, and they have essentially made it to where you can build high-quality apps on the Dart platform in Flutter, and but you reduce like 90% of the boilerplate that comes with Dart. um It's super, super cool and a super cool alternative to like using ClojureScript for like an Expo app or something like that, if you want.

Speaker: then there's um There's Vabashka, which is for CLI kind of tooling. um okay There's Jank, which is still in alpha, but Jank is Clojure on the LLVM with seamless C++ plus plus and C interim.

Speaker: It's really cool. um Okay. It's super cool. um So you can build building just on the in the native runtime. um Recently, there's they're they're ah there there's a... So Borkdude, a prolific open source developer in the community, has a bunch of different ways you can use Clojure from JavaScript environments. He also makes Babashka.

Speaker: I'm trying to think what else there is. There's there's a new language called Jolt ah by Yoggthos. He's a pretty prominent member in the community um that lets you do like native binaries and he's found a way to make it possible to bring in a lot of existing Clojure libraries. ah Basically, he figured out a subset of the interop problem where like a lot of Clojure libraries will be doing host interop on the JVM, so it makes it difficult to pull into a new dialect. But Joel, you can pull in a lot of them just automatically, but you can so you can get like fast startup times and and build sort of native binaries.

Speaker: There's, I feel like I'm forgetting one. But the point is, you learn one language one time, Clojure. And then you we have all of these talented people extending our reach into other platforms.

Speaker: And I find that to be so powerful because I say, oh, now I can go and build, like I built a game engine in Jank. enclosure on OpenGL, but just like straight in like C++ plus plus inter interrupt directly into like the important native bits. Like right I built mobile apps in in Dart instead of having to depend on the JavaScript ecosystem like enclosure dart.

Speaker: Anytime my reach is extended like that, I'm just like, man, like this is such a good investment to learn this programming language because now I can just I can go anywhere on any any compelling platform. I can be there, but I'm getting the benefits of Clojure's sort of pragmatic a relationship with every host platform. We always try to get the most out of each host platform that we can Clojure.

Speaker: I would certainly love to see a version of Clojure that compiled to things like ESP32 hardware. Because if there's one thing I want when I'm programming with hardware, it's the bit the ability to live inspect the system while it's running. Oh, yeah. right um i didn't know it but I didn't know its reach had extended quite that far.

Speaker: That's pretty cool. pretty Pretty far. And getting further by the day, oh, there's like some new initiatives. ah Oh, i'm I'm not as familiar with these, but I think it's Gloger. I think but the name of it is Gloger. There's someone doing a Go version as well, like a Go dialect. Oh, yeah.

Speaker: And so, there' I mean, there's an interesting thing that's happening in the community right now. there's a lot of dialect springing up because ah ai coding has made it possible to to ah Basically, if you look at Clojure as a spec, and and people have been working on Clojure test suite, which basically creates a common test suite between dialects. If you can implement that test suite, then you have yourself a dialect. um So people are really experimenting right now with with ah with all sorts of different platforms and dialects. So experiencing a kind of a renaissance in our ability to

Speaker: extend our reach. And it really is, even if I only had Clojure JVM, Babashka, Clojure Dart, and Clojure Script, that's already like, close to everything you could need.

Speaker: That's yeah, that's a vast amount of industrial programming, right? Yeah, and it's pragmatic, like I can, I can use it to do professional things to make money with. um And I don't have to carry around like a bunch of different programming languages with me Yeah, I suppose as as long as you can, on the host, you can handle creating immutable data types.

Speaker: The surface area of the language isn't that hard to recreate, right? That's right. Contrasting with Ruby, where I know the creator of Ruby said, never, ever try to create your own Ruby parser.

Speaker: ah Yeah, interesting. Yeah. Okay. so So let's park closure for a minute or assume it is the bedrock of the rest of this.

Speaker: But if you- Wait, wait, I have to point out, I have to point out, because everyone is always, everyone is, you know, like there's so many arguments. ah There's so many people trying to convince people to use like their programming language or whatever.

Speaker: um And I just wanted to state that at the end of the day, ah you know we can build the same we can build the same thing in any general purpose programming language. And so at the end of the day, it comes down to preference, biases, like I'm pretty sure anyone watching this is going to know my preference and biases for closure, um and supporting evidence.

Speaker: So I'm never going to be able to objectively convince you that my programming language is better than yours. And I think a lot of programming language discussions devolve into that. What we really need to do is accept that it's okay to have preferences. Not everyone has to choose the same thing.

Speaker: But when someone brings supporting evidence, we look at it and we examine it. And so the supporting evidence that I would like to drop here for Clojure is there is a ah multi-billion dollar bank, the largest bank, the largest digital bank outside of China, that is built entirely in Clojure.

Speaker: In fact, they bought Cognitec, the makers of Datomic, the database built with Clojure, And they own that now. And their whole bank is an existence proof for the utility of closure and industry, I think. Which bank is that? wish more people knew that.

Speaker: Which bank is that? New Bank? Okay. New Bank. I didn't know they were closure. that they they They bought Cognitech. So Cognitech is the steward. The core team of Clojure works for Cognitech, and they also build Datomic.

Speaker: NewBank is a heavy Datomic user, and so like their their bank think was like, huh, well, we like use this database for our entire thing. ah Maybe we should just like own that.

Speaker: um And so, yeah, there's a giant bank that is built out of Clojure. And I think that's relevant when people, because a lot of the times online you'll be like, oh, yeah, like that looks cool. But like no one no one really uses that in production.

Speaker: But it's like there's an there's a huge existence proof. And not only them, like people have been using Clojure forever and to make a lot of money out in the commercial world. But there's a bank built out of it. I think that's ah pretty substantial.

Speaker: Yeah, that's hard to argue with. Personally, I'm not looking for a definitive answer, but I want to see who's enthusiastic about what they've built and what what's being built that's working out there. Because I'm not expecting a final answer. I'm expecting a whole bunch of good answers from the world. right and I have to ask, and this is slight aside, but how how long ago did... um new bank by like the the governing board, if you will, of Clojure? And has it changed the evolution of the language till?

Speaker: Oh, it's um been years at this point. um I'm not sure the exact number, but it's been more than two or three years. It could even be longer than that. Has it changed the evolution of the language? No, because the the so the core team works at Nubank for the most part, but the language is still led by Rich right now.

Speaker: And so the actual process of the language has not changed. I'm sure Datomic has gotten... sort of more ah upgrades and support and there's even more attention on it than there was before, like as a database product, because that's in NewBank's purview at this point. like Datomic is not an open source database, it's ah it's a database product. um But the language has, ah nothing has substantially changed about how that process seems to work. It still evolves at the same pace.

Speaker: which is is quite slow. um I think last Conj, there was like ah some people from Netflix. They gave a presentation about 10 years using Clojure at Netflix for a particular service.

Speaker: And the the main thing they had to say was like, I don't think that Clojure upgrading like to the next version of Clojure has ever once broken our code at Netflix. looks like That's pretty sweet. i mean we The language has ah an extraordinary commitment to backwards compatibility, and I think that comes out of the Lisp ethos of sort of like,

Speaker: strong but small core um that evolves slowly over time. And then you let the community do the rest because the language is extendable through macros and libraries and packages and things.

Speaker: yeah Yeah, it's a uniquely extendable language in that way. Okay, i i want to I want to move on to actually using it because I want you to give me a flavor of how you approach a problem. Maybe we can get a sense of how that's different to many other languages and approaches.

Speaker: um And let's make it concrete. What are you working on now? How are you building it? What is your closury way of building things? Yeah, so um what I'm working on now, or what our company is working on now, is we we build information systems. So we build sort of mission-critical systems of record ah for our customers and ourselves. um So any kind of business application you can think of, ah that's what we do. And we...

Speaker: So our technological foundation is primarily driven by my experience. um And my experience since around 2021 2022 was how do i figure out how to build how to build information systems at companies when i am my head's on the chopping block and I have to mitigate the execution risk of the process. Because software is, as sometimes I think the industry likes to gaslight itself on how it's doing, but software is one of the most expensive and and risky endeavors I think that you can take on.

Speaker: And so there's a lot of execution risk that comes with that at any company that has any initiative. And so I've been trying to figure out this problem for myself for a while because I just don't like failure.

Speaker: And um so I learned about this ah product design methodology called event modeling from a guy named Adam Dimitrick.

Speaker: And I also learned about it a bit from a Clojure community member, Bobby Calderwood. And it's basically this limited grammar that lets non-technical and technical people come together and describe the information flow over time through an information system on like a digital whiteboard or a real whiteboard.

Speaker: And it kind of simplifies and atomizes the work to be done. So it puts it into these small units. And then from that time onward, I've been figuring out how to make like an actual ah how to map that one-to-one building with Clojure so that when I look at a system I built with Clojure, it looks like an event model.

Speaker: And for anyone curious, by the way, it's eventmodeling.org. You can go look into It's really cool. And... With this latest iteration, I've basically like I finally just figured out what I like. And it's a domain specific language for building event sourced information systems that if you were to visualize them look like an event model that you would see in like a digital whiteboard if you use the event modeling process. and so Right. yeah So you're that you're doing this thing you're doing presumably you're doing this thing where you take Clojure LISP's macro system to build up the language to reflect event modeling terms and stuff.

Speaker: Yeah, exactly. the... yeah the like people So people think that like, oh no, like AI coding, like ah it produces so much drifting software architecture. But if you think about it, like teams of people have been producing drifting software architecture on a slower time horizon for decades at this point.

Speaker: yeah um Everyone who's ever experienced the moment where someone on the team or a management or whatever made the call is like, oh, we got to rewrite this because it's too complicated to change and no one understands it anymore. That's the that's the end game of drifting software architecture.

Speaker: and um

Speaker: One reason that happens is the lack of constraints. And even Clojure as a general purpose programming language, it doesn't give you any constraints. You like you can do whatever you want with that. And that's very cool.

Speaker: But sometimes what you want is constraints. And so the idea that you can take a general purpose programming language like Clojure, slap on a kind of macro layer that creates a domain-specific language, and then suddenly you've got this very constrained problem-specific programming language that has all of the primitives and benefits of the underlying well-designed programming language infused throughout. So it's a way of like focusing attention in a general purpose environment.

Speaker: And that leads to ah ah ah kind of some kind of flavor of uniformity. um which I think that's the first time I've ever heard LISP's macrosystem as an argument for reducing the power of the language.

Speaker: Well, it focuses the power of the language. Yeah, yeah, yeah. That's a better way to it. In a specific direction. Yeah. Okay, so what is that specific direction? but i I'm going to pretend I know nothing about event modeling here and make you explain it to me properly.

Speaker: so what do we do? Yes. So the idea of event modeling um comes from, it has its origins in domain-driven design. But like I said, Adam Dimitrick basically realized like domain-driven design is a cool idea and all. and It has a lot of useful ideas.

Speaker: But you have to read like a thousand page book essentially to to understand it and know how to use it. And even if you read that book, you probably have to read again. And so the idea that anyone on your team is going to actually do that um is laughable basically. And it's like only DDD enthusiasts are going to do that.

Speaker: So the idea that you have non-technical people that you need to work with and technical people even – people are rarely going to be on the same page about all the concepts that come with it. And so Adam said, how can I distill this down into like the simplest form?

Speaker: And the simplest form is a set of primitives that can express this, the, the information state changes and flow in any information system.

Speaker: Um, and those primitives are commands, uh, views or read models, of events, and ah automation. So we call them to do processors because it evokes like a for non technical people, it makes them think like, Oh, I have background automation that has a to do list that it goes through. So we call it a to do processor. So commands, views, to do processors, um screens, ah trying to think if there's

Speaker: I'm blanking or not. I think that's that's that basically covers it. um And using those, you can map out any kind of information system. So what you're building up is domain data types and ways of manipulating them and then looking at them?

Speaker: Yes. Right. Take me through these. You said views and screens. I didn't understand what the difference between a view and a screen is. Yeah, let's go with command query screens to do processors.

Speaker: ah So a screen is obviously a screen or a surface area. um it It could be a screen. It could be ah the end result of like an API request or something like that. It's essentially like the surface area that you provide to whatever client is using the system that needs to interact with it. um And so a screen and an app is like you go in and you see that maybe you have like a spreadsheet or something. That's your screen. It's bringing together domain data for you. You can manipulate it That feeds back into the backend and and requests. Usually in a screen, like you might go into like a user profile screen and the user wants to update their email address.

Speaker: Well, whenever they go to update that email address, the screens typically are going to trigger a command. And a command is just packaged intent to change the state of the system. And ah if the state of the system is in a place where it can change, in other words, the business logic or invariance all check out, that command is going to produce one or more events.

Speaker: And events are the recorded history of everything that's ever happened in the system. A view, this is like an overloaded terminology. So you you could call it a view, a read model, a projection.

Speaker: Views or read models or projections are just a perspective over the event history, so everything that's happened in the system at a particular point time.

Speaker: And then those end up kind of emerging as domain entities with kind of business logic baked in that then power another screen or another experience that then feeds back into commands and and the loop kind of repeats. And you might have to-do processors which see things changing in the event history and react to that as well.

Speaker: Okay. That's kind of the you have you have a central state of the world in there.

Speaker: One or more? One or more um what? so I'm trying to piece this together from first principles. i Let's take it from the user perspective because every non-technical user will understand that a screen is a place where they can see stuff and do stuff.

Speaker: Yes. That's what you're saying. Then the stuff they see is a view on the data in the system. That's right. Just like any other system. um right you know in in In more conventional systems,

Speaker: We're literally just talking about you know SQL queries that provide the data for the stuff the user sees on screen. Right. The stuff they can do is commands. Right. So I can build this back in my head from the screen to some commands and some views.

Speaker: What's going on in the data core? Because, i again, I'm trying to do this from first principles. I get the impression this is not just a state stored in a Postgres database that's live updated. Right.

Speaker: that's That's right. um ah Well, so, Postgres database ah is actually, it doesn't matter too much. like the The system that we're actually talking about uses Postgres as a storage engine, but it's how you use Postgres that matters. So most conventional software development still ah orients around mutable point-in-time state in a database. So unless you've designed time into the database schema, which is also kind of tricky to do, and most people don't, then you're always working with... um

Speaker: This is just a normal relational database schema. Maybe you have 100 different tables that all relate and you're always working with the latest current state of the world. And when you change that, you lose information if you're not storing it somewhere versus what we're talking about, um which is not you can do event modeling without event sourcing. But I do event modeling with event sourcing, which is in a paradigm where every single change to the state of the system is recorded as an event. So if the user changed their email address, we emit a user email address changed event. If the user um makes a payment, ah we record that a payment was made. and But you can apply that to every single action that a user can achieve through a command.

Speaker: And so there's no ah there's no like relational schema or point in time, like place-oriented container for that state at a foundational level in an event source system. You just have the log of events as it has happened.

Speaker: and then views or read models can project the actual a semantic domain from that. And it's the view and read model layer that ends up, you could you could literally project your views to a relational database schema if you wanted, because that's where your domain entities emerge in this kind of system. um But you could never take your relational ah database schema and get the events generated.

Speaker: that caused it to be in that state back. So it's like ah we're going one level lower in terms of foundation, in terms of the simplest form of data is what has happened in a system. And then we can project up to a higher level from there until we get to the point where we are composing things like users and bank accounts and things like that, that someone can actually see on a screen and then issue commands from.

Speaker: you're almost seeing, record all the ways in which the system has been asked to change. yeah And then you're almost seeing producing state as lossy compression over that audit log.

Speaker: Exactly right, except we can actually go back if we realize we made a mistake or missed something, as if we captured the data in the first place, then we can change our mind a lot easier about what the correct projection of that state is versus one not capturing ah versus one always like you in other words you have to do it you you have to have a culture of like database migrations to change like this entire like concrete structure that you have um versus just like bumping the version up on a read model projection and having a new shape um and what i've noticed about this paradigm is like

Speaker: you know What do most people do when they go to like start designing a system? They start thinking about the entities and the objects and their structures as foundational elements. But what I've realized is like things change over time in a system and entities come and go.

Speaker: And if you approach design from a place of what can happen in this universe, and then let entities be sort of an emergent byproduct, you still end up with entities, but you end up with entities that are not foundational to the system. And so you can actually change your mind a lot easier ah because you don't have just one concrete representation of the current state of the system. You can change it.

Speaker: Right, yeah, because I remember being taught in the object-oriented gulags that you start with asking questions for a system about what you can do such that you can bake it into mutable objects that change the world.

Speaker: But in in doing that, you you drop the idea of what can you do being first class. Right. and And I think you're saying if we hold on to this idea of what was the intent, then we can reinterpret what the objects might have been along the way.

Speaker: What happened? What's the universe of facts that can occur in the system? And as we, I think it's probably pretty apparent in our modern day and age, ah ah multiple observers of the same set of facts can come to different conclusions. And yeah that's kind of the core insight at the bottom of this is like whenever you just have one sort of mutable point in time,

Speaker: ah shape of your data, which is what most relational databases end up being for a team, you are coupling together sort of the read and write model into this one concrete shape, and that leads to inflexibility. um It leads to ah There's issues with with being tolerant to to change over time, and it strikes as sort of what what is foundational to the system. Is it that these things can happen, or is it that I have – today I have these objects, and they have these properties, and they have these methods and stuff like that?

Speaker: Right, yeah. Okay, I want to go a bit more concrete on how you do this because um we're being quite abstract. I'm going to try and pin it down. One thing that um doesn't quite sit naturally with me is if you're using Postgres as the store here, I can't naturally see how you translate an event log and rolling over it to achieve derived state, how that fits in a Postgres implementation.

Speaker: Yeah, so um the the tool that we're talking about is an open source library that I've made called Grain, and it's basically this DSL for event-sourced information systems on Clojure.

Speaker: And it has, at its core, kind of the the sun to its solar system is an event store protocol. And that protocol is the only way to access data the state of the system. It has like two interesting functions attached to it, which is read and append. So you can either read stuff from it or you can append new events to it.

Speaker: That protocol supports like Any, ah remember, for anyone ah not familiar with Clojure, a protocol is basically like an interface in like another object-oriented language or whatever. Right.

Speaker: So that protocol, any any but storage backend or database product or whatever can implement that protocol. It just has to be able to satisfy the constraints of the event store specification that Grain has.

Speaker: And so in Postgres, what that looks like is a single table called events. okay And it has all of the right metadata to do all of the right indexing. um so And I'm sure we'll get to this, but the the the idea that you have to reduce overall events all the time to get the current state of the world is like the the parentheses are weird when it comes to Lisp. so like there's it's just a So it's just a problem that has to be solved in event sourcing, and everyone who does it solves it But basically, there's in ah a table of events, and um it has the right metadata and indexing on it to make reading from it efficient in production applications.

Speaker: Do you also do something like, I mean, a classic problem with event sourcing systems is it's great when you start, you fast forward a couple of years, you've got a billion events in the database, something crashes and you have to reconstruct state.

Speaker: and you don't really want to go back and reprocess a billion events to get there. Do you handle that case? Yeah, that's one of those sort of case by case, like there's different ways to work around that problem. The idea that you have to keep all events forever is an oversimplification of what event sourcing is capable of.

Speaker: You keep all events until you decide that you don't need them anymore, essentially. And so when you start to have like really events, read models that need reconstruction and whatnot. um there is There are practices where you can basically snapshot the state of that read model at certain times. um In accounting terms, they they close the book so an accountant doesn't reprocess the balance for all of time just to get to the current state of the the account balance. They'll close the books periodically and start from a new snapshot. And so you can start from a new snapshot, and that's your basis for going forward. It's kind of a compression technique. And you can choose to archive those older events in cold storage, or if you really don't need them, you could get rid of them. um But there's ways to work around the idea that you have this massive read model

Speaker: and you need to rebuild it from scratch and it's going to take five days because it's a billion events or whatever. You would you would need to develop a snapshotting strategy ah at that point.

Speaker: And um yeah, Green can help with that. And there's other techniques that can that can help with that as well. yeah There's not a universal solution built into the framework. um The yeah universal solution um that goes into the framework You can store snapshots as events um in the event store. That's totally an option. um Drain does have a read model processor, which handles incremental snapshotting and caching. And I do have plans to make it... ah

Speaker: sort of handle this case a bit better um in terms of storing maybe some of these snapshots like in object storage or something like that and allowing remodel processors to to pull that in and and get like a warm starting point. But even that's like a slightly different thing than what we're talking about, which is like,

Speaker: the management of like, you know, like billions of different events. um In general, event sourcing doesn't really have a one size fits all flavor. And so um it's sort of like on a case by case basis, you're like, oh, I can see that this is this is happening, I need to do something about it, maybe I'll choose like a snapshotting strategy, in which case, you can do that with green, green has watermarking, and the capability to store like any kind of event that you want. so you could do that.

Speaker: um And maybe we'll build that into the framework at some point. and Okay. Okay. So let me just reconstruct my mental model. You've got screens the user looks at.

Speaker: They are driven... but The user can do things which records data into the system as events. We roll over those in some fashion to generate views which power those screens and other screens.

Speaker: um How do you get external systems? Like if I were running a stockbroker, I want to connect to my bank periodically and get bank transfer data in.

Speaker: How does that get modeled? Yeah, so in event modeling, um there's a there's patterns for pulling data in from external systems, and there's patterns ah for receiving like sort of updates like webhooks and things like that.

Speaker: But at the end of the day, if you don't store something as an event, then it basically didn't happen in your system. And so anytime you're accessing external data, you're probably going to want to have... um an adapter of sorts that that is able to take in that data from the stockbroker or bank or whatever, and it's going to create events that actually have the data and record the accessing of that information so that anything downstream, any consumer downstream of that ah can build it into their read models.

Speaker: And now you can make a choice. You could say, like, do I want an impure read model and have a read model that actually queries an external system? And it's it's like literally designed to not store that information.

Speaker: But if you want to store it, then the right thing to do is just to say, I'm contacting this external service and I'm going to create events that properly inject that information into my domain. I'm going to have maybe like a ah domain representation of that. And then I can use that in my reducers to build the current state that includes that information.

Speaker: Right. So you're building up some kind of process that doesn't have a screen but sends commands effectively. Yes. um So in green, we have ah periodic tasks, which are kind of like cron jobs.

Speaker: um And we also have to do processors, which are the reactive processors. So if you have like, if you periodically need to sync bank transaction data, you'll have a periodic task. And all the periodic task does is emit an event. Essentially, it says,

Speaker: hey, time to do this work, and then a periodic to-do processor will be subscribed to that event essentially and say, ah, it's time to do this work. Let me go contact the external system, and then I'll put that into events, or I'll call a command, and then it's in the event store. Now your anything downstream kind of has a pure ah interpretation of that data.

Speaker: Right. So some of your events are domain data, and some of your events are signals that things should happen. Yes. Are there any other kinds of events or does that pretty much cover it?

Speaker: I mean, that pretty much covers it. ah There's, um yeah, some are some are more like system metadata, especially in grain, and some are at your actual information system domain. Okay. And do you have to worry about reactivity actively in that system? I mean, have you got have you got your Postgres implementation, for instance?

Speaker: Are the views reacting to new events as they come in live? So that um that really depends. ah So Grain has a couple of different pubs of mechanisms.

Speaker: There's one that is kind of in process. And when events get written to the event store, it kind of publishes like this in process message for any consumers that are sort of running in the same JVM.

Speaker: And then in a more distributed scenario, there is an event tailor component that tails changes in the event store. And so that will be running on any node in that setup, and it will alert the process to things that are happening.

Speaker: And so depending on the kind of like application that you want to build, if we're talking about front-end, then Grain does have a Datastar adapter. And Datastar is ah sort of the successor to HTMX in that you stream...

Speaker: live HTML fragments over an SSE connection from the backend to the client. And so Grain fully supports a reactive loop where the user initiates a command, the command is successful, the relevant data to the query the screen is subscribed to changes, and the data star adapter pushes the rendered fragments to the client. So you have a fully reactive setup.

Speaker: And then on the back end, you do have the to-do processors, which are made, they they're actually polling-based. So some interval, they poll the event store for ah the events that they care about, and that's how they react to things that are kind of happening behind the scenes.

Speaker: Okay. So it's a it's a genuinely mixture of reactivity and polling. Yes. It depends on what you're what you're actually doing. Yeah. But to do processors started out as purely reactive. But, um you know, when I looked into sort of like the prior art and how a lot of other products that are kind of like green act,

Speaker: um When you do the polling situation for the back-end reactive components, it ah it provides a little more stability to the system because the event log acts kind of as its own back-pressure mechanism. It doesn't matter how fast anyone's producing to that, you have a time cycle that you can actually just choose how much of that work you want to do at any time versus like having it all pushed to you and having to figure out sort of um your own back-pressure solution in that case.

Speaker: Right. Yeah. That's like a little bit more of like the in the guts, but yeah. So I want to know how this works out in practice. and I know you've been rewriting an old Rails app into this style. So give me some details on how that's going, because those are two very different styles of application.

Speaker: What was the first style of application? Oh, the Rails app. Like a Rails app, yeah. And a very opinionated one, and you're moving it to a very different set of opinions. Yeah, well, I mean, opinionated is debatable, I guess. like It's true that Rails as a framework is opinionated, um but when I talk to the the developer that I'm working with to rebuild this thing, and keep and and now we're kind of moving into like the sort of we're building this today. So we're using AI and the developer uses AI to maintain the existing Rails app.

Speaker: And so we have comparisons about how coding agents work in something like the production Rails app versus the grain application that we're rebuilding. And, um you know, she essentially says like,

Speaker: you ask AI to do something in this Rails app, and depending on the day, it's gonna give you a different way of doing it. Because if you think about sort of the total amount of sort of approaches and code that's out there on the internet and in the AI's training set about using Rails, it may be an opinionated framework, but there's like a lot of different ways to do things correctly in a framework like Rails. And so you don't get like a consistent uniform approach. And she's kind of highlighted that as ah as a problem when it comes to debugging, adding features, et cetera.

Speaker: And ultimately what you have is is you have a mutable point in time solution that is lossy. You don't record everything that's ever happened. And in an app that has a lot of like interesting like finance complexity and workarounds and things like that that people do and edge cases that the the users run into,

Speaker: it's really difficult to actually automate the discovery of root causes because you have to reconstruct the state at the the state that led up to the final end state that is a problem. And if you threw all that state away, then that makes it hard to debug.

Speaker: And then in addition to that, um semantically, this the the whole sort of like, ah you know, MVC like controller, ah REST API type of dynamic in addition to like, uh, rescue and like trying to, this app needs to sync to Salesforce and like an LMS system and having to specify like, I'm going to do this now, or I'm going to do it later. And later just means things get shot off to a job queue. And like,

Speaker: there's so many ah There's a lot of incidental complexity in that system. And moving it over to grain essentially looks like... um So we use a we use PolyLith, which is a sort of a backend approach, an approach to structuring backend systems in Clojure. And I think and think it actually goes beyond Clojure, but it's mainly for Clojure.

Speaker: And what it does is it gives you a It forces you to take apart all of the components that make up your final deployable artifact, and it creates more of like a workshop vibe to where with a REPL, you have access to all of these small components with very well-specified small interfaces that work together. and then you wire them together into a deployable artifact, and you could have different targets that you want to deploy to, and you could reuse the same components. And so...

Speaker: We're using like the simplified polyolith architecture kind of paradigm. Now within that, we have foundation components and we have service components. Foundation components are things like an API client or ah the ability to write to S3 or something like that, to read and write. So it's it's things that don't manage information system state.

Speaker: um And then we have service components. And in Grain, service component is any component that has inside of it commands, queries, read models, ah to-do processors, periodic tasks, um and schemas.

Speaker: And if you were to visualize a Grain service component, it would look like for anyone that goes to look it up later, it would look like an event model. So a green service component is kind of like ah you could think of it as a bounded context and like the domain driven design kind of thinking, but it's really just an application service area that brings together like concepts that tend to be like highly related to each other.

Speaker: and form a boundary for the rest of the system. And so like in this application, we have, we have a, this is a ah ah sales engine for um training dental technicians. And so in this application, we have like an academic service and that handles all of the metadata about courses and terms and classes and some compliance issues and things like that.

Speaker: We have a financial service, which is all about actually encapsulating that essential domain complexity that makes their finances tough, but also it does things like the payment substrate and actually sending payments to the gateway and reconciling and all those things.

Speaker: We have an enrollment service that is all about the student's journey through their application process to get enrolled in the classes. We have, um we actually replaced, but this is just a funny AI thing. We actually are replaced the integration that they were using for document signing. We just built one. and So we got a document signing service. And so we have this kind of ecosystem of grain services that all work together.

Speaker: And anytime a service needs to ah talk to, like get the canonical functionality from another service, they just, it's a command. So you just, they call a command from one service from the other.

Speaker: um And what happens? It responds, no, you're sending a command. How does it get a response? We're in process. Yeah, keep in mind we're in we're in process. we're I'm not talking about microservices. I'm actually talking about kind of like a tidy monolith kind of setup.

Speaker: And so if the enrollment service needs to process the checkout, like the the students ready to pay for their, they've gotten through the application process and they need to process the checkout. um There's two things that need to happen.

Speaker: We're going to need to store domain events about that enrollment, but we're also going to need to store domain events about the the financial happenings. So like the fact that we made the payment to the gateway, the fact that that was successful, the fact that um We have Ledger. There's a Ledger. So the the Ledger line items, the debits and credits that go on a Ledger. yeah And so with Green, we i tried or we try to make it to where you can do atomic batches of these transactions. And so commands compose together in the following way.

Speaker: The enrollment service is going to be the entry point for the user's command to say, I'm ready to check out and get this done. the Internally, that command can call another command and tell the command processor in Grain to skip storing the events and just return them.

Speaker: And so you can get all of the sort of side effects of doing everything that you need to do. You get back a batch of events. You bundle it with the events that you're already going to store. And then you can store those in one atomic batch.

Speaker: And that's kind of how the cross-service command boundary works. Most of the time, like the default is that you do a command, you emit events, and the command processor stores it immediately. um But to do composition, you can compose these by saying, hey, skip that for now. I'm going to do that in the sort of the parent, the caller.

Speaker: ah do you Do you store the fact that those events were stored together? i mean are you just a depend If there are five of the events, are you appending five new events and the connection is lost, or are you appending an event which is a super event that joins five together?

Speaker: So Grain kind of takes some inspiration from Datomic here. um Datomic has what's called reified transactions, which basically just means like if you commit data into a Datomic database, there's an actual like transaction entity that gets created that you can go back and cross-reference and you can even um put metadata on it and things like that. So Grain has that. So anytime you append a batch of events, they do get appended as a batch in one transaction.

Speaker: And um there's a grain transaction event that occurs alongside of them that has the connection to the event IDs that were stored. And you can also put metadata on it and things like that. And so a grain has what I would call it reified transactions. So you you you kind of have that connection.

Speaker: Okay. Yeah, that makes sense. So...

Speaker: I'm trying to think of a way to phrase this question that means you won't just say, yes, it's better. How how in practice is the um is the grain version of this application working out compared to the Rails one? I mean, you must be getting pros and cons. What are the pros and cons?

Speaker: Okay, the pros are, so grain as a DSL is very self-descriptive. um It's declarative. it so It's a set of declarative tools that says more like a recipe of how things should be done rather than specifying out every detail.

Speaker: A grain application then also inherits that quality of grain. and as um And as an application gets bigger and more mature, it becomes even more self-descriptive and more declarative.

Speaker: That has the effect of really steering coding agents. It helps them really understand or like simulate understanding of what the application does and how it works.

Speaker: And it helps them make changes because there's a very clearly defined uniform model for making those changes. You can only ever be adding new commands, queries, read models, periodic ah processors, to-do processors, or schemas, et cetera. That's all there is to it. You open up any...

Speaker: grain service component and you see these basically these registrations of those primitives. And so there's only one uniform architecture model. And so whether it's a human being looking at it, which is a necessity for me, by the way,

Speaker: i built grain because I refuse to be accountable for code that I don't understand in production. And so there's an immediate problem with AI. It's like, well, how can I generate a prolific amount of source code and also understand how it works when I look when when i look under the hood? Because AI is going to introduce variance all the time in terms of the solutions and approaches it takes The difference between the Rails app and the Grain app is that in Rails, you're subject to that variance, but in Grain app, you're not subject to that variance because anything novel outside of the Grain primitives is simply wrong, and it needs to be redone and put into the right form factor.

Speaker: And so in terms of ai There was a paper um called ah Code as Agent Harness. um There's like all the like big the big wigs in the industry got together and they wrote this paper and they kind of decomposed like the phenomenon of these harnesses into a few different categories.

Speaker: One is model internal capabilities, which is like you know how good is ah GPT 5.5 or how good is Opus 4.8? What are its own like internal capabilities?

Speaker: yeah The next layer is system supplied infrastructure. So that's like tools and things like that. That's like Codex's ability to read and write to the file system and stuff like that. Okay. And then the third layer is agent initiated code artifacts.

Speaker: That's like, what do what do agents um produce in terms of code that can either be short lived or long lived? And where grain sits on the spectrum is in two places. if you look at system-supplied infrastructure as like a compositional space where you have like harnesses like Cloud Code, Codex, whatever the other ones are, OpenCode, whatever, on one side, and then you have tool systems like you maybe have your like markdown skills and your instructional harness and things like that. Well, if you look at Grain as a system-supplied infrastructure in terms of putting the DSL in the coding agent's hands,

Speaker: And then you look at the Grain app as an agent-initiated code artifact, then what you have is humans or agents producing a high-quality, self-descriptive, declarative, long-lived code artifact that describes the information system that is being encoded. And by giving them the tools that they have to use, they have to produce it in that way.

Speaker: And because they have to produce it in that way, whether you're talking about humans or AI, you're going to get a uniform architecture that anyone who's familiar with it can come and see how it works and basically never be surprised by what they're looking at, at least to a certain extent.

Speaker: And you can't really say that for the Rails app because there's not sure it's opinionated, but it's also like quite general and open versus like a more constrained and enclosed system that has the power to express what needs to be expressed. But when you go and look at anything built in grain, it's the same model all the way down. So you're never surprised by what you're looking at. And that helps take accountability and ownership.

Speaker: And so just the build quality And then the debugging capacity capacity um and the ability to arrive at how a user got to a certain state or how the system got to a certain state without having to guess or simulate what happened.

Speaker: So what you're saying is The secret, one secret, your approach to building reliable apps, both human wise and in the age of AI, is reduce the number of architectural primitives to a small, fixed, clearly defined number and have the system be inspectable so you can understand how those pieces are interacting.

Speaker: Yeah. And the reason that's not the norm, like AI aside, The reason that's not the norm is because software architecture is inherently like a people problem.

Speaker: And I don't know if that's controversial or not, but it's not an objective thing. It's a people problem. Because if you think about like all the companies you've ever worked at, you might have known all the tools on the job description. But when you got to the see the code base for the first time, you're probably surprised by ways that they were doing things with those tools that you hadn't seen before.

Speaker: and That has to do with who got there first, who came after them, is there a management hierarchy, how are decisions made, and all of these people problems combined together to make The definition of what good architecture looks like, looks like change over time.

Speaker: And that change over time creates the variance that makes a code base that's been around for a while have competing architecture patterns like legacy, and this is how we do it now. That variance eventually does lead to, not in all cases, but a lot of cases, I don't understand this anymore, and we have to rebuild it.

Speaker: And so taking the approach of let's just, let's lock this down. Let's lock our expertise down into the set of constraints where we control the expertise that the constraints allow us to utilize, but it's at a lower level, a platform level to where what we're building with on top just automatically applies our expertise. And that's the purpose of any DSL really. It's like,

Speaker: to constrain um highly capable general purpose problem solvers like people or AI into a set of rails that say, we have this expertise underneath, here's how you leverage it. And in that way, anytime you're using the tools, you're doing it right.

Speaker: yeah so yeah that Yeah, that is one trick that I don't think a lot of people have caught on to yet, um but I think will become popular soon. I don't i don't know when, but people are still kind of in ah in a space of thinking that you can constrain agentic decision making with specs alone, like Markdown and spec languages even. And I think that just because of the statistical nature of these things,

Speaker: you can't like You have to have a a concrete rock bottom floor that something can't drift past. And that's a code level thing. And the DSL is you know maybe one solution to that. Yeah. And if you're going to take that solution, a language where you can define new primitives and reconstrain the language, refocus it, is a natural fit.

Speaker: At least for exploring that world. Yeah, and something that we've been doing for a long time as software engineers. DSLs have existed for a long time. It's not a new idea. i think all the ideas needed to tame AI are already exist. like We don't need anything new to actually constrain these things and get all the benefits out of them and and minimize like the risk associated with them.

Speaker: Do you think this is i want I've wondered if this is yet another great time to rediscover old techniques like event sourcing is a fairly old technique. Lisp is a very old technique.

Speaker: Are we going to go into a new phase of experimentation because it's suddenly a lot cheaper and rediscover a bunch of old techniques have been waiting for us to find them? Look, I hope so. i really hope so, because unfortunately, like the industry at large is I don't think it it don't think it thinks like this, but I think that people within the industry, some of them will start to think like this. And what I mean is ideas have never been more valuable now.

Speaker: Unfortunately, that means you can shoot yourself in the foot faster than ever, or you can achieve novel combinations of existing ideas faster than ever. And there's a lot of old, good ideas out there.

Speaker: Closure, in fact, is just a composition of the best ideas from Rich's perspective on software engineering built into an executable tool that you can hand someone. And so this idea of curating ideas because the labor costs now involved in executing on those ideas has been reduced so substantially, yeah we can now look back and say, oh, that technique that wasn't super useful in the 80s is actually like,

Speaker: pretty sweet and I can bundle that into something I'm building because I can achieve the actual results I need faster than ever. um i think that there, I hope that there will be a renaissance in that way of like, let's go back and look at all this stuff that we didn't have time to act on before or were unaware of before and build novel combinations of things that can help us tame AI or just even get cool results that ah just weren't possible before because no one would ever put the the time into exploring all of those end to end, right?

Speaker: Yeah, yeah, yeah. It makes me wonder, have you ever... um Because you're in interesting place where you've got these two semi-parallel implementations, the Rails one and the Clojure one.

Speaker: Have you ever kind of taken the clawed transcripts of working with both on a similar feature and gone through one of those agent evaluation frameworks?

Speaker: that puts a concrete measure on how effectively did this session achieve its goals?

Speaker: um Not really, because I've just, ah I mean, I think that's a cool idea, but I i don't work in that Rails app. um So I leverage my little, my mentee. She's the developer at our at our client's business, um and I'm i'm teaching her software engineering principles and grain But she's the domain expert, so I rely on her for everything that I'm kind of acting as the muscle right now in terms of I can steer the AI to produce code that will actually stand by and support in production. And she knows what we need to do. And so we don't even really talk about the Rails app anymore. We just kind of like, we just go like this project would have probably taken like a team of three to five people over a year to do. It's like an enterprise engine, like rebuild. It's one of the most risky and expensive things you can do.

Speaker: And we're almost done. We're coming up on the end of it after working on it for about three months together for three to five hours a day. Um, and so we don't even talk about the old thing anymore. Like we don't, it's pretty, I think if you,

Speaker: It'd be interesting to run that experiment, but it's not like we don't spend any time in that app at this point, other than her doing her maintenance. Fair enough. it It might make an interesting data point in this. It probably would. Yeah.

Speaker: I'd be curious. yeah and in and And my argument isn't that... like I think it's much like the general purpose programming language argument. like We could probably achieve like the same results in more than one way.

Speaker: um But I think there's got to be like supporting evidence at some point about some methods producing more reliable results. But yeah, i don't I don't have any measurements to share on that. Yeah. We certainly need both, we need don't we? We need the hard evidence data points. And we also need to go into work on a Monday morning and not have our hearts sync when we open the code.

Speaker: Yeah, or just, um you know, five, six months after, not even six months, let's talk let's just say like five years after launching this product, someone who needs to go add on to it or look at it should not be met with a question mark when they look at how it's constructed.

Speaker: So I guess I should be clear. Grain is solving, Grain is trying to eliminate the architecture variance as a problem category. There's still product and domain variance that happens.

Speaker: Even if you just use Grain, but you only ever like tell Claude, like, hey, I want to do this, I want to do that. That's not like a, like your product... is ah there's variance in in how it's going to work. And so that's where like, once you eliminate architecture as an error category, now the spectrum and stuff that people are touting comes into handy. It's the other side of the coin. Like technically you can live without the spectrum and stuff if you have architectural uniformity, because that's the higher risk than like your product varying. But you can also control product and domain variance with the spectrum and stuff at that point, because it maps onto a uniform architecture. So you can decouple a lot of spectrum and stuff right now,

Speaker: either says, oh, we don't care about the architecture, or it tries to bundle the concept of do this architecture with this domain and product. And you want to get rid of that, so you're just talking about product, and then it sits on top of uniform architecture.

Speaker: um Yes. so it kind of yes two Two different problem areas. It kind of ties back to your opening thought, doesn't it, which is that Architecture is accidental complexity. It's not something about the specific domain you're building for.

Speaker: it's not yeah It's not the dental records thing. It's not the accounting system. If you can deal with definitively the architectural accidental complexity, you can focus on the interesting essential complexity, which are the decisions of the domain.

Speaker: Yeah, and I think um something that is worth stating, everyone is so freaked out by AI right now that a lot of conversations about programming tend to devolve into a monolithic approach to to the topic.

Speaker: But human beings, we build a lot of different kinds of software. Programming languages, embedded systems, frameworks, runtimes, libraries, information systems, video games, databases.

Speaker: Each one of those categories is going to have different tolerances and concerns when it comes to AI-generated code and process. And some of them will overlap and some of them will be distinct. And so the idea that a DSL can solve the architecture problem for information systems seems clear to me.

Speaker: The idea that it would probably be able to solve a lot of problems in those other categories seems apparent to me that it's worth a try. But grain wouldn't be, you wouldn't build a programming language with grain. It's specifically for information systems.

Speaker: But what if there is an interesting DSL that can... that could be designed to make the AI construction of programming languages more reliable or databases more reliable or any of that. So I think like it's an interesting technique to explore across that spectrum.

Speaker: ah And also admitting here that like this isn't solving all software development. This is literally targeting architectural variance for information systems. And maybe we could learn other techniques to go across category as well from that, but it's not for that.

Speaker: Yeah, we could infer that future. I could see a future where we start building languages specific to certain domains and a future in which we build a language that's just for building those languages.

Speaker: Yeah, exactly. That'd be an interesting place to be. Yeah. Yeah. um Oh, well, on the spec, just in terms of how we're working, by the way, I just wanted to throw out there, um there's a company called Juxt, J-U-X-T. They're kind of like a beloved Clojure consultancy in the community. But they've produced a specification language called Allium.

Speaker: like A-L-L-I-U-M, and I use that for everything now. It's a very powerful way to create specifications for these projects, and then it it it'll map onto something like Grain very well. So it's solving both of those ah kind of domain and product and architecture all in kind of one package at that point. i would recommend anyone that's trying to get a hold on things to go look at Allium.

Speaker: We had one of the co-creators of Allium on the podcast a few weeks back. So I'll link to that in the show notes. And thank you for plugging another episode. Yeah, of course. um It's really a powerful tool. I'm actually continuously surprised by the the benefits that it's bringing. I was actually going to try to make my own after using it for a bit. And then I realized,

Speaker: No, it's doing too much good stuff. And now I'm thinking of it more as a, ah I'm thinking of it more as a host, which is interesting, like thinking of a set of skills like Allium as a host platform to build my own up against it, that depends on it.

Speaker: And so I built a, um there's like a mirrored set of skills for grain that are all about building out an event model specification that is actually runtime checked via the REPL using Grain's code agent tools. So it helps basically go from, there's like an interlocking Allium spec that is like the full behavioral spec, then the event model topology that maps to, that will map to Grain primitives. And then when the AI goes to actually build out the Grain primitives, there's a definitive link saying like, hey, you missed these primitives that were in the event model, and the event model has to link to elements in an Allium spec. And so it's creating this kind of multi-tiered, into a narrowing, but runtime checked specification system, because I can't do anything with the Clojure REPL in Allian.

Speaker: But if I put this middle layer in that specific to Grain, I can have the code agent use Grain's code agent tools and actually use the REPL to say, have I completed the job Yeah, yeah, yeah. Because Allium is very much deliberately avoiding implementation details. yeah But having a bridge from that to implementation details to implementation, I can imagine is very neat.

Speaker: And then imagine, like so grain implementation-wise already looks like an event model if you were to visualize it. But the event model data structure that is... ah expressing what grain should do literally is an event model. Of course, that's going to look like an event model if you visualize it. And then the fact that it's linked to parts in the Allium spec mean that if we built a tool that visualized a grain system built with all these components, you could build a full blueprint visualization that has all the behavioral specifications, the topology of the information flows, and the links to the grain actual implementation primitives all in one place, which

Speaker: should be a powerful way to kind of gather around a system as people and understand what it's doing from a visual perspective. We're still on our way to that. We started with visual event modeling, but kind of dropped it for a while because we're not sure about like the time it takes to do it. like We want a more integrated tool. So we're just like, okay, grain can produce event models, so we'll be comfortable with that for a while. But we want to come back around to now we have this way to visualize this like fully specified grain application, basically. Yes, we now have these automated programmers with the patience to take that whiteboard, turn it into specs and code, and turn it back into a whiteboard.

Speaker: Yes. That will be a fun system to model with. ah Maybe that's enough background work and research to leave with. Cameron, thank you very much. um I look forward to hearing how that re-implementation goes when it launches. Yeah, definitely. It will keep you up to date um and you know maybe ah be a return at some point to, because we're always learning stuff because we're really waiting in the murky waters of all this AI stuff. So if we learn new things, it'd be nice to come back and and update you.

Speaker: I look forward to it. Cameron Barre, thank you very much for joining me. Thank you, Chris. Thank you very much, Cameron. If you want to hear a bit more from Cameron, he recently gave a talk about all this a conference called Closure Conj, and that's now up on YouTube. So I'll put a link to it in the show notes in the description, and you can find it there.

Speaker: If you'd like to hear any more about closure from me, i have been invited to MC Euroclosure next year in Prague. It's um early next year in the spring. The CFP is about to open. So if you've got something to say, do submit it because I want to see some good submissions.

Speaker: And I'm going to encourage Cameron to submit. But for now, if you've enjoyed this episode, please make sure you like it and rate it and share it with a friend. Make sure you're subscribed because I've got some really great episodes. I am slightly struggling to get edited, but they're coming very soon and I promise you some great guests. So subscribe and you won't miss them.

Speaker: But for now, I've been your host, Chris Jenkins. This has been Developer Voices with Cameron Barre. Thanks for listening.

Speaker

Speaker

Speaker

Speaker

Speaker

Speaker

Speaker

Speaker

Speaker

Speaker

Recommended