Code and the Coding Coders who Code it

Episode 58 - Aaron Patterson

Drew Bragg Season 1 Episode 58

Ruby core team member Aaron Patterson (tenderlove) takes us deep into the cutting edge of Ruby's performance frontier in this technical exploration of how one of the world's most beloved programming languages continues to evolve.

At Shopify, Aaron works on two transformative projects: ZJIT, a method-based JIT compiler that builds on YJIT's success by optimizing register allocation to reduce memory spills, and enhanced Ractor support to enable true CPU parallelism in Ruby applications. He explains the fundamental differences between these approaches - ZJIT makes single CPU utilization more efficient, while Ractors allow Ruby code to run across multiple CPUs simultaneously.

The conversation reveals how real business needs drive language development. Shopify's production workloads unpredictably alternate between CPU-bound and IO-bound tasks, creating resource utilization challenges. Aaron's team aims to build auto-scaling web server infrastructure using Ractors that can dynamically adjust to workload characteristics - potentially revolutionizing how Ruby applications handle variable traffic patterns.

For developers interested in contributing to Rails, Aaron offers practical advice: start reading the source code, understand the architecture, and look for ways to improve it. He shares insights on the challenges of making Rails Ractor-safe, particularly around passing lambdas between Ractors while maintaining memory safety.

The episode concludes with a delightful tangent into Aaron's latest hardware project - building a color temperature sensor for camera calibration that combines his photography hobby with his programming expertise. True to form, even his leisure activities inevitably transform into coding projects.

Whether you're a seasoned Ruby developer or simply curious about language design and performance optimization, Aaron's unique blend of deep technical knowledge and playful enthusiasm makes this an engaging journey through Ruby's exciting future.

Send us some love.

Honeybadger
Honeybadger is an application health monitoring tool built by developers for developers.

Judoscale
Autoscaling that actually works. Take control of your cloud hosting.

Disclaimer: This post contains affiliate links. If you make a purchase, I may receive a commission at no extra cost to you.

Support the show

Speaker 1:

Hello everyone, welcome to another episode of Code and the Code Encoders. Who Code it? I'm your host, drew Bragg, and I'm joined today by the man, the myth, the legend, tenderlove, himself, mr Aaron Patterson. Aaron, for anyone who somehow doesn't know who you are, would you please do a brief introduction for the listeners?

Speaker 2:

My name is Aaron Patterson. I do go by Tenderlove on the internet. I am on the Ruby core team and the Rails core team. I've been programming Ruby, let's say professionally, since when I say professionally I mean getting paid to do it. That doesn't mean I'm professional at it. I don't know. I've been doing Ruby as a job since 2008 or so I've been doing it for quite a while. I don't know. I've been doing Ruby as a job since 2008 or so, so I've been doing it for quite a while. Yeah, I don't know. I have a lot of open source code and I write a lot of code and stuff.

Speaker 1:

Write a lot of code YouTube video, blog posts. Occasionally you do a little bit of everything. Yes, in all the ways that most listeners interact with Ruby you've touched, whether it be Rails or Ruby itself. So the way that this will work for anyone new to the show is I'm going to ask Aaron three questions. I'm going to ask him what he's working on, what kind of blockers he has. He can talk about a recent blocker he had if he doesn't have a current one. And then the last question will be for him to share something cool, new or interesting that he has recently discovered built. This is a little bit of a loaded question for someone like Aaron, but it'll be a great one once we get there. So you are a busy man working on a lot of cool stuff. So when someone asks you what are you working on, how would you answer that I am working on many things.

Speaker 1:

I don't know.

Speaker 2:

It also depends on who it is. If someone I don't know is asking me what I'm working on, I'll just be like computer stuff, I don't know.

Speaker 1:

That's fair. Not printer, computer stuff, but computer stuff. Yes, okay, well for your day job what are you working on?

Speaker 2:

Sure, yes, I think I have an answer for your audience, of course. Okay, so at work, I'm working on two main projects at work. One of them is ZJIT, which is a new JIT compiler for CRuby, and then one of them is better Ractor support in Ruby, and those are the two main things that I'm really focusing on at work at the moment to both of those because I am interested in both of those.

Speaker 1:

I'm curious on your team at work, matt Shopify. How do you get that kind of work? Because I think a lot of us work on application development, so we're building a Rails app, so it'll be okay. Hey, this is the slice of the application that we want to build next, or this is the feedback that we've gotten On a team like yours, where you're almost building an ecosystem in a way, how does that work? Come down to you.

Speaker 2:

All of our work is driven by the needs of the business. The thing that we're trying to aim for is better utilization of our production machines. So whatever we can do to better utilize our production machines, that's what we're aiming for, and our team happens to focus on the language level of that particular problem. That's kind of how we get there. Yeah, of course, like ZJIT is for making sure we use a single CPU faster Not faster, but more like more CPU and then Ractor concurrency or parallelism work is like how do we use all of the CPUs? So that's kind of how they go together.

Speaker 1:

That's actually a really cool description of the two different projects and how almost different, in a way, they are. They're serving two different goals. How do you balance that? Sometimes, I think, at least in my work sometimes jumping between very backend heavy work and very frontend heavy work, especially with it being generally two different languages, two different ways of thinking about building the thing. Sometimes it can be really hard to jump between those two different things. How do you deal with that in projects where it's like single CPU, concurrent CPU usage?

Speaker 2:

It is pretty difficult At work. We have two different teams. We have a team that's specifically working on Ractor stuff. They're focused on that. We have another team that's working on the JIT compiler stuff and they're focused on that, and I kind of move between both teams. I'm trying to help with any type of blockers that they have or just doing development. Wherever we're short, I'm trying to fill in on those particular things and doing the context switching. It's hard. I don't have a good answer for you.

Speaker 1:

That's fine. Maybe you had like a hey, here's how I do it at my level. I've been doing this forever, so I've figured out a trick, or hey, it just never gets easier.

Speaker 2:

You just do the best you can applicable to the other projects, because both of these projects they're sprawling throughout the internals of ruby, like they touch everything in ruby internals. So like if you work on stuff in ractors you're going to be touching, like the garbage collector, for example. And when you're working on the jit compiler too, you also have to touch the garbage collector in that case as well. Working on the JIT compiler too, you also have to touch a garbage collector in that case as well. Same with the virtual machine. All these things kind of get tied together so you can take the knowledge from one and apply it to the other. It's really just like the specialized sections where it's maybe kind of difficult.

