 Hey everybody, how's it going? So those of you that have seen me speak before, know that I usually like to start my talks with something a little weird. This is not gonna be an exception. You will not be spared. So I wanna first make sure that we are all sort of in sync. So I'm gonna count to three, and when I say three at that instant, can everyone just clap? One, two, three. Turns out sync needs a little work. Let's try again, ready? One, two, three, that's so much better. Let's snap this time. One, two, three. Awesome, okay. So don't do it yet, but this time, when I get to three, everyone that is able to, please crack your knuckles. It's gonna be weird. Anyone wanna leave the room before this happens? Okay, ready? One, two, yeah. Yeah, yeah, that was weird. I like to start my talks with a little bit of disgust. That way everything seems like an uphill climb afterwards. I'm here today to talk to you about the future. No Vim today. No live coding, which is good because I have both hands taken. So I'd have to live code with my something, my face. All right, so I wanna talk about the future, and I wanna talk about the future of writing correct code. I think this is probably pretty important. I think it's gonna matter. I flew over here on an airplane that has apparently about five million lines of code in it. Yeah, and probably some of those awesome errors that Brittany was pointing out, if I had to guess. Not all of that is safety critical, I assume, but it's likely that we will soon be riding around in cars that have millions of lines of code in them, that drones will fly over our head with possibly millions of lines of code, trains, planes. It's all gonna happen, I think. And so writing correct code is important. Maybe that's not super relevant to us. I've never written safety critical code, like code that kills people, if it's wrong. But you can write, it's really important that code be correct even if you're writing web applications, right? To pick a totally hypothetical example, imagine you wrote some sort of web app that acts as an elite about 143 million social security numbers out as a quasi-governmental agency. Let's just say, that would be bad. So we wanna avoid that. So I've been thinking a lot about what is the future of writing correct code? How are we going to do it? How do we do it today? How can we do it better? And that's what this talk is about. And I asked myself early on, what is a good model? Like what are other places we can look for possible inspiration and possible clues about what this is gonna mean for us? And the answer to me, surprisingly, came in the form of chess. Does anybody know who this guy is? Yeah, Gary Kasparov, former world champion in chess. For a while, this was the best chess entity in the world. There was no option if you wanted better chess. He was the most correct chess player there ever was. And then in 1997, do you know what happened? He got beat by a computer, Deep Blue beat him, financed by IBM, and he lost. He lost a six game set, a six game match. And this computer was so far advanced from what had been seen before that Gary actually accused the Deep Blue team of cheating because there was a move in the second game that was so good he was convinced that only a human could have thought of it. Turns out, computers get better. So Gary lost, and then at that point, the best chess playing entity in the world, the most correct one was this computer. But then something was discovered. If you took a really strong chess engine and had it spit out potential moves to a grandmaster, that combination was stronger than just the chess engine alone. And that's actually still true today. So still, if you pair a grandmaster with the strongest chess engine, that pair will beat the chess engine. So today, if you want the most correct chess, the answer is a human plus a program to help that human. And I think that's where we're going. I don't think we're gonna have much success at making dramatically smarter humans. And even if we did, we'd have to make all of us smarter. But if you can make a really good, really smart program, you can help everyone, even if we don't make anybody else smarter. And so I think that's the future. We're gonna have, maybe call it an AI, if you wanna be fancy and impress people, but programs. Programs to help us write programs. Programs to check our work. I have some bad news. I don't think Ruby is gonna be the answer to this. I know we're at Rocky Mountain Ruby. I don't think this is gonna be, these programs are gonna be written in Ruby. That's actually good news though, because Ruby has been around a long time. And we would hope that we can do better, right? Like we would hope it wouldn't be stuck with the same language forever that things wouldn't progress. So that's actually good news. And I have more good news, which is the future is now. I've actually spent the last six months writing code with the help of programs to help me write code. And it has completely changed things for me. I am writing dramatically more correct code. I am refactoring with much more confidence and I am enjoying the heck out of it. And I wanna tell you about the first program. The first program is Elm. Somebody asked about that over there. This is a perfect dovetail. Where'd Brittany go? This is a great, she's gone, amazing. This is a nice dovetail with Brittany's talk, because Elm solves a lot of these error message issues for us. All right, so the thing I like in Elm, so here's what Elm is. How many people have heard of Elm by the way? Okay, how many people have running Elm in production? Aha, yes, so Elm is an interesting point. Elm is a programming language that compiles into JavaScript. So if you want to write some sort of UI for a browser, if the browser is the target, you might use React, you might instead use Elm. Elm has a really powerful type system. It's sort of descended from Haskell. If you've written some Haskell or heard of Haskell and seen people say, oh my God, the type system in Haskell is amazing, and it is. It's sort of like the creator of Elm chopped off the most complex parts of Haskell and simplified some things and made it a little more beautiful and we have this wonderful language that compiles into JavaScript so we don't have to write JavaScript. And that the generated JavaScript can be quite correct. So I've been writing a lot of Elm and liking it quite a bit. So, notice how we had a lot of people that have heard of Elm but only like two, it looked like a running Elm in production. I think Elm is about to cross the chasm where it moves from a thing that only a couple early adopters are using to a thing where it crosses over into mainstream acceptance and enough people are using it, enough companies are using it, that people can trust it and feel like it's gonna be good. And that's kind of part of my mission. I'm trying to help it cross the chasm right now. I'm hoping to inspire some of you to try this out. Correctness. I think this is a pretty good correctness measure. How many runtime exceptions do you get? Cause every runtime exception is like, oh, you screwed up. There's a thing that you didn't anticipate something went wrong. If you're running a pretty popular app that gets a decent amount of traffic, you're probably getting lots of these, right? Like, how many people have seen like 10 exceptions a day? No? Okay, just me. All right. I feel like if you look in your honey badger logs, you will be contradicted. Runtime exceptions. Not so good. Here's an example. So there's a company that is running 100,000 lines of Elm. They're an early adopter. They embraced Elm very hard. 100,000 lines of Elm, which turns into JavaScript. They've been running for about three years in production. How many runtime exceptions have they had in those three years? Zero. None. I challenge you to write 100 lines of JavaScript and run it for three years and get zero runtime exceptions. I don't think you can do it. The reason you get so few runtime exceptions in Elm is because it has a type system. I know you've probably heard of type systems before. You've maybe experienced some type systems. Maybe you wrote some Java and said, yeah, type systems are no good. Java's so verbose. I hated it. Not all type systems are created equal. Elm's type system is amazing. Let's look at some examples of some Elm code and I'll talk about the type system in these examples. So this top line is a type signature. It says the function length takes a string and returns an int and there are examples below. Length takes hey, returns three, or length takes an empty string and returns zero. This one line of code, which is simple to type and easy to think about, gives you quite a lot of security and guarantees. So Elm, the Elm type system, when you write that, will look through all calling sites of length and make sure you only ever pass it a string. You cannot compile an Elm program that will pass something that's not a string to your length function. That's a guarantee. If it compiles, you never have done that. Also, Elm will look through the body, the body of the length function is not here, but Elm will look through the body of your length function no matter how complex and make sure that it always returns an int. It will walk through all of the branches of your ifs, through your case statements. You can't fool it, it knows. You always return an int guaranteed. That one line gives you so much security. Another example, here's concat. This takes a list of strings and returns a string. Here's our example down below. There's our list and our output. Same thing. You can never call this function if you're not passing in a list of strings and it will always return a string. Strong guarantees. Last example, here's repeat. This has a little bit of a new idea that you haven't seen yet. The repeat function takes an int and some type A, some sort of type A here and returns a list of As. So here are our examples. We repeat takes like a three, let's say, and then all right, which is a string and returns a list of, all right, all right, all right. Nice. I wasn't sure if that was gonna work. Yes. All right. So this A right here just stands for any type. Send me an int and something and I'll make sure to send you a list of those somethings back. And again, that one line, Elm guarantees this is true. It's like a contract. It will not compile if this is not true all through your program. By the way, type signatures also make great documentation. So here we say the isAdmin function takes a user and returns a Boolean. That's pretty clear. That's like executable documentation. I bet you could write that function just given that type signature. Is logged in, takes a user and a session and returns a Boolean. I bet you could write that too. Here's a little example. You're familiar with map from Ruby, right? We have map on enumerable. Map, this is some new syntax you haven't seen yet but this says the type of map, map takes a function from A to B, that's what's in the prens there, and a list of As and returns a list of Bs. It makes sense if you sort of think about this in Ruby. Imagine you have a function from string to int and a list of strings, you get back a list of ints. And so every time you call map, Elm is checking to make sure that this is working correctly. This type system makes a big deal. It lets you have some pretty incredible error messages which I wish Brittany could see, but here we go. The branches of this if produce different types of value. The then branch has type string but the else branch is number, hint. These need to match so that no matter which branch we take we always get back the same type of value. Right? That's a pretty darn good error message. There's actually a repo of all the possible error messages that an Elm program can make and people file issues against this repo saying this error could be better and they will add things to like hints down at the bottom or links to further documentation. Here's another example and this is the kind of thing that trips up newbies so they add an explicit error for this. This condition does not evaluate to a Boolean value true or false. This is a common thing so if you're a Ruby programmer you probably assume that you can use this sort of truthiness type thing right here like the list length, this comes back with an integer. I assume I can just use the integer as in the if. Elm says, you haven't given me a condition with this type, int, but I need it to be bool, hint. Elm does not have truthiness such that ints and strings and lists are automatically converted to Booleans do that conversion explicitly. This kind of friendliness is all throughout the language. It's like having a friend debug your code for you. This is kind of the thing you can get with a type system and a smart compiler. This is a program helping me write programs. This stuff is a big deal. I wanna turn now to one of my favorite topics which is nil, my favorite, I mean Lee's favorite. I tweeted this a while ago. The sad truth here, this is funny because it's true, unfortunately. I wish it weren't. Challenge, try this with your coworkers for a few weeks and see how long it takes them to catch on that this is all you're doing during code review. Can this be nil? What happens when this is nil? Ruby loves to return nil. When Ruby doesn't know what's going on, nil is Ruby's, I don't know. It's like find some things, I didn't. Okay, what do we get, I don't know. Cool. Something went wrong, what should we do? Return nil, it would be great. Okay, cool. So let's go back to those Honey Badger errors or whatever error tracker you happen to be into. Whose most common error is no method error on nil? Yeah, everybody. Everybody, those are, and again, go check it if you don't believe me. That no method error on nil, classic error. It's like the Ruby error. If there were a quintessential error, it would be that one and it would be that you've gotten back a nil somewhere and you accidentally called something on it. Like what's the bug there? You thought you had a thing, you actually had nil. You tried to call a thing on that nil, right? Ruby's answer to this problem of hey, nil shows up all the time is never forget to check for nil, right? Just be perfect. You should always assume that something might be nil and you should have tons and tons of guard clauses all over the place, right? Nobody writes code like this, right? We write it very directly. We write a very simple unit test that has like a perfect non-real world scenario and the test passes, we're like, great, everything's good, move on. And then later, somehow, that whatever thing is not there, you get a nil and it blows up and you get an exception at runtime, which is when your users see it. Let's talk about how Elm handles this. Okay, so here's a concrete example. Here's some new things you haven't seen. This is how you make a type in Elm. So there's a type called person and a person is like an object and has two pieces of data on it, has a name which is a string and a person has an age which is an int. And then we have this can vote function which takes a person, returns a boolean. So we say if the person's age is over 17, true, otherwise false. Okay, so imagine this scenario. We've written this code and we go, oh, actually, you know what? Person might not always have an age. We might not always have an age. We don't always know the age of somebody. What do we do then? Well, Elm has an awesome answer. So Ruby's answer would be, check for nil everywhere. Every single time you use age, don't forget to check for nil. And you would go, right, definitely not doing that. Gonna go do something else instead and we're gonna blow up in production all the time. Here's what Elm does. Elm has this interesting type called maybe, which just kind of has this like interesting like, maybe an int? I kind of like that a lot, actually. So what we've done here, we've actually, so think of maybe as a wrapper. We've sort of wrapped up the int that may or may not be there. It's like it's in a box now. And everywhere that we want to look at the age, the person's age, we have to explicitly unwrap and say, I know this is not an int. It's actually a maybe int. It's in that maybe box. And so because the maybe box maybe has an int and it maybe has nothing, I will handle both sides. I'll handle both things. And Elm will not compile if you don't handle both things. You cannot forget to handle both things. It just will not compile, your code will not run. So everywhere you go access that person's age, you have to say, what do you wanna do if it's not there? That sounds a little annoying, but it's actually rigorous. It's actually saying, we want our code to be correct. And if you're telling me that sometimes age is not there, you gotta tell me what I should do when it's not there. Here is how, don't worry about too much of the syntax here. There's some concepts we haven't covered, but here's our new can vote function when we switch int to a maybe int. I'll basically just translate, which says, let's look at the person's age, case, person age. When we look at their age, if they actually have an age and that age is over 17, then true. If it's under 17, then false. But if they don't have an age at all, then return false. So maybe it can be a just or a nothing. And it's not so important that you understand there, but the clear thing, this nothing down here, this is actually a big deal. This is saying like, this is what I want you to do when the age is actually not there. And this kind of thing rarely shows up in Ruby. Like, if age.present, maybe you might get that. But basically, Elm won't compile if you eliminate this lines of code, which say, here's what you do when age is not there. This reduces so many, so many bugs. It's a little hard to, it might be hard to imagine how this works in practice. I can tell you, once you get used to working with maybes, looking at Ruby will give you panic attacks. You're like, isn't that a maybe name? Isn't that a maybe int? Isn't this a maybe int over here? It's such a difference. It's such a high level of rigor and it prevents so many, like you'll never, ever get like a no method error on nil in Elm. It just won't even compile to that point. So, I'll make a bold claim now. And I'm actually repeating a bold claim, but I'm adding my emphasis to it. A former coworker of mine, Athopot, has written a lot of Elm. And he's written a lot of Ruby, a lot of Ruby. And he said, I would rather, I'd have more confidence in a code base that was an Elm code base with no tests than a Ruby code base with tons of tests. And that's pretty huge because like my code to test ratio is like two to one ish. Maybe 1.8 to one. I'm running a lot of test code. I would love to not write test code. And it turns out if your type system is strong enough, you need to write way fewer tests. So you can go a lot faster and make fewer errors and do less work because we have programs to help us write programs. And we should be using them. I want to talk to you about a real world example. This is some Elm code I wrote. I decided I wanted to make a sort of image evolver. And so I have a goal image and I want to randomly mutate my way from a set of random polygons into that image. So say I have a picture of my face and I will throw down 100 random polygons on a canvas in the browser and I will mutate them. And if that image looks more like my face than it did before, I'll keep that version and mutate from there. And if not, I will throw away that mutation and try again. It's kind of like a hill climbing, like a naive hill climbing algorithm. I'm just sort of making these random changes and every so often that change is closer to where I want to go. Okay, that becomes my new baseline. Here's an example. So on the left is the goal image, is that those set of squares. And on the right we have 125 random polygons, random colors, random positions and random transparency, opacity. And you're watching them mutate. The bottom left number is how many iterations? The bottom right number is our current similarity to the goal image. And you can kind of see we're getting there. So we're just, like you see all this flickering because it's trying lots of things. It's like, is this better? Is this better? It'll pick like 20 polygons at random and change random things. Like one of their vertices, the color, the opacity, just tweaks it. And we're starting to get pretty recognizable. So this is a program I wrote because I want to just kind of flex my own skills and see what this was like. Here is another example. This one's just static. Faces are harder. The squares are, like the more detail there is, the harder it is. But you can kind of see we're getting there. Like we're never going to get a great face because the algorithm is actually quite stupid. But we're getting the outlines. Like it's, you can see we're getting there. Now I wrote this program and in the original version I, instead of using polygons on the canvas, I used circles because it sounded simpler. I was like, well a circle just has a center and a radius and that seems easier than a polygon that has a list of vertices that I need to keep track of. And I was new to Elm, so I was like, I'll just use circles. So I built the first version and I got it working and I was like, okay, this is going to work a lot better with polygons. I'm going to have to do this refactor. And I made a new branch and I was like, this is probably going to suck. Here we go. By, as point of comparison, imagine you've written a Rails app and it's a to-do app. And you have to-dos and you can create a to-do and you can rename a to-do and you can edit them and you can assign them and you can complete them. And then someone comes along after a couple of days and is like, hey, guess what? That to-do app. We're actually going to make it be an issue tracker for software development teams. So to-do we should rename to issue. And to-dos no longer have a due date. And when you assign it to do, you might actually assign it to a team of people. An issue could be assigned to a team of people instead of a single person. And you have this change to a very fundamental data structure in your app. That's what I was changing. So like this circle's data structure was in basically every function of my app. It flowed through all the way the Elm app. And I was like, okay, this is going to be pretty terrible. So I made the change to the top level. I said, okay, hey Elm, circles aren't a thing anymore. Now they're polygons and they don't have a center. They have a list of vertices and a vertex looks like this and these are the coordinates and all that. And Elm immediately spits out like 15 errors. So I'm like, okay, here we go. And I get to the first error. And it's pretty simple. And Elm is like, you saw this error message. This is pretty friendly. It's like, hey, you gave me a circle and you need to give me a polygon. And I was like, okay, no problem. And it's like, oh, hey, you referenced a radius and that's not a thing anymore. Oh yeah, like now it's a vertex. And I very mechanically walked through this list of 16 or so errors that actually were like pretty straightforward. They were well explained and they were pretty easy. And then I finished and I solved the last one and it compiled. And I was like, no way. And I refreshed my browser and it worked perfectly the first time. And I was like, my life will never be the same. I got goosebumps when that happened. It was just like, oh my God, there were no tests in this. I was new to this thing. I'd always ready any tests. The type system and the compiler washed me through what would have been a really hairy refactor in any other language. It was simple. I never stressed. And as soon as it compiled, it worked perfectly. It's just like so mind-blowing. So type systems. They're all right. Let's talk about the second program that I've been using to help me write better code. Property-based testing. Is anyone familiar with this? Okay, wow, not even at the chasm yet. Awesome, this is great. I'm excited for you. Okay, so let's say we wanna write a function. And we wanna make sure the function is correct. So we're like, cool, we're gonna be good programmers. We'll write some unit tests. So let me think of an input. Let's say we have a reverse function. I should pass it a list of three items. And then when I call reverse on that, it should be reversed. And so we hand write an input, and we hand write an output, and we say those should be the same. And we do that a couple more times. We're like, that seems good. And we move on with our lines. And we often miss a lot of edge cases. And it's because it's annoying to come up with lots of inputs and think of lots of edge cases. And it's annoying to have all that test code there. And if you write 500 lines of tests for reverse functions, like this seems stupid, like why am I spending all this time here? So property-based testing takes a different approach. It says, hey, you know, thinking of test cases and checking that they're correct, that sounds like a great job for a program. That's not a great job for a human. So let's imagine we are testing this reverse function. With property-based testing, instead of coming up with test cases, you come up with properties. Properties are things that are always true for the function you're testing. Can anyone think of a property that should, for the reverse function, no matter what input we have, what should be true about the output? Can anyone think of some properties? Yeah, absolutely, yes, that's a great one. So no matter what input I throw into reverse, the output of reverse should have the same length as the input, right? That should always be true unless something is horribly wrong. Excellent, really good. Any other ideas? Any other properties? Yeah. It's still a list, yeah, totally. The type should be right. We should still have a certain number of things. Yeah. It should have the same content, absolutely. We shouldn't lose elements or gain elements. We shouldn't accidentally de-duplicate things. Awesome, so just armed with those three properties that we just rattled off without too much effort, we can then have property-based testing. Elm has a property-based testing framework. Generate us lots and lots of input and make sure that for every input, the properties hold. So it says, okay, here's an empty list. Do the properties hold? Yes. Okay, here is a list with 10,000 items. Okay, the properties hold. Here's a list with 20 Unicode items followed by some integers with a null byte tacked on at the end. Okay, the properties still holds. It thinks of these lots of nasty edge cases for your code for you and makes sure that they're right. So unlike a unit test where you run five examples, you're like, okay, those tests pass, a property-based test just hasn't failed yet. So it will basically run, let's say, a hundred different inputs through your thing. Make sure the properties hold and say, you're good so far, let's move on to the next thing. And you throw those in your test suite and you basically have code testing your code for you. This is a pretty big win. So here's the question, does this stuff actually work in practice? Does it actually test, catch real world bugs? And the answer is yes. So here's a real world example that a friend of mine ran into and he had written some tests, some property-based tests for a JSON encoding and decoding library. And when you have an encoding and decoding pair like that, property-based testing really shines because you can say, generate me some input, encode it, decode it, make sure I have the same input. You basically round trip all the way through your program and back and say, I should have that same thing back and you can generate me a whole list of things. I want you to make sure that everything works this way. It's like a really powerful property and a really powerful test. And you can just set this up and say, that should be true, throw examples of this all night, I'll be back in the morning and run a billion tests through that, testing your encoder and decoder. He tried this and his almost immediately failed and he discovered something. When he encoded something, it had a microsecond of precision and when he decoded it, it was down to a millisecond of precision. It had truncated that precision at the end there. Now does that matter? Maybe. For them, it actually matters. If you compare these two times, are they equal? That sounds like an opportunity for a pretty subtle bug where two times look like they should be equal when you encode and then decode, but they're off by microseconds. And this is the kind of thing, how could you find this in a unit test? Can you imagine catching this in a unit test? Probably not, but this is the kind of thing that property-based testing catches with ease. So I'm just about done. I want to tell you one more thing. Anybody know who this is? David Allen, yeah. So David Allen came up with a system called Getting Things Done. And one of the core tenants of Getting Things Done is that when you think of a thing you want to do, even if you maybe want to do it, you need to throw it in a system that you trust. You need to get it out of your head because as he says, brains are for having ideas, not for remembering things. And I think we should take that and apply it to programming and realize that our brains are for dreaming up programs, programs that are ambitious and change the world and do wonderful things. And they are not for chasing down nil and watching for edge cases and worrying about microseconds. So that's the talk. If you liked this, this is my newsletter. You will probably like the things I send out. I'm really fired up about Elm these days, so I talk about it a fair amount. I'm also about to launch a course called Refactoring Rails, about taking care of aging Rails apps. So if you have an aging Rails app and you're starting to feel the pain, you might enjoy that too. And if you sign up there, I'll send you information about that course as well. And thanks very much. It's been a lot of fun. I had to write some things down. I had some complaints. Yes, good, good, good. The complaint board is open. The first one is that you went through an example of discriminating biographical details of a person, age, and name. All anchored around discussion of maybe and you passed up the opportunity for phone number leading to obvious call me maybe jokes. Oh, damn. Second. Strong criticism. More grievous. I actually was about to shout it, but I was like, no, I don't wanna blow the punchline and then it never came. Damn it. It looks like you're writing a crud app. Would you like help with that with a little clippy graphic? Okay, all right, all right. Like when you're like, programs, I write programs for you. Yeah, sure, sure, sure. I thought that was coming in the intro. Okay. Other than that, I was sold. I don't know if anybody else, like so I used to do Java a long time ago. And just like type is a thing I have found myself like craving, especially in teaching. And so seeing like pre-condition, post-conditioner like input type, output type, it was honestly, I was like, I feel like you're going home. That'd be so beautiful. And the error message about the type mismatch coming out of the if was like really, really neat. And if you didn't see those things, if you saw those things and you're like, why would I care? Then just get into those honey badger logs and you'll find out why you should care. Yeah, exactly. So aside from my private commentary, we have time for like a couple questions. Yeah. Is it like comments? Ruby contracts, that's called. So I can actually recommend a gem. This is, if you like the maybe idea and you want to try it out in Ruby, it's not going to be as slick as Elm. But if you look at the wrapped gem, like as in like wrapped up in something, some former coworkers of mine said like, I miss maybe so much in Ruby. So here's the concept. And it's not quite as slick, but it'll give you the give you the sense of it. Yeah, yeah. So if you can't hear, he said, he's calling me out because in the past, I have prescribed null objects as an answer to the nil problem. And I don't think what I said was in conflict with that, which is that like nil is a problem and there are various ways to solve it. I happen to really like Elms a lot now. I would rather have a maybe than a null object most of the time. But because it's enforced by the type system, it's like, if a thing expects an int, you can never hand it a maybe int. It's just like, I'm not interested in that, man. Like you gotta give me an int. And so you just can never forget. Whereas like you can still have runtime errors with a null object, right? Like you have a null object standing in for a user, like you have a guest, let's say, and then you call like last signed in. You're like, oh, I didn't actually define last signed in on that object and it blows up. Yeah, so the question was, is there a testing framework for Elm? And there is, yeah. So that property-based testing is available in Elm as well. So there's an Elm test library. It has normal unit testing like you're used to seeing, but also has, they call it fuzz testing, but it's the same thing. What are the major hurdles around getting Elm in production? That's a good question. I think it's mostly cultural, honestly, at this point. It feels technologically ready. There are fairly easy ways to get it working in like the Rails asset pipeline, for example. As like at Thaubot, they are writing a fair amount of Elm. It's happening. I think it's mostly just like, do people trust it? We're at the phase where it's people need to look around and say, are other companies that are like sort of conservative like us using it too? Okay, fine, we'll use it. I think that's where we're at. That's it. That's all for you. All right, all right. You're done. All right.