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

Rails to Phoenix: How Elixir can level-you-up in Rails

00:00

Formale Metadaten

Titel
Rails to Phoenix: How Elixir can level-you-up in Rails
Serientitel
Teil
58
Anzahl der Teile
86
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
Elixir has rapidly developed into a mature language with an ever-growing library of packages that excels at running web apps. And because both Elixir and Phoenix were developed by Ruby / Rails programmers, the ease with which you can learn Elixir as a Ruby developer, is much greater than many other languages. With numerous code examples, this talk will discuss how learning a functional approach to handling web requests can improve what we do every day with Rails. This talk is aimed at people who have some familiarity with Rails but no experience with Elixir is necessary.
35
ZeichenketteWinkelTelekommunikationFramework <Informatik>Objektorientierte ProgrammierspracheBimodulKlasse <Mathematik>DatenstrukturHash-AlgorithmusMultiplikationsoperatorErlang-VerteilungServerLaufzeitfehlerTermAuswahlaxiomCASE <Informatik>Ideal <Mathematik>MAPFunktionale ProgrammierspracheParametersystemSchlüsselverwaltungProgrammierungRechenschieberArray <Informatik>Gebäude <Mathematik>Physikalisches SystemBitEchtzeitsystemArithmetisches MittelGruppenoperationImplementierungApp <Programm>ZahlenbereichInstantiierungProdukt <Mathematik>ResultanteCoxeter-GruppeVerkehrsinformationBenutzerbeteiligungEinfache GenauigkeitDifferenteProgrammbibliothekKartesische KoordinatenVirtuelle MaschineZusammenhängender GraphVirtualisierungOffene MengeÄhnlichkeitsgeometrieAggregatzustandProzess <Informatik>SpeicherabzugProgrammiergerätCodeCoprozessorWeb-SeiteQuick-SortMehrkernprozessorKeller <Informatik>AnfangswertproblemGefrierenProgrammierspracheAusnahmebehandlungVerfügbarkeitPlastikkarteSystemaufrufFehlertoleranzComputerunterstützte ÜbersetzungCOMOverhead <Kommunikationstechnik>SystemplattformÜbertragungsfunktionEreignishorizontSkalierbarkeitGeradeMapping <Computergraphik>MereologieService providerProtokoll <Datenverarbeitungssystem>MathematikUnendlichkeitKontrollstrukturElastische DeformationElement <Gruppentheorie>Endliche ModelltheorieKonfigurationsraumStabilitätstheorie <Logik>
BitGamecontrollerArithmetisches MittelBenutzerbeteiligungGruppenoperationProjektive EbeneZweiFunktionale ProgrammierspracheElement <Gruppentheorie>ParametersystemDefaultAnpassung <Mathematik>Kartesische KoordinatenDifferenteSymboltabelleResultanteSoftwareentwicklerAggregatzustandSichtenkonzeptCodeOverhead <Kommunikationstechnik>App <Programm>Migration <Informatik>Elektronische PublikationÄquivalenzklassePlotterTupelCASE <Informatik>BenutzerfreundlichkeitVolumenvisualisierungZahlenbereichMathematikRepository <Informatik>Web-SeiteSchlüsselverwaltungCoxeter-GruppeAbfrageDatenstrukturMAPSpannweite <Stochastik>Framework <Informatik>Endliche ModelltheorieInterface <Schaltung>Generator <Informatik>TabelleEnergiedichteFortsetzung <Mathematik>BimodulÄhnlichkeitsgeometrieMixed RealityBitrateSystemaufrufRechter WinkelEinfache GenauigkeitIterationDirac-GleichungUmwandlungsenthalpieRekursive FunktionLogische ProgrammierspracheOrdnung <Mathematik>Quick-SortObjektorientierte ProgrammierspracheComputerspielKlasse <Mathematik>Transformation <Mathematik>RandwertExogene VariableMultiplikationsoperatorSchlussregelLoopDatenflussDatenbankMereologieTemplateAtomarität <Informatik>ImplementierungVariableInstantiierungHydrostatikDatensatzInformationsverarbeitungOrdnungsreduktionSocket-Schnittstelle
SchnittmengeEndliche ModelltheorieAbfrageDatensatzZeiger <Informatik>Exogene VariableOrdnung <Mathematik>DatenbankDatenstrukturDreiecksfreier GraphBimodulCodeEinfügungsdämpfungGruppenoperationTemplateDatenfeldFunktionale ProgrammierspracheResultanteVariableSchlüsselverwaltungSichtenkonzeptDiskrete UntergruppeTabelleRelativitätstheorieBenutzerbeteiligungTextbausteinMathematikVolumenvisualisierungFramework <Informatik>AuswahlaxiomMaßerweiterungFormale SpracheTypentheorieGamecontrollerDivergente ReiheDifferenteValiditätZahlenbereichKartesische KoordinatenMapping <Computergraphik>Flash-SpeicherBenutzerfreundlichkeitApp <Programm>EntwurfsmusterEinsOrtsoperatorGrenzschichtablösungMustervergleichAggregatzustandFehlermeldungCASE <Informatik>MusterspracheBefehl <Informatik>Informationp-BlockTransformation <Mathematik>Service providerErwartungswertDefaultVektorpotenzialBoolesche AlgebraMomentenproblemRepository <Informatik>VersionsverwaltungSystemaufrufMereologieAutorisierungLeistungsbewertungElektronische PublikationMessage-PassingAlgebraisch abgeschlossener KörperInverser LimesRechenschieberTupelZweiKreuzvalidierungMatchingReflexiver RaumMultifunktionKomplex <Algebra>KonfigurationsraumAdditionSoundverarbeitungXML
Virtuelle MaschineCodeSchlüsselverwaltungFunktionale ProgrammierspracheErlang-VerteilungFramework <Informatik>ProgrammbibliothekProzess <Informatik>CASE <Informatik>KurvenanpassungÄhnlichkeitsgeometrieElektronische PublikationBenutzerbeteiligungKartesische KoordinatenFehlertoleranzMusterspracheVerschlingungDifferenteApp <Programm>SchwebungSoftwaretestSoftwareentwicklerPhysikalisches SystemAutomatische DifferentiationBildgebendes VerfahrenEinflussgrößeAdditionInformationProgrammiergerätVererbungshierarchieTopologieProfil <Aerodynamik>Selbst organisierendes SystemSpeicherabzugLesen <Datenverarbeitung>Einfach zusammenhängender RaumGruppenoperationSkalierbarkeitProjektive EbeneGewicht <Ausgleichsrechnung>BitMathematikResponse-ZeitBestimmtheitsmaßImplementierungAppletRechter WinkelDatensatzDelisches ProblemTOEQuick-SortWarteschlangeByte-CodeMereologieExogene VariableDatenbankXML
JSONXML
Transkript: Englisch(automatisch erzeugt)
Welcome to my talk. I'm gonna talk about the Phoenix framework today. My name is Christian Cook. I'm from NCSA in Chicago. So I've got a couple few goals for
today's talk. I want to familiarize you with the Phoenix framework generally, and I also hope to root our discussion of the Phoenix framework in terms of Phoenix, Elixir, OTP, and Erlang. So we'll sort of explain some of
those in a minute here. So what this talk is not. No Phoenix puns. I'm very sorry. No memes. And you're a smart RailsConf crowd. I certainly don't need to pander to you by showing you pictures of my cats, because that would just insult you. So why not Rails? Sort of reasonable question to ask at RailsConf.
I didn't come here to tell you to blow up your Rails apps. I'm not an evangelist kind of guy. Rails is great, and I enjoy working with it. I know it very well. It's fast to deliver functionality. It's fast to develop. However, I don't really love trying to grow my Rails apps. It usually
requires more than just spinning up additional servers, and it often requires a new technology in the stack. Redis, Sidekick, another Q agent, maybe Elasticsearch, something like that. So let's consider one alternative. The Phoenix framework bears a lot of similarities to Rails and shares many of the same
motivations. Chris McCord was a Rails programmer before writing the Phoenix framework. There are many aesthetic and structural similarities between Rails and Phoenix. Both are designed to be productive frameworks, both emphasize convention over configuration, and both are server-side MVC frameworks. There are
also a number of important differences. Phoenix is written in Elixir, which is a functional language. A Phoenix app is compiled, and a Phoenix app isn't really an app at all. It's an implementation of an OTP application written in Elixir. So I may use Elixir and Phoenix a little bit
interchangeably today. In part, this is because the Phoenix framework is the implementation of an app written in Elixir, and by design, Phoenix programming is Elixir programming. Yes, I will. That's next slide. Two slides
from now. So Phoenix framework is a web framework written in Elixir, which is a functional language that grew out of Erlang. So let's look at what each of those components implies. Functional programming offers data immutability, meaning actions taken on data structures always return a copy of
a new data structure while leaving the original unchanged. There are no objects or classes. Instead, you pass data like hashes and arrays to functions, which are first-class elements. So Erlang is a programming language used to build scalable, soft, real-time systems with requirements on high availability. It was
originally developed by Ericsson to support their telecom infrastructure, and the fault tolerance required to seamlessly failover from one switch to another makes Erlang an ideal choice for use in the web. Erlang has a somewhat different syntax that bears some similarity to Prolog, and it's used by a number of companies in production. Ericsson, since they developed it, sort of
obviously. WhatsApp, another one of the more common, more well-known companies running Erlang. The OTP, to your question, is the open telecom protocol, which I've also heard defined as the open telephony protocol, but basically it's not quite a full-fledged framework. It's more a
collection of Erlang libraries and design choices that will create applications that conform to and offer a distributed, highly available, supervised, and failure-resistant runtime. So Elixir is a dynamic, functional language that has a syntax that's somewhat similar to Ruby.
It was written by Jose Vadim from Platformatech. He was previously a member of the Rails core team. Elixir offers the promise of building distributed, scalable, maintainable applications that can run with very low processor overhead, as well as very low latency. Why Phoenix?
Because Phoenix is written in Elixir, which is written for the Erlang virtual machine, it's a functional, immutable, asynchronous, scalable, lightweight, and fast. A Phoenix app is compiled with the added certainty that that can provide around runtime stability, and it's well suited to the current state of the web, with multi-core processors running multiple processes on
a single machine or many machines distributed geographically. So there are a number of companies who are running Phoenix in production. The Bleacher report is the example I've heard more. According to their reports, they were able to handle eight times more traffic while reducing the number of
servers when they migrated from a Rails stack to a Phoenix stack from 150 servers to five. And if I'm honest, that sounds a bit too good to be true. And there's probably a little fudge room in there, maybe they dropped a Redis server or something like that, but anyway. So that's enough talking about programming languages, let's look at an example. So here,
we have a Ruby example. We define a class and a class method to modify a hash at a given key by adding the third argument to the initial value. When we evaluate it, it does pretty much what we expect, except we will note that our original hash and our new hash both have the same
value, and if you looked at the object ID of the two, they'd be the same object ID. So let's write the same thing in Elixir. Instead of a class, we're going to define a module, and we define a function within that module. Because a module is just a group of functions, there's no concept of instance or class methods. When
we evaluate the example, we see the result is similar. New map looks like our modified map in the Ruby example, but the first one, MyMap, has remained unchanged. Let's just break down a little bit what's happening within our transform function. So we call
this update bang method, and we call it via the map module. We don't call a method on an object because there are no objects, so I don't call update on a hash. Instead, I'm going to reference through the map module and use this update bang method. In this case, update bang will raise an exception if the key isn't present
in the map. So the update function takes three arguments, a map, a key, and a function. And the third argument is an anonymous function that takes the value at the map key as its argument and passes that value to its own function code. And then here the function code uses the angle brackets string
concatenation operator to combine the two values. So let's look back at the Ruby example one more time. We could try to make the Ruby example immutable. .dupe would keep the original hash unchanged. We could also try .freeze, which will do the same thing, in this case by raising a
runtime error. But both freeze and dupe only produce a shallow copy. So if we were to trivially change our Ruby example, we can see that immutability doesn't hold for nested objects. And quite frankly, neither of these approaches is practical across your code base. I'm not going to litter .dupe or .freeze with every method call I
write, and if I'm trying to make Ruby immutable, probably I've sort of started down the wrong path from the get go. So some language-specific concepts that probably have already come up and will come up again, just so we're all on the same page. Elixir uses lists, not arrays, maps,
not hashes, although they're very similar. There are no objects. Elixir has modules, and modules package groups of functions together. The beam is the Erlang virtual machine, and that's where all Phoenix and Elixir code is
executed. We see we have a tuple, which is ordered collection of elements between the curly braces, where the symbol or atom at the front represents the success or fail state of a function call, often. And in general, there's no for loops, no iteration. Elixir favors
recursion and higher order functions like map and reduce. So looking at a Rails project in the Phoenix project, there are a number of, you know, similarities with how they're used. Mix module in Elixir combines the role of
Bundler and Rake, Bundler and now Rails under the Rails 5 world under a single wrapped in a single module can generate migrations. There are a number of code generators. You can generate an application, controllers, views, etc. Unlike Rails, Phoenix requires you to
explicitly define your pluralizations. And if we look at project structure, a Phoenix project is reasonably similar to a Rails project. It doesn't look all that different. The underscore build folder contains our
compiled project files. The deps folder contains our dependencies, privilege, static files, as well as migrations. And if we look at the web folder, the web folder in a Phoenix project is pretty similar to the app folder in a Rails project. Channels in this case are the Phoenix equivalent of action cable or web sockets.
And if you look at Phoenix 1.3, which is in release candidate form, it's going to introduce a number of changes to the project. And it's way beyond the scope of this talk to go into all of them, but it's important to note, if you knew up a Phoenix 1.3 project, it's going to look a little bit different than your 1.2
application. So Postgres is the default adapter for a new Phoenix project. And there is support for other adapters present. It's not always up to date with the latest release of Ecto. Personally, I was really surprised to see that there's not one but two Microsoft
SQL adapters. And both the MS SQL dot Ecto and Amnesia adapters are new, at least on the Ecto repo page, since I started working on this presentation. Things are happening. Okay. So Phoenix is an MVC framework, and let's kind of step through that a little bit and look
at the different pieces of that. Let's look at a controller first. So we have a Rails controller and a Phoenix controller. And there's not a whole lot of difference between these at sort of a glance, but let's look a little bit more closely. A Phoenix controller action requires two arguments. The first is the con,
which represents the web request. And in Rails, we get this implicitly via application controller. You know, from the Rails docs, they tell you as much. It comes in invisibly to you as a developer. And that's okay. That's not a bad thing. It's part of the speed and ease of development that Rails offers. But there's a
different way of looking at, of approaching your controller. So as Phoenix requires the con passed in explicitly, maybe you're wondering, maybe we wonder how the con got there. So let's look, let's jump back up the stack and look. So this is the endpoint module, and we don't need to get into like what's happening here
in specifics, but I just want to sort of call out that with our Phoenix app, we're declaring sort of each plug that we're going to put in, which a plug represents more or less a transformation. So it'll be each transformation that happens to your web request as it comes into your application. We're going to parse, log, do all those
other things, and that will convert an HTTP request into structs that you can then modify with your application code. Most of the time, you don't need to worry about this, like rack in Rails. But when you do, when you need to look into what's happening, it's nice to know that it's mapped out somewhere and you can view it. This is also a really great place to add your own custom
functionality, if you need something like a rack, something to do that thing that you need. So plug and rack are rather similar, and actually, if you look there, specifications are really similar. Plug, however, can be used almost anywhere, while rack is wired into place at the beginning
of a rack app. If we were going to do that, insert a rack in Rails, we can do that, but it sort of involves shimming relative to some other piece of functionality, not necessarily anywhere you want to use it, but early in the call stack, related to something else.
Okay, so back to our controller. Second argument in the controller is a map in this case, which is going to destructure the request parameters in a variable called data. Since we're in an MVC framework, it looks like we're probably developing something RESTful, we expect there might be a database query here.
So let's put that in and see what that would look like. Note with the query, we're not invoking the query through the model, like a find or a find by, instead we specify the repo, the active repo module through the application itself, and then specify the model table against which we want to query
in the function arguments. This is going to be a slightly different, the interface to the repo will be slightly different in Phoenix 1.3, again worth noting. So we can change our query to use something other than the primary key, and our controller
is going to require that we declare the view template that we're going to use, there's no implicit assumption based on which view file we're going to render. And again, that's not a trivial issue in an app like this, but something that's been around since Rails 2 to 3 to 4 on its way to 5, where your view folder has 40 sub-folders in it, that's definitely no small amount
of cognitive overhead saved. So we're going to use the plug render function to name the view template we want to use, and assign the result of our query to a variable for use by the template. And because we're explicitly rendering, we can easily switch out the view template we want to show.
So, stepping back a little bit, Phoenix isn't an application itself, I think I mentioned this earlier. But it's an important distinction that becomes apparent after you work with Elixir and Phoenix for a while. Phoenix itself is an implementation of an OTP app written in Elixir. The Phoenix layer is more or less a routing layer
to the code that you write. With Rails, you're relying on the often implicit promise that your app offers, whether it's rendering a view by naming assumption, routing based on a similar assumption, or exposing instance variables across responsibility boundaries. Rails is there to help, but it makes Rails the centerpiece
of what is more ostensibly your business and your application logic, because of the way you interact with your own code and the Phoenix code in the application by passing data structures to functions instead of sharing Rails or active record objects between classes, it's conceptually harder, at least for me, to think of Phoenix as a thing
that's doing a lot of the heavy lifting. And I think this can leave your application code more isolated and less prone to getting bound up in the MVC framework layer. So if we go back to our Elixir example,
how can we structure this and make it just a little bit more functional? And we can use the pipe operator for that. So piping values which is passing the value from the left into the function on the right as the first argument, is a more idiomatic way of expressing the flow of data into a function.
So we can move our map to the left of the pipe operator and then it's passed in as the first argument to this function. And in our example, that's a small improvement. The code executes the same, maybe reads a little bit better. But if we look at another example,
for me, this expresses better the way data flows through a module as a series of discrete but related steps. This conceptualizes back better to the HTTP request response cycle and expresses what your application code is doing. Future me appreciates code that's self-evident and doesn't rely on implicit actions
and potential side effects. And partially, that's because current me forgets what my code does within a few days of committing it to source control. So any web request can be viewed as a function. And for me, this was a big aha moment of writing functional code for the web.
The call itself has all the information you need to figure out the state of the world and serve the appropriate response. Web requests are stateless functions. They have to be to work on the World Wide Web. And this is just as true in Rails as in Phoenix. There's no reason you can't or shouldn't structure your Rails app in a way
that expresses a discrete series of transformations to get you from request to response. I think this is where the structure imposed by your language and your framework can really have an influence on how you write your application code. So back to, look now, we'll step now
and look at what's happening in a Phoenix model. Side note, as frameworks go, Phoenix has gotta be one of the harder ones to Google for. Phoenix model does not get you what you think you're gonna get you most of the time. You definitely need to include the elixir modifier on that one. Okay, so as we saw in the controller show function,
database queries don't rely on model functions. They rely on the model in order to reference and access the database table name, but the queries are functions called on the Ecto repo module. So this really limits our models to handling schema and data validation.
So if we expand the web model function from the previous slide, we can just see that it wraps the import of a few Ecto modules, including change set and query. We can define a UUID as the primary key, and then we define the schema within the model itself
as opposed to a schema file. Set field names, name and a type. We can also add default or null values to our schema here. So Ecto change set was one of the modules we imported. Change sets are how Ecto transforms data structures
like maps and lists into more formal structs which we can then insert or update into the database. And Ecto provides a number of functions to manipulate our change set data. Cast will make a best effort attempt using the field type defined in the schema to coerce the params non-destructively.
Can validate our required, ensuring that our params map has keys for all the values that we expect to be in our database on our table. And we can update change with something more, a more invasive coercion. So this is definitely longer and more boilerplate
than writing an active record insert. But you are explicitly calling out the validations that you want. It's also less dry. You're adding validations which probably duplicate what the structure that you've imposed on your database. But by validating in the model, you can handle errors and provide error messages back to the user.
You can also write a custom validation. Note we're pattern matching via the case statement here on the result of the valid position ID function. Then we can take our custom validation
and wire it back into the change set validation pipeline. Within our custom validation, we can add an error message back to the change set which we'll send back with a request response. So this is more or less the extent of our pretty basic Phoenix model.
But because we're also not using the model to query the database, the models naturally get skinnier as they're primarily concerned with ensuring data validity. There's definitely a whole lot more to say about working about Ecto and working with the persistence layer, but that would actually be an entire talk on its own. Somebody gave one here last year, Ecto and active record.
So that's something, a reference there if anybody wants that. So back to a controller which will handle the result of interacting with our model layer. So the model validations we were just looking at
come off a change set function. We use our change set example to perform an insert action. So the create function is the corresponding controller action. So let's simplify our example a little bit. Note again, we're gonna pattern match off the result of our repo insert function.
The repo insert will return a tuple, the order collection of items between the curly braces, and the okay atom tells us that the insert works as expected. So we can continue on with our happy path. When the change set fails whatever validations we've defined,
we can return error messages back to the view. We can take those errors, set an HTTP status code, add error and flash warning messages, error and warning flash messages, and put the entire create function back together, we can render a view, which would look like this for a very vanilla Phoenix app.
It's a little brief aside. Rich Hickey, the author of Closure, has a great talk where he discusses the perils of easy, easy versus complexity, and how easy can disguise complexity, and I definitely encourage you guys to watch it.
So Rails is easy. It embraces convention over configuration, and it's able to do most of what I want automagically, but the ease of Rails disguises and hides its complexity. Rails is often performing some complex work under the hood. Phoenix, on the other hand, is less easy. It requires more explicit code,
and it requires you to think about your application differently. The explicitness of your Phoenix framework application encourages keeping things simple. The model in Phoenix is a good example of this. It really only does data validation. You need to use an entirely different module to query the same table, but this command separation structure
can keep the overall structure of your application simpler and easier to reason about, and in general, this design pattern is entirely possible within Rails, but it's often the way a language or a framework is used that reflects the design choices we make when using it.
Okay, so we move from a controller to a model, back to a controller, and let's look at the view. Phoenix views are pretty similar to Rails views. They work pretty similarly. They're functions. They're invoked by the Phoenix render function, usually from the controller,
and you need to explicitly define the view module template and the variables for the function, for the view function. This is a basic application template, not particularly interesting on its own. Really, the part we care about is where the template wires up the render function.
This is similar to a yield block in a Rails template. To find our view module, you can add a decorator function to that module, and we can render our show template. This template uses EEX code,
which basically is ERB code, similar enough that it shouldn't look all that different. Then we can take the title function that we've defined and add it back into our template, which would look like this. I did have BoJack on there before DH's keynote yesterday,
so odd little coincidence. Okay. Anyway, so that was a pretty long walk through a basic web request, but I really wanted to emphasize the similarities between Phoenix and Rails. Getting started writing Elixir code in Phoenix shouldn't be too daunting of a challenge,
and it should feel familiar enough for you to dip your toe in to try it out, but Phoenix isn't Rails. They're similar, but they're not the same, and it's when you start getting into the differences that you start to appreciate the strengths of Elixir and the Phoenix framework.
We didn't really even touch testing, but take it from me, your tests run fast. There's little to no stubbing. There's little to no mocking or doubling. Tests are easy to write and usually don't require a lot of elaborate setup. I feel like I'm not doing the Phoenix side justice if I don't mention microsecond response times, which everybody loves,
but we wrote an app that basically does one thing and has one database record, so that's not really a meaningful measurement. As you grow your application, you can add queues and background workers just by adding code. You don't need any additional infrastructure. A queue and a queue worker exists as Erlang Beam virtual machine processes,
which get started and supervised by your application itself. Your application has the capability for failure handling built in. Within the OTP framework, it provides a supervisory tree, and that supervisory tree sort of monitors
everything that's running. It monitors your application, monitors the queue workers that you've set up, other pieces of it, the connection to your database. Within that supervisory tree, your app can and is designed to crash. It just gets restarted and keeps right on going while barely missing a beat.
But Elixir and Phoenix are too immature, you might say. And while it is true that Elixir and Phoenix are relatively new, a Phoenix framework application is an OTP app. And this is a pretty well understood, well defined operational pattern. Phoenix is Elixir code, and Elixir transpiles to Erlang byte code
that runs on the Beam virtual machine. This code base has been matured and battle tested since the 80s, which makes it older than Java. The Beam, like the JVM, is the party piece of the Erlang ecosystem. It's super lightweight, fast, fault tolerant, scalable, and because it's so lightweight,
you can grow your application with less need for additional infrastructure. There are cases where a library that you need or some piece of functionality may not exist in Elixir yet, although that's becoming less and less true every day as more packages are written for the Elixir ecosystem.
However, in many cases, similar libraries possibly do exist in Erlang, and Elixir is fully interoperable with Erlang. So it's no problem to import the libraries that you might need. Should I use Phoenix? Part of me wants to say categorically yes, and you should experiment with Phoenix.
I mean, it depends though. If the subtext of this question is can I do this XYZ thing that I need to do, then the answer is probably yes, but you may need to write your own code or your own package for it. But I've often found that when I get really tied to the how of an implementation, I may not be thinking broadly enough
about the what and the why, and there may be a different path towards the same end. However, if the should I use Phoenix question is more like is Phoenix right for my company, or maybe more accurately, is my company right for Phoenix, then that answer depends a lot more on your company and your risk profile for new technology.
If you wanna be an advocate, I think it helps if you can develop some core competency within your organization, whether that's a group of developers who attend meetups, write some toy apps for an informal learning session like a lunch and learn. I think the key is to grow interest and a competency in the framework first.
The learning curve, however, is not as steep as you might think. The similarity between Rails and Phoenix really works to your advantage here. So let me leave you with a quote from the great Jim Jarmusch film, "'Ghost Dog, Way of the Samurai.'" So the second half, it's the same for anything that's called a way.
If one understands things in this manner, he should be able to hear about all ways and be more and more in accord with his own. I hope I've helped you to understand the Phoenix framework a bit better and maybe consider using it for an upcoming project. With that said, I'm not advocating abandoning Rails. Phoenix is new and shiny, and that has its own appeal,
but it's a different tool in our toolbox that may be more suited to some problems than others. As Rails or Elixir programmers, I believe there's a lot of benefit in exploring different technologies, not just because they provide a different way of doing things, but because different methods can reflect back on how you've done things and reveal new aspects
of what you've been doing all along.