Speaker 1:

So in Ruby we already have YJIT. Yeah, a lot of people are using YJIT and it's been pretty successful. That, if I'm remembering correctly, was originally written in C and then written in Rust. Yeah, that's correct. Yes, so I guess. Two questions what is ZJIT if we already have YJIT, and then what is it written in?

Speaker 2:

We'll start with the second one. First, zjit is also written in Rust, because that's just an easier one to answer. So YJIT is an awesome project. It's based on Maxime, it's based on her PhD work. It's really really amazing, really great.

Speaker 2:

It's what we call a basic block versioning JIT compiler and I think it's her new research. This is her thing, and what it is is. If you imagine, most JIT comp and I think it's her new research, this is her thing. What it is is, if you imagine, most JIT compilers are what we call method-based, where they'll take a whole method and they'll compile the entire method and then you're done, whereas lazy basic block versioning is even more lazy than that. For example, let's say we have an if statement in your code and only one side of the if statement is taken, the JIT compiler, yjit, will only compile that side of the if statement. So, whereas a method-based compiler will compile both sides of the if statement and whether or not both sides are used, the method-based compiler will compile both, whereas YJIT will only compile the single side. And where that's very, very helpful is that warm-up speeds are very low because we're compiling literally compiling less code. It's very, very lazy.

Speaker 2:

The other nice thing is any types that we discover at runtime. So when it's compiling code it'll take a look at the objects that came in and check their types and then it'll generate machine code based on those types. It's able to propagate those types through the system and generate more machine code specialized based on those types and not do type checking over and over. So you can imagine like every time you call a method on an object it's got to check what type that thing is in a normal program, whereas in YJIT when it discovers that type on the first one it'll do the type check. The first method call it'll do the type check, but the second one it's like oh, I already know what that type is, I'm not going to bother doing it a second time. Zjit is just a method-based compiler. It is more traditional JIT compiler, the idea being that we're taking the things that we learned from developing YJIT and implementing it and bringing those into ZJIT and seeing if we can get an even bigger speed boost out of that. Sorry, that's a lot of stuff.

Speaker 1:

No, I mean it's good, especially if folks aren't familiar with JIT compilers at all, yjit especially being a very new way of doing JIT compilation. And it was interesting that you brought up the boot time. I could be remembering this incorrectly, but if I remember correctly, that was sort of like the problem with TruffleRuby. If I remember correctly, that was sort of like the problem with TruffleRuby. It's like TruffleRuby was faster but it took significantly longer to boot because it did some of that work behind the scenes.

Speaker 2:

Yeah, yeah, truffleruby takes a really, really long time to warm up. And going back to how the business drives the things that we develop, we deploy all the time. We're deploying over and over again, and I think this is a pretty common thing in businesses these days. So, like that, warm-up speed matters. We can't just let it sit there and warm up for hours or whatever it's got to be like now. So warm-up speed is very, very important to us.

Speaker 1:

So because of how the two compilers work differently, ZJIT will take longer to warm up than YJIT, but because it does the whole method block, it may be faster?

Speaker 2:

Yeah, that's a great question. Let's try and break down the advantages of a method-based compiler. So the advantages of YJIT, of course, are that we compile less stuff, when the advantages of an entire method-based compiler are. When you compile a method, we break the code down into what we call basic blocks. These basic blocks are essentially chunks of code that don't have any branches in them. So if you think about an if statement, an if statement is going to have, say, four basic blocks. We're going to have one at the top all the code that's executed before the if statement. Basic blocks we're going to have one at the top all the code that's executed before the if statement. We have two blocks one that represents one side of the if statement and the other that represents the other side, and then we have a fourth block down at the bottom where those two blocks join. So you can imagine kind of a diamond shape.

Speaker 2:

An issue that comes in is register allocation.

Speaker 2:

In our CPUs we only have a certain number of registers that we can use and anytime we have values, if we can keep them in register.

Speaker 2:

The longer that we can keep them in register, the faster our program will be, because reading a value from a register is much faster than reading it from memory.

Speaker 2:

So an issue with a lazy basic block versioning thing type compiler is that when you compile one side of that branch we can't know necessarily what values will land in a particular register. We have to get those registers to align all the way through that diamond shape and the only way we can see where those registers land is if we compiled the entire method, where those registers land is, if we compiled the entire method. So going through the machinations or whatever process we have to do of getting those values to line up, that means we end up spilling a lot more stuff to memory. Spilling is a term in JIT compilers or compilers just meaning we take stuff from the CPU registers and we put it into memory. We spill it to memory. So what we want to do is reduce spills as much as possible, and a full like method-based compiler is able to reduce spills much more than a lbbv compiler. So those are the advantages that we're looking for.

Speaker 1:

We're hoping to get out of this method-based jit is that we'll be able to reduce spills so is this a situation where you're taking what you've learned from building YGIT, building ZGIT and then ZGIT will be the thing, or is this a we're building ZGIT with the learnings from YGIT, but we're going to use both, or is that not how?

Speaker 2:

It could go either way we expect. So we do expect that ZJIT warmup may be slower than YJIT, but we don't expect it to be significantly slower. We're hoping that it's going to be in the range that it's like nobody cares. But if it's outside that range, like if it's very slow, we could imagine ourselves having a multi-tier compiler where it's like, okay, we'll start off with yjit and we get that fast warm-up or low warm-up time and then, as the program is running, since we have more time, it's like oh, okay, now we'll use zjit to compile the method and get even faster code. So we could end up in a world like that. But just for simplicity's sake we're hoping not to. We want to have z-jits warm-up time be so fast that that's just the one and we just use that one.

Speaker 1:

But it really just depends on how the numbers shake out so I guess the next logical question is after z-jit does it become a-jit or is there a new like? Like one JIT I'm joking.

Speaker 2:

YJIT was essentially like that pattern of like yet another JIT compiler right, yeah, yet another JIT compiler, yes, and then ZJIT is just like well, we're making it.

Speaker 1:

Yeah, it's after Y.

Speaker 2:

Got. It Should have started out at A.

Speaker 1:

I don't know. It's interesting because you're working on Ruby, right? You're working on Ruby improvement, but you're not actually writing Ruby to do so, right? You're writing Rust. Yes, this is a pattern that I shouldn't have been as unexpected as it was for me when I heard people working on Ruby were working on C right, Because they're working in C to work on Ruby. You're working in Rust to work on Ruby. We're working on C right, Because they're working in C to work on Ruby. You're working in Rust to work on Ruby's JIT compiler. What was learning Rust like?

