Rails to Phoenix
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Part Number | 19 | |
Number of Parts | 89 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/31549 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
RailsConf 201619 / 89
1
2
3
5
7
8
11
12
19
22
28
29
30
31
33
45
47
50
52
53
58
62
67
71
74
76
77
78
79
80
81
84
85
86
88
89
00:00
Process (computing)Web pageMessage passingNumberObject (grammar)Physical systemBlogExecution unitCache (computing)Integrated development environmentBenchmarkComplex (psychology)Key (cryptography)Multiplication signWebsiteReal numberMultiplicationCartesian coordinate systemResponse time (technology)Modal logicFormal languageThresholding (image processing)Shared memorySpeicherbereinigungSymbol tableCompilation albumSoftware developerServer (computing)Dependent and independent variablesTraffic reportingCausalitySoftware frameworkDatabaseVolumenvisualisierungParallel computingAverageProduct (business)Computer font2 (number)Instance (computer science)Game controllerCharacteristic polynomialEndliche ModelltheorieTotal S.A.Service (economics)ResultantStrategy gameBasis <Mathematik>MiddlewareLibrary (computing)Functional (mathematics)HookingRouter (computing)Run time (program lifecycle phase)Erlang distributionLoginComputer animation
06:19
Product (business)CodeContent (media)Cartesian coordinate systemMultiplication signSoftware developerState of matterFunctional programmingComplex (psychology)Complex systemAssociative propertyFormal languageBenchmarkMathematicsSoftware frameworkFreewareResponse time (technology)Order (biology)BlogCache (computing)DatabaseDisk read-and-write headData typeResultantDependent and independent variablesSemiconductor memoryPhysical systemTemplate (C++)String (computer science)Field (computer science)Computer wormCompilation albumProgrammer (hardware)Imperative programmingLimit (category theory)Row (database)Focus (optics)Traffic reportingLambda calculusServer (computing)Group actionCASE <Informatik>BuildingRemote procedure callMathematical optimizationFunctional (mathematics)Serial portPoint (geometry)TrailGraph (mathematics)Ocean currentSound effectOverlay-NetzSimilarity (geometry)Regulator geneStructural loadWebsiteMereologyIdeal (ethics)Form (programming)Right anglePortable communications deviceMusical ensembleFigurate numberWindowReal numberComputer programmingReading (process)Set (mathematics)Computer animation
12:28
Functional (mathematics)Design by contractParameter (computer programming)Erlang distributionMixed realityHuman migrationGame controllerCodeHash functionLevel (video gaming)Variable (mathematics)Instance (computer science)ResultantRouter (computing)Different (Kate Ryan album)Data structureQuery languageCycle (graph theory)Point (geometry)Statement (computer science)Sound effectFormal languageRow (database)BuildingConfiguration spaceRight angleCondition numberElectronic mailing listBitDivergenceSoftware frameworkConcurrency (computer science)Similarity (geometry)Library (computing)Standard deviationMereologyPhysical systemPattern languageSoftware developerPattern matchingReading (process)NumberCartesian coordinate systemPerspective (visual)Regular expressionStrategy gameGroup actionBlogDistributed computingFunctional programmingSocial classGreatest elementKey (cryptography)MultiplicationIntelligent NetworkAreaUsabilitySummierbarkeitCodeSystem callElectronic signatureMathematical optimizationObject (grammar)Imaginary numberParallel computingComputer programmingMatching (graph theory)Bit rateDisk read-and-write headElectric power transmissionLatent heatPressureSpiralType theoryMoment (mathematics)Software testingCentralizer and normalizerCodierung <Programmierung>FrequencyInformation privacySource codeComputer animation
21:18
NumberSoftwareInfinityOSI modelRule of inferenceComputer animation
22:13
Concurrency (computer science)Cartesian coordinate systemConnected spacePhysical systemErlang distributionAreaComputer programmingFocus (optics)Group actionOrder (biology)Reading (process)Propositional formulaMultiplication signPoint (geometry)MereologySocial classLibrary (computing)Different (Kate Ryan album)Boundary value problemExpert systemBroadcasting (networking)Web pageSoftware developerSoftware frameworkOrder of magnitudeMathematical optimizationCache (computing)Programmer (hardware)Client (computing)CodeInstance (computer science)Disk read-and-write headRight angleFreewareWeb-DesignerAuthorizationData conversionElectronic mailing listSingle-precision floating-point formatServer (computing)Lattice (order)Implementation2 (number)SoftwareRow (database)Wave packetBlock (periodic table)InferenceOnline helpRadiusTable (information)Endliche ModelltheorieQuicksortSimilarity (geometry)BuildingBit rateComputer animation
28:22
Computer animation
Transcript: English(auto-generated)
00:03
When we hear about Phoenix, we hear about performance. This is what most people, when they first install Phoenix, they're blown away at the
00:20
average response times that they're seeing. It's a common thing to see almost daily a new developer putting out their delight in sub-1 millisecond response times. Some have even never seen the U symbol in the logger before. So it's a great hook for those looking for something much faster than Rails.
00:42
But we have to ask ourselves, is performance enough? Can you go to your boss and say, this other framework is much faster, so we need to rewrite everything in? Do people, do your users even care about the performance once you're past a certain threshold? Let's look at one guy's opinion.
01:01
So recently, David was on a podcast talking about Rails performance, and he said, I see a lot of hello world benchmarks that respond in nanoseconds. Who gives a flying F? Have you ever met a customer who said, your page rendered in 100 milliseconds? That is not good enough. I need nanoseconds.
01:22
No one ever asks for that. The thing is, DHH is right. He's absolutely right. None of our customers are complaining if our page renders in 100 milliseconds instead of 50 milliseconds, or 200 milliseconds instead of 100 milliseconds. So the question is, who wants some 100 millisecond response times?
01:43
Real question is, how difficult is it for your engineering team to achieve that performance on a consistent basis? How much effort is required to reach this threshold? Let's take a look at a real world example. So if you're unfamiliar with Bleacher Report, they are a sports news website, and they
02:04
were running Rails for a very long time. They had, in the summer of 2014, they began a rewrite in Phoenix. They were probably one of the first large commercial websites to actually do so. And at that time, it could have been considered a legacy Rails app, pretty decent size,
02:23
50 models, 40 controllers. The characteristics of their production environment prior to the rewrite was 150 AWS instances. Their responses were log jamming, and by that, we mean the response times were taking so long that as new requests are coming in, the responses were not coming out fast enough,
02:43
and so the application server would fall over. Multiple engineers per application, and multiple complex caching strategies were in place. Afterwards, the results of the rewrite after they went to production with Phoenix, one-fifteenth of the servers previously required.
03:02
10 to 30 millisecond response times on average. Largest average spike, 400 milliseconds, and there's been only one instance of a spike ever reaching up to 800 milliseconds. These applications are supported by about one engineer per application,
03:21
and there's no caching in place. So you may be asking, how is this possible? Well, as Rails developers, we are told two things. First of all, we're told that we should make a sacrifice of performance for productivity.
03:44
But beyond that, we're also told that we should call the database as little as possible, and we should render from the cache as much as possible. Chris McCord, the creator of Phoenix, is seeing more and more reports coming in of applications that have made the switch from Rails to Phoenix
04:03
and seeing as fast or faster response times without any caching in place. Others have seen the same and share the story. And it's not just Rails. There are many other languages and frameworks that suffer from cache-itis that Phoenix has been able to alleviate. How can this be?
04:21
Why is Phoenix faster in some cases without caching than other solutions with caching? This doesn't make sense to most people. It doesn't compete. Elixir is a concurrent language. Instead of objects passing messages between each other, like you have in Ruby, you'll have many independent running processes running that are passing messages.
04:43
Elixir runs in the Erlang VM, also known as the Beam, which manages its own processes. So unlike operating system processes that start close to one megabyte in size, the processes in Elixir are far smaller, usually weighing in around one kilobyte. In addition, they are all isolated for another, and they each have their own garbage collector.
05:02
So while the total number of objects to be collected may be similar to a Rails application, in a Phoenix app, any given garbage collection is limited to its own process instead of running on the entire world. This means the collection ends faster and it runs on much fewer objects. To facilitate all these processes, Erlang is a language that Elixir is built on top of.
05:25
Erlang consumes as many cores as possible in the CPU, whereas Ruby was built and is still meant for a single-core world. Elixir being a compiled language means that Phoenix can push a lot of the work to compile time that Rails has to do at runtime. An example of this is the Phoenix router.
05:41
So in Phoenix, you have a system called Plug, which is a base library, and Plug provides functionality similar to Rack in that you build out these middlewares. And during compile time, Phoenix will actually compile different paths that the middleware can take,
06:01
whereas at Rails, this has to be negotiated at runtime. Phoenix makes really smart use of the Erlang VM. Let's see an example of this. In a blog post recently written by Evan Miller, in this post Evan describes certain tricks Phoenix uses to enable extremely fast template compilation.
06:23
The TLDR of this is other languages will delete and create new strings all the time during template compilation, whereas Phoenix makes use of Erlang's IO data type that allows for memory reuse. And finally, the database is not slow, or it's not as slow as we've been led to believe.
06:41
The idea that the database is bottleneck is all too common misconception. Here we see Chris warning that one shouldn't fall into that trap. And Parker Selbert recently wrote a blog post talking about this. Back in February, he built two APIs, one in Rails and the other in Phoenix. The Rails API was built with Ruby 2.3, Rails 4.2,
07:05
had Redis cache, and using Ectomodel serializers, had a warm cache, warmed up the cache before the benchmarks ran, tuned the fetch to as little data as possible from the database, all associations are preloaded, all content is cached as strings,
07:20
no marsh-length, so you get the highest performance, all data is generic, not customized to the current user, that means that they're benchmarking against different users in the application, and we don't want to get a custom one every single time that's going to bust the cache. The request is paginated to 100 primary records without a limit on sideloads, and finally the payload for the Rails side of things weighed in at 160k.
07:45
On the Phoenix side, latest Elixir and Phoenix, no entity cache, fields are fetched from the database, so every request is hitting the database, all JSON responses are serialized on the fly directly in views, includes customized data based on the current user,
08:01
so unlike the Rails application, this is actually building out responses based upon users' customized data, the request isn't paginated at all, there are 250 primary records, so we're getting more data coming back, and the payload, 724k. The results of this is that the Phoenix application
08:22
is serving up two and a half times more records, with four and a half times more data in those records. Without any caching, the Phoenix API response times are on average one and a half to two and a half times faster. Why does this matter? To better understand that, let's take a look back at the Bluetooth report results.
08:48
Phoenix doesn't ask you to compromise performance and productivity. In fact, because of its performance, you could argue engineers will be more productive. When it takes less people to support a similarly functioning application, those resources can be put to better use.
09:03
Instead of putting out performance fires, or having to manage a complex caching graph, the engineers can focus on features. No matter how you spin it, not working on caching is always going to be faster, there's always going to be less work than having to work on caching. The question is, can Phoenix's performance benefits free engineers to work on bigger ideas?
09:27
To help us answer this question, I think we have to look back. The question right now isn't, why Phoenix? Why Rails? I don't mean to say this as a hit on Rails, just the opposite.
09:40
I owe my career to Rails. I think a lot of people in this room owe their career to Rails. Rails has changed many people's lives. Instead, this is more taking a look at what made Rails popular in the first place when it first came about. To me, the reason Rails did so well is because it had two very compelling stories.
10:03
First, it allowed you to build something in half the time for half the cost. Second, it optimized for developer happiness. So the business people were happy that they're spending less time and less money to get their product out there. And the engineers were happy that they get to work in a language and a framework that they actually liked.
10:27
I've spent some time already talking about how Phoenix can be more economical to build out. So let's focus on this. This is what we're taught from day one as Ruby developers. Ruby optimizes for developer happiness.
10:41
And Rails piggybacks on that. I want to challenge it. Does it actually do that? While the Ruby you write today feels good, what about the Ruby you wrote last year? Does that feel good? You go back and read it? You go back and use it? What about last month?
11:01
What about last week? Does the idea of going back and looking at code that you previously wrote make you happy? The true problem... Sorry, this is a very common complaint in many languages. But it boils down to a comment on the language features rather than any syntax.
11:22
The true problem is state. We spend all this time building up state in our heads. Having to construct this complex system in order to understand how even the most simple of code changes affect anything else. Imperative programming languages like Ruby that allow for the state of the entire application to be changed anywhere else requires more time to reason about.
11:44
So when you're making changes to code you've written in the past, you're dealing with having to recall from memory that application state. And how other methods in the system could affect that state. Reading other people's old code only increases the complexity and makes people more angry.
12:00
Which could really explain why we hate reading other people's old code. Elixir has an antidote to this. It's called functional programming. Now functional programming is not a magic wand. Many people still see the lambda and they get scared because math. But let's see how simple functional programming can actually be.
12:23
Does everyone understand that the result of this would be 3? Congratulations, you're a functional programmer. It's not that simple though in reality. Some traits of functional programming in Elixir are that functions are pure. By saying functions are pure, this is a big concept to understand.
12:45
We mean that functions don't have any side effects. So in Ruby you may be modifying instance variables or global variables or something else outside of the function itself. Whereas in Elixir you have data coming in, you have data going out and there's no other effects outside of that contract.
13:03
Variables are immutable. So in a distributive system or if you're learning concurrent processes, having to deal with what the values are in the variables that you previously read could lead to race conditions. And immutable data is just something that helps alleviate that from the get go.
13:23
And finally explicit is greater than implicit. This is kind of the, in Ruby and Rails we say convention over configuration. And Phoenix definitely subscribes to convention over configuration. But in Elixir you hear quite a bit of explicit is greater than implicit. An example of that could be active record callbacks.
13:42
So if you go back and read someone else's code that's calling save, you don't know what other 50 things could be happening. Whereas in Elixir, you're going to have a very explicit code path that things are taking. You don't have to go code-splonk. You don't have to find out where the effects are taking place.
14:05
So all this results in a system that allows you to be more happy. Simpler systems are easier to hold in your head. Documentation is easier to find. You don't have to figure out, okay, what class is this instance part of? And code is much easier to test.
14:24
When building Elixir, Josiah Velline had the opportunity to learn from other languages. He took what he saw as some of the best features of other languages and put them in Elixir. He took the syntax from Ruby. He took the pipe operator from F sharp.
14:42
If you're unfamiliar with the pipe operator, let me introduce it to you. So writing deeply nested function calls like this is not unheard of, but it's also very difficult to read. So you may break it up like this. The pipe operator simplifies it even further.
15:01
So the idea here is that the result of the previous statement is now injected as the first argument in the corresponding function. So baz takes qux as its first argument. The result of baz is now piped in as the first argument of bar and so on. The reason why this ends up being really nice is
15:22
Elixir also cleans up a lot of Erlang code. So Erlang doesn't have the pipe operator. And what would be considered to be the primary argument is not really consistent in its arity, in its place within the function arity of the Erlang standard library.
15:41
So Elixir had the opportunity to actually wrap all these Erlang functions and have a discussion over, okay, we expect perhaps this list to be the primary argument. And so we're going to always assume that it's going to be the first argument here, which plays in really nicely into the pipe operator.
16:01
Pattern matching is something that was already in Erlang. But if you're unfamiliar with the concept, it's not regular expression pattern matching. Pattern matching has to do with function definition. So here we see three functions that are defined. They all have the same name. They all have the same arity. You can have different arity when it comes to pattern matching,
16:22
or rather function definitions in general in Elixir. But the difference here is the first argument. So for the first function we see 0, 1, and x. And in this imaginary contrived example, we're going to pretend that multiplication is super slow, and we've optimized that quite a bit.
16:41
So what ends up happening internally, if you call a function with 0 as the first argument, it will match back to this function, and it will give us whatever result we put in. If you have 2 as the first argument, it matches back to the bottom one, because the bottom one has a greedy matcher in a variable name.
17:05
Where this comes into play, ends up being really nice, is a place, for example, in Phoenix controller actions. This is a simple action. The URL params are passed in as the last argument. But what if you had a query param that you wanted to work with?
17:23
In other languages, you may have some ugly conditional statement inside the function. Or you may have some other way to parse it out at some point during the request cycle. In Elixir, we can pattern match directly up here. We can just grab it out.
17:43
So now we can work directly with the query object, and we can still work with the original params object. So the value of whatever was assigned to the query, this is called a map that the second argument would be used to, called hashes in Ruby.
18:03
But what's assigned to the q key is now bound to the query variable, and then the entire map is bound to the params variable. It doesn't stop there. We can even match on specific values within the map.
18:20
The top function will match if q is equal to bar. And now we have a very specialized function to handle that parameter. By learning what has worked in other languages, by making the best use of Erlang to avoid having to write caching strategies until absolutely necessary, Elixir and Phoenix optimize for your long-term happiness.
18:45
But how different is it working with Phoenix from the perspective of a Rails developer? Let's compare the two frameworks on a very high level.
19:00
Here's a Rails application structure, Phoenix application structure. Primary difference is gonna be instead of app, things live in web. Rails router, Phoenix router. Rails controller, Phoenix controller. Rails migration, Phoenix migration.
19:23
Rails generator, Phoenix generator. Elixir also implemented tooling that should be familiar to Ruby developers. Rake and Bundler are represented by the tool called Mix in Elixir.
19:41
Mix is also a first-class citizen. It's built into the language. Other tools like IRB, Ruby Debug, and Pry are also represented natively in Elixir. You know, IEX, the Erlang debugger, and Pry is part of IEX. In fact, most other tools in services or libraries
20:03
that you may go and use outside of Rails are built into Elixir. It's leveraging everything that's in Erlang. So Phoenix was inspired by Rails. And Elixir was inspired by Ruby. And you may be convinced of this,
20:22
that Elixir is Ruby for Erlang and that Phoenix is Rails for Elixir. But Elixir is not Ruby. Phoenix is not Rails. Chris wrote a blog post specifically on this. He acknowledged the similarities between the frameworks
20:40
but then discusses where the two diverge. And if you're interested in knowing that, I suggest you go check it out because it's on my company's blog. A little bit of marketing right there. This is evidenced by my feeling that the least interesting part of Phoenix is actually the performance story. That may seem strange considering all the performance numbers
21:01
that I've already shown you. Now Rails changed the world. The past decade, in my opinion, would have been very different for startups and tech companies if Rails didn't exist. Most startups, if they weren't using Rails, they were likely using something inspired by Rails. And every day we're met with another comment
21:20
on how the sky is falling, on how startup investment is drying up. This quote is actually, I think from about two years ago, or three years ago, the former CEO of Yammer, after Yammer IPO'd, he pretty much just peaced out
21:40
and said, that's it, that's the last idea I just cashed in, see you guys later. And Marc Andreessen weighed in and said, that's nonsense, there are an infinite number of great ideas out there. But how do we facilitate, how do we enable those ideas to come about? I believe that technology is the lens through which ideas are grown.
22:00
How do we expect to find the next best thing, next big thing, if our lens hasn't changed in 10 years? Phoenix enables us to think about how we build software in a very different way than we built it in Rails. OTP, you can think about that as a standard library for building distributed applications.
22:22
It's part of Erlang, it's been there for, I think, close to 20 years at this point. And Phoenix itself is a distributed application. Some people may be confused about that. When you think about Rails, you think about the monolith, and it's at the top of the pyramid.
22:40
But in reality, within the Elixir Erlang ecosystem, Phoenix, while it may seem like a big thing, it actually just can play a much smaller role and a supportive role amongst a wider system of applications that are running alongside and next to one another. So by leveraging the best that OTP brings to the table,
23:03
Phoenix is solving incredibly difficult problems. A little while ago, Chris decided to test how far he could push Phoenix by scaling up its WebSocket connections. After a few performance tweaks to the framework, he was able to get two million concurrent connections over WebSockets with a single Phoenix application instance.
23:23
Now, it was a very beefy box, had a lot of RAM, had a lot of cores, but even still, just one application instance was pushing this. There weren't any empty connections running. All the connections were simulating, chatting across the network, and a broadcast to all of the nodes
23:41
took under two seconds. So Phoenix continuing to push the boundaries on what is possible with so little allows us to dream up a different class of applications that were only achievable by multi-billion dollar companies in the past. This is the same leap forward that Rails gave us when it first came about 10 years ago. Recently, Chris's focus has been on Phoenix 1.2,
24:01
which will come with a new feature called Phoenix Presence. Presence allows it to solve a problem amongst connections that's supposed to be unique, but may be coming from different areas. So imagine a situation where you join the same chat channel under the same account, but from different devices.
24:22
And what if that chat is being facilitated by multiple servers? So you may join, there may be a net split, and then you join server A, and on the other device you join server B, and then there's a conversation going on amongst, isolated between the two servers, and when they join back up in the network, how do you resolve the differences at that point?
24:41
This is an incredibly, insanely difficult distributed programming problem to solve. And Phoenix is going to give this to you out of the box for free. 1.2, I believe, is due out in a few weeks, or at least candidate right now. So, how do you learn Phoenix?
25:00
The nice thing is that as Rails developers, you already have a huge head start over everybody else. We went through this at Dockyard. We had a client application, we were actually rewriting it from Node, and I was the lead developer on it, but we basically were running out of time,
25:24
so we wanted to put more engineers on it. All of our engineers were trained up on Rails. So, I gave them, Dave Thomas's Programmatic Programmers book, Programming Elixir, and I said, read part one. Part one is about 200 pages.
25:41
It goes over Elixir syntax, basic functional programming, and after about a week, they had enough knowledge that they can start making meaningful contributions back to a Phoenix application. Now that's not to say that they were expert Elixir developers. They weren't Elixir Phoenix developers. They weren't expert distributed application developers, but Phoenix is so similar on its face
26:03
that if you are already familiar with an MVC framework like Rails, then you're going to have a much easier time transitioning to it, and this is a huge value proposition for companies that may be concerned about adopting Phoenix, right? So, the concern is always, we agree it's really cool technology,
26:21
but who's gonna build it? Who's gonna maintain it? Well, there you go. It takes about a week to retrain a Rails team in order to work with Phoenix, and this wasn't just our experience. We've seen a lot of other companies, a lot of other people mention similar experiences. So, some reading material, if you're interested.
26:40
There's some really good books out there on Phoenix and Elixir. The middle one, Sasa Yurik. If you want a more advanced introduction to the books, Elixir in Action. Programming Phoenix just came out. It just reached publication. With Chris McCord and Joseph Vellin and Bruce Tate writing it. And finally, Elixir for the Rubyist is not out yet.
27:05
It's under development by Hal Fulton. So, two of these names on the list may seem familiar. Dave Domics and Hal Fulton, also known as the authors of the Programming Ruby or Pickaxe book and the Ruby Way. I think there's something here where the authors of two of the books
27:21
that were most responsible for evangelizing Ruby are now evangelizing Elixir. In closing, let's go over how we've answered this question. Why Phoenix? You can offer an order of magnitude and performance.
27:42
You can avoid having to wrap all your coding caching. Implementing features costs less money and requires less engineering support. Optimizes for long-term developer happiness. Takes about a week to retrain existing Rails developers. And it's going to change the world.
28:01
So thank you very much. My name is Brian Cardarola. I'm the CEO of Dockyard. And if you're interested in working with Elixir and working with Phoenix, please come and chat with us. Chris McCord, the creator of the framework, works for us. We hired him about five months ago to work on Phoenix full-time. So thank you guys very, very much.