Stack described above is the one I’ve been working on for professionally for the last year and I wouldn’t recommend it.
Main reason is the absurd amount of complexity with costs heavily outhweighting benefits gained from the solution.
For example, simple task of adding new entity consists off:
On backend: creating migration, creating data entity (Ecto), writing structure module (sanitization, validation, basic logic, if needed), mounting queries, writing input mutation(s), writing output query/queries, unit testing
On frontend: creating component, creating form, writing graphql queries, writing mutation, wrapping components/queries in components, connecting query to components, providing action handlers for inputs, unit testing, integration testing
Now I have authors list. And even though I am full stack I haven’t yet spent even single minute on having Proper UX Design set in place. Oh, do we need to add author’s birthdate? Dang, let me adjust all of that.
In my opinion technical debt accumulates faster than in other solutions. GraphQL is complex. React (done right) is complex. Apollo is complex (Elixir is simple, yet it’s only one cog). Deciding on doing file upload in GraphQL led me to a rabbit hole, which took at least a week to dug out from.
When trying to find the source of all development issues my thoughts go towards GraphQL. Maybe it is too complex or we didn’t had enough experience in it? Yet it was really nice to work with when all the backend and frontend boilerplate was already written. It makes sense, even though requires some heavy thought behind it. Maybe it’s Apollo, which locks in one of two specific trains of thought, or Absinthe, which requires side-hacks in order to get some bit more advanced features working with Apollo, like file uploads or query batching.
From a perspective I’d say this is just too much. Every single part of this stack adds overhead to the other parts. Maybe if I had more specialized team members it would get easier, but being only 3 of us, with me, as a full stack, constantly switching between all of that was a tiresome, low-productive effort. Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis, even though we had to scrap most of the frontend.
I guess there would be some benefit on having the write up of all of it, since it doesn’t even scratch the surface of the year of development issues with this stack, but even in this very "short" form it may serve for a word of warning that this stack is not necessarily something you want to care for.
Phoenix Live View[1] fits the gap between server rendered HTML pages and JavaScript rendered front-ends. If you’re after a responsive web UI without needing to learn so many disparate frameworks and technologies it could be a good fit.
We've also felt the complexity of React and Apollo. It works best when, as others mentioned, you've got distinct teams that can focus on each part. In situations where that isn't the case the same decouplings that make it easier for teams to operate independently just add overhead and complexity.
We're in a similar boat these days so in fact our latest projects are back to simple server side rendering, but we're still making the data retrieval calls with GraphQL. It ensures that that the mobile app and reporting tools we're also developing will have parity, and we don't need to write ad hoc query logic for each use case. The built in docs and validations have simply proven too useful to pass up, and you really don't need a heavy weight client to make requests.
> Maybe if I had more specialized team members it would get easier
It really would be easier. We are using this stack, and have separate backend and frontend devs. Each love their side of the stack. Being backend myself, I don't find the it too time consuming to get new entities going. However, I imagine if you were doing the whole stack and repeating ecto schema, absinthe schema, apollo queries, then it might get more tedious.
I particularly enjoy how easy it is to modify the schemas once it's set up too. If we ever need to expose more data, it is usually done within minutes.
There is a massive benefit in the forced standardization of GraphQL too. Being rigorous about standardizing APIs and how you do filtering, sorting, embedding, nesting and so on is tiring and a waste of time - you end up writing mini frameworks. Absinthe and GraphQL reduce this pain for us considerably.
Similar experience but without GraphQL. We had server side rendering with a Node server. Our production server became a Node farm with Phoenix + PostgreSQL requiring less than 1 GB of RAM and Node using at least 8 extra GBs. We eventually ditched SSR, send the React app and wait for it to render. We're back to 1 core and (mostly unused) 4 GB. It's a business application with complicated UI, customers don't mind waiting a couple of seconds of they want to start from a bookmarked screen.
For a simple UI I'd generate HTML server side with eex and spare us the cost of front end development. It's also a productivity nightmare. The amount of work needed to add a single form field with React/Redux is insane.
This the best part "Main reason is the absurd amount of complexity with costs heavily outhweighting benefits gained from the solution." Couldn't agree sooooooooo much ! thx xlii.
I will remember that one "Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis" . thx
Thanks for this. I'm not a web dev, but yes, after seeing the 3rd or 4th "and then we do this" I've just started scrolling towards the end of the post and got bored fairly quickly. My thoughts exactly: this seems way too complex for creating just a few pages; too many dependencies, too many things to remember and update.
Unless I'm missing something, Hasura requires much less effort on the back-end than what you're describing with Absinthe. Hasura runs in its own process (or processes; it doesn't have any state of its own so it can scale), and it can deliver events to your back-end via webhooks, so it doesn't matter what language you use for the back-end.
As for file uploads, it seems to me that the best way to do that is to have the front-end upload directly to your cloud storage service. Both S3 and Google Cloud Storage have a feature called signed URLs, where your back-end can create an object, grant the necessary permission, then give the client a temporary URL to upload to that object. Then just store the URL in the database.
Most of the things I've seen people complain about being complex seems to be a lack of either understanding, proper tooling and in most times, the codebase itself.
Newer technology usually adds a ton of different patterns and abstractions that can hide a lot of things away from you, so it becomes hard to understand, unless this information is presented as a 101, or you invest all your time to read documentation about each individual part of everything. Which, to be frank, nobody has time for.
I have used GraphQL with Apollo and React for the last couple of years for different kinds of projects and what I have noticed is that the tools themselves, while quite abstracted, also try to support a lot of edge cases which can make it hard to apply to your project. I had to either use other libraries or build my own that helped me abstract some of the things that were most commonplace but required a lot of boilerplate otherwise.
I have found tremendous value by following the philosophy of not to pre-optimize for everything, until it becomes a burden to work with. Although, it can be hard to determine when something is going to become so big that it will be difficult if not impossible to refactor, but you learn that as you go.
I agree! You must not use all fancy new things to build your application, just because they exists.
We also took a step back and removed GraphQL-stack and use simple clean REST-Api only, it has increased our productivity and we don't have to divide our time for an other module, which must be maintained too. Before we used REST and GraphQL, becuase it make no sense to put everything into GraphQL.
I assume you're keeping Elixir/Phoenix as RESTful api, and then React on the front-end? I've heard Redux suffers from similar issues with complexity, so what will you do?
Hey folks! Co-Author of Absinthe here, happy to answer any questions about it. The post here is good, although these days we recommend using Dataloader vs Absinthe.Ecto. Dataloader really extends the idea behind Absinthe.Ecto while providing in request caching, pluggable backends, and easier query manipulation.
I've got an app with back-end oauth-based login. I had a bit of a headache integrating the sessions with Absinthe and finally arrived on this in my Context module:
```
def call(conn, _) do
context = build_context(conn)
Absinthe.Plug.put_options(conn, context: context)
end
def before_send(conn, %Absinthe.Blueprint{} = blueprint) do
if blueprint.execution.context[:logout?] do
Auth.drop_current_user(conn)
else
conn
end
end
defp build_context(conn) do
with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
{:ok, data} <- MyApp.Token.verify(token),
%{} = user <- get_user(data) do
%{current_user: user}
else
_ -> %{}
end
end
# rest of the file
```
Then made a Logout middleware that sets logout? to true in the resolution context.
Is digging into the Blueprint as in the code above necessary? Is there a simpler way of solving this?
I just started a new app, and I decided to try Phoenix LiveView. It's really simpler and easier to work with. It won't work well with all kind of apps, especially those with offline support, but for many many apps/website it is a good fit.
The last app I did was a mobile app written in Elm with Phoenix. I can't recommend Elm enough, it makes JS much easier to work with. For communication I used a simple REST oriented API.
While I understand why GraphQL exists, I think it is a major addition in complexity for little to no benefits. I tried it, wrote half a project with it, but they removed it. When things gets complex (for example, let's say the user.email field can be redacted under some conditions) your endpoint becomes really hard to manage. GraphQL certainly have a use for large API with graph oriented datasource (well, like facebook), but it is a specific use case.
Does anyone here who is working with elixir professionally have a sense of what kind of mastery is needed to jump into an elixir dev role? I've used it on and off for ~4 years at this point and in a few side projects (most recent one using everything in the title, weirdly enough) but can't really tell if I'm "qualified" to look for a job in it. It's this weird loop of "I've only done something as a hobby so I'm not qualified to do it professionally" vs "If I don't use it professionally I'll never be qualified to do so".
> I also find a lot of joy in its pattern matching and its pipe operator. It’s refreshing writing heavily functional code that still approaches the beauty of ruby, and I find that it drives me to think clearly about my code and write fewer bugs as a result.
This.
I use Elixir everyday. While it feels 'refreshing' to use pattern matching, sometimes it gets over-used, as a simple swtich-case statement is easier to read.
Same with pipe operator. I found newbie programmers tend to pipe for the pipe's sake, and write functions just to form the 'shape' of pipe, but the data shape changes are not easy to see and inspect.
Great article. One point though (not aimed at the author).
The majority of engineering articles I see today are along the lines of "X with Y, using Z", where X, Y, Z are specific products, frameworks or libraries, often trademarks.
I rarely see more generic engineering/architecture topics such as: [virtual DOM based / string based client side templating] with [JSON/Protobuf] over [REST/Websocket] with a [compiled / interpreted / JIT compiled / virtual machine based] backend runtime, built on [RDBMS/NoSQL/hybrid] etc.
I've just been trying to get back into elixir recently, myself. I'd done some basic crud 'helloworld' stuff when I first tried about a year into professional development.
I've since had the Fortune to spend time learning about cloud native apps, distributed service patterns, and supporting infrastructure (spring cloud, pcf, vanilla k8s, gcp) and now returning to elixir having at least better understanding of what erlang and OTP offers.
I'm super excited to see I'm not alone in finding this sort of stack is worth fiddling with(although, I tend to pick Vue when not using angular for work).
Thanks for posting this!!
For those interested in what resources I'm leaning on:
The Manning 'Elixir in Action' and the Pragmatic Programmer's graphql texts along with exercism.
Anybody else have any preferred resources for these technologies?
Major kudos to them for open sourcing their platform. It covers like 50+ common web dev features.
I pretty much skimmed the docs to get the ultra basics, looked at that source while I was building my own app and then asked questions when I got really stuck. Even managed to sneak in some refactoring on their code base a few days into learning Elixir. It's super approachable without much more if you have some type of programming background beforehand.
Elixir and OTP are really nice, but I'm frustrated by the type system, even with typespecs. I finally gave in and started learning Haskell. I would still choose Elixir/Phoenix for some web apps though.
Why is Elixir always being paired with Phoenix? Can’t I just have a backend API running on Elixir and a javascript front end to interact with it? I’d prefer a simple React, Postgres, Elixir stack (REP).
I have just starting learning Elixir and Phoenix. It seems like a solid replacement for Rails. But I keep getting hung up on performance. With React, it seems like you are developing more API back-end, then web server back-end (if that makes sense). But when you look at the performance compared to Go and the steeper learning curve, why not just use Go? Kubernetes really solves the share-nothing let-it-fail aspect. Obviously, Erlang/Elixir are hands-down a good fit for fault tolerant distributed systems where performance may not be as critical or you can use NIF. But outside chat and a few other use-cases, not sure the majority of web services falls into this. However, the functional code is so nice. Anyways, any input on how to convince engineering leadership how Elixir might be better than Go would be helpful.
It always bugs me that, with Absinthe, you have to define Ecto schema and GraphQL schema separately, when most of the time, they are very similar. Can Absinthe somehow figure out from Ecto?
Always wanted something like that as well. I am guessing until we get something like `clojure.spec` -- true typing, even if gradual (but at least not a success-typing) -- then it's not happening anytime soon.
It's 100% the syntax. It appeals to ruby devs and also I think it's a lot easier to grok in general. But you can write Erlang code directly in an Elixir file, you can use Erlang libraries directly in Elixir, and Elixir compiles to the same BEAM instructions that Erlang does, so there's functionally no difference in terms of capabilities or what happens when they run.
It's not just syntax. There are semantics differences.
A big one is that Elixir allows rebinding of a variable. Erland does not, which tends to lead to ugly names since in erlang you can't do common reassignment patterns like x = x * someConversionFactor.
* `mix xref` has a family of sub-commands that gives you good code analysis (callers, callees etc.)
* First-class Unicode support. All strings are UTF-8 by default (you can fallback to ASCII if you need it; there are also good transcoding libraries in both Elixir and Erlang).
Many folks seem to prefer Elixir to "Plain old Erlang" because for those who come from an imperative and / or object-oriented background, picking up Erlang means taking on a significantly steeper learning curve.
When you get to the absinthe part you lose me. It's the same gut feeling I had when I see Redux where it works but it was created at the very beginning of this entire workflow discovery and better tools/approaches have since come out.
Redux is still a solid choice. Apollo is used more often now because it handles a lot of GraphQL optimization but it's by no means a panacea. And sometimes, despite being an advocate of Apollo, I miss the simplicity of Redux.
[+] [-] xlii|7 years ago|reply
Main reason is the absurd amount of complexity with costs heavily outhweighting benefits gained from the solution.
For example, simple task of adding new entity consists off: On backend: creating migration, creating data entity (Ecto), writing structure module (sanitization, validation, basic logic, if needed), mounting queries, writing input mutation(s), writing output query/queries, unit testing On frontend: creating component, creating form, writing graphql queries, writing mutation, wrapping components/queries in components, connecting query to components, providing action handlers for inputs, unit testing, integration testing
Now I have authors list. And even though I am full stack I haven’t yet spent even single minute on having Proper UX Design set in place. Oh, do we need to add author’s birthdate? Dang, let me adjust all of that.
In my opinion technical debt accumulates faster than in other solutions. GraphQL is complex. React (done right) is complex. Apollo is complex (Elixir is simple, yet it’s only one cog). Deciding on doing file upload in GraphQL led me to a rabbit hole, which took at least a week to dug out from.
When trying to find the source of all development issues my thoughts go towards GraphQL. Maybe it is too complex or we didn’t had enough experience in it? Yet it was really nice to work with when all the backend and frontend boilerplate was already written. It makes sense, even though requires some heavy thought behind it. Maybe it’s Apollo, which locks in one of two specific trains of thought, or Absinthe, which requires side-hacks in order to get some bit more advanced features working with Apollo, like file uploads or query batching.
From a perspective I’d say this is just too much. Every single part of this stack adds overhead to the other parts. Maybe if I had more specialized team members it would get easier, but being only 3 of us, with me, as a full stack, constantly switching between all of that was a tiresome, low-productive effort. Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis, even though we had to scrap most of the frontend.
I guess there would be some benefit on having the write up of all of it, since it doesn’t even scratch the surface of the year of development issues with this stack, but even in this very "short" form it may serve for a word of warning that this stack is not necessarily something you want to care for.
[+] [-] slashdotdash|7 years ago|reply
[1] https://github.com/phoenixframework/phoenix_live_view
[+] [-] benwilson-512|7 years ago|reply
We're in a similar boat these days so in fact our latest projects are back to simple server side rendering, but we're still making the data retrieval calls with GraphQL. It ensures that that the mobile app and reporting tools we're also developing will have parity, and we don't need to write ad hoc query logic for each use case. The built in docs and validations have simply proven too useful to pass up, and you really don't need a heavy weight client to make requests.
[+] [-] aspett|7 years ago|reply
It really would be easier. We are using this stack, and have separate backend and frontend devs. Each love their side of the stack. Being backend myself, I don't find the it too time consuming to get new entities going. However, I imagine if you were doing the whole stack and repeating ecto schema, absinthe schema, apollo queries, then it might get more tedious. I particularly enjoy how easy it is to modify the schemas once it's set up too. If we ever need to expose more data, it is usually done within minutes. There is a massive benefit in the forced standardization of GraphQL too. Being rigorous about standardizing APIs and how you do filtering, sorting, embedding, nesting and so on is tiring and a waste of time - you end up writing mini frameworks. Absinthe and GraphQL reduce this pain for us considerably.
[+] [-] pmontra|7 years ago|reply
For a simple UI I'd generate HTML server side with eex and spare us the cost of front end development. It's also a productivity nightmare. The amount of work needed to add a single form field with React/Redux is insane.
[+] [-] maitredusoi|7 years ago|reply
I will remember that one "Right now we’re disassembling app to a classic REST-app and we’re seeing development speed increase on week-to-week basis" . thx
[+] [-] molteanu|7 years ago|reply
[+] [-] namelosw|7 years ago|reply
Although complex, GraphQL (done right) is much easier than REST (done right).
[+] [-] mwcampbell|7 years ago|reply
As for file uploads, it seems to me that the best way to do that is to have the front-end upload directly to your cloud storage service. Both S3 and Google Cloud Storage have a feature called signed URLs, where your back-end can create an object, grant the necessary permission, then give the client a temporary URL to upload to that object. Then just store the URL in the database.
[+] [-] fnky|7 years ago|reply
Newer technology usually adds a ton of different patterns and abstractions that can hide a lot of things away from you, so it becomes hard to understand, unless this information is presented as a 101, or you invest all your time to read documentation about each individual part of everything. Which, to be frank, nobody has time for.
I have used GraphQL with Apollo and React for the last couple of years for different kinds of projects and what I have noticed is that the tools themselves, while quite abstracted, also try to support a lot of edge cases which can make it hard to apply to your project. I had to either use other libraries or build my own that helped me abstract some of the things that were most commonplace but required a lot of boilerplate otherwise.
I have found tremendous value by following the philosophy of not to pre-optimize for everything, until it becomes a burden to work with. Although, it can be hard to determine when something is going to become so big that it will be difficult if not impossible to refactor, but you learn that as you go.
[+] [-] progx|7 years ago|reply
We also took a step back and removed GraphQL-stack and use simple clean REST-Api only, it has increased our productivity and we don't have to divide our time for an other module, which must be maintained too. Before we used REST and GraphQL, becuase it make no sense to put everything into GraphQL.
[+] [-] fouc|7 years ago|reply
[+] [-] srndh|7 years ago|reply
Any alternatives that you would like to suggest?
[+] [-] y4mi|7 years ago|reply
still with elixir/phoenix or did you scrap even that part?
[+] [-] benwilson-512|7 years ago|reply
[+] [-] AlchemistCamp|7 years ago|reply
```
```Then made a Logout middleware that sets logout? to true in the resolution context.
Is digging into the Blueprint as in the code above necessary? Is there a simpler way of solving this?
[+] [-] schneidmaster|7 years ago|reply
[+] [-] bnchrch|7 years ago|reply
[+] [-] kuon|7 years ago|reply
The last app I did was a mobile app written in Elm with Phoenix. I can't recommend Elm enough, it makes JS much easier to work with. For communication I used a simple REST oriented API.
While I understand why GraphQL exists, I think it is a major addition in complexity for little to no benefits. I tried it, wrote half a project with it, but they removed it. When things gets complex (for example, let's say the user.email field can be redacted under some conditions) your endpoint becomes really hard to manage. GraphQL certainly have a use for large API with graph oriented datasource (well, like facebook), but it is a specific use case.
[+] [-] sugarpile|7 years ago|reply
Same goes for react.
[+] [-] diNgUrAndI|7 years ago|reply
This.
I use Elixir everyday. While it feels 'refreshing' to use pattern matching, sometimes it gets over-used, as a simple swtich-case statement is easier to read.
Same with pipe operator. I found newbie programmers tend to pipe for the pipe's sake, and write functions just to form the 'shape' of pipe, but the data shape changes are not easy to see and inspect.
[+] [-] hliyan|7 years ago|reply
The majority of engineering articles I see today are along the lines of "X with Y, using Z", where X, Y, Z are specific products, frameworks or libraries, often trademarks.
I rarely see more generic engineering/architecture topics such as: [virtual DOM based / string based client side templating] with [JSON/Protobuf] over [REST/Websocket] with a [compiled / interpreted / JIT compiled / virtual machine based] backend runtime, built on [RDBMS/NoSQL/hybrid] etc.
Am I alone in feeling this way?
[+] [-] em_jones|7 years ago|reply
I've since had the Fortune to spend time learning about cloud native apps, distributed service patterns, and supporting infrastructure (spring cloud, pcf, vanilla k8s, gcp) and now returning to elixir having at least better understanding of what erlang and OTP offers.
I'm super excited to see I'm not alone in finding this sort of stack is worth fiddling with(although, I tend to pick Vue when not using angular for work).
Thanks for posting this!!
For those interested in what resources I'm leaning on: The Manning 'Elixir in Action' and the Pragmatic Programmer's graphql texts along with exercism.
Anybody else have any preferred resources for these technologies?
[+] [-] nickjj|7 years ago|reply
Major kudos to them for open sourcing their platform. It covers like 50+ common web dev features.
I pretty much skimmed the docs to get the ultra basics, looked at that source while I was building my own app and then asked questions when I got really stuck. Even managed to sneak in some refactoring on their code base a few days into learning Elixir. It's super approachable without much more if you have some type of programming background beforehand.
[+] [-] bdibs|7 years ago|reply
It's written by the creator of Phoenix (Chris McCord) and the creator of Elixir (José Valim), and it's a fantastic intro to Phoenix.
[+] [-] thegabez|7 years ago|reply
https://codestool.coding-gnome.com/courses/elixir-for-progra...
[+] [-] stephan83|7 years ago|reply
[+] [-] rossenberg79|7 years ago|reply
[+] [-] onebot|7 years ago|reply
[+] [-] adamnemecek|7 years ago|reply
I kinda know the theoretical differences but I’m curious what hn thinks. Has someone actually deployed things in some of these.
I do have preference towards rust but maybe there’s something better.
[+] [-] AlchemistCamp|7 years ago|reply
The main difference is I've been using TypeScript and the Absinthe resolvers are using Dataloader.
[+] [-] TN1ck|7 years ago|reply
[+] [-] sntran|7 years ago|reply
[+] [-] pdimitar|7 years ago|reply
[+] [-] hestefisk|7 years ago|reply
[+] [-] schneidmaster|7 years ago|reply
[+] [-] TylerE|7 years ago|reply
A big one is that Elixir allows rebinding of a variable. Erland does not, which tends to lead to ugly names since in erlang you can't do common reassignment patterns like x = x * someConversionFactor.
[+] [-] pdimitar|7 years ago|reply
* Tests inside documentation.
* Macros.
* Protocols.
* `mix xref` has a family of sub-commands that gives you good code analysis (callers, callees etc.)
* First-class Unicode support. All strings are UTF-8 by default (you can fallback to ASCII if you need it; there are also good transcoding libraries in both Elixir and Erlang).
[+] [-] jaytaylor|7 years ago|reply
[+] [-] nathan_long|7 years ago|reply
Elixir's whole reason for existence is to make the Erlang programming model easier to use. Try it and see if it does.
[+] [-] AlchemistCamp|7 years ago|reply
The two really big features are macros and protocols.
[+] [-] macintux|7 years ago|reply
This drives me batty. More familiar? Sure. “Friendlier” is very subjective.
[+] [-] draw_down|7 years ago|reply
[deleted]
[+] [-] yeskia|7 years ago|reply
[+] [-] schneidmaster|7 years ago|reply
[+] [-] swyx|7 years ago|reply
[+] [-] sergiotapia|7 years ago|reply
[+] [-] AWebOfBrown|7 years ago|reply
[+] [-] chrisco255|7 years ago|reply
[+] [-] johnnyji|7 years ago|reply