Speaker 2:

I'm still learning it. I hate Rust. I'm so sorry. It's like I don't like it.

Speaker 1:

It's not a good language. Yeah, I mean I was going to kind of go into like Ruby's not your only language. You've been doing this for a while, so I'm sure. So I didn't know if learning Rust wasn't that hard because you've worked in languages like Rust or like you just love Ruby and friggin. Rust is nothing like it and this is annoying, or Rust is fine it's just like it's fine.

Speaker 2:

It's an okay language. It's better than C, but the bar is like so low. Yeah, it's better than c, but the bar is like so low. Yeah, yeah, it's a very, very low bar. If I were to pick a systems language to use for this project, I would actually pick zig. I love zig a lot. It's a very, very, very good language.

Speaker 2:

Biggest problem with zig is the language stability. Like I could never in good conscience recommend Zig as a language for a long-term project. You know what I'm saying. So I think it's just because Rust is trying to save you from everything. But sometimes I'm like look, I know what I'm doing, please just like leave me alone. And like Zig gives me that. It's like just enough handholding. But if I want to do my own thing, I can and it's really fine. And I can understand how people would like that. They want to do systems programming and they don't want to have to deal with seg views or any of those particular problems. In this case, like in a JIT compiler, a JIT compiler is essentially generating machine code at runtime and no amount of safety checks in Rust are going to ensure that that machine code that we generated at runtime is safe. It can't make that guarantee. We're still debugging segmeets Like it didn't save us from that.

Speaker 1:

So what do we get out of it? That's my next question is why, then? I feel like when I had Kevin Newton on, he talked a little bit about the reasoning. That was a while ago, so I'll just ask you to reiterate why go with Rust instead of? I mean, you said you like it better than C, so I guess there's that reason.

Speaker 2:

Huge, huge, big reason. A JIT compiler is like it's a big beast. It's a lot of stuff Like a lot of code you got to put together and as far as like code organization goes, like C is terrible. Rust is way, way better at that. You can organize your code nicely. It's much easier to understand and find things. It's easier to make abstractions than C, so you can make nice abstractions. You don't have to know all the other stuff that's going on behind the scenes. So I think it's like for what we're doing, it's a very appropriate choice. For sure. It just doesn't mean that it's my favorite.

Speaker 1:

This is one project ZJITIT and working in Rust and then flip the coin and you go over to Ractor work. Yeah, and I guess we got to repeat some of the questions of like, if anybody's unfamiliar with Ractors, let's talk a little bit about what Ractors do in Ruby. And then do you get to write Ruby now, or we still? Are we back in Sealand? Ruby now? Are we back in Sealand or?

Speaker 2:

is it something else? Back in Sealand. Back in Sealand.

Speaker 1:

You're working on Ruby. You are not touching Ruby. Got it no.

Speaker 2:

So for folks that are listening to your program that don't know, ruby has a GVL similar to Python, which means that anytime you try to run CPU intensive code, it means that Ruby can only use one CPU at a time. You know if you need to run Fibonacci or whatever in parallel, it's not going to happen. You can't do it. There are some things we can do in parallel, like, for example, you can do multiple IOs in parallel so you can like read from a socket in parallel. That'll work. But any type of CPU-bound work you can't do that in parallel, and that's all due to the GVL. So Ruby already has fibers, it already has threads, but the threads and fibers only allow us to have IO-level parallelism where we want CPU-level parallelism, and Ractor solves this issue. So Ractors have their own GVL, but you can have multiple of them running in parallel. So just imagine, like I don't know, you get all the safety of a GVL but you get parallelism too. So that's what we're working on.

Speaker 2:

Ractors are different from threads. It's kind of like I think the name came from like actors. It's like an actor-based system. It's ruby actors, ractors. So the main thing with them is they put a huge restriction on the data that you can share. If you want to pass a value between ractors, it's going to copy the whole thing, unless the whole thing is read-only. If it's read-only, it'll just share a reference, but if it's not read-only, it's going to copy it before it passes it to the other thing. I mean, that's kind of an issue, but not really. If you know about the data that you're passing between the things, it's not too cumbersome to say I need to make sure everything's frozen. Where the issues come in, though, is like lambdas, for example. This is a subtle problem, but people will run into it for sure, and they'll just be like I don't understand why this is a problem.

Speaker 2:

Let me, like, set the stage for you. You've done define method before, probably, and you give define method a block, and that block captures the surrounding environment, right? So if you have a local variable defined outside of that block, inside the block, you can access that local variable, and it's because the block captured the environment that was surrounding the block. I feel like every I don't know two or three years, people discover a memory leak in their application and it's due to this. It's because they made a giant object and then they did a defined method or something and now that giant object's sticking around in their heap forever and ever and it's because the lambda captured the environment. So let's say your defined method method with a block, let's say it writes to that local variable. It can do that, that's a thing it can do and that's a form of mutation in mutating the surrounding environment. So what that means is that methods that are defined method with a block, you can't call them inside of a Ractor because they could mutate that surrounding environment and Ractors only allow immutable objects to be shared between them.

Speaker 2:

We have these weird subtle problems where you're like why can't I just this is a defined method, why can't I just use this thing? So we're trying to solve problems like that. The other types of problems we're trying to solve are just bottlenecks inside of Ruby itself. I'll describe to you my favorite one so far the John fix. This is a great bug and it's very easy for people to understand the issue.

Speaker 2:

So if you take JSON input and you're parsing a hash, for example, and the hash has a key like a string key maybe it's just like hello points to world, so you have hello as a key. But let's say you parse that JSON twice. So you have two hashes, both with the same contents hello world. If you get the keys out of the hash, that string, hello. So those two strings, hello. Those are identical strings, and I don't just mean the contents of the string. If you check the object ID of them, it's exactly precisely the same object and I think most people probably know about this. Think about how that works under the hood. How do we do that? Because we're parsing two different JSON documents. There has to be some sort of data structure that how do I say this makes them unique. You know what I'm saying? Like looks it up and says like okay, that's the hello string, I got to go get the same one and like stick it into this hash.

Speaker 2:

So we have like a global hash table it's global inside of the VM, a global hash table where we look up those strings then insert them into the hash.

Speaker 2:

