Ruby/Rails backend with a React FE seems to be pretty popular these days, and is where I spend most of my time, but honestly I'd rather go back to Rails views with Stimulus for some of the components that need it.
3 years ago we were breaking monoliths into microservices, now it seems that we overshot that, and I'm much more likely to see people sticking with one main monolith, and maybe one or two other services. Similarly, I'm much more likely to see people choosing NOT to use JS frameworks these days than use them.
I've taken the approach of building the monolith first with an architecture that allows me to easily pull parts of it out into services when I hit constraints and need that scalability.
This is the approach we used at a certain budget shaving startup and it worked very well.
The boosts to dev speed from a monolith / monorepo over a more "scalable" approach are huge and allow you to focus on business problems instead of self imposed tech challenges.
If you push to keep your code organized into modules with simple function APIs, and make sure that code outside your modules doesn't use the package internals directly, then your function APIs can be easily extracted into a REST/gRPC call, and the only thing that changes (to the first approximation) is your internal service (function) calls become external service (api) calls.
Obviously you now need to add an API client layer but in terms of code organization, if your packages are cleanly separated then you've done a lot of the work already. (Transactions being the obvious piece that you'll often have to rethink when crossing a service boundary).
The advantage of this approach is that it's much easier to refactor your internal "services" when they are just packages, than it is to refactor microservices after you've extracted them and set them free (since upgrading microservice APIs requires more coordination and you can't upgrade clients and servers all in one go, as you often can inside the same codebase).
I've been there. This only seems to 'work'--until you try to raise throughput/reliability and lower errors/latency. What ends up happening is that the module boundaries that made sense in a monolith don't make sense as microservices when communications are allowed to fail. Typically the modules are the source-of-truth for some concern with consumers layered above it. This is the worst pattern with microservices where a synchronous request has to go through several layers to a bottom-level service. With microservices you want to serve requests from self-contained slices of information that are updated asynchronously. The boundaries are then not central models but rather one part of a workflow.
This is the biggest favor you can do for yourself. The developer experience is as easy as production without descending into container induced madness.
Elixir and Phoenix. The contexts pattern used in Phoenix is the most modular, easily microserviced way of structuring apps I’ve ever used. I slapped myself on the forehead when I first saw it. Duh. It’s really fantastic. Highly recommend
Vue.js seems to be particularly popular with the Ruby/Rails and PHP/Laravel crowd. I'm guessing it has to do with the SSR integration with the framework's views.
The beauty of rails is how simple and quick it is to get a robust (if bare) application up and running.
Even if you use sinatra or padrino instead, the wealth of the web community built around ruby still makes it rather easy.
If I were a tech lead and had to make a choice now, in 2019, I would still (probably) choose rails-api for a backend with some separate frontend.
I've been slowly working on a backend for a web app in Rust, and it's really made me realize the sheer amount of things rails provides for you. It has its problems, of course, but so does everything else.
> I've been slowly working on a backend for a web app in Rust
I'm interested in why you made the choice to use Rust for a web app? It feels like you love a good challenge, but there are obviously technical constraints for some apps. I look around and wonder if the options have really changed a whole lot since 2006?
If you need productivity, choose Ruby or Python (or maybe JS.) Java and .NET exist if you have more exacting performance requirements (these platforms are somewhat more pleasant in 2019 and I see Go as joining their ranks.) Or, you choose to go really low-level with C/C++, or perhaps Rust in 2019. The latter never seemed like a viable option unless you had extreme performance concerns.
I'm working on three projects for three customers right now. Phoenix, Django and Rails. From my personal experience:
1) Elixir + Phoenix have some definite advantages when spawning asynchronous tasks (no external dependencies, it's in the language) and it runs reasonably quick.
2) Coding in Ruby on Rails is faster. An example, it's much easier to write queries in ActiveRecord than in Ecto, which is over engineered for the common needs of a web app. Queries, updates, transactions, everything are productivity killers there. For 99.9...% of the web apps it will be fast enough and the CPU will sit idle most of the time.
3) Django is as bad as Rails for concurrency and performances. The ORM is more verbose than ActiveRecord but luckily it's much closer to AR than to Ecto. The project structure is very loose compared to both Rails and Phoenix, which is bad for onboarding people coming from other Django projects ("ah, you put that there, why?")
My suggestions. If you know you're going to build a big application, scale it, etc, use Phoenix. Small to medium size: use Rails unless you're already a Python shop. In that case use Django. Smaller frameworks like Sinatra or Flask: it's not worth using them with the exception of very small applications that won't grow bigger. If they do you'll start adding components and turn them into an homegrown nightmare resembling the more polished Rails and Django.
How much experience do you have with each of the three? Like many, I moved to Elixir + Phoenix from Rails and having a considerable amount of experience with Node.
I've found Ecto to be a productivity boon once I understood it, except for very small projects. For those, I reach for Rails just as you suggested.
My real productivity boost with Phoenix is that Elixir (or the Erlang VM, to be accurate) can take care of so many things I'd be dishing off to Redis, Sidekiq or other tools in a Rails app. That and the lack of surprises from metaprogramming or ActiveRecord default behaviors I haven't kept in mind while building.
That's been my experience with sinatra and padrino as well.
The promise of a lean app with rails' ecosystem is too good to be true, and it is. It's so much easier to just start with rails from the beginning instead of slowly adding things piecemeal.
I still like sinatra and flask but they have one purpose: very very simple webapis, that's it. Nothing even remotely as complex as user sessions or forms.
I've been playing with Pheonix/Elixir here and there but I've never sat down and written a full app in it. I guess that's the future ultimately, at least for larger companies that need that performance.
You can annotate your types if you want and get a lot of editor benefits. Also, I don’t buy the “we need strong types for everything” argument, as optional typing in Clojure, JavaScript, and now Ruby gives you lots of benefits without having to think all the way through the app’s architecture.
Can you give me an example of this? I'm genuinely wondering if I've just dodged a pain point so far.
My other strongest languages are Golang and JavaScript, so I'm used to (duck-)typed and untyped approaches, and I've never felt that Elixir was difficult to work with because of the lack of types. I've treated pattern-matching like type inference, and as long as you have guard clauses, the worst that can happen is a runtime error which you "let fail" anyway.
I guess the guard clauses are a bit of boilerplate, but I feel like these complaints have a different root. I just don't see it yet.
I just like using Rust. This is a small side project of mine so it's been more fun to experiment with using Rust to make a backend api than to stick with rails.
I also recently dealt with maintaining / upgrading a large, not well architected rails app. Debugging mysterious issues in Rails (and asp.net webapi2) sucks compared to Rust.
For a professional project I would largely go with rails, depending on the situation. asp.net isn't bad either.
I would say the productivity comes from the established community around Ruby, when it comes to web development.
It's true that you end up running into certain types of bugs that a strongly typed language would've caught immediately, but in java/c#/rust you end up having to hand implement more stuff depending on what you're doing.
Ideally I would use rust, but I also think Ruby can work quite well if defensive programming practices are followed, as well as running linters, static analyzers, etc. Most issues seem to stem from poor architecture or tightly coupled, dense code.
I lead the work of node APIs but have always worked on convincing people Lumen/Laravel/Rails would be a good choice because of the ecosystem and opinions. Even in the node/express world it’s insane how much rails does for you over express.
Principally the file organisation that pretty much forces you to stick all your controllers in one single directory. If you want to create a truly modular application (think microservices) RoR defaults seemed to work against you.
I understand what you mean, but that is one of the trade offs when using opinionated frameworks.
I've found that a more effective way to achieve the same results as microservices is to deploy the monolith to multiple servers and have each one acting in a specialized capacity.
This tends to make testing a lot more consistent and improves code reuse dramatically.
If you really want to make it modular you could always package the functionality you want to share into gems.
Devise[1] alone makes it worth using rails for many webapps. Throw in things like simple form[2] and will paginate[3] and it's incredible how much you can get done cleanly and quickly. Using something like intercooler.js, you can build a solid modern web app in a fraction of the complexity of most systems today.
That's interesting! I do some ajax with the js.erb files, but I honestly find that it's pretty messy & gross. Maybe I could eliminate those .js.erb files with intercooler.js (or clean them up).
Do you find that intercooler lets you avoid using .js.erb files altogether?
Ruby is among the slowest languages out there. Which is fine for most webapps, but calling it great for CPU intensive tasks... I don't understand what logic is being used to come to this conclusion.
> Ruby has the same thing that every other language has, call outs to C code under the hood for most of the real work.
Yes, this is exactly the problem. Ruby is so slow that you end up writing C extensions when you want to do any non-trivial computation. The documentation is bad, the tooling is bad, the build/CI complications are bad, and there's not much community info online about the process. And now your RoR developers have to support a C library, where a segfault can kill an entire Ruby interpreter.
I don't think most RoR apps run into these problems, which is why RoR is such a great thing in the first place, but we shouldn't brush aside how slow it is, and the implications of that when it becomes a problem.
> Ruby is so slow that you end up writing C extensions when you want to do any non-trivial computation. The documentation is bad, the tooling is bad, the build/CI complications are bad, and there's not much community info online about the process.
Perhaps for a classic C extension that's true, but (for portability across Ruby interpreters and other reasons), using FFI is usually the preferred way to do new C interop, and none of that is true for FFI.
When Rails came out, Ruby was the slowest language by far because its interpreter literally just walked an AST. No intermediate byte code, no VM, it just eval'd an AST. This reputation stuck longer though, although yeah I imagine Ruby is still slower than Python and Node even now despite having a VM.
When I get half a million users I’ll start worrying about it. Most startups fold way beyond this point. Saving dev time and the pleasure and ease of working with Ruby more than make up for the speed difference.
It will scale well beyond that point if ut is properly organized into services.
Check out some of the comments above about "service oriented monolith".
That being said, know your limits. If you are doing a lot of image processing for instance it may be worth investing in an external service in a language that is a better fit for that problem.
Ruby 3x3 is a thing (make Ruby 3.0, 3x faster), and Ruby 2.6 is said to include an optional JIT compilation mode.
I haven't seen any benchmarks or analysis that say JIT is ready for production use, but indications are strong that the next version of Ruby will include a lot of surprising things, like maybe a type system?
Ruby got a reputation for being slow very early on, and my understanding is a lot of that was due to slow code in Rails (version 2 and/or 3?). Rails is a lot better now. Ruby continues to evolve. Still, I don't know how many Ruby developers have chosen it for reasons owing to CPU-intensive work. This does seem like a stretch, even if we are beating Python in some benchmarks.
With the amount of object allocation in standard Rails behavior, it seems to currently perform worse in most applications. But it's a stepping stone for the future. Basically, it's there - but few/no people are using it.
Yes the key thing the Ruby JIT needs to eventually do is reduce the load on memory allocation and the GC - that's where the benefits will be in the future.
Yes, it's there, you can enable it, but I haven't heard anyone recommend it for production use, and it's not enabled by default. I tested it myself, it seemed to make my boring use case a little bit faster, once the JIT cache is initialized.
If you can call it that... my site is actually a Jekyll blog, in development mode, this is running in the most needlessly Ruby way. (A production deployment would have pre-rendered all the pages, and there wouldn't be a need to have a Ruby process even be present on the running server.)
Each visitor hits a Ruby process which renders the page directly, or serves it up from filesystem cache. I'm not honestly sure which, it could be monitoring the filesystem for changes to the markdown files, and only recompiling them when it observes a change on disk... or it could be rendering each request freshly for the visitor.
It would be hard-pressed to call this a production deployment though, I can say without a blog post or any hard data to back it up that when I did side-by-side trials, the page loading response times were marginally faster with the JIT enabled, after repeated trials.
After watching @tenderlove's talk from RailsConf 2019, I think I'm obligated to say also about this that, your results will improve if you paid attention to cache hit ratios, and consider tuning the JIT Cache size to achieve the maximum benefit. Perhaps these things will be better in a later iteration of Ruby JIT?
I was listening to a recent interview with DHH and he makes a great point that Rails and Ruby are about optimizing for developer happiness and dev speed. Computing power keeps becoming cheaper and ruby keeps getting faster but developer's salaries continue to be the number one cost for software companies.
Computing power can be increased by scaling vertically or horizontally. Vertical scaling has an upper limit, and when one reaches it, in certain contexts, end of the happiness :-)
Case in point: GitLab; they have Go microservices. GitHub was also hiring Go developers, so very likely they've done the same.
Of course, few companies are GitLab/GitHub, but the point is that it's not possible to make absolute statements about power being cheap.
Agreed. Yet even if you don’t reach Gitlab scale, languages like Go and Elixir can help you deliver the same service at 10-20% the cost.
By being able to run your service on a single server instead of ten allows your team to focus on features instead of scaling your application using background concurrency, caching, or “micro-services”.
Imagine what you could do when you don’t need to manage a fleet of web-servers.
100% agree. Developer happiness, conceptual compression is another important concept that DHH likes to talk about. If there's a hard topic that devs need to understand sometimes, Rails makes it a point to guarantee that devs don't need to understand all those things all the time.
Conceptual compression means that you can unpack those ideas and work with them when you need them, or pack them back up and don't pay attention to them when you don't need them.
The biggest example that I can think of is ActiveRecord. I can't tell you the number of times I've seen presentations by programmers who work with other frameworks like .NET, who have embedded large, complex SQL queries into their code. They don't think twice about it, don't even flinch, parsing SQL is part of the cost of doing business in this language.
Just a disclaimer, I work with Rails every day, but I don't really like it all that much and I really don't like ActiveRecord. We have Rails legacy apps and I maintain one of them. I'd still use Rails for some things, but it I'm pretty picky about what I think it's good for.
Having said that, it makes me a bit sad that there are quite a few developers who actively avoid SQL. Granted the syntax is absolutely awful, but it is a very useful and powerful language. The concepts behind relational databases are embraced by SQL in ways that I can't imagine in any other language (I'm sure there must be other good relational query languages somewhere, but I've never been exposed to them). Honestly, if I have developers working with a relational DB doing anything even a little bit complex I want them to not not think twice or even flinch to use or read it (and I say that not really being all that accomplished with the language myself).
Rails has some good niches where it is very well suited. ActiveRecord is OK if you have a very particular data model. However, as they say, if a hammer is your only tool, every problem starts to look like a nail. You may find that if you approach the problem differently, you will find reasons why people choose not to use very simplified ORMs.
As a counterpoint, I love ActiveRecord. It's a Swiss Army knife that's got pretty much everything you need, you just need to find the right tool and use it.
What ActiveRecord isn't is discoverable. Which is to an extent understandable. Its domain is literally anything you could express in SQL. But you really can do anything you want with it, you just have to find the right abstraction. A tool I use a lot is to .to_sql which will show you the compiled SQL fragment in a debugging session. You can compose with bare SQL fragments and ActiveRecord even includes an intermediate library so that you can work at the relational algebra level if you want.
It's not that ActiveRecord forces you to work a certain way. It just doesn't advertise all its features.
Long ago I liked ActiveRecord(AR), then I worked at a company that did large aggregations in SQL and found it to be severely limited in working with complex queries which use many JOIN statement and sub-queries. During that time I came to enjoy working with the Sequel[0] gem, and its fantastic documentation. Now, after working with Ecto[1] in Elixir, I've found that the ROM[2] builder pattern is a better approach to abstracting SQL and mapping the results to Ruby objects. It's much cleaner and more maintainable than a long mess of AR queries.
Ryan Bigg wrote a great book about breaking away from AR in Rails called Exploding Rails[3]. It's a good read.
From what I've seen of Sequel is looks awesome and I've been dying for an excuse to use it.
found [ActiveRecord] to be severely limited in
working with complex queries which use many
JOIN statement and sub-queries
I'd humbly suggest that this is not a flaw of ActiveRecord whatsoever! I think this is AR working exactly as designed.
I would certainly agree that SQL > AR once your queries grow past a certain (fairly low) complexity threshold.
But, AR (wisely) doesn't try to be a complete replacement for SQL. It very happily (and even elegantly, I might say?) lets you use raw SQL pretty much any time you, the developer, feel it's more convenient/productive. That principle is pretty explicitly baked into AR.
Many ORM frameworks don't make this easy at all, whereas with AR it's very painless.
I have a lot of beef with AR but I think this is one of its strong points.
Yes, I agree. For simple to low complexity queries AR is hard to beat, especially if your SQL knowledge is limited, and it allows for arbitrary SQL statements as string arguments.
My biggest issue with AR is that the arbitrary SQL cannot be combined or reused in a way which leads to compositional queries. An example would be to break up a large SQL statement into several smaller statements and then recombine them in different ways, safely.
What I found with Ecto and ROM is that, while simple to low complexity SQL was about the same as AR, maybe 10% harder to understand, complex to very complex statements were about equivalent to their SQL counterparts and could be composed together. That was a major win over AR for non-trivial queries.
Yes, I know about Arel, and I’ve used it a few times to do hairy queries, but honestly it felt like swimming against the current and I wouldn’t recommend anyone write Arel queries by hand.
Wow, this blew up! Thanks for all the information!
I've used sequel before and agree, it's closer to the SQL and does the job well. ROM looks excellent. I'm just reading down the page you linked, and I love every word. I can say I've had all of these problems with ActiveRecord, and ROM's answers to them all look great!
Hmm... I see your point. There is always the confusion between ActiveRecord the software and "Active Record" the pattern. It's really the latter that I dislike. I've used AR-the-software to build my own relational object mapping with scopes and enjoyed it quite a lot. But there was lots of SQL in that code :-)
I agree! SQL-avoidant developers are something we actively try to screen out in our interviews. (Being too gung-ho about using raw SQL -- something I've been accused of --is bad too, but at least it's not a path of willful ignorance)
Granted [SQL's] syntax is absolutely awful, but
it is a very useful and powerful language. The
concepts behind relational databases are embraced
by SQL in ways that I can't imagine in any other
language (I'm sure there must be other good relational
query languages somewhere, but I've never been exposed
to them)
Is SQL's syntax really that awful? I think any language that maps fairly closely to relational algebra will wind up looking similar, and a lot of frustration that we feel toward SQL's syntax is because sometimes we want to express procedural concepts in a language that is based on relational algebra.
It's somewhat significant, I think, that when Microsoft came up with LINQ (sort of a language- and backend- agnostic query language) they basically settled on something that looks like SQL but puts the FROM clause first, mainly so the editor can give you typing hints.
I'm not saying LINQ was a rousing success, but that was a pretty major greenfield MS devtools initiative and they have some pretty smart language/compiler people, so it's interesting that they settled on something not too different from good ol' SQL.
I probably shouldn't have said the syntax is awful because as you say, I've never seen anything better. But I just think that SQL is this nice composable structure but it seems needlessly awkward to write from my naive point of view. I keep thinking that something more lisp-like would be nicer, but perhaps it wouldn't work. Maybe it's something I can try as an experiment some day :-)
I love Ruby/Rails and owe my career to it, but I don't see the advantage of choosing this stack in 2019 over Elixir/Phoenix for greenfield projects. Elixir tooling and libraries are now up to par and I'd argue have surpassed RoR.
At this point, it's just as productive (perhaps even more as you don't have to glue on a bunch of additional components) and joyful to work with like RoR but massively scalable out of the box thanks to the Erlang VM.
Or not! Here in Toronto we've had a req open for a senior Ruby dev since like last October. We're seriously debating switching over to something else for no reason other than lack of available Ruby talent. Relatively view new devs are picking up Ruby nowadays. Job posting is here if anyone's interested --> https://angel.co/company/akira-2/jobs/106786-sr-full-stack-r...
My experience with ruby devs is that they are usually employed and not actively searching. Since it isn't the flavor of the week anymore you don't have as many programmers entering the field as ruby devs.
Mentoring is always a solid option too, the language and frameworks are simple. Any CS graduate worth his degree will pick it up in no time.
Maybe I'm biased, but I recently relearned rails (having done one project in it about 6 years ago) and found it much easier to pick up than Elixir/Phoenix, despite liking Phoenix quite a bit.
I’ve been considering Phoenix and looked at Elixir which made me realize how much I love Ruby. So I am starting my new Rails project and loving every second of it.
Among my peers (undergrads in college) Rails has largely lost mindshare to python and Node (and even PHP!). At the 3 hackathons I went to this semester I could not find a single other person who knew Rails (so I always have to switch to node or python). If you take a look at devpost, Rails is increasingly losing popularity: https://devpost.com/software/built-with/ruby-on-rails
My plea to the Rails community and Rails senior devs:
1) Fully embrace SPAs as an option, React and Vue are ubiquitous among my peers. I've seriously seen people who have difficulty understanding a for loop that know how to use react at a basic level.
2) Increase your outreach to hackathons and universities, I'd love to see more Rails companies coming out to the big hackathons and mentoring students. At the biggest hackathon in MLH (Bitcamp 2019) I couldn't find a Rails or Ember mentor. I'd also love to see more courses on coursera and other places that teach Rails, in my search for Rails courses on Coursera I only found 1 course (although Traversy media has been making some great content on youtube)!
I would say that Rails has fully embraced SPAs as an option. With Webpacker you can have React or Vue installed by default. That being said, at one point we decided to start using React to handle the front end of our Rails app and it has become a huge regret.
Recently we decided to switch back to using Rails + Turbolinks + Stimulus.js and the development speed and experience have been so much better.
It's disheartening to hear so few young people are using Rails. In my 10+ years experience, I always get drawn in by the new hot thing but always end up finding my way back to Rails for the better.
I hadn't done rails in a while when I brushed up on it, and found Turbolinks to be a game changing addition.
SPA's just require way too much work and fine-grained detail for starter applications. I would not recommend to do full-stack node over rails if you care about iteration speed early on.
I would think understanding declarative code like React is much easier for beginners than procedural code like loops and switches. For example, tons of analysts and other business people can pick up SQL for reporting, but would have a lot of trouble writing code.
I’m surprised it’s Python/Node taking that mindshare. From my perspective (fintech industry), it looked like 100% of the decreasing usage of Rails by other fintech startups have been due to choosing Elixir’s Phoenix framework instead.
A lot of finance is built on event sourcing (without calling it that), in that you can’t just overwrite your existing DB state with new state; everything has to be a ledger with a history, and you have to be able to trace the “provenance” of your data—what version of your business rules were used to compute any derived results, both so that those results (and results derived further from them) can be recomputed when you update your logic; and so you that you can use the version of the business rules appropriate to a given dataset (e.g. the tax laws appropriate for the year a given invoice was generated) when auditing that dataset, or when migrating that dataset to a new storage format.
Basically, you want:
• a runtime that forces a functional/immutable programming style (because it’s so much harder to avoid errors in algorithms that operate on mutable data), and which has lots of persistent data structures to make this style efficient;
• concurrency for processing unrelated batches;
• workload isolation (where one batch-job workload crashing doesn’t bring down your whole job processor);
• runtime inspectability and tracing (because it’s hard/illegal to replicate production customer data in staging to get matching conditions)
• a solid library to transit data between a DBMS and native record types (a Record-Relational Mapper lib), with fluent syntax for advanced relational querying features;
• and, of course, a solid Rails-like backend MVC framework to stick on top, to give people an API and web app view into your system†.
So, a lot of these companies are choosing an Elixir+Phoenix stack 85% because of ERTS, 10% because of Ecto, and 5% because of Phoenix itself.
For the companies that realize that what they’re doing is Event Sourcing, the https://github.com/commanded/commanded CQRS/ES framework is also an exciting “feature” of the Elixir ecosystem.
† Sometimes this is considered a separate need—you can build the business event-processing system, in Elixir, as a custom e.g. gRPC API service; and then you can use whatever you want for the web-API/web-app service that fronts it. Sometimes these shops use Rails for the web layer! But just as likely they use Node (because they delegate the front-end layer—now free of business logic—to the front-end devs, and front-end devs know JS) or Elixir+Phoenix (if the same backend devs writing the event-processing system are tasked with writing the web layer.)
To play devil's advocate, I am a junior dev in a relatively large US market and Rails is still very popular. I'm currently using it as the backend API for my side project. It's just simple and fun to code in.
Most of my career has been spent building web apps in Rails, Node, a smattering of python frameworks.
Python is pretty great, but the fragmentation of the ecosystem puts it behind Rails imho.
Node is a constant headache and I have seen it be the death of more than one startup. Unless you are using a typed version of JS it is going to be a problem, I can guarantee it.
Rails kinda just works. It isn't as performant as I would like, but for the overwhelming majority of web apps that isn't an issue.
I know it can be hard to find rails devs, so just make them. Hire competent CS grads and train them. If they actually earned their degree it will be easy.
If you are in an executive role at a startup building a web app and optimizing for team velocity it is always in your interests to choose an opinionated framework that has already solved the hard problems for you and imposed structure on your team.
>My plea to the Rails community and Rails senior devs:
The problem is, I think, DHH doesn't really cares about it. He made it clear that Open Source shouldn't come with responsibility to make it great or fit your needs. It is more like a gift to the rest of the world with No Strings attached.
There is also the resources and company backing problem. Python is THE languages in Data Science, Node is javascript that is everywhere, and PHP, despite many people despise it, still runs the vast majority of the web.
With Github and Shopify now actively contributing to Rails, and Stripe working on Ruby, hopefully things will be better in the future.
Rails is ideal for the "Get Shit Done" approach to development. However, Ruby's type system does not provide many assurances, making mature codebases harder to maintain, refactor, and extend (as compared to codebases in, say, well-written Java). I am surprised this was not listed as one of the criticisms. Sorbet, a type checker for Ruby, stands to make this criticism less valid: https://sorbet.org/
I used to think that, as a C programmer used to passing void-stars around bristling against C++ in 2001 (which, to be fair, was not a good language. I continued to think it through a years of writing Python and Ruby. Then I picked up Go, which I thought I would hate due to typing, and: the opposite thing happened.
Particular example: every time you've accidentally pulled the wrong key out of a map (because that's how everyone does structs in Ruby and Python and Javascript) and gotten a null pointer exception: those were probably type related issues, and they're easily buried and discovered only in production, or through stupidly intensive unit testing, much (not all, but much) of which exists mostly to cover for (wait for it) lack of typing.
Passing a string where you expect a number is not the only kind of type errors modern type systems are about. They can catch a particular class of domain-level bugs, and more importantly, it acts as a pair programmer who tells us about all the logical edge cases that we forgot to think about, as we program.
This is done using what is called "sum types" - we can tell the compiler that say a user can be "Premium" or "Regular". This distinction will affect policies across the codebase - eg. Premium customers get a discount on their shipping costs. We'll have robust unit tests and integration test that cover all these policies.
But even with the tests, we run into trouble when the feature request to create a new type of User comes in - "Semi-Premium" who gets only their _domestic_ shipping free. We now have to hunt and peck across the codebase, and change all our policies so that it handles this possibility well. Our tests are of no help here - they are meant to verify existing facts about the system, and can't tell when this user fall through certain policies because we forgot to handle it there. The type system on the other hand knows exactly the places where we decide things based on the kind of user. It will then realize that a new type of user has come in, and our policies don't handle them. This turns what usually is a high-risk and difficult task into an almost mechanical one.
What's happening here is that we're telling the compiler more things about our domain. This means the compiler can then remind us as we go about our business writing code, to handle every case, to never pass a null, and to help model our data structures that prevents inconsistent states from ever happening. The more we can encode in types, the more powerful this becomes.
> ...We now have to hunt and peck across the codebase, and change all our policies so that it handles this possibility well.
A stellar case about Object design, not strong typing. I don't find type solves this issue, in fact, it even might be the opposite. Its way looser and more flexible to be able to say how things actually behave than to define the exact type they are to see how they behave.
The static analysis is definitely a huge plus, but it's a bit tangential: if a static analysis tool were able to point to the same issues, the reason would evaporate. (It's like saying a language is better because it has a better community, which is practical but not intrinsic).
Objects don't solve this. Untyped programming is more flexible, but are able to express less things about the domain explicitly than typed languages (not Java/C#, but rather OCaml/Haskell etc.).
> if a static analysis tool were able to point to the same issues, the reason would evaporate.
This static analysis tool is called the type checker. The experience of using a type checker is best when the underlying language has sound types. Sorbet, TypeScript, Typed Racket etc. bolt a type system on top of an existing language, and so don't have a choice but to be unsound, and so their qualitative experience isn't as nice as it could be. Even so, they're highly recommended for large codebases.
> It's like saying a language is better because it has a better community, which is practical but not intrinsic
Not at all. This is an intrinsic feature of programming languages and has a huge effect on how we think of programs.
It is however not a silver bullet and when building web applications, practicality - tooling, ecosystem etc. trumps everything else. However if you run into difficult problems which require deep attention to data modelling, and wish for a better tool to help frame the problem, then Typed Functional Programming might be a good try.
Static typing is not very important for new, small projects where the developer can keep the whole thing in their head. It can even be a drag during the design phase if the design is changing rapidly due to the added overhead of changing interfaces. Progress has been made reducing the overhead in recent years and proponents may disagree.
Fast forward to when the same project grows and matures. Now it is huge and no one knows every subsystem. The original developers skipped town years ago. There are -1x to 10x developers on the team, and contractors who must "hit the ground running." Static typing is now crucial to prevent the whole thing from collapsing like a house of cards.
I recommend the best of both worlds, such as prototyping in a productive language, then port when the design solidifies. Or, use a language that can be gradually typed later as your startup matures into an established company with customers that rely on it.
Type errors aren't the problem, understanding what the code is doing is the problem. I like to know what I'm looking at -- is it a primitive? A simple array? A complex, stateful object? This helps establish clearer relationships between systems.
I have a talk where I turn "you called a function from the wrong thread" into something caught by the type checker in C. Getting an error message at compile time pointing out the specific line with an error, rather than segfaulting at a later point in roughly 2/3 of executions, is a big improvement in developer experience.
"where I turn" This wording really hilights something that I think a lot of dynamic typing proponents don't fully appreciate. Using a type system is a learned skill, not something you magically get for free. You need both a good type system and the experience/education to use it to its fullest. Encoding threading rules in the type system isn't something C ships with out of the box, and it's not something a lot of people would think of, but moving that into the type system rather than relying on unit/manual test is a huge win.
Absolutely. That's pretty much the point of the talk I mentioned (with some hope of helping the audience get better at it). The only thing I disagree with is needing a good type system - "need" is too strong; the C type system isn't good but is enough to be pretty useful if you use it right.
How many team members contribute to your codebase? I've personally found that I need it less when it's just me committing code, but need it at my large company when traversing unknown codebases.
Even when its just me, if I leave a project for >1 month and then try to come back and pick up where I left off, I have a much easier time getting back up to speed with a typed language vs un/dynamically typed.
I agree that avoiding type-related issues are not a particularly big selling feature. I, however, like typed languages because the tooling tends to be substantially better, as tooling is much easier to build when you have type information available. Tooling makes my job a lot easier, faster, and, in my opinion, results in better code long-term. We're getting much better at building tools for dynamically-typed languages, but there is still a gap.
Type-related issues are super common in rails apps with several contributors. Do you ever get "undefined method foo for nil:NilClass"? That's a type error.
There isn't really a universal agreement on how to handle nil in the ruby community, and as long as that remains true, type issues will continue to be a daily reality of using the language.
That's the beauty of gradually typed systems like sorbet. You only define types where you need to. Plus, it's really only one extra line of code per method. And that extra LOC gives you a lot.
So many of the production bugs I've encountered in my rails career (probably around 2/3rds) would immediately go away with a type system. Now, that's not to say that I won't encounter other issues, but type systems provide other benefits as well. Typescript annotations make JS seem pretty sane, and editor integrations like VSCode's support for TS is amazing. You get go-to-source functionality that isn't based on heuristics, compile-time error reporting as opposed to runtime error reporting.
Sure, it might not make sense in your situation. But every single job I've had in my career would've been much easier with a type system, and depending on how solid sorbet turns out to be, working in ruby might look quite a bit different in the next few years.
Ease of refactoring is the big one, especially with an IDE since you'll catch a lot of problems as you write.
However, any decent IDE will work with type hinting (putting types in the comments, basically), and as long as you're fastidious about using them, you can realize those same benefits.
I think the advantage of type systems isn't something trivial like ensuring a function receives an integer instead of a string. It's that (in powerful type systems) you can encode your own domain constructs into types, which reduces cognitive overhead.
The first point is definitely a positive point for typing.
The second I feel is by design. A runtime exception is better than undefined behavior, which was the other option when many of these more abstract languages started taking hold. We're just reaching a point where languages with undefined behavior are being competed against by projects like Rust which offer similar performance without the same risks. Runtime exceptions become less attractive when you can avoid them and avoid undefined behavior with more comprehensive solutions.
Ruby is strongly typed, just not statically typed (by default). There's a lot less ambiguity in Ruby's types compared to, say, JS or PHP. But yes, optional type checking would be a nice thing indeed.
I run engineering at Calendly - We are on Rails and it's been great for us for multiple reasons.
1: There's a large ecosystem or gems to do almost anything you need for basic SaaS apps
2: We are based in Atlanta, and there aren't too many Rails developers here compared to out west. However, the language of Ruby itself is easy to learn - we have hired engineers with backgrounds in PHP, Scala, JavaScript, Python, etc.. and they were all able to pick up Rails within 30 days.
3: The framework (in general) encourages good practices. Easier to avoid terrible database schema naming and structure.
The downsides for rails which we experience is in our builds - takes a long time to run tests and precompile assets, and it hasn't been straightforward thing to solve.
Whenever I read about Rails it makes me want to try it again and "understand" it this time... But it always ends up feeling wrong, and I go back to old reliable Django.
I can't put my finger on it, but Django just feels intuitive to me and Rails just feels strange. Maybe it is the convention over configuration mindset which results in lots of magic, but I just can't get comfortable using Rails.
I love the idea of Rails and it's community, but I don't think it will ever be the tool for me.
And as a Django + Python lover, I feel the need to refute the idea (not just in this article) that it is mainly for academic/scientific purposes. Python is a great general purpose language which can do just about anything. It's not the best tool for every job, but for the generalist, it is wonderful. And Django is incredibly solid and can build an MVP in no time. And maybe I'm just a bad developer, but I don't see how the extra configuration necessary when using Django vs Rails is going to add up to much time at all when building your MVP. Is a couple of extra hours really going to make a difference?
Mostly interested in commenting on your last paragraph. But I think your first point of Django being more intuitive is probably due to your (I'm assuming here) background of growing with Django through time. As someone who has developed in both, I find that Django is basically RoR version (n-1). People claim that Django is too complex for them over Flask. I say it just takes time to learn all the features.
Anyway, as for the academic/scientific thing. I couldn't agree more. I feel like they are just perpetuating something silly. I can guarantee you that no one in academia is using Django for research. Probably some weird transitive logic like science => numpy/pandas => python => django.
But I think this stems from the target audience of the article:
"""
The only person who won’t find much use for this guide would be a high-level master Ruby on Rails developer. If you’re not on that level yet, then you’ll definitely be able to learn something!
"""
That is, if it's useful for everyone, it's useful for no one. The whole comparison table is unsubstantial. As interpreted languages, they are going to be roughly the same performance especially on a single thread. If anything, Ruby might be better on multi threaded due to the GIL but then this is contradicted in the table where it claims that Django is more scalable which I think is more of a comment as to how project architecture is laid out in Django with everything supposed to be a separate app.
Re: Maybe it is the convention over configuration mindset which results in lots of magic
Yes, the "magic" is a double-edged sword. I find a similar problem with Dot-Net MVC: when the "helpers" work as intended, they are great. But when they don't, troubleshooting takes way too long. (MS-MVC is more or less a clone of R-on-R.)
Its auto-matching of URL's to controllers & methods etc. (routing) is an example of this. A bunch of IF or CASE statements to do the same thing would probably be much quicker to debug, even if it's more typing up front. (Or even put the mappings into data tables for bigger apps.) One can scaffold (auto-generate) IF/CASE statements for such if needed to reduce the original build grunt-work.
All these "magic helpers" are over-complicated and opaque. Yes, I know that once one gets used them they know how to cajole them, but the learning curve is unacceptable for things that should be relatively simple. The helpers are more trouble than they are worth because they try to fit too many application types and styles, and rely on reflection, which makes following the actual helper code a pasta factory visit.
Must it be that way? I want clear and logical processing, not a bunch of organic dark-grey boxes. Maybe I'm old fashioned and relying on a bunch of dark-grey boxes glued together is the proper "new way"? #Idontgeddit
Maybe it's better for big projects or large pool of devs where you hire plug-and-play specialists who know a specific technical area well such as routing or views. But in shops with smaller staff or that divide tasks by project or entity instead of tech specialties, this doesn't make sense. But orgs feel compelled to do it that way because "everybody else is": MVC-Lemmings.
Yeah, the magic is a big problem for me. When I was a absolute beginner, even knowing which magic (ruby gems) to pick was a problem. In contrast, Django has all of the functionality within the library, all working together. After I launched my website and became more experienced, I started replacing the default parts with custom parts. Django's explicitness and lack of magic made that easy.
When I initially started learning Rails, I was feeling similar. I was concerned about code feeling like "magic" and things happening w/o my understanding.
However, I got more comfortable with it and soon, for the most part, I LOVED the way everything was structured. Other frameworks just felt off.
I credit a lot of this to just understanding the internals of how Ruby works. I highly recommend the Well Grounded Rubyist. If you go through it, you'll be comfortable digging through your code, gems, etc. Things won't feel like magic and you'll be able to pinpoint how and why things are happening in your application.
Rails is one of the most productive web frameworks out there and once you know how things work, you're off to the races.
Perhaps I am an odd duck (I come from heavy Java backend experience including Spring and struts and now Go) but Rails feels incredibly clunky.
I am working on a mid-size Rails app and it's a total horror show. Setting up debugger alone was very difficult (had to locate just the right patch version of a ruby gem that would work with my app); I can't just follow the calls because things are wired together behind the scenes based on names and sometimes through delayed jobs. I can't just read code and understand what it's doing I have know Rails and a myriad of other tools (react on rails and such).
So far RoR has left a fairly unpleasant taste in my mouth.
Having spent a lot of time on legacy Rails codebases that need new features and fixes, existing ones coming in from clients, and so on, I'd consider it practically write-only as soon as eyeballs are off it for a couple months or a handoff happens. Heroic testing practices that I rarely see in the wild could save it from that fate, but little else. Ruby's got some great libraries available but there's too much magic and "wtf is this even?" going on in your average Rails codebase. Sinatra with Sequel over it any day if it's my choice and the product's intended to have any kind of lifespan, and we're using Ruby.
Frameworks like Rails/Django are still an extremely viable way to go; even just for an API. I've seen so many cases of early microservices adoption when something like RoR would have saved them a ton of headache. If you need that kind of scale then great, but it's more likely that "you ain't gonna need it".
As far as I can tell, blocks are just some sort of hack to get around the performance overhead of using lambdas. They feel like more of a bug than a feature. Why, otherwise, would it be sane to have an ubiquitous feature that is almost a lambda, but less flexible, while also having lambdas that no one uses.
Ruby was my first programming language. I would agree that it's natural feeling, but it is the opposite of simple.
I don't really understand your argument here, blocks and lambdas are both procs. A block is just a special proc that can't be assigned to a variable. There's nothing less flexible about a block as opposed to a lambda, they both utilize the same functionality.
Our fascination with which language /framework is the best, most performant, whilst understandable (we are all attracted to shiny technology) misses an important point.
Engineering is all about trade offs and there are plenty of technologies that are comparable nowadays.
I think it's more a matter of picking something that makes sense for you and your team as well as the particular problem you are tackling and sticking with it than engaging in a holy war.
For example if you need great concurrency support.. perhaps you are better off with elixir
if you need heavy computations.. go for python
if you need safety, then perhaps look at go or rust
Rails really optimizes for developer happiness, comes with a huge ecosystem and is mature enough that can be called a boring, stable technology to build on.
That being said, it certainly isn't the right choice for everything.
Disclaimer:
I mainly use ruby on a day to day basis but also love elixir, python and even go.
When our Co-founder and Engineering Fellow Dmitriy Zaporozhets decided to build GitLab, he chose to do it with Ruby on Rails, despite working primarily in PHP at the time
GitLab CEO Sid Sijbrandij thinks his co-founder made a good choice:
"It's worked out really well because the Ruby on Rails ecosystem allows you to shape a lot of functionality at a high quality," he explained. "If you look at GitLab, it has an enormous amount of functionality. Software development is very complex and to help with that, we need a lot of functionality and Ruby on Rails is a way to do it. Because there's all these best practices that are on your happy path, it’s also a way to keep the code consistent when you ship something like GitLab. You're kind of guided into doing the right thing."
And yet, when I think of gitlab I think of the insane resources it consumes and the slowness of the system even when you overprovision by a factor of 2 on cpu.
Not saying I dislike gitlab, I actually really like it and we use the on-prem gold edition licensed to 6k seats. (as in, I put my money where my mouth is when I say I like it)
But of the things people complain about regarding rails that's that it's: large, heavy, slow et al.
All of those points carry directly over to gitlab and are the biggest argument against using the product.
Ruby allows optimisations in C for critical parts. In that sense I'd pin gitlab issues less on the language or framework, and more on a lack of resource to cover the weaker points.
I say that having seen a ton of PHP code base that is not stellar in itself, but has the more critical parts well optimized, going through external C libraries or DB hooks on parts that really warrant it.
I just realized I've never actually seen on-prem Github. GitLab on the other side is everywhere, especially thanks to the Community Edition. I've never seen really big deployments, but for instances with 100-200 people on board there was never any problem (and DevOps guys sit right next to me, so I would probably hear something).
As for the hosted solutions, IMO both GitLab.com and Github.com are pretty slow. I've just recently started using non-premise solutions of both and Github slowness is really taking it's toll on my patience.
Sure, but that's an indictment of Atlassian products, not of Java for web.
For context: Ebay, Amazon and Google (Plus, Talk) are using Java for web. None of those are what I would consider slow.
If people were condemning Java for web as being slow, and Atlassian used Java and was slow, then you could reasonably assume that the reasons are correlated.
That is the case I'm making about Gitlab and RoR.
EDIT: you're mentioning Jira Cloud being slow, but we have the on-prem version and it's also very slow.
I have my doubts. From my experience, JRuby is generally slower. Source: used it in production at a billion dollar company, benchmarked it personally on individual items, contacted core devs when it was slower for advice.
Is the CEO really the best person to ask about such in the weeds details of the codebase, like how the choice of web framework is working out? The CTO seems like a much better choice but even they might be too far removed, I’d rather hear from a sampling of engineers and tech leads that are actually coding and solving technical problems in the app every day.
I stopped reading at "Python, the language used in Django, is a language very commonly used for academic purposes, whereas the Ruby community — thanks to Rails — is more business-oriented."...
Rails doesn't scale, or more accurately described as Rails does not scale easily or cheaply. A lot of people doing consultation may have heard their client ask if they should switch to something else because Rails does not X, or Y or Z. ( Normally the standard reply is But GitHub and Shopify is using Rails as well, which should settle the argument )
Rails, by default is still viewed as a Server Rendered Only Framework. Although its API Mode could be used with any front end such as Vue.js, it doesn't seems to be well known or gaining any momentum.
I am wondering if Ruby Rails will ever pick up something similar to liveView [1], instead of sprinkle of Javascript, LiveView could get rid of it. It fits the narrative of Ruby Rails, and for those that don't like it could still use it as Rails-API and some other front end.
Edit: Turns out there is something similar on Rails called Fie. [2]
It just basically meant it uses more hardware resources than Go or JSP. Which is a problem in places like China, when the Scale is 10x of anywhere else, Salary is 5x lower, and Computer Resources are 2x - 3x more expensive.
A few more Data Point, The total online population in China is more than online population of US, Canada and Whole of EU combined. And while US, Canada and EU are separated by 10+ time zone, China is in one time zone, the peak traffic of anything that gets small traction in China is likely to be 10x if not a lot higher.
The average salary of an entry level Ruby on Rails Dev in China is under 24K USD.
And while everything is cheap in China, for whatever strange reason their Cloud Compute Resources and Bandwidth are 2x of even Amazon in Hong Kong or Australia ( Some of the most expensive instances in AWS ).
And you get the idea why pretty no one outside of High Salaries Region, ( US, UK, Germany, France ) are using Ruby Rails. Sometimes the equation or selling point just doesn't fit.
It is a complicated question, and I don't have an answer.
Ruby Rails is still the best for SaaS type of product, in the West or the East. With SaaS, your cost of scaling is proportional to your revenue. That is why you see BaseCamp, Shopify, ZenDesk, AirBnB, Github. All of them works well because the cost of framework or languages shouldn't really matter, since every user are paying you. But in a freemium model or ads based model, which is extremely popular in the East, your initial breakeven point will be a lot higher.
But it is not about the technical challenges, there are very little Ruby talent pool in China. Many Rubyist couldn't find jobs and switched to Java or Python. The phase for many Rubyist is; I love Ruby, but Ruby doesn't feed me. And precisely because Ruby Rails is more expensive to run for those in charge, they will adopt something faster like Go. ( Something like Gitea [1] came from China) Which means even less project being done on Ruby, and even less Jobs, with no demand in Jobs also mean people are not going to learn it. It is a vicious cycle.
And without a talent pool, it doesn't really matter what languages or framework you love or thinks works best.
I think I'd only ever choose RoR for a throwaway project, in 2019. I do love RoR and the development experience but Elixir and Phoenix give me almost exactly the same feeling but with WAY better performance and an Immutable/Functional language that results in much cleaner and better code. It's an investment to learn Elixir but it's 100% worth it and honestly I'd choose Phoenix over Rails almost every time.
I suppose I could see myself picking Rails if 1) I need particular functionality that is well-served through a gem that doesn't have an equivalent Elixir package, or 2) I need others to work with me and there's a pool of Rails devs available.
Other than that I'd also go for Phoenix/Elixir, especially with LiveView being a thing now.
What is your IDE setup? I like elixir, but it seems that IDE support isn't where I'm used to in other languages. Specifically module auto imports/aliasing. Not a big gripe, but I haven't found an IDE I like as much as Rubymine for Elixir.
> Ruby is multi-threaded in its operations in contrast to NodeJS which makes it much better for CPU intensive operations.
>NodeJS is single threaded and was designed for heavy I/O bound applications and is great in this domain. BUT when it comes to heavy computing requirements, NodeJS is terrible.
OMG people still don't understand concurrency and parallelism and how to reap out maximum throughput out of CPU bound an IO bound tasks.
I had the opportunity to run some hackatons / startup coaching last year with some CS student at University of Wisconsin- Madison. I spoke in front of some large lectures and asked students what languages they were learning.
Literally no one out of a few hundred students was learning ruby. Go/nodejs/etherium is what people were putting their energy into.
I suppose they will be in for a bit of a shock when they try to make their way into the workforce to find out that the majority of enterprise-land is using Java or .Net and a bit of python mixed in.
They shouldn't be surprised, if they're fully informed on the technical and business merits of every platform available. I wouldn't build a business on anything except CoreCLR or Hotspot. C# or Kotlin for me.
I'm just talking out loud here, not to you specifically, but things are the way they are, for good reasons. It's not a "mistake", the way the landscape is today, most technical leads are not sheep. Technical & business requirements have been mulled over time and time again by many thoughtful people, usually leading to these two platforms. A lot of us don't care about what's cool. We demand true technical innovation to adopt a new platform, not technical churn and there's an awful lot of that, if not outright downgrades.
For me, all these things matter.
-Good tooling
-Industrial-strength language design
-Backwards compatibility and support
-Breadth of domains you can target and target well (server-side, desktop, mobile)
Learning a language is trivial, as an educator it is your responsibility to teach them that in order to be a successful developer they should be flexible and well rounded.
I used to do a lot of work in Rails in around 2012-2014. Around 2015 or so I sort of pivoted out of web development and more into data engineering, so I took a bit of a hiatus from making side projects/web apps. When I revisited Rails years later, it seemed like so much had changed with all of the new versions and updates that it seemed like overkill, I just shrugged and ended up using Sinatra instead.
Is there a good resource out there for someone who used Rails 2.x / 3 a lot years ago to get caught up to speed on what they need to know to get up and running with the new(er) versions?
I've been using Hartl's Ruby on Rails Tutorial and Agile Web Development with Rails 5 by Sam Ruby. I had much less experience with Rails 3 than you so you might find it redundant but those have been the two best for me.
3 years ago we were breaking monoliths into microservices, now it seems that we overshot that, and I'm much more likely to see people sticking with one main monolith, and maybe one or two other services. Similarly, I'm much more likely to see people choosing NOT to use JS frameworks these days than use them.