We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

ActiveRecord vs. Ecto: A Tale of Two ORMs

00:00

Formale Metadaten

Titel
ActiveRecord vs. Ecto: A Tale of Two ORMs
Serientitel
Teil
57
Anzahl der Teile
89
Autor
Lizenz
CC-Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Unported:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
They bridge your application and your database. They're object-relational mappers, and no two are alike. Join us as we compare ActiveRecord from Rails with Ecto from Phoenix, a web framework for Elixir. Comparing the same app implemented in both, we'll see why even with two different web frameworks in two different programming languages, it's the differing ORM designs that most affect the result. This tale of compromises and tradeoffs, where no abstraction is perfect, will teach you how to pick the right ORM for your next project, and how to make the best of the one you already use.
81
CASE <Informatik>Erlang-VerteilungReelle ZahlSoftwareQuick-SortProgrammierspracheAppletVerfügbarkeitSystemprogrammierungServerObjektorientierte ProgrammierungSystemaufrufEchtzeitsystemQuaderDatenparallelitätInternetworkingTelekommunikationFramework <Informatik>SpeicherabzugGanze FunktionBenutzerbeteiligungFormale SpracheMultiplikationsoperatorVirtuelle MaschineProzess <Informatik>Relationale DatenbankByte-CodeGeradeFunktionale ProgrammierungSkalierbarkeitObjektrelationale AbbildungSchreiben <Datenverarbeitung>GruppenoperationArmstrong, JoeCoprozessorNormalvektorDatensatz
Erlang-VerteilungFramework <Informatik>Funktionale ProgrammierungSechseckProdukt <Mathematik>ValiditätBenutzerbeteiligungGrößenordnungDatensatzRechter WinkelKundendatenbankMigration <Informatik>CodeSystemaufrufBitrateQuick-SortSoftwarewartungEvoluteTeilbarkeitÄhnlichkeitsgeometrieGenerator <Informatik>RoutingReelle ZahlAbfrageComputeranimation
Mailing-ListeEinfügungsdämpfungErlang-VerteilungUmsetzung <Informatik>Objektrelationale AbbildungEchtzeitsystemTelekommunikationBitSystemprogrammierungKlasse <Mathematik>StandardabweichungAggregatzustandServerProgrammierungBrennen <Datenverarbeitung>MultiplikationsoperatorSpieltheorieDatensatzDifferenteFront-End <Software>Vollständiger VerbandDatenbankMessage-PassingQuick-SortSelbst organisierendes SystemApp <Programm>GamecontrollerFramework <Informatik>Cluster <Rechnernetz>Reelle ZahlBenutzerbeteiligungVideokonferenzAppletWeg <Topologie>AbfrageMehrrechnersystemKommunikationsdesignEinsMereologieSuite <Programmpaket>Prozess <Informatik>KommunikationssystemMathematikEndliche ModelltheorieFunktionale ProgrammierungGebäude <Mathematik>AdditionModulare ProgrammierungKontrast <Statistik>Deskriptive StatistikMetropolitan area networkSichtenkonzeptObjektorientierte ProgrammierungBildgebendes VerfahrenArithmetisches MittelRechter WinkelEindeutigkeitSoundverarbeitungEin-AusgabeTexteditorNeuroinformatikResultanteCOMDatenstrukturListenprogrammgeneratorBildschirmmaskeRegulator <Mathematik>InstantiierungTeilbarkeitWeb ServicesSchreib-Lese-KopfDemo <Programm>Strukturierte ProgrammierungHash-AlgorithmusComputerarchitekturPuls <Technik>GeheimnisprinzipGruppenoperationDatensichtgerätComputeranimation
AggregatzustandAbfrageEndliche ModelltheorieElektronische PublikationRelationale DatenbankTrennschärfe <Statistik>EindeutigkeitAbstraktionsebeneRechter WinkelArmstrong, JoeMultiplikationMAPMultiplikationsoperatorSoftwareentwicklerMereologieHeegaard-ZerlegungDatensatzLoginTabelleStreaming <Kommunikationstechnik>TypentheorieGesetz <Physik>StandardabweichungInstantiierungWeb-SeiteEinsHyperbelverfahrenBestimmtheitsmaßSkalierbarkeitComputerunterstützte ÜbersetzungFortsetzung <Mathematik>p-BlockReelle ZahlProdukt <Mathematik>Weg <Topologie>QuaderMathematikObjektorientierte ProgrammierungComputerspielThumbnailKlasse <Mathematik>ProgrammierungForcingDomain <Netzwerk>Mapping <Computergraphik>Zellularer AutomatGüte der AnpassungProzess <Informatik>Brennen <Datenverarbeitung>Kartesische KoordinatenSichtenkonzeptDifferenteDatenbankMessage-PassingZeitstempelKundendatenbankTransaktionKonditionszahlDifferenzkernGrößenordnungBefehl <Informatik>Objektrelationale AbbildungQuick-SortMixed RealitySoftware Development KitTermAppletComputeranimation
Wort <Informatik>SichtenkonzeptDifferenteGruppenoperationEndliche ModelltheoriePoisson-KlammerPerkolationstheorieTabelleMeta-TagMereologieQuick-SortExogene VariableObjektrelationale AbbildungDatenbankDatensatzMinimumObjektorientierte ProgrammierungMusterspracheRepository <Informatik>ProgrammierungAutomatische IndexierungModulare ProgrammierungMicrosoft dot netAbfrageTrennschärfe <Statistik>GeradeDokumentenserverInstantiierungAliasingGamecontrollerEntwurfsmusterReelle ZahlKlasse <Mathematik>MathematikMapping <Computergraphik>MustervergleichApp <Programm>DatenfeldEinsBenutzerbeteiligungHash-AlgorithmusFigurierte ZahlMultiplikationsoperatorZählenEchtzeitsystemBitElektronische PublikationLogische ProgrammierungRechter WinkelWeb-SeiteTelekommunikationSpeicherabzugCluster <Rechnernetz>RuhmasseFortsetzung <Mathematik>GarbentheorieEinfach zusammenhängender RaumTextbausteinTermStrukturierte ProgrammierungArithmetisches MittelVerkehrsinformationDifferenzkernBesprechung/InterviewComputeranimation
Kategorie <Mathematik>AbfrageEinfügungsdämpfungLesen <Datenverarbeitung>Endliche ModelltheorieLoopExogene VariableTabelleElektronische PublikationDifferenteSichtenkonzeptGrundsätze ordnungsmäßiger DatenverarbeitungMAPLastDatensatzSchnittmengeObjektorientierte ProgrammierungQuick-SortEinsKontrast <Statistik>Figurierte ZahlMultiplikationAssoziativgesetzTextbausteinMathematikProgrammierungMailing-ListeMereologieGemeinsamer SpeicherSpielkonsoleZeitrichtungInstantiierungRechter WinkelReelle ZahlComputersicherheitBildschirmmaskeGrenzschichtablösungEinfache GenauigkeitTermE-MailDomain <Netzwerk>Relationale DatenbankBitSchreiben <Datenverarbeitung>EindeutigkeitValiditätTransaktionDatenbankKonditionszahlAbstraktionsebeneForcingBenutzerbeteiligungBefehl <Informatik>Funktionale ProgrammierungSummierbarkeitFaltungsoperatorWort <Informatik>MultiplikationsoperatorMustervergleichProjektive EbeneResultanteRegulator <Mathematik>Basis <Mathematik>DatenfeldWellenpaketCASE <Informatik>ProgrammierparadigmaEinfacher RingDateiformatEin-AusgabeProgrammiergerätTypentheorieAdressraumArithmetisches MittelFlächeninhaltEinmaleinsAuflösung <Mathematik>Güte der AnpassungMusterspracheKomplex <Algebra>SchlussregelGamecontrollerFortsetzung <Mathematik>ATMInjektivitätMengensystemVorlesung/KonferenzComputeranimation
SoftwaretestDifferenteObjektrelationale AbbildungErlang-VerteilungKartesische KoordinatenNeuroinformatikSchnittmengeRechenbuchMathematikMixed RealityProgrammierparadigmaApp <Programm>ResultanteRobotikLogische ProgrammierungQuick-SortDatenbankEinfach zusammenhängender RaumCodeTwitter <Softwareplattform>FehlermeldungExogene VariableAbfrageRelationale DatenbankVerschlingungEndliche ModelltheorieDatensatzMultiplikationComputerspielEindeutigkeitReelle ZahlObjektorientierte ProgrammierungSichtenkonzeptWeb ServicesDomain <Netzwerk>BitValiditätSchlüsselverwaltungComputerarchitekturBenutzerbeteiligungMereologieMAPServerÄhnlichkeitsgeometrieMusterspracheTrennschärfe <Statistik>ImplementierungFunktionale ProgrammierungRechenwerkAssoziativgesetzModifikation <Mathematik>Web logTypentheorieEinfügungsdämpfungBitrateDatenfeldProzess <Informatik>Fortsetzung <Mathematik>InverseInstantiierungDifferenzkernEinfache GenauigkeitMigration <Informatik>TermDefaultGüte der AnpassungAuswahlaxiomE-MailQuelle <Physik>p-BlockRechter WinkelEinsDivisionÜberlagerung <Mathematik>RechenschieberKonditionszahlKlasse <Mathematik>Array <Informatik>GrößenordnungMultiplikationsoperatorMessage-PassingLesen <Datenverarbeitung>Anpassung <Mathematik>DatenstrukturNummernsystemDruckspannungSoftwareEntwurfsmusterEigentliche AbbildungKontextbezogenes SystemTextbausteinSuite <Programmpaket>Elektronische PublikationTotal <Mathematik>Computeranimation
Office-PaketDebuggingProgrammierungSichtenkonzeptWeg <Topologie>MultiplikationsoperatorRechter WinkelProgrammiergerätApp <Programm>Computeranimation
Web ServicesPhysikalischer EffektGamecontrollerMultiplikationRechter WinkelFunktion <Mathematik>Vorlesung/Konferenz
Computeranimation
Transkript: Englisch(automatisch erzeugt)
So my name is Brad Urani, I work at Procore. This is a talk about object relational mappers, ORMs, specifically comparing and contrasting Ecto and ActiveRecord.
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 Vailum. He was a long time core member of Rails, he kinda split off and went 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 Ruby-esque syntax, and it's designed for very, very high concurrency. It does that very, very well. And Elixir actually compiles to Erlang bytecode. 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 here, he was working at Ericsson at the time, and he wrote software for this telephone switch, which ran a million lines of Erlang, with nine nines of availability. So this was the kind of thing, so if you wanted to design a phone system, for instance, to serve the entire continent of Europe,
he figured out how to do that. With this incredible technology, it's really set off amazing. It was invented way back in the 80s, but it kinda fell out of popularity when the internet sorta came around in the 90s, and we didn't need that kind of high-concurrency real-time systems, you know, Perl and Java kinda 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 kinda 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. 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 really difficult, kinda weird syntax that's not very user-friendly. So along came Jose, and he created Elixir, which basically runs on Erlang, but with sort of a Ruby-esque kinda syntax that's real nice and pretty.
Really, the similarity with Ruby's 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 kinda familiar to you. So now it's sort of like a pretty Ferrari, right? So these guys sorta 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 kinda 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 ActiveRecord, 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. Did you hear Chase 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 slow, it's more Ruby that's slow, so it's not really his fault, but Elixir and Phoenix are very fast.
What would you do with that? A real-time communication system would be a good example. And what's kinda 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 kinda built into the Erlang VM. Contrast that with, for instance, Action Cable, which is this new feature in Rails 5, what happens is the server first gets written to Redis, which has this pub-sub, so it goes 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. Their backend systems are actually written in Erlang. And you can do this real easy, because remember, this is an MVC web framework built on top of telecom technology, so it's like Rails with this more powerful subsystem. But actually, that kinda 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 ActiveRecord. 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 ActiveRecord relatively recently, and Ecto even sooner than that. Actually, really, looking at this list, the one that stands out is sort of like the one that's different, the kinda odd duck. It's really kind of ActiveRecord. 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 style of program that avoids state and mutable data. I realize that that's not, that doesn't mean 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 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 sort of methods and objects, like methods and data in the same class, that encapsulation. It's not object-oriented. It's functional, and no mutations.
So that means in Ruby, you can have a hash, and you can 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 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 ActiveRecord before I get to Acto. This is all gonna be sort of, you've probably all seen this before. If you've viewed ActiveRecord. 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 top-level architectural designs of ActiveRecord 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 lunchroom 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 record. 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 chaining 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, dot starting after, dot ending before. It reads real nicely. That's sort of a nice feature for readability and stuff. ActiveRecord, it looks nice.
Down, I've got has many 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 dot each, and for each party, I do parties dot active users, 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 dot active users, 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 ugly kind of preload thing in, so it's kind of polluted my nice English-looking syntax with something a little more computery. 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, 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 can be tough to track if you're 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 sort of a higher level, why did this happen? ORMs are kind of this leaky abstraction, so if SQL's a dog, right,
I want to treat a dog nicely, I want to feed it 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 it 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 to write good ActiveRecord, 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 ActiveRecord 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 ActiveRecord, our nice English-looking syntax,
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, we're getting data, data's the banana, what we got is this ActiveRecord model,
which is an object, and it's got all this 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 normally in these tables,
we've got a lot of stuff on these tables, including our timestamps, 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 ActiveRecord kind of encourages this, I call this problem seeing stars, right? Seeing stars is this indiscriminate use of ORMs, where it's always select star,
and we can fix this by adding a dot select, but then you've got that ugly ActiveRecord stuff with 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,
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, 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 what's querying what, because we've got a query spread out across so many files.
So just to sum it up, sort of the Rails ActiveRecord 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 ActiveRecord reads really beautifully. We've got objects, not data. We've got gorillas with bananas. Domain models over relational models.
So domain model, it just means we've got a class that represents something in life. So I had a party table, like a party's a real thing, a user's 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. It kind of favors developers over DevOps. Why do I say that? Well, because a lot of times, like your DevOps or your DBAs, or looking at the query log, and it's like, oh, wow, look at last night. This was the slowest query that ran. Look at this giant thing. Where's that query? And the developers are like,
oh, well, it's split across 12 scopes over four different models, kind of all chained together. It makes it hard. If you just want to find the slow query, find that query. It's kind of tough to track it down sometimes. 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 active record applications with a lot of people using them, these N 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, the way in active record, you can just take a model and .save kind of everywhere you want. 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. Were 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 active record, 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. If you're not really used to SQL, it's really easy to get started. So it's got these big thumbs up. It's got its benefits, too. And overall, I'd say, active record, this is my opinion, kind of, but convenience over explicitness and performance. So it's real fast and easy and convenient to 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 gonna 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 McCord,
and Leah Cholley says, Rails and Phoenix are web perks that share a common goal. So they are and they aren't the same thing. They're very, very similar. If you're doing sort of like a crud app kind of thing, they are very, very similar, but then Phoenix has this 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 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,
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 ActiveRecord, you don't define the, you don't predefine the fields, right? It's all dynamic. In Ecto, you actually have to define them, but you can see it's got hasMany and belongsTo 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 explicitness.
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 to 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 forConference, which returns a query.
If you're big on design patterns and stuff like this, this is called the repository pattern. It's Martin Fowler defined this in his book here. First is 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 the instance methods on active record, but active record, the Rails thing,
is actually sort of like a melange of the class methods are more like repository, where the instance methods are more like active record, and there are hints of data map or other things mixed in there. So while active record is sort of this 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 Ruby Rogue's podcast,
and Avdi Grimm was on there, and we were talking about this, and someone said, hey, will someone define active record and 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 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 query something, change a field, and hit save, and save it back. I'm gonna get to writing in a second, and it doesn't return objects, it returns structs. So it's returning bananas, not gorillas. It's just returning data
in the 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 that are 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 mapper.
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, right? One's not actually an ORM. 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 in parties,
so I'm getting a little alias there, where party.conferenceID, and then also check out the select, select party.name, party.startTime, party.endTime. What's kinda 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 kinda 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 gonna forget it as easily and have the seeing stars problem, are you? If we wanna preload users, we can, just like that. It's still pretty SQL-esque looking.
If you wanna group by, I thought this was kinda 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 kinda cool, real SQL-looking. Map's real nice. Does anyone know where they got this syntax from, actually this idea of this kinda 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, Bill's like, yeah, yeah, nice dance moves, Bill. Where'd you learn that? Yeah, we're still mad about IE6, you know.
So in the end, I take all these queries and I sorta put them together all in the same file. Realize, those are the ones I just showed you, but there they are, and it's kinda like a bunch of SQL queries. Now, you can write this in a way that kinda composes them and reuses them. It's not very dry, because they all three have the same where condition. In ActiveRecord, 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 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. So, also, 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 functions, not methods, right? It's not parties.first, it's list.first.parties. It's a function, and I do firstparty.users. I get this association not loaded. In ActiveRecord, this would be an ActiveRecord 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 layer and think ahead and write your preload back down there,
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 of the querying is actually done in the model layer. There's not that sort of laziness where you're passing this ActiveRecord 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 queries 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 layer, and you're forced to think ahead of all your preloads and stuff. It's not gonna randomly fire one off in the view. So now, onto writing. 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 for creating new users. I know that I define this change set. It's gonna take the name, the email, address, and the age. Cool, so that might be like your form, your sign-up 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 sign-up form. If we want one for 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 can kind of just free-form it, like set a property and hit that save, or set a couple properties and hit that 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 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 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, you know, 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, you know, is a lot of this database querying, and how you're sort of translating from that user input, and like 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, right, a little easier to track down and 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 it's also, 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's gonna have joins, a query, most queries, like 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, 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 and 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 and 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 like kind of cool select syntax and the way it kind of,
you know, it 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, you know, if you're not at scale, it may not matter, but at big scale, when you've got, you know, dozens of web servers firing on a single database server, the database server becomes your bottleneck, and the 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, you know, in the world of active record, we've got domain models, oh, I've got a user, that's a real life thing, I've got a user class, you know,
a nice easy connection there to think about, whereas 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, that depends what you're used to, you know, if you want code that looks like English,
you know, 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 doing things of this, you know, and writing queries, then, you know, you may have the opposite experience, it may be a little more readable, it's kind of like robot talk, you know, instead of like users and conferences, we've got change sets and schemas and queries, but that might be more natural
if you're used to like 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 sink, insert into dishwasher, 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 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 the 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 active record, it 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 like 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 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 new column, oh, it's email, let me generate a nice error message for you, so that's really clever,
and then there's the testing story, you know, I don't have any slides, you know, 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 it's 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 cool more features,
like so JSONB, that's that thing in Postgres, where you can 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 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 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, I see these frameworks as no, I like the Ecto way, but that may not be for you, and that's okay, if you value the kind of convenience that it gives, then so 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, Trailblazer's 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 sets, so, you know, again, different trade offs if you wanna 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 a 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 like services, and how you can like change the way you use Active Record, I also gave a talk there at a meetup in 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 Urani, I tweet at, at Brad Urani, follow me, I'll 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 work in 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 like crazy, Rails architects, JavaScript front-end, which is React and Redux, pretty much every cool thing you can think of, I'd love to talk to you about that if you're interested,
or there are also about 15 of us here, mostly wearing Procore gear, so come and talk to any of us, finally, hallway track 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 hallway track 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, cause you 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 know, you just use the controller or a service layer to kind of put stuff in a multi and run it all at once. Cool, thank you very much.