So you can imagine, if you have two different Ractors that are both trying to parse JSON at the same time, there's going to be contention on that global hash table. So we had an issue filed I don't remember how long ago it was, but a while ago where somebody was like yeah, if I try to parse JSON in Ractors, it's slower. It's literally slower than if I do it without Ractors. I could do it in serial and it's faster than doing it with Ractors and it's because of the contention on that global hash table. So John implemented a fix which was a lock-free data structure, a lock-free hash table, so we could eliminate that contention and now it's faster, like it's much, much faster to parse JSON between reactors and we're finding different bottlenecks like these global data structures within the virtual machine. So it's like kind of two-pronged approach. One is fixing language features, like that defined method thing that I was talking about earlier, and the other one is bottlenecks within the system.

Speaker 1:

I feel like in Ruby I mean, ruby is a very mature language now, so a lot of the every version is less holy crap. Look at this new feature and more like subtle little things that, depending on the type of programming you're doing, might not be anything that you're going to use, but something like we're moving a lot closer to frozen string literals and being the default and we got the new data object, which is essentially just an immutable object. Is that work because of the work that you're doing in Ractors, where you're kind of like trying to create a better, less mutable environment for Ractors to run in? Or is it just so happened? Like you said, we want less mutable stuff and like, yeah, this stuff's here too. They have nothing to do with one another.

Speaker 2:

And I'm just reading into it. Yeah, it's not particularly related. No, so the Ractor thing is coming in. Moving back to the business again, talking about where our stuff comes from, we have an application at work. The workload on it is random. When a request comes in, we don't know if it's going to be mostly IO bound or if it's going to be mostly CPU bound. It could be either IO bound or if it's going to be mostly CPU bound. It could be either. We don't know, and it's a huge problem.

Speaker 2:

So imagine you have an application like that and you're using a forking web server. So typically what you do is you'd pre-fork, maybe I don't know 1.5 times or 1.25 times the number of CPUs. You have some number, some coefficient, and let's say you have this random workload and all the requests come in, but they all happen to be IO bound. So in that case, like, let's say, you had a machine with 10 processors, just to make the math easy, and you're doing 1.5. So you have 15 processes, you're servicing 15 IO bound requests in parallel, but the machine if you look at CPU utilization on the machine, it's like nothing because they're all IO-bound. You could be serving more. You could be serving more, you're capped at 15. Now, on the other end of the spectrum, let's say you have all CPU-bound requests coming in and you're trying to service 15 CPUpu bound requests on a machine that only has 10 cpus. Now your latency is getting messed up because those 15 processes are contending, all trying to get cpu time.

Speaker 2:

How do we solve this problem? What we'd like to do is we'd like to have a way to automatically scale the web server up and down so that we can say like oh, we have more capacity on this machine. We're serving 15 IO bound requests. We can spin up another and take more, take more, right? Or hey, we're serving 10 CPU bound requests right now. I don't want it, don't give me anything, please. Don't give me anything, please. So we would like to be able to spin that up and have the web server automatically scale to whatever the load is on that particular machine, and we think we can do it better with Ractors than with any other. Concurrency primitive. That's like the ultimate, ultimate goal. That's the world we'd like to get to. Of course we're not like it's going to. We'd like to get to.

Speaker 1:

Of course we're not like going to take a while to get there.

Speaker 2:

Yeah, yeah, yeah. So that's kind of the thing that we're aiming for and what we're trying to drive these particular features. So it's not to do necessarily with that read-only data structures, but those for sure help.

Speaker 1:

So they help, but they weren't born of this project, they just help this project along. Yeah, yeah, yeah. How often does it happen that you potentially have a larger project and you get shoot-offs like what I just described? That's completely inaccurate, but I'm hoping it does actually happen in your work, where you're like, hey, we got to do like X thing and it's like cool, well, we can shoot this off, like this can become its own feature within Ruby, so let's go build that thing and then these multiple other things get done and then that makes this main project easier. Does that happen at language level, like it does sometimes in application development?

Speaker 2:

or is it not? I feel like it happens all the time. It's just a very small scale. Every single project seems like an offshoot is basically what it is, because the project's very nebulous. It's like oh okay, we need to make ractors better.

Speaker 1:

It's like okay, that's so well defined.

Speaker 2:

I love it like what does it mean exactly? So we end up finding these like weird bottlenecks. I mean, a huge one that we haven't talked about at all is garbage collection. So that's a thing. It's like, oh okay, well, are we going to dedicate resources to working on GC for this particular thing, because GC is going to be a bottleneck as well? Just to answer that question, we are not putting any people on GC at the moment.

Speaker 2:

Koichi is working on a per-ractor garbage collector. So the issue just to tell folks what the problem is I was talking earlier about we're finding bottlenecks on global data structures. So if you have any global data structures, of course those multiple threads are going to contend on those data structures. And one thing that is a global data structure is our garbage collector. All Ractors allocate out of the same garbage collector. So you can imagine we get to a point where it's like, oh, we're contending on allocating objects or freeing objects or whatever. So what we would like to have is the GC per Ractor, and Kuiti's been working on that. We're hoping that's going to ship in Ruby 3.5. That'll help a lot. But yeah, we end up with weird offshoots like that. The define method problem we were talking about earlier. We've got a bug to deal with that, the way that we can support that. We're working on something like that.

Speaker 2:

So it seems like if you just looked at the ticket from the outside, it would seem like really weird, like why, why are you doing this? But it's all to support this particular thing. In that case, I don't know if you're listening to, care, I'm gonna say it anyway. Say I'm going to say it anyway. Say it anyway, I'll say it anyway. What we want to do is, when we share a Lambda sharing Lambdas between reactors is a huge pain. It's a huge pain. I feel it's a thing people would want to do.

Speaker 2:

John and I have tried to make Rails Ractor safe. We've tried to do it just as a way to figure out what stuff we need to work on, because a lot of stuff we have to do is discovery. We're trying to eat an elephant here, right. Where do you start? We have no idea. So we're just trying to do discovery. I think the first hiccup we landed on was like oh my goodness, we've got all these lambdas that we can't share. For example, the router has lambdas. Every time you're doing special stuff in the router, it's going to have lambdas. We got all the access support, callbacks, whatever. Those are all lambdas too and they're shared throughout the entire application, so those have to be passed around via Ractors.

Speaker 2:

