 So my name is Brad Urani, I work at Procore. This is a talk about Object Relational Mappers, ORMs, specifically comparing, contrasting Ecto and Active Record. Ecto is an ORM for Phoenix, which is a web framework for Elixir. So Elixir is pretty a relatively new programming language that was created by this guy, Jose Valem. He was a longtime core member of Rails. He kind of split off and started his own programming language. It's a really neat language. It's very, very, very fast. It's a functional language. It's got kind of a rubiesque syntax, and it's designed for very, very high concurrency. It does that very, very well. And Elixir actually compiles to Erlang byte code. So Erlang was this language. It's made for massively scalable, soft, real-time systems for requirements on high availability. Erlang was invented by this guy, Joe Armstrong, way back in the 80s for telephone systems. So this year, he was working at Ericsson at the time. He wrote software for this telephone switch, which ran a million lines of Erlang with nine nines of availability. So this is the kind of thing. So if you wanted to design a phone system, for instance, to serve the entire continent of Europe, you figured out how to do that with this incredible technology. It's really sort of amazing. It was invented way back in the 80s, but it kind of fell out of popularity when the internet sort of came around in the 90s, and we didn't need that kind of high concurrency, real-time systems, and Perl and Java kind of became the de facto languages of the internet. Erlang kind of was forgotten about a little bit, but it's sort of made a comeback recently as people have realized that there are use cases for that kind of stuff on the internet. And what's really, really neat about it is if you have a bunch of servers running Elixir or Erlang, you can run thousands of processes concurrently, and they can all communicate with each other, and they can communicate across machines. So these little green boxes symbolize processes, right? So imagine this is telecom technology. So imagine you're routing thousands and thousands of phone calls in real-time, and you have this server deployment. They kind of automatically cluster and give you these real-time distributed systems, which is neat. But the thing is, Erlang is sort of like this ugly Ferrari. It's this incredibly fast, neat technology with this like really difficult kind of weird syntax that's not very user-friendly. So along came Jose, and he created Elixir, which is basically runs on Erlang, but with like sort of a Ruby-esque kind of syntax that's real nice and pretty. Really the similarity with Ruby is kind of only skin-deep. It's a functional language, so it's really different how you use it. But if you're used to Ruby, Elixir will look kind of familiar to you. So now it's sort of like a pretty Ferrari, right? So these guys sort of combined, and now they've got this car that's not only fast, but beautiful to look at. And then Phoenix is this MVC framework built with Elixir, a productive web framework that does not compromise speed and maintainability made by this guy, Chris McCord, and it has a lot in common with Rails. So it's MVC, it's got the routes, and the generators you're used to. It's got this package manager called Hex, which kind of takes the best of both Gem and Bundler. It's got this thing called Mix, which is like Rake, and it has an ORM called Ecto, which is what I'm talking about today, which has similar features set to active record queries and migrations and validations. So a lot of people say Phoenix is like, it's sort of like the next evolution of what Rails should be, you know? We'll see that that metaphor holds partially, but not entirely. And it is fast, like I said. So often an order of magnitude faster than Ruby and Rails. D.H.A. says, man, I wish Rails was that fast. He's a race car driver, too. To be fair, it's not really Rails that's low, it's more Ruby that's low, so it's not really his fault, but Elixir or Phoenix are very fast. What would you do with that? Like a real-time communication system would be a good example. And what's kind of interesting is if you build, for instance, a mobile app like this, and you've got Elixir with all these processes and this sort of automatic clustering or whatever, when you send a message from one phone to the other, the message goes server to server. And that's kind of built into the Erlang VM. Contrast that with, for instance, ActionCable, which is this new feature in Rails 5, what happens is the server first gets written to Redis, which has this PubSub, which goes so-called server to Redis and back to server again. That's not what happens in Elixir, it's the server's cluster and the real-time messages go between the servers. So it's a fundamentally different model, a fundamentally faster model. But why stop at simple text messages, right? So using this kind of technology, you could build, for instance, a video chat system, like a video conferencing system you could do an MMORPG real easy for real-time data. The one I have pictured there, that's League of Legends, which is or maybe was the most popular game in the world, it predates Elixir, that their back-end systems are actually written in Erlang. And you can do this real easy, because remember, this is an NBC web framework built on top of telecom technology. So it's like Rails with this more powerful subsystem. But actually, that kind of stuff isn't really what I'm talking about today. I'm talking about ORMs, I really don't have much experience building those real-time systems. So I'm talking about a different part of Phoenix, the design of the object relational mapper, and comparing and contrasting that to active record. This is my own personal journey of ORMs throughout the years. These are the ones that I've developed against. These are a lot of Java and C-sharp ORMs. I landed on active record relatively recently, and Ecto even sooner than that. And actually really looking at this list, the one that stands out is sort of like the one that's different to kind of odd duck is really kind of active record. It has a pretty unique design. But before I get to ORMs, I need to talk really quickly about functional programming. As I said, Elixir is a functional language. I describe it as a stylo program that avoids state and mutable data. I realize that it's not. That doesn't mean like a whole lot, right? And I don't have time to get into a full description of exactly what functional programming is. But if you're coming from Ruby, probably the things that will surprise you most are that Elixir has no objects, right? So like Ruby has modules and classes. Elixir only has modules, which also means there are no methods, right? It has functions that you can call, but it doesn't have that like sort of like methods and objects like in methods and data, like in the same class, that encapsulation. It's not object-oriented. It's functional and no mutations. So that means like in Ruby, you can have like a hash and you could change one of the values. You can't do that in Elixir. If you have like a kind of a hash, it would be a struct, but if you have a struct and you kind of change one, you change a value, you get back a new struct every time you do that operation. And if you have a reference to the old one, it's still there. It uses a mutable persistent data structures. So it's fundamentally different under the hood the way it works, which means you have to write it in a different way, even though the syntax is similar. But let me jump first into Active Record before I get to Acto. This is all gonna be sort of be, you've probably all seen this before, if you've viewed Active Record. So it's a bit rehashing, but I think it's important to sort of start at the basics here just so that when we get to Acto, it's a conversation not just about how and how you do things different, but why. Like what is the philosophy behind these designs? How do the like top level architectural designs of Active Record and Acto differ really? So I made a little demo app. It's called Hallway Track. As you can see, in addition to being an engineer, I'm also a graphic designer. But the joke is that so you go to a conference right and they have multiple tracks, like they may have the database track, the front end track, the Ruby track. The hallway track is kind of a joke. That's like the people you meet in the halls. So like, hey, what's your favorite part of the conference? Oh, it was the hallway track. So this little app is like, so people can like sort of self organize in the hallways and get together with like what they're interested in. So you set up like a little meeting and it shows like here are the people who are going to the Phoenix Fanatic one. It's meeting in the lunch room at three o'clock there. So what we've got here is we've got a conference, right? Which is RailsConf, which has a party, which is like Phoenix Fanatics, which has users. So my database looks like this. Conference has many parties, which has many users. So like we start up setting up active records. So here's our party class. We have a scope for conference. We've got a scope for starting after. We've got a scope for ending after. So each of those are like little reusable bits of queries, right? That's kind of what a scope is. It's kind of a reusable bit of a query. So then I might combine those together into the scope for conference and time. And I'm changing these together. So here is one of ActiveRecord's strong suits. It's one of these really nice kind of English style syntax. For conference.startingafter.endingbefore. It reads real nicely. That's sort of a nice feature for readability and stuff. ActiveRecord, it looks nice. Donna, I've got HasMany users and I've got this scope for active users. And then on the user I've got this active scope. So there's another little bit. And now I call this the controller for conference and time. So standard Ruby stuff here, standard Rails. And then in my view I do this, parties.each. And for each party I do parties.activeusers. And I display that list of parties and I display the users for each one. And kaboom! Oh, what happened? Oh man, that does not look right, does it? So this is the n plus one problem. If you've done enough ActiveRecord you've probably run into this. And the reason it does that is because, well, I start with these parties and for each one I'm calling .activeusers and what is that? That's an ActiveRecord relation, right? We all know that if we use ActiveRecord. It's an ActiveRecord relation and that thing has not been preloaded so it's running a query each time it does that. So how do you fix that? Well, okay, in my scope for parties, topic and time I have to add this preload in there. That is kind of, all of a sudden my English looking my scopes, I've kind of added this like only kind of preload thing in, right? So it's kind of polluted my nice English looking syntax with something a little more computer-y. I could move it, I guess. I could take it out of the model and put it in the controller but realize here what I've done is I had a parties model, right? I had a scope Active on users and now I've got this preload here on controller. I've got my query spread out across three files, don't I? It's only one SQL query but it's spread out across three files. Now this does look pretty nice but that's a little, that can be tough to track if you're like looking for these queries in places. It's a trade-off. It's a trade-off in, it's hard to find the SQL in here but it reads nice and reads like English and it's very convenient. It's very fast, easy rapid to develop. I get the queries I want which is good but what happened here, right? From like sort of a higher level why did this happen? ORMs are kind of this leaky abstraction. So if like SQL's a dog, right? I want to treat a dog nicely. I want to like treat and feed a dog food, take it outside, right? But Ruby is sort of like this cat and object-oriented programming is sort of like this cat and you wish you could just treat it like a cat and feed a cat food and set out a litter box but you know there's this dog underneath and you can't forget that there's a dog so like to write good active records, to write good Rails, you can't forget about the SQL. You have to know it's there and you kind of have to know SQL and you kind of have to do both so you can't just use active record naively, right? You have to remember that there's a dog under that cat and that's called a leaky abstraction, that's the term of that because you can't just use the abstraction without also understanding how it works which is why our active record are nice like English-looking syntax starts to kind of, starts to kind of get a little uglier when we try to make the SQL right, you know? So concerning object-oriented programming, Joe Armstrong said about objects, you wanted a banana but you got a gorilla holding the banana so we're running this query right and we're getting data, data's the banana. What we got is this active record model which is an object and it's got all this like behavior mixed in. That's kind of the gorilla, you know? And that can be difficult to reason about sometimes when you start passing these models all around, all over the place and you've lost track of the queries and you've got this thing that's a gorilla holding a banana and you really want the banana and you end up calling the gorilla, you know? So it's interesting. So as I said, we got the queries here. You could use a join instead and make it into one query but there's another performance problem associated with that. We fixed our M plus one, didn't we? But I think there's still a performance problem here and that is, in my opinion, well, the select star because, you know, normally in these tables we've got a lot of stuff on these tables including our time stamps, for instance, which I'm not even displaying on the page but I'm pulling them back anyway, aren't I? Even I'm pulling back a lot of stuff that I don't need and active record kind of encourages this. I call this problem seeing stars, right? Seeing stars is this indiscriminate use of ORMs that's where it's always select star and we can fix this, you know, by adding a dot select but then you've got that ugly active record stuff with like the strings because we have to alias our tables and all that stuff. It's easy to fix but it's also easy to forget about, you know what I mean? It's a pretty common problem I've seen in a lot of big Rails products is just select star everywhere because people don't take the time to do selects and if you realize also, part of those scopes are supposed to be reusable, aren't they? But once you start reusing scopes, each scope, each time you reuse it, each time you reuse it, might have different columns selected so you're reusing a scope from one model but you're tacking on different select statements and other parts and that all of a sudden also becomes real, hard to keep track of like what's querying what because we've got a query spread out across so many files. So just to sum it up, like sort of the Rails active record philosophy here is that it kind of favors English over SQL, right? We have extracted away the SQL which is good for readability, good English, Rails active record reads really beautifully. We've got objects, not data, we've got gorillas with bananas, domain models over relational models. So domain model, like it just means like we've got a class that represents something in life. So I had a party table, like a party is a real thing, a user is a real thing, a conference is a real thing and our classes match those. Our classes match like real world entities, things that we actually have to think about in real life versus a relational model which is tables, which is specific to database, which is a more computer oriented thing, less of a real world thing. Rails is big on productivity. If you've been in the Rails community long enough, you've heard about the 15 minute blog, right? It allows you to develop things really, really, really fast, which is if that's what you need, then that's a great feature, it's awesome. It's tough on scalability because it's so easy to do those N plus ones and forget because it's so easy to forget about the select. There are a few other performance related things that I'll talk about later. I kind of do favors developers over DevOps. Why do I say that? Well, because a lot of times like your DevOps or your DBAs are looking at the query log and like, oh wow, look at last night, this was the slowest query that ran. You know, look at this giant thing. Where's that query? And the developers are like, oh well, let's split across 12 scopes over four different models. Kind of all chained together, you know? It makes it hard if you just want to find the slow query, right? Find that query. It's kind of tough to track it down sometimes. And SQL and Rails are they really friends? I don't know, they're kind of frenemies for that reason, I think, just because it is hard to think in SQL and also think in Rails, you know? So this is what I see at scale when you get huge ActiveRecord applications with a lot of people using amazing plus ones, the seeing stars problem, app level constraints. Those are things like unique constraints in the model which is this weird kind of anti-feature, I think, that it's not safe against race conditions. Transaction management, I see, you know, the way in ActiveRecord you can just like take a model and like dot save kind of everywhere you want. You know, it seems like people forget transactions a lot. It's lazy, right? You string this query together and you pass something off. You can pass it all the way to the view before you start looping through it before it actually runs the query. So it's hard to keep track of where the actual query is run. We're Java champion forcefully protecting programs from the cells. Ruby included a hanging rope in the welcome kit. This is DHH. And that's kind of true in Rails of ActiveRecord too. It allows you to get things done fast. But to its credit, great English-like readable DSLs. It's conceptually simple. It's comfortable for beginners. You know, it's kind of, if you're not really used to SQL, it's really easy to get started. So it's got these big thumbs up you know, it's got its benefits too. And overall, I'd say ActiveRecord, this is my opinion kind of, but convenience over explicitness and performance. So it's real fast and easy and convenient to create, to kind of chain these scopes together. But that explicitness that like, here's what's actually happening. Here's that SQL that's running is lost a little bit. And it's easy to make performance mistakes. Explicitness, by the way, ding, ding, ding, that's our word of the day. I'm going to be saying this a lot today. So look out for this word. So let's move on to Ecto now. So Phoenix is not Rails, as Chris record. And Leah Chalade says, Rails or Phoenix or Web Perks to share a common goal. So they are and they aren't the same thing. They're very, very similar. If you do like, if you're doing like sort of like a crud app kind of thing, they are very, very similar. But then Ecto has, or Phoenix has this like superpower of real time communication. So if you're doing that, then they're very different, of course. But the way that Ecto works is it's a lot, it's a little different. So there's not just a simple model. There's a model file, but there's not a model class. It actually has four things. A repo, a schema, a change set, and a query. So already it's conceptually a little more. There's more to think of, right? So here, for instance, is the first thing is the schema, right? So in the model file for party, it's got a model file, but not a model class. So first of all, you have to define the schema. We know that in active record, you don't define the, you don't pre-define the fields, right? It's all dynamic. In Ecto, you actually have to define them. But you can see it's got has many and belongs to in the same kind of way, but it's explicit, right? So there's that word, explicit, because we're telling Ecto what fields we have, which allows us to do some, it turns out to be convenient. It's a little more boilerplate, but it turns out to be convenient. Here's our controller. So that first line is kind of weird. So for our index action, the connection, that has the request and response and stuff. It's passed in, it's not global, right? There again is that expliciteness. That little funky thing with the brackets in the first line there. That's like a, it's a pattern matching. It's kind of destructuring, getting the conference ID out of the request hash. And then we have repo.all. So repo is this module that represents our database, and we're passing party.forConference, which is what gets our query. So it's kind of two pieces. So we've got a repo that represents our database, and then we're party.forConference returns the query, and so then they get run, and then we're explicitly calling index.html and passing it the parties. So it looks kind of like Rails, but a little more explicit. And we've got kind of two parts to our query instead of one. We've got a repo, and we've got this for conference, which returns a query. If you're big on like design patterns and stuff like this, this is called the repository pattern. It's Martin Fowler to find this in his, one of these, in his book here. Versus the active record pattern, an object that wraps a row in a database table or view. So active record pattern, like the original definition, kind of describes like the instance methods on active record, but active record, the Rails thing, is actually sort of like a melange of like, like the class methods are more like repository, where the instance methods are more like active record, and they're hints of datamapper, and other things mixed in there. So well, active record is sort of this like meta pattern that's kind of grown over time to encompass more and more. The patterns in Ecto are a little more finite. I actually was lucky enough to be on the RubyRogues podcast, and Avdi Grimm was on there, and we were talking about this, and someone said, hey, will someone define active record in repository? And I went and defined him, and I got it totally wrong. He was like, actually, Brad, that's the opposite of what they mean. So this is, so if Avdi's in here, this is for you, I wanted to show you that I got this right. But what's also interesting about Phoenix is that, well, it has no save method, and so you can't just like query something, change a field, and hit save, and like save it back. I'm gonna get to writing in a second, but, and it doesn't return objects, it returns structs. So it's returning bananas, not gorillas. It's just returning data in this sort of struct that you throw around that doesn't have methods hanging off of it, and then it's immutable. Those structs come back, they're immutable. You can't change them. Once they're there, they're there, which means you have to design your programs a lot differently. Interestingly, it returns structs, not objects, which means it's not an object relational map, or I'm a little bit embarrassed to say I didn't quite figure that out until after I had proposed this talk. The title is a tale of two ROMs. One's not actually an R.M., whoops. But that's okay. They serve the same function. Okay, so here in the model, I need to put together a query, right? So here's my query for conference. I pass in a conference ID, and I've got this cool syntax from party and party, so I'm getting a little alias there, where party.conference ID, and then also check out the select. Select party.name, party.startTime, party.nTime. What's kind of cool is if you get in your mind, well, for one thing, this really looks like SQL, doesn't it? The select's on the bottom instead of the top, but it looks really SQL-esque, doesn't it? It's almost like SQL written in Elixir, which is kind of cool. And then also the select syntax is really nice, much prettier than the Rails one, and if you get in the habit of always putting that in like you're writing a query, you're not going to forget it as easily and have the seeing stars problem, are you? If we want to preload users, we can, just like that. It's still pretty SQL-esque looking. If we want to group by, I thought this was kind of cool. We have that group by. Oh, that should be user ID, sorry. But then select party.id and count of user.id. I thought that was kind of cool, real SQL-looking. Maps real nice. Does anyone know where they got this syntax from, actually this idea of this kind of like SQL-looking syntax inside the code? It actually came from csharp.net, .net MVC. It's a rare case of like an open source world taking a cool idea from Microsoft, right, instead of the other way around, instead of vice versa. Yeah, it feels like, yeah. Yeah, nice dance moves, Bill. Where'd you learn that? Yeah, we're still mad about 86, you know. So in the end, I take all these queries and I sort of put them together all in the same file, realize those are the ones I just showed you, but you know, there they are, and it's kind of like a bunch of SQL queries. Now, you can write this in a way that kind of composes them and reuses them, but it's not very dry, because they all three have the same wear condition. In an active record, you would make a scope and you'd reuse it, it'd be more dry. Here we're repeating more stuff, and this is a little bit more of my own opinion than something forced on you by ECTO, but there is some merit to that, right, because it's real easy to track down those queries. It's real easy to find them when you look in your query log, and oh, here's the one that locked to this table last night. It's real easy to go back and look at them. It's real easy to imagine this, to go from ECTO back to SQL and back again without sort of getting lost in all these model chaining, all these scopes together. If I ran this in the, Phoenix has a console like Rails, so if I run this in the Phoenix console, I get parties, I get all the parties, right, and then I get the first one, notice list.first of parties, that's where functions, not methods, right? It's not parties.first, it's list.first.parties, it's a function, and I do first party.users. I get this association not loaded. In active record, this would be an active record relation, and it would actually load it, wouldn't it? It would actually run another query you can't do that in ECTO, you can't do it, that's a null object that, for one thing, this is immutable, so it can't just load the new stuff in, it can't run another query and load stuff into that object because it's an immutable object anyway, but this solves the N plus one problem, you cannot do it, it's impossible to write an N plus one in ECTO. Now that means you have to sort of go back to the model error and think ahead and write your preload back down there, but you know, which is a little more boilerplate and a little less dry, but that might be a good thing. It's eager too, it's not lazy, so all the querying is actually done in the model error, there's not that sort of laziness where you're passing this active record thing all the way up to the view, or God forbid, the view helper, and then running it in a loop and then firing off a query. I've seen big Rails projects where it's like, okay, I'm gonna look at the query as this request runs, oh my gosh, there's one in the model, one in the controller, one in the view, and one in the helper, oh boy. This sort of forces it all to be at the model error, and you're forced to think ahead of all your preloads and stuff, it's not gonna like randomly fire one off in the view. So now on to writing, right? When you write, whether that's an update or an insert, we've got these things in Ecto called change sets. So here's a change set like for creating a new users, right? I know that I define this change set, I'm gonna, it's gonna take the name, the email address, and the age. Cool, so that might be like your form that, you know, your signup form, for instance. Okay, and this is what it looks like, that funky little arrow thingy that's called a pipeline operator, it's kind of like a Linux pipe, it's an Elixir thing, but look closely at this. First of all, there's actually explicitly casting, right? Rails automatically casts, Ecto explicitly casts, and it's got required and optional params. We explicitly do the validations right here on this method, and then we tell Ecto that there's a unique constraint on email. So that's one change set for your signup form. If we want one for like update, say you just update someone's email, we have to make another change set, we don't share the change set. So the top one is an insert operation, the second one is an update operation, two completely separate change sets. So contrast that, for instance, with an active model, an active record model, you know, you can kind of just like freeform it, like set a property and hit.save or set a couple properties and hit.save. You know, you can't do that here, it's like you have to explicitly define each write operation. This one's an update and it looks like this, it has these validations, this one's an insert, it looks like this, it has these validations, which is more explicit. There's that word again, right? Ding, ding, ding, explicit. And also you can allow these to have separate validations, so you don't end up with a lot of like convoluted if statements on your validations and stuff. A little more boilerplate to set all this up, a little more work, but increased explicitness, increased flexibility in having multiple different sets of validations and things like that. So definitely a different approach. And it has this cool concept called a multi, where a multi is basically a bunch of inserts or updates. So here we've got three, I've got a topic insert, a party insert, an update conference, and then you kind of wrap these in this transaction and you can get the, you can pattern match on the result, okay, or error, and handle the result as a whole, right? This is kind of cool because if you get in the habit of using these, so like every time I have a post, I'm gonna do my writes in a multi, then you never forget to wrap that in that transaction. Very common mistake I see in big Rails apps is the saves are kind of scattered all over the place and it's real hard to sort of wrap them all in one transaction or people just forget to wrap them in a transaction. This kind of helps you with that, and it's a little cleaner too, in Rails you'd have to do like, if topic.save and party.save and conference.save, here you just kind of create this multi and you save it and you get either an okay or an error. So you're thinking transactions now, instead of models and domain models, right? You're thinking in terms of, this is a single database transaction. So yeah, separate validations, a little more explicit, better security also, we don't have to deal with, we don't have to worry about the problem of strong params. There's less text passing around, so less opportunity for SQL injection because it's got a better query syntax really. And again, explicitness. Functional programming is about making the complex part of your program explicit. In most web apps, the complex part is a lot of this database querying and how you're sort of translating from that user input and shaping the data and getting it into the program or how you're taking that relational data, querying it, mixing and matching, turning it around, reshaping it and passing it out to the view. So Elixir makes that a little more explicit, a little easier to track down to figure what's going on, like at a slightly lower level at not as high of an abstraction level. So also, if you're big into big terms, Ecto follows the command query responsibility segregation principle, which is that read and update use two different models, and we saw that. The queries I had had joins in them, right? But the updates have these explicit change sets. It's not like one model for reading and writing. It's a separate thing, a query for reading and a change set for writing. So that's conceptually interesting, but there's also kind of like, right at the database level, kind of a real compelling reason to do that, think about a query, you know, in anything non-trivial, a query is going to have joins. A query, most queries like in any, in big complex apps have a lot of joins, and when you get that, you get back a certain set of columns that does not match what's actually on the table. You're getting back columns from multiple tables. So your queries have a certain set of columns based on what joins they have, but your writes don't have joins, do they? The writes always write only to a single table at a time, an insert. An insert only inserts into a single table. An update only updates a single table, but your reads read from multiple tables, yet, for instance, in Rails, they're sharing the same model. The fact, even though they're pulling back different data, different sets of properties, they're sharing the same model, we have that responsibility segregation here in Ecto, so they read an update using totally different paradigms, totally different sets of columns, totally different, well, you know what I'm trying to say. So, just to kind of like sum up, Rails has objects, Phoenix has data, right? It's gorillas of bananas versus bananas. Rails has methods, whereas Phoenix has functions, and then Rails stuff is mutable. You can pull back an object, change a property, pass it around, do whatever you want with it. Phoenix is immutable, once you query that data, you've got that struct, it doesn't change. On the SQL level, or I guess at the ORM level, Phoenix or Ecto, right? It favors SQL style syntax over English style syntax. It looks more like SQL, it feels more like a relational database. Overseeing stars, I won't say it solves that problem, it definitely doesn't, but I feel like with that kind of cool select syntax, and the way it kind of looks like SQL, you have that query, I feel like it makes it a little easier to remember, not to just indiscriminately select star every time, which, if you're not at scale, it may not matter, but at big scale, when you've got dozens of web servers firing on a single database server, the database server becomes your bottleneck, and those select stars are a performance killer. Rails is lazy, Ecto is eager, all those queries kind of fire when you'd expect them to. You know, we're not passing these lazy objects off to the views where the SQL then gets fired from the view. Rails is dispersed, the query gets spread around across scopes, across multiple files, where in Ecto it's more confined. It's all in that model layer. So what's the downside of this? Well, it is more boilerplate, we have to set up all those change sets, we have to define our schema, we have to create those multis and stuff. There's more typing involved. You know, you're gonna end up with more code to kind of set that stuff up. It's a little bit more complex of a mental model because in the world of ActiveRecord, we've got domain models, oh, I've got a user, that's a real life thing, I've got a user class, an ICE connection there to think about where is, this is more complex, that command query responsibility segregation means I've got to think about this differently when I read than I do when I write. It's less dry, we've got more repeated code because we're reusing it less. A little less readable, oh, that depends what you're used to. If you want code that looks like English, if that's what you're used to and that's how you like to code, then it probably will be less readable. If you're used to SQL and writing queries, then you may have the opposite experience. It may be a little more readable. It's kind of like robot talk, instead of users and conferences, we've got change sets and schemas and queries. But that might be more natural if you're used to talking in computer terms. I like talking in SQL, for instance, my wife's like, Brad, will you do the dishes? I'm like select dishes from sync, insert into dishwashers. She's like, what are you saying, Brad? I'm like, oh, sorry, sweetie, I was talking SQL. She's like, you are so weird. But then, right, so we get, I guess the trade-off here is sort of the opposite. We've got like, it's explicitness, there's that word, right? It's explicit what we're doing. Performance, there are, I won't say it's better performance, I'm saying there are few caveats, right? Fewer pitfalls that you can fall into. You still have to be mindful, of course, but there are fewer pitfalls you can fall into for performance, you can't do M plus ones, you know? Over-convenience, it's not quite as fast to develop. I wouldn't want to do the 15-minute blog in Phoenix, but you know, I bet you could do it at 30, you know? So it's just a different set of trade-offs. And that's really what this is about, is that neither of them are better or worse. It's just a different set of trade-offs you get with these different designs. Also part of the act of philosophy is just to sort of like, let the database do what it's good at, you know? It adds constraints by default. In your migration, if you set up a foreign key with references, you get that constraint by default, you know? It does not have polymorphic associations like Rails, which if you come from the world of SQL, like I did before getting to ActiveRecord, yeah, that is just so weird. I don't know where that pattern came from. It does not have these app-level unique validations, you know, like so active. If you want a unique validation, you put it on the database. It doesn't have that application-level one where you can do unique validation in Rails. That one is weird. It runs a select query first before doing the insert. It's not safe for race conditions. So you can't do that. And it's kind of clever, like Ecto has some neat ways. The downside of relying on the database is that the error message is uglier, but Ecto has some neat ways around that. I don't know if you remember, but in the change set, I defined unique constraint on email. When it gets like a unique constraint failure, it says, oh, which one is the unique column? Oh, it's email. Let me generate a nice error message for you. So it's really clever. And then there's the testing store, you know? I don't have any slides about the testing code, but believe me, the testing ends up being easier when you've got this more explicit model, when you're passing just data around between your functions. You tend to do less mocking because your business logic is not sort of like mixed into the model. You're not sort of like blending together business logic. So like say you had this financial app, you might have like Calculate, Interest Rate or something. That's not in the model. It's a function and you're taking the data, you're running the query, taking that data, passing it into that function, getting a result back. Well, that function then becomes really easy to unit test. It's a pure function. That's one of the benefits of functional programming. So just sort of the way you're forced into this paradigm, a slightly stricter paradigm with Ecto, you end up with more pure functions. You end up with less stubs and mocks, which is really nice. Also because this runs on Erlang VM, the test runs are massively concurrent. You fire off like the test suite and it starts up like 30 at a time. Like in parallel, which is really awesome. It does have a cool, cool more features. Like so JSON B, that's that thing in Postgres where you could save JSON in Postgres and query against it. It's got support for that in a real slick way. And they say there's a Mongo adapter coming, which is kind of cool. You can also take Ecto and like supply your own adapter for it. So someone like, someone found a way to write an adapter that hits the GitHub API. So it's Ecto, but instead of reading from a database, it's hitting the GitHub API and you get that same SQL type query. That's kind of cool. If you wanted to like swap out a database implementation with an API implementation or vice versa, you get that same, you don't have to change any of your app level code, which is kind of cool. That's kind of neat. But you know, in the end of this, realize that ORMs are a choice that you get trade offs with each one and one size does not fit all. So obviously I think I've shown, this is completely unbiased, right? I see these frameworks as, no, I like to act away, but that may not be for you and that's okay. If you value the kind of convenience that it gives, then this will be it. And also realize you have a choice, even if you're stuck with Ruby, you know, there's another gem out there called SQL, which has some similarities with Active Record, but with also a different set of trade offs. You know, it doesn't have the N plus, I don't think it has the N plus one thing. You know, so it's, you can swap this out in your Rails app. Use the ORM of your choice. Trailblazers, another one, which it actually uses Active Record, but it's got like a application architecture on top of that, including something that looks a lot like Ecto's change sense. So, you know, again, different trade offs if you want to use this. You can also use Active Record in a way that looks more like, you know, some of these other design patterns, more like Ecto if you want to, maybe by adding a service object or something, or you may just use Active Record as it is. Just realize that, you know, just because you're using Rails doesn't mean you're forced into the Active Record way necessarily. So a few resources here, a couple great articles. This Bikeshed podcast with Jose Valem is awesome. That's really, really good. If you're interested more in immutability and those immutable data structures, those persistent data structures, this is changing the Unchangeable. That's a talk I gave at RubyConf, which explains not just why, but how those things work. And then if you're interested more in services and how you can change the way you use Active Record, I also gave a talk there at Amitabh and Santa Barbara, which is, and there's a link to that one for you. If you're more interested in Phoenix, check out Friday, Brian Cardella. He's the CEO of Dockyard. He's giving a talk just on Phoenix. I haven't seen it yet, but I'm betting it's pretty good. So check out that one on Friday. Who am I? My name is Brad Yorani. I tweet at Brad Yorani. Follow me, I follow you back. I'm really a Twitter addict. Connect with me on LinkedIn. I've got this blog. I don't update it very much. I've worked at Santa Barbara at Procore. Procore makes construction management software and it's an incredible place to work. This is the view from our office. You can whale watch while you program. It is one of the coolest places to work. It's the coolest place I've ever worked. I moved all the way from St. Louis, Missouri to Santa Barbara just to work there. We're hiring crazy Rails architects. JavaScript front end, which is React and Redux. Pretty much every cool thing you could think of. I'd love to talk to you about that if you're interested or they're also about 15 of us here, mostly wearing Procore gear. So come and talk to any of us. Finally, HallwayTrack the app is live if you would like to use it, right? To get together with your fellow programmers here and set up a little HallwayTrack meeting. It's really raw, but it does work. And you do have to wait for that Heroku spin up time when you load it the first time, so be patient. But it is live and it does work. So any questions? Oh, you mean the devil incarnate? Yeah, those don't exist in Ecto. He asked about callbacks. Do callbacks exist in Ecto? And no, I don't think they do. If they did, I didn't go looking for them. But there are other ways to solve that problem, right? So you might put like a service layer or do it in the controller where you kind of run everything in that. I showed you that multi where you're kind of doing all the writes in one multi. You just use the controller or a service layer to kind of put stuff in a multi and run it all at once. Oh, cool. Thank you very much.