The plan so far is we can detect when a lambda tries to access an outer environment, so we can detect when it's reading. Okay, we can also detect when it's writing, so we can detect when you do write. So what the plan is is if you do a write to the environment, we're going to raise an exception, so you can't share that. But if you do a read, that's fine. So like if're going to raise an exception and say you can't share that, but if you do a read, that's fine.

Speaker 2:

So if you want to capture an environment, you do a bunch of calculations or whatever and you want to cache that, essentially by passing a lambda around. Good on, you, have fun, it should work fine. But we also need to detect cases where maybe you write to the local variable after you declare the lambda. This would manifest as a race condition essentially if you allowed that. But we can detect that case as well. So we're trying to work on like loosening the restrictions for these things, where it's like, if it's read, only if you're just making a lambda that's just reading data. We're good, no problems. Right now it's very, very restrictive. It's basically like no can't do it.

Speaker 2:

So and that's, of course, a non-starter. I mean, think about like a Sinatra app. The Sinatra app, it's all Lambdas, right. You'd never be able to write a Ractor-based web server for it. It just wouldn't work, just can't do it. So yeah, wouldn't work, just can't do it. So yeah, that's fun, though. I enjoy it it's fun.

Speaker 1:

I have a slight headache just trying to keep it all in my head. But yeah, sure, fun, let's go with that I think it's fun because it's very challenging.

Speaker 2:

It challenges the creativity in my brain because it's like okay, well, we have this code, how do we support it? To me, that feels like a very creative endeavor, because it's like you're forced to think in different ways. How do you solve this problem? So that's why I particularly enjoy this type of work. How do you?

Speaker 1:

balance the work that you do for your day job at your mom and pop shop shopify, as you love to say every time. I've heard you say that about 10,000 times, but every time you say I work at a mom and pop shop Shopify, I'm just like I forgot to say this in my intro.

Speaker 2:

Yes, I work at a mom and pop startup called Shopify.

Speaker 1:

Yes, I don't know why. It just makes me laugh every time, the same way that I laugh when my teenager groans at my dad jokes. It makes me feel good on the inside. How do you balance that work? You just talked about two pretty intense things. At the end of the day, I feel like your brain's going to need a rest. Maybe not because you're Aaron Patterson, because you're also on the Rails core team, so you also do Rails work At your level. Is that work part of your job, or is that completely separate and that is just your hobby? Labor of love why not? It's?

Speaker 2:

also part of my job. I'm on the security team and we have to do security crap. Most of my recent work on Rails has mostly been security stuff, which I hate, by the way. It's terrible. If anybody invites you to be on a security team, just say no, Absolutely do not do it. This is my pro tip for you Don't do it, it's terrible. So that's most of the work and we do that. It's part of my job. I guess I do that during my day job. So also what I'm trying to do is at work. We have, I described, two teams JIT team and Ractor team, but we actually have more teams than that.

Speaker 1:

I was going to say, yeah, you gotta have a team for everything.

Speaker 2:

Really, there's more teams than just the two. Shocking I work at a mom and pop shop, but we do have more than two teams, that's true, and a handful of developers yes, a few, a few. These two teams are on what we call the Ruby infrastructure team and we also have a Rails infrastructure team, which I am not on the Rails infrastructure team. I'm on the Ruby one, but we have a lot of folks working on Rails stuff and on the Rails side of the house and I feel like a lot of my time is especially working with Rails. It isn't necessarily me doing it directly, but working with the other folks on that team to try and level them up, get them contributing and more working on the Rails team. So I see myself as trying to multiply my effort, more managerial. I suppose I start thinking about this. I'm like I almost said a curse word I'm, you can curse, I curse.

Speaker 2:

There's so many episodes I'm like I checked the box explicit content yeah, okay, yeah, yeah, I can't not curse I feel like I'm turning into kind of like a manager or whatever, but it's really just like I want to get more folks leveled up so that we have more contributors coming into the rails team and working on rails itself. I don't know if this is common for developers or just people in general. So I was writing rails apps and I was like, how does rails work? That's how I started like working in rails and contributing to rails and then I was like, okay, this is interesting. Well, how does ruby work? And then I started going into that and I'm like, okay, and then like, keep going deeper and deeper, and that's. I don't know if it's a blessing or a curse, but I feel like if my case is common, if that path is common, then we need to get more people into rails so they can keep funneling down Right, sure, fall further and further down the rabbit hole. Yeah, exactly.

Speaker 1:

Okay, exactly, I guess that would make you the right person to ask. At the last RailsConf, ufuk had people stand up at the various levels of contributions to Rails. Yeah, when we got to like, hey, if you have a commit in Rails. Like when we got to like, hey, if you have a commit in Rails, stand up. I don't know how surprised I was, because there was also a lot of first time to RailsConf folks there, but there were people that I recognized still sitting, which was somewhat surprising. How do you recommend someone get into contributing to Rails Rather than my first one was? I had a problem at work. We solved it. I felt like it should be in Rails Great, that's a pretty straightforward. But if someone's just sitting there going I want to work on Rails, I just don't know how. Yeah, how would you suggest they kind of go about getting themselves into that world?

Speaker 2:

Basically, I suggest the same way I did, which was just start reading the source code. Many times I recommend people oh go, take a look at the docs, you can contribute to the documentation. But I have to admit, like I don't do that, I don't document my code. Are you kidding?

Speaker 1:

okay, but to be fair, it is super important. Good documentation is crazy important and, like, no one wants to do it. That's why it's important, right? If everyone is documenting, it wouldn't be a problem, it wouldn't be that important. But because no one wants to do it.

Speaker 2:

I don't know, man. This is what cloud is for these days. I'm like tell me what this function does. I'm too lazy man. Just like do it for me.

Speaker 1:

Do it for me the amount that I use. I've used AI sparingly because I feel like at least 40% of the time I ask AI a question, it's wrong, and I've only caught it because I know what I'm asking. I'm just asking it to give me a different point of view and it spits out some shit and I'm like, no, that's completely wrong. And I'm like, oh shit, if people start using AI without knowing, it's going to get crazy. I think documentation is still crazy important and also, what else are we going to train the AI on? Of course, if we don't put real human work into stuff, the AI is not going to get trained. I was just this morning.

Speaker 2:

Maybe we'll talk about this a little bit later, but just this morning I was using AI. I was like, okay, if I have a measurement in Lux and I know an ISO, an aperture, can I calculate a shutter speed? It's like, oh, absolutely you can. It spits out a bunch of formulas. I'm like, okay, that's very interesting. Then I start doing the math. I'm like math literally doesn't like it. Your math doesn't math. Yes. I was like, okay, can you plug a few numbers in there and give me an output for it? So it does that. And it's like, oh, these, these numbers don't seem right.

Speaker 1:

I'm like great job but I'm like congrats job, Congrats AI. It's called learning.

Speaker 2:

It's as if I asked a sixth grader to do this.

Speaker 1:

Dude, that is actually a great description. I have nephews who are like four, five and six, and the confidence they have when they are giving me the wrong answer to something is exactly how AI talks to me it's more endearing when it's a child, though.

Speaker 2:

Sure, they're cuter, yes, so back to contributing to Rails. What I like to do, or what I recommend to people who don't want to write documentation, is, though please write documentation. We need it, Please, please, yes, of course, course, so important. Start reading the source code, so in your application you know you're using rails or whatever. Pick a method that you didn't implement, something that's implemented in rails, and go read it and see how it's implemented, and unfortunately, this is like a steep learning curve. But once you start doing that, you'll kind of get an idea of the architecture of the internals, and then, once you can get an idea of the architecture, you can fitting into your brain. It's like, oh okay, well, I see a way, here's a way we can improve this architecture, and that really drives ways to refactor or improve the internals. I don't know it's high effort, but just read a lot.

Speaker 1:

I suppose I feel like that was a big turning point or a big level up for my career is when I got really comfortable reading the Rails source code, not getting afraid to be like I don't know what this is doing, I don't know what the various options are. Opening up Rails code and looking at the method and looking at everything that happens under the hood. I got a lot more confident in my day job. Now I'm reading more code but a lot more confident with Rails. Was able to make a couple of commits to it because of that. So agreed, not just from a hey, I want to commit to Rails, but like just to level up your day job, Knowing how the stuff you're using works is a huge, huge thing.

Speaker 1:

Sure, I'll agree with that one. So the next question on our guardrails question is blockers, and you sort of talked a little bit about things that are blocking especially with the rack. We're only at number two. Okay, yeah, yeah, no. Number two. Okay, yeah, no that's great. I'm you. Tell me when you have to go. I am here for you. Yeah, sorry, we're only on question two. You did sort of answer a little bit with the Ractor stuff. Blockers for Aaron Patterson. What do they look like?

Speaker 2:

Blockers. I think this is funny. We're doing stand-up questions, but we're both sitting down. I'm standing, are you? Oh, okay, well, I'm not For your listeners out there. I am sitting down for the stand-up meeting.

Speaker 1:

Yeah, I modeled it after stand-up, just because I'm like people are generally used to like what are you working on? What blockers do you have? Cool, it's a stand-up Blockers.

Speaker 2:

Blockers. My job is to solve blockers.

Speaker 1:

That's a fun job. What's a recent blocker that someone came to you, say from the Rails infrastructure team, and you were able to get them unblocked. What does that portion of your job look like, unblocking people?

Speaker 2:

One thing that I'm actively working on at the moment. I don't know if you saw Hartley's talk at RailsConf, but it's very good. I recommend it. Go look up his talk. I'm sure it'll be in the show notes. Yeah, I'll put it in the show notes.

Speaker 1:

I'm committing to your show notes. There you go. Yeah, I'll put it in the show notes. I unfortunately did not make it to a lot of talks at RailsConf just because I was doing program committee stuff and running around like a headless chicken. But they're all up on YouTube now, so I'm slowly but surely working through them. Step one was re-watching Aji's keynote and then watching Marco's. So good.

Speaker 2:

So good, so good, yeah, so good, so good, yeah. So Hartley gave a talk about different improvements to Action View and he was like, oh, I have these ideas and we should do this and this and this. And I watched his talk and I was like, yes, we should absolutely do that, you're right. So I'm trying to work with him on how to get him unblocked on implementing these things. So John Hawthorne implemented a pre-compiler for ERB templates and Hartley in his talk was like we need to upstream this and this should come with Rails by default. And I was like, yes, it should please make it.

Speaker 1:

So the wonderful thing about open source if you want it make it, so Make it so.

Speaker 2:

So I am blessed that he is on the Rails infrastructure team so I can ping him on Slack, and I was like I like your idea. What do we need to do? So he's starting to work on that. The first thing is for template caching, like. Another thing he was talking about in his talk was template caching. So we have to detect calls to render in ERBs in order to like figure out what the cache keys are. And we're doing that with basically a pile of regexes, which, as we all know, is like the best way to solve every problem. Sure, Just slap another regex on there, you're good to go. Actually, no one needs to read this shit later. No, and you know what? I recently read a blog post about how you're supposed to add slash O to all of your regexes, because it just makes them faster.

Speaker 1:

Yeah, that's exactly what that blog post. I'll include that blog post in the show notes too. This is going to be very long show notes just all your listeners out there.

Speaker 2:

Just put that O on the end.

Speaker 1:

Slash O at the end it'll be.

Speaker 2:

Everything's faster, so it's all better. But yeah, so he was talking about how to detect these render calls in a more robust way than a pilot reg access, and that's by using a actual ruby parser. So the first step was getting this ruby parser integration in and on by default, and I merged his pr to do that. So in Rails 8.1, we'll be using Prism for detecting render calls for your template cache keys. I asked to keep the pile of regexes around. It's a configurable option, so if you happen to run into any problems, you can like downgrade if you need to, but hopefully there won't be any problems and then we'll just delete the regex-based one later. So those are the things we're working on.

Speaker 1:

Is the work that Marco's doing on Herb and ERB on your radar? Yes, I hear parallels right now.

Speaker 2:

Yeah, yeah, yeah, marco's work is awesome. Love, love his work.

Speaker 1:

Yes, is that one of those? As you're working with some of this Action View stuff, you're considering the things that he's doing and seeing. Maybe this can get plucked and brought in, or we can make Marker's life easier by doing X. Or how does coordination with the community work at a Rails core team level?

Speaker 2:

Sure, I have to admit I didn't think about it until just now. Okay, cool, well, there you go. So I've seen his work and I'm a huge fan of any way we can get language servers and stuff that type of stuff. So I guess, for folks that aren't familiar with his work, he's doing a lot of work on the language server and editor integration. Specifically, If I remember correctly, the talk you gave at RailsConf was about Curb H-E-R-B, which is a language server for ERB, so making ERB better.

Speaker 1:

It's a tool chain. Essentially it's multiple things. It's the LSP, the parser for ERB and HTML. If you're not familiar with what we're talking about right now, go listen to the episode that came out right before this one, which would actually be Marco talking about some of that work. We'll have a link to it in the show now.

Speaker 2:

We'll have a link to it in the show yes. I hadn't thought about that. We should, because I'm a huge fan of his work and I think that type of stuff should just ship with rails out of the box. I'm not sure what that looks like. Booting an lsp server in addition to your rail server is like heavy lift, I think mainly because I don't think we want to maintain that type of complexity, because that's a lot of stuff.

Speaker 1:

Yeah, he has a long list of like. This is what I'd like to get out of Herb. Here's some tools that we can build, because now we have a good parser for HTML with ERB, we have a linter, we have a formatter, we have all these things that build on top of one another and then, oh hey, also kind of compiling erb is a little slow because we're doing it this way. We can do it another way now, and potentially this should be part of rails, where we treat our erb views a little bit more like code because sort of what they are. But yeah, I mean, he's got a lot of interesting ideas and there was a lot of parallels in what you had just said.

Speaker 1:

So I didn't know if, obviously, it's not on your radar, wasn't on? Your radar Sounds like it is now. But from a being on the Rails core team perspective, how does that kind of community I don't know if it's engagement or coordination or what have you how does the Rails core team keep tabs on what the community is doing and things like that? So how do?

Speaker 2:

we keep tabs. The short answer is we don't.

Speaker 1:

It's on the people building things, to kind of bring it to the core team's attention.

Speaker 2:

Yeah, yes, yes. Longer answer is the way that we work on the core team is very independent. There's basically zero coordination among the folks on the core team. So, like I go to conferences and watch talks, that's how I know about these things or things that the team brings to me, but we don't make any sort of coordinated, concerted effort to know about these things. So the best way, if you have a thing you think should be in Rails, best thing for you to do would be to file a pull request or something like that, or, I don't know, speak to one of us directly, like email me or actually don't email me.

Speaker 1:

Don't email you if you are expecting a response or your wife might respond that's a fact? Yes, for sure. So the last question is my favorite. So what is something cool, new or interesting that you've recently learned, discovered, built or building? Anything doesn't have to be coding related, but this is code and the code encoders who code it. So what do you got I'm?

Speaker 2:

working on another hardware project. It's not an analog terminal, but this is code and the code encoders who code it.

Speaker 1:

So what do you got? I'm working on another hardware project.

Speaker 2:

It's not an analog terminal bell. No, it's not an analog terminal bell. That'll be in the show notes too. I love promising things. It'll be in your show notes. This is great.

Speaker 2:

So during the pandemic I'm going to give a little bit of history here. During the pandemic, I bought a camera because I was like I'm going to be doing a lot of meetings at home. I would like to have a high quality camera with which to show my face across the Internet. So I bought a camera and then I was like I should probably learn how to use this as a regular camera. So that's what I did and it turned into a hobby hobby which is very expensive hobby. If anybody is into this hobby, you will know it's quite expensive. And one thing I learned about is that, as a programmer, the problem with any hobby is like I somehow turn it into programming. Of course, yeah, because that's like my actual interest here.

Speaker 2:

Apparently, I shoot my photos all in raw. To be more explicit, I do photography as a hobby. I shoot all my photos in raw, and what that is when you shoot a photo in raw, you're getting the data directly from the camera's sensor, whatever the sensor recorded. That's what you get and that's great and fine. But the problem is sensors are different. Each sensor is different. I have an olympus camera. Even if you buy two olympus cameras, like the data it's not going to be exactly the same.

Speaker 2:

So they calibrate these things at the factories and the calibration is all done in software. So if you shoot in raw, that calibration stuff doesn't apply to the RAW file at all because you're getting truly the data that just came from the sensor. So, like when your camera records red or whatever it's not truly that color you have to have this calibration applied to it. The calibration gets applied to it if you also record the image as JPEG. So the JPEG version gets the calibration stuff applied to it, but it also gets other like whatever they want to do to the jpeg.

Speaker 2:

So of course, me, being the nerd that I am, I'm like okay, well, I need this calibration data. I want this calibration data because then I can apply that to the raw photos, like when I take them out of. Because then I can apply that to the raw photos, like when I take them out of the camera. I can apply that calibration data and get better colors. It's true, you absolutely do get better colors. So what you do is they have these color tester things. It's like a thing with a grid of colors on it, but the colors are known in advance. You take a photo of that and then in software you lay a thing over it and you're like please fit this to these particular colors and since the colors are known in advance, you can come up with the right coefficients to apply. Now I know it's a long yak shave, but here we go. So when they do these calibrations at the factory, they have these color charts, but you have to shine a particular color of light on it.

Speaker 1:

Okay, that makes sense.

Speaker 2:

So, like think about, during the day, like in the evening, the sun is very orange and the light that shines from is very orange, versus during the day, it's quite white. Our eyes perceive white differently. Like we'll look at white, it's not actually white. You'll notice if you've ever mucked with the white balance on your camera. It's like oh, why does this look all weird? In my camera I see white. Why is it blue on here? But they record it at what they call d50, which is 55 000 kelvin, like temperature color temperature yeah yeah.

Speaker 2:

So if you want to reproduce what they do at the factory, you need one of these color checkers. Plus you need a 5000 kelvin light source. So I'm like, okay, well, how do I get 5000 kelvin light source? Maybe you can buy a light that produces 5000 kelvin light. But then it's like are you sure that that's the color that's reaching? Is that actually the color that's at the color checker? Are we sure? Are we sure? So I'm building a color temperature sensor. Of course you are. Yes, that's my current project at the moment. I've already spent way too much on this project.

Speaker 1:

Time or money or both. I've already spent way too much on this project. Time or money, or both Both.

Speaker 2:

I've already bought so far three color temperature sensors, so you can just buy a color temperature sensor. If you try to buy a commercial one or whatever, like a built product, they're very expensive. So even though I've already sunk time and money into this, I'm still saving money. Because your time is useless.

Speaker 1:

Exactly, I don't value my time't value.

Speaker 2:

I don't value my time? I absolutely don't value my time. So that's free, that's all free. We only got the cost of parts here, which is really nice. I've already bought three sensors and, interestingly, like the first two sensors sucked. It was just like bad, and I hate going through data sheets, it's so like just annoying.

Speaker 2:

And it turns out that the first two sensors I bought you have to calibrate it. So basically, you get some readings out of the sensor, you get basically RGB values they're just like some number and then you have to convert those into what they call the CIE spectrum, which is some more nerd shit. And then you take that and you can convert that into the color temperature. What we're looking for, the thing that we're actually trying to get to, but the coefficients that you need to apply to each of these RGB values changes depending on the sensor that you're using, and what sucks is you have to calibrate it in order to find those coefficients. That's the whole point here.

Speaker 2:

I'm trying to build a calibration that I don't want to calibrate shit. I'm building a calibration thing. I don't want to do more. Don't make me do more. So the third one I found it's self-calibrating. I'm like, ah, okay, that's what we need, so I finally found a sensor like that. I got that going. I'm going to build a color temperature meter so that I can calibrate my camera and then, finally, I'll be able to take photos.

Speaker 1:

It's a lot of work to take a photo.

Speaker 2:

I know right, you can't just point that thing and click the button.

Speaker 1:

It's amateur hour, yeah, come on, come on.

Speaker 2:

We got to put more work into this.

Speaker 1:

Way more work.

Speaker 2:

Yes, like I was saying to you earlier, unfortunately this project, as opposed to my other projects, might be useful, which is a bad thing. I need to come up with a more useless project. The thing that sucks about this is I'm going to calibrate the camera once and that's all you need. That's so much worse. Oh my god, just once, but it's still cool. It's like whoa. I think all of us, all of us have wondered what temperature is this light? We're all wondering that constantly, all the time, all the time. Yes, you just want to know. You're like boy. This seems like a cool light. I bet this is like I don't know 3200 kelvin.

Speaker 1:

I'm not even sure if that's the right one I don't remember if the value goes up or down for the temperature?

Speaker 2:

I have no idea. I'm so bad at this. I'd have no idea. I don't know. But yeah, we're building a tool, exactly. Yes, I'm building a tool for this. I don't need to know this. You build a tool for this. It's like I don't remember anything. I don't think or remember anything at all. That's why I always read the source code. It's like well it's, I can just go read the code. Why do I need to?

Speaker 1:

remember shit, a little bit of just-in-time knowledge, right yes, yes, absolutely. I mean, remembering stuff is for losers man I mean humans invented writing for a reason exactly we didn't have to remember, we don't have to remember stuff.

Speaker 2:

Yes, that's why I got a calendar. I love the calendar.

Speaker 1:

If it's not in the calendar, it doesn't exist, not gonna work sometimes, if it's in the calendar, it still doesn't exist, to my brain at least yes, indeed, indeed yes.

Speaker 2:

That's a project I'm working on at the moment. I'm having a really, really great time. I don't know, it's a lot of fun, because it's like oh, I took a photography hobby. How do I get programming involved in this? I don't mean to get programming involved in it's, just that this happens well when all you got's a hammer. You know what I'm saying, sure, yep yeah, everything, every single thing.

Speaker 1:

That happens in programming too, where you're like I found this cool thing you can do in Rails and suddenly every place can get solved by this unknown, obscure Rails concept that's why all of my websites, just web soets all the time, just only WebSockets.

Speaker 2:

That's all it is, that's all you need. Yeah, don't need live updates, I don't even care, but still WebSocket.

Speaker 1:

Yeah, what is HTTP anyway?

Speaker 2:

We got.

Speaker 1:

WebSockets? No idea, amazing. Okay, so out of respect for your time, we'll wrap this shit up. But Out of respect for your time, we'll wrap this shit up, but this was awesome. I was very nervous going into this podcast recording, which I never get nervous for podcast recordings anymore. I used to get very nervous. I still get a little nervous because I'm like I want this to be good and I want the other person to have a good time. But this is.

Speaker 2:

I was talking to Aaron Patterson, who I guilted hard into coming on my show.

Speaker 1:

I have to make it good. This is great. I have to say out loud thank you for everything that you're doing for Ruby, for Rails, have done for us, and for the countless hilarious keynotes that just have been the best part of every RailsConf I've ever gotten to. Thank, you.

Speaker 2:

I really, really appreciate that. It's very kind of you.

Speaker 1:

And thank you for coming on the show, setting time out of your day to just talk about all the cool things. Anytime you're working on something and you just want to talk about it, feel free to come back on Love to have you. Sure, the format doesn't change, it's. What are you working on? What blockers do you have? What's something cool? You got going on and you always seem to have something cool going on.

Speaker 2:

We never have the what's cool going on at our standups. I got to integrate that.

Speaker 1:

Sounds like you need to integrate that. Yeah, bring that back to Shopify and get them working on that, and then let me know if you do, and I'll include that in the show notes too. Nice. So, aaron, for those who don't know, where can people find you on the internet?

Speaker 2:

You can find me on bluesky at tenderlovedev or you can email me. It's aaronpatterson at gmailcom. Oh, and then I got to plug my YouTube channel. What am I?

Speaker 1:

doing? Oh yeah, of course, yeah.

Speaker 2:

I'm so bad at this. It's Tenderlovescoolstuff on YouTube. Yes, come watch me live stream once in a while. I keep a strict schedule.

Speaker 1:

Of occasionally Of occasionally, yes, but always great content and great guests on that one too.

Speaker 2:

So I will include all of that in the show notes, all the show notes you don't even need to listen to this episode, just read the show notes and click all the links.

Speaker 1:

But hopefully you did listen to this episode and you enjoyed it and we will see you, or I will see you, at least, or I'll be in the next one. Aaron won't be, I will be. I don't know how to end a podcast. Can you tell, okay, listeners, I will be. But yeah, I don't know how to end a podcast. Can you tell, okay, listeners, I'll see you in the next one. Bye.

People on this episode

Podcasts we love

Check out these other fine podcasts recommended by us, not an algorithm.

Remote Ruby Artwork

Remote Ruby

Chris Oliver, Andrew Mason
Ruby for All Artwork

Ruby for All

Andrew Mason & Julie J
IndieRails Artwork

IndieRails

Jess Brown & Jeremy Smith
The Bike Shed Artwork

The Bike Shed

thoughtbot
Code with Jason Artwork

Code with Jason

Jason Swett
The Code Gardener Artwork

The Code Gardener

Alan Ridlehoover & Fito von Zastrow