Hacker News new | past | comments | ask | show | jobs | submit login
From Rails to Clojure, Then to Java, Then Back to Rails (engineering-management.space)
195 points by donbonifacio on March 21, 2018 | hide | past | favorite | 153 comments

In the "things learned from Ruby" section:

  Focus on strong object oriented programming
What I've learned from Ruby is to favor functional programming concepts - minimize side effects, isolate state mutation, etc. Ruby is definitely OO, but when my Ruby code is more functional it's less buggy, easier to understand and move around, and generally less convoluted.

Like Gary Bernhardt's notion of a "Functional Core, Imperative Shell" — https://www.destroyallsoftware.com/screencasts/catalog/funct...

Damn, I knew I got this idea from somewhere, I just totally forgot where. Thanks for posting this link - I'll have to rewatch it.

Brian Goetz has talked about this as well - see FP is Dead - Long Live FP (https://youtu.be/ROL58LJGNfA).

I watched that video in my earlier 20s and it changed how I wrote code.

I've always had better luck restricting FP to using things like map, reduce, filter, (etc) inside methods as an implementation detail, but having the structure of the app use OOP.

Incidently that is how FP in done in Smalltalk as well.

All those methods, and reactive patterns (Observer and such) were already present in Smalltalk-80.

Elixir, I suppose, would be a 'modern' example too.

This is where I've landed when writing Ruby code, as well, and it's served me really well.

Ruby used functionally is great, but it takes an already time-inefficient language and amplifies that weakness (lots of creating new collections and returning them, whereas normally in Ruby you would modify elements within the collection).

Isn’t there something called LazyEnumerator that deals with this? In terms of chained .map at least

Lazy enumerators don't make each individual step of the iteration any more efficient. They just let you stop the iteration partway through without traversing the entire original enumerable. Given this contrived example:

   a = (1..1000).lazy.select(&:even?).map{|i| i*2 }.map{|i| i + 1 }.map(&:to_s).map(&:reverse)
There is no benefit from lazy if you do `a.join(",")`, since that iterates over the entire sequence. But if you do `a.take(10)` or `a.detect{|s| s == "14" }`, the chain will only be executed for each item in turn until 10 elements are produced or an element is the string "14", respectively.

Ruby's composabilty really puts Python to shame. I can't imagine how Ruby lost out to Python.

While I much prefer writing Ruby to Python, Python is just dead simple to get real work done with. It's not elegant, but it's still so much less nasty than C++ or Java.

Ruby's #1 problem, for me at least, is that your small project someday runs into a performance wall. I don't know the latest benchmarks, but last I recall Python was about 8x faster than Ruby when both are being interpreted. Yes, there's JRuby, but that's not something you can drop into a new system and do useful things with immediately (without more setup).

And with Clojure, you get all the elegance and lovely collection manipulation tools you can possibly want, much faster performance, and a huge stable pile of Java libraries (compared to Ruby).

So with Python and Clojure as one's main tools, life is quite nice.

Say what? Ruby has been faster than Python for interpreted code for years now.

Granted, Python is faster for tasks like those Numpy solves, but in arbitrary execution performance, python is just slower. 8x has NEVER been true. That must have been you doing some really bad stuff in one of them but not both.

I stand corrected. I used Ruby for 6 years, up to 2015. I wasn't using Python during those years, but I thought I had heard or read about the 8x speed difference in favor of Python.

For sure, Ruby was slow for what we were doing... processing millions of rows of data was so slow that it caused me to decide to try Go (and the same Go program was hundreds of times faster). But I didn't try Python then.

I think there was a short period about 11 years ago before the release of Ruby 1.9 where the Python VM was basically re-written to be as fast as YARV, but YARV wasn't released yet.

MRI Ruby has been slightly faster than Python for about a decade and the next release of Ruby will contain a basic JIT compiler giving MRI Ruby a significant advantage over CPython.

What's your evidence for Python ever having been 8 times faster than Ruby? Apart from the numeric libraries, written mostly in C or Fortran, Python has at best only ever gained a very marginal speed lead over Ruby and that has been wiped out in recent releases of MRI. Ruby's startup time is a bit slower than Python's but once they're off to the races both of these horses have been neck and neck for a long time.

There is absolutely a benefit to lazy if you're doing more than one combinator even if you ultimately need to iterate over the whole list such as the join case.

The lazy version basically turns into

   a = ""
   (1..1000).each { |i|
       next if i.odd?
       i *= 2
       i += 1
       a << i.to_s.reverse + ", " # ya ya, trailing comma
Where as the non lazy version turns into:

    odds = []
    (1..1000).each { |i| odds << i if i.even? }
    doubled = []
    odds.each { |o| doubled << o * 2 }
    incremented = []
    doubled.each { |d| incremented << d + 1 }
    strs = []
    incremented.each { |i| i.to_s }
    a = ""
    strs.each{ |s| a << s.reversed + ", " }
Which means that you have you went from N iterations to 5*N iterations, with 5 intermediate arrays in this case.

That's how it could work theoretically, but in practice lazy iterators are much less efficient due to their implementation. I benchmarked it here:


When applied to the same amount of work, lazy iterators consume more memory and take more CPU time than the equivalent non-lazy chained iteration.

There's a place for lazy iterators, but you should pretty much always profile first. If you have performance problems due to a long chain of iterators, you're always going to get better results by merging some of those iterators into a single block than by making the whole chain lazy. The best use case for lazy iterators is if you're trying to avoid having the original collection in memory (if it's being streamed from a file or the like). And at that point, if you're trying to optimize for performance, you should avoid chained iterations, period.

The good news is that in the vast majority of cases this just doesn't matter. Chaining iterators works fine until you start running into performance problems.

Thanks a lot for the insight!

I've learned to think of objects as not exactly monadic, but monad-like in their pattern of use: whenever there's a bit of state that I need to track and isolate from all other state in the system, wrap that in a class. The allowable state transitions then become methods.

Right, and most of the times, if state have different life times, then it should be in different classes.

What do you mean by life times?

Is the state eternal or not (ie is it immutable)? Will the state typically change at the same time as some other state (like an address, or stuff related to some other event)?

As someone stated, good OO and good functional is approaching the same goal from different sides. Your code being functional doesn't really make it less OO.

Except if you're using immutable data, pure functions and explicit state there's a lot less room for OO which (as presented by ruby, java etc) involves mutable data accessed/changed via methods that conceal their state.

Nothing in the concept of OO enforces mutability. The paradigm doesn't enforce anything at all there. You want immutable objects that never changes once created? Fine, go for it.

I'm literally writing a blog post about this now (I'm building an objects system from scratch using only FP techniques in JavaScript... slightly eclectic ;-) ). The very interesting thing about this exercise is that immutability solves a huge host of problems in OO systems. As a very small example, even the diamond inheritance issue just goes away because nothing is ever updated. If B and C both have the base class A, you can safely instantiate A twice because it doesn't matter which one you use.

I'm about half way done. When I'm done, I'll post it on HN, but if you are interested here is what I've got so far: https://github.com/ygt-mikekchar/oojs/blob/master/oojs.org If anyone ends up reading it, I'm very happy to receive criticism, so feel free to leave an issue.

Sure, you can simulate immutability in Ruby or Python but it's not real immutability. It's just shallow freezing. The real point, however, is that immutability is not idiomatic in OOP languages. Objects are intended to store state and have it modified. It's not what a language can be made to do that matters. It's what is idiomatic.

And yet 99% of code written in Java, Ruby, <Generic OOP language here> is based on mutable objects.

Its not about what is possible, we're all familiar with the turing completeness of everything. It's about what is: the default + easy + encouraged in the ecosystem.

Yes - Ruby makes it easy.

I'm run into the downside, though, that Ruby isn't optimized for immutable operations - tons of object instances created. I've needed to 'pythonize' my Ruby code in the worst cases - using mutable functions to dramatically reduce memory usage.

Can you say more about this Pythonisation process? How is Python any better at immutable operations? Ruby and Python are both OO languages which can be used in a procedural style. Hell, you can write a whole app in Ruby without creating a single class if you so choose.

I think what they mean by 'pythonize' is ditching fluent functional style and embracing dumb imperative mutability for the sake of efficiency. Rewriting sequence compositions as stateful for-loops. Idiomatic Python is more pragmatic than Ruby in that regard, stupid=good (yet ironically Ruby's strings are mutable and Python's aren't :p).

IMO, Python is worse at immutable operations. Ruby makes it easy (but inefficient). Python makes mutable code the easy path.

I've tried a lot of different languages over the years including Ruby, Go, Scala, and Haskell.

Ruby actually isn't too bad if you don't use Rails. Between Rails and stuff like Spring over in Java-land I've developed a strong hatred of huge frameworks :)

Clojure, however, is the one language that just 'clicked' for me almost immediately. In fact, I'm pretty certain I'll be sticking to Lisps as much as possible from here on out. That is, when I have the choice: I work in a Java shop during the day like everyone else :O

I'm agree with you as well except the Rails point.

I have used and deploy Go/Elixir/Clojure to some certain success. One thing I feel is that no code is as beautiful as Ruby code to my own eye.

What make Rails bad in your opinion? Is its performance? I think Rails is just a framework with many utility/helper to help you out.

I think there's a valid case to be made that Spring and the rails auto-loader are pretty annoying/stupid in larger projects (or anytime you're going against the grain).

Every framework has its downsides. Personally I think rails is nice (I am very productive with it).

This is a fair point.

Yes, I come into lots of issue with Spring autoloader to the point I have a zsh alias call `kill_rails` to make sure it kills all the rails and spring instances :(.

That's being said, Spring is an external tool to help solve the slowness of booting up Rails app. I have work in Java/Python and they are slow to bootup too just depend on how big the project is.

Tool like: https://github.com/Shopify/bootsnap help to speed up this as well.

Other option to look at: https://github.com/danielpclark/faster_path

These solves the issue of scanning path, address slowness in a different way compare to preload mechanism of Spring.

GP was talking about this Spring: https://spring.io/ not the Rails preloader...

Yes, I meant the Java uberframework beloved by architecture astronauts. I suppose I should have been more clear - I haven't uses RoR since version 3, so my knowledge of that ecosystem isn't very current :)

What libraries do you use for building web stuff?

Over on Reddit[0] one of the problems for newbies to Clojure is not knowing what to use for web development.


compojure-api[1] is great for building a REST API, and I really like HugSQL[2] for talking to databases. Buddy[3] is good for handling auth.

[1]: https://github.com/metosin/compojure-api

[2]: https://www.hugsql.org/

[3]: https://github.com/funcool/buddy

If you want end-to-end Clojure/Lisp while retaining access to large ecosystems there's Clojurescript for the browser and Node.js, Hy (http://hylang.org) for scripting and data science plus Clojure for JVM/large applications with performance. Now you can get a VPS with 4Gb of RAM for £5/month at Hetzner (http://hetzner.com) so JVM memory usage is no longer an issue. These are exciting times indeed for developers.

Clojure also clicked with me too, and I also dislike huge frameworks.

There's a trade off there though, rails gives you so much, you almost forget what the problems are with slapping different libraries together.

Definitely room for a "blessed" path to web dev in clojure.

re-frame is the uncrowned king of web dev at the moment, there is nothing like it out there

That's only for the front end though. Lots of people use Compojure which is essentially Sinatra if you're coming from Ruby. Luminus is a far more complete solution that feels like Rails. After I cut my teeth enough times starting from nothing, Luminus unfortunately feels a little clunky because you have to figure out how it's all glued together.

There's Macchiato (https://macchiato-framework.github.io) by the author of Luminus if you want to target Node.js.

Same here. It's really hard for me now at my day job when I have to write OO. Clojure and functional languages opened up my eyes to OO shortcomings I've been trying to work out for over twenty years. Since I started, I've always felt that there were many things not right about OO and developing using these giant frameworks with so many useless abstractions has gotten old and tiring. Most of my time is spent working around the shortcomings of OO nowadays rather than writing actual code for business purposes. Since I started with Clojure, I have little patience for the complex for complexity's sake song and dance of OO. I'm trying to go full time with Clojure. I'm shocked that the author couldn't find Clojure programmers. Must not have offered remote work.

I know. You think I'd be sitting here slinging Java code if there was a shortage of Clojure programmers?

Even in my local area (not a tech hub), I'm aware of one small software shop using Clojure, and they have no trouble finding people.

I had a similar experience.

I felt so strongly about not wanting to go "backwards" anymore and use Go/Scala/Java etc I started my own company so I could dictate the technology decisions, so naturally I used Clojure!

I still use Javascript in most of the UX because ClojureScript I felt wasn't ready for primetime production but we'll be phasing that out over the next few years into ClojureScript.

As someone who'd always pick CLJS over Js, for the heck of it I tried to build React / Redux / React-router based app and was blown away at how dev friendly the experience was.

No fiddling with tooling, the entry barrier can't be lower. With CLJS it's always figuring out the latest versions, trying to get Figwheel off the ground is at least a day worth of work for me. What was piggieback again and why don't I have it? Which profiles are loaded implicitly by lein, should I use figwheel-sidecar? Then add building your app to be reload friendly (even though by default it's a good practice)

There's just so much going on even after having built 3-4 projects with CLJS and using the same stack, I am still struggling.

Once you get through the first few days of battling with libs it's breeze.

That's where Js gets behind. Redux / React is all about transforming and shaping your data. You end with lots of boilerplate. Even though ES6 is drastically better with syntactic sugar for manipulating data, it's still far behind Lisp nature of data transforming.

It's a tough choice nowadays. I want to get moving fast and feel strongly inclined towards sticking to Js.

This is a bit odd, that concurrency would be listed as something they gained from Java, rather than Clojure:

"Clojure: considering the difference between being easy and being simple. Clear difference between data and logic that changes that data. Distinction between pure functions and functions with side effects, try to separate them. Let’s try to make simple code by default."

"Java: performance, concurrency and strictness."

Maybe they mean they were forced to think about concurrency in Java?

I don't think any reasonable person would argue that concurrency is easier in Java than in Clojure.

The author mentioned that the Clojure project never had to scale. My guess is that the size of the Java project demanded concurrency and the Clojure project didn't. Overall, this blog is light on details and heavy on how the languages/projects made the author feel.

My reaction to this claim was that, since Clojure is on the JVM, any advantages Java has, Clojure also has.

Of course, idiomatic Clojure might require less mindshare to make highly performant, concurrent code...

You may be right, still, modern Java does have some excellent concurrency libs, like Akka https://akka.io

The articles touches on this but just to reiterate because it's worth saying: The most important thing about Rails is knowing how the internals work and maintaining a clean code architecture. Too many times have I seen a mess in Rails because developers only use the APIs and DSLs given to them.

The best Rails codebases are the ones who have ample "vanilla" Ruby and just happen to use Rails alongside said Ruby.

Really interesting comment.

I've started working on a Rails project. I don't have any professional experience with Rails in particular.

Can you recommend any code bases I can read that meet these criteria?

My personal favorite codebase is GitLab (thanks syste and team!) — they do a very fine job of pumping out Ruby while hanging out in the Rails ecosystem. https://gitlab.com/gitlab-org/gitlab-ce — points of interest are in lib, as well as how they organize and call service objects from app/services.

As well as services, a couple of other folders you might want in app are for decorators and form objects.

Form objects aren’t very common, but I’ve found it to be a really great patten for simplifying code. Strong params and ActiveRecord methods are fine for simple updates, but as soon as you get to something more complicated (e.g. what if you want validations to apply only for certain users?) it just becomes a mess - and you need to decide whether to have fat controllers or models.

The idea is you put all the logic for parsing params and updating records into a form object. It keeps controllers and models skinny, and can easily be tested without having to go through a controller. The reform gem provides a few helpers that make them easy:


Awesome, the form pattern is a useful one. My coworker and I were just talking about how frustrating it was to have validations coupled so tightly to the models and this would have helped a lot. Thanks!

I guess the idea is that you don't need to only use what Rails provides you, and you're free to bake in your own Ruby as you see fit. Rails is just Ruby, after all, and its modular enough at its core to be extensible for all sorts of applications. You just might have to do some digging to get your Ruby code Rails-friendly, such as using POROs alongside a form builder requires you to include ActiveModel::Naming

Yes, this is the BIG problem I see with Rails yet you don't hear many developers complaining about it. Django and Laravel seem to treat validation separately from the model and so does Node.js. Why is Rails so rigid about this? Although I generally like Rails this the single potential show-stopper for me.

This codebase is a pleasure to read. Thank you for the recommendation :)

I may sounds weird but Ruby is what make me look more into functional programming. I get done a lot with Ruby due to all method it has on Array/Object/Enumerable.

The beauty of `&` is awesome and I discover that Elixir/Clojure has that and bring it to next level. I think Ruby has inspired many other language to take that path too.

One of the thing about Ruby is that it optimize for happiness, which sound weird. But I really feel happy to write Ruby code. I feel like the language really care about me.

Who don't want to have stuff like this:

``` 10.seconds.from_now ```

Or just call method without `()`, and without even wrapping hash in `{}`.

Of course, lots of these are do-able in Lisp and I like Clojure. But I think the flexibility Ruby gives us is under estimated.

  *> 10.seconds.from_now*
I don't understand this kind of weird "urban sprawl OOP" where classes are extended with anything.

Why is 'seconds' a method on numbers? Is there also a 'pixels' method so that I could draw a line like this:

If this doesn't seem like a sensible way of programming graphics, why does the 10.seconds thing get a pass?

It's not just 'anything' that objects are extended with in Ruby, they are extended with suitable methods pertaining to the domain at hand. 'seconds' is a conversion method of one type into another. You might as well ask why do objects have a 'toString' method. Because it is convenient.

Why does one conversion method have the “to” prefix and the other doesn’t? I guess because the inconsistency looks somehow prettier.

But this is what drives me up the wall with the Ruby ecosystem: seems that whenever API designers had the choice between consistency and fun gimmick, they chose the latter. It makes some people feel happy, but often makes the next person who has to read and maintain the code miserable.

10.seconds is not part of the Ruby stdlib. It's Rails ActiveSupport. That's why it's different.

Thank you for explaining this.

If you think about it, that's just fluent API. And it's up to your to use it to design thing that's convenience and readable for you. When you design an API, you put it to the context. When in context:


is beautiful to me.

Let's do a quick google for "Fluent API in Java" and you will get something like this:

Invoice inv = Invoice.builder() .createdOn(new Date()) .withDocumentNumber("5150") .shippingOnTrailer("2112") .suppliedBy(InvoiceActor.builder() .named("101") .as(InvoiceActorType.DC) .build()) .beingSentTo(InvoiceActor.builder() .named("42") .as(InvoiceActorType.STORE) .build()) .with(InvoiceItem.builder() .as("TK421") .orderedQuantity(10) .shippedQuantity(8) .build()) .with(InvoiceItem.builder() .as("FN2187") .orderedQuantity(5) .shippedQuantity(5) .build()) .build();

The only difference here is that Ruby allow you to attach method to anything.

But then you have category in Objective C, extension in Swift/Kotlin.

The language gives you the tool, to make your job easier, that's why I wrote "I feel happy when writing Ruby" because I have that power.

To even protected you from changing everything, you can also use refinement to scope this, though it seems underestimated currently IMHO.

When using it the right way, it helps everyone write readable code.

Back to your example:

> 10.pixels.from.top_left_corner.to.southeast.using.red.stroke

It's up to your however you want to design it.

It looks nice, but then again:

  irb(main):002:0> 10.methods.count
  => 133
Oh well...

What's wrong with this? What is your expecation?

Depends of course what kind of systems you're building. Just the footprint of an integer is too large in Ruby to do anything fast. I'd expect features such as `days` to be a part of Date library that holds an interface accepting certain kind of objects giving dates back. If I don't need these features, I don't want to hold the functionality at all inside an integer.

Exactly. Right tool for the job. Ruby doesn't give you fast. That's part of its trade-off. Want web apps and scripts? Go Ruby. Want fast? Control over everything? Go native: C/C++. But also become trench into the swamp of memory leaks, stomps, corruption. Fun times.

Or use Rust/Haskell/OCaml/Clojure.

As a hiring manager, I now look for at least two languages, and they don't need to be the tech stack we're currently using. If you're a good developer, and you know more than one language, you understand that picking up another is not an insurmountable barrier. You'll pick up our stack soon enough.

What I need to know is if you can think critically, not if you already know what we plan to teach.

Same here. Depending on the team I'm leading, I can actually consider not having the current stack a plus, as an outsider's perspective on architecture can help grow a team and codebase that's been stuck doing things the same way for a long time.

There's some penalty in ramp-up time, but I find it more than pays for itself down the road.

What else do you look for ? I recently spoke to a hiring manager and he told me that my resume made me look very “green”. It’s only after talking to me that he noticed my resume paints a bad picture.

Years of Clojure has made me a dramatically better Rails programmer. The biggest change for me is that I now treat ActiveRecord models as gloried hashes with easy-access to a namespace of related functions (ie. methods) that operate on all manner of underspecified partial data, rather than traditional OOP-style objects with sophisticated invariants. I'm also very careful to strictly separate reads from writes, and to discard/reload all mutated objects immediately after transacting with them.

My new business is built on Rails for a very long list of reasons, not the least of which is that both Ruby is quite a nice language, and Rails is actually quite good for back-office/lo-fi/quick-and-dirty, server-side-rendered UX with relatively low web traffic.

Have you tried Elixir/Phoenix?

Clojure does not have an alien syntax. As with Lisps in general, it has almost no syntax. (operator param param ...) Done.

It has much more syntax than any other Lisp I know, and it seems alien even coming from Common Lisp. In addition to the usual (list) and 'quote and `backtick and :keyword and ;comment, it has [vector] and {map} and #{set} and #(function) and @deref and #"regex" and ^metadata and #?(:conditional) and ...

(Yes, several of those are implemented as reader macros. I don't see how that would make them any easier to learn than the same feature implemented as "syntax".)

That's not a knock against it. I think Clojure is the best general-purpose language today, but we've come a long way in the past few decades. Lisp is no longer a language whose implementation fits on a blackboard (and everyone extends it in their own way). We've discovered a bunch of common functionality that almost all programs need, and pushed it up into the stdlib and language.

It's only optional though. You can use (list 1 2 3), (vector 1 2 3), (hash-map 1 2 3 4), and (fn [x] (inc x)).

I wouldn't say features like [vector] are optional alternatives to (vector), if you can't write even the simplest function without using the [vector] syntax.

That's not relevant here. Most code uses those things, no??

True, but i don't have to remember 5 different map implementations either :)

> As with Lisps in general, it has almost no syntax

That's not true. Lisp has lots of syntax - actually more than most programming language, because the syntax is arbitrarily extensible via macros.

The idea of (operator param param ...) is only valid for function calls, but even then the syntax is slightly more complex, since several Lisps have keyword arguments.

But besides (operator param param ...) Lisp also has:

((lambda (arg ...) . body) param ...)

(special-operator ...)


(macro-operator ...)

Each special operator comes with syntax. Example is LET.

LET is not

(LET param param)


(let (arg ...) declarations . body)

where arg is arg | (arg value)

declaration the is something complex like

(declare (type (integer 0 3) arg))

for a type declaration for the variable arg, which is an integer in the range of 0 to 3.

Alien is relative. For someone who has only worked with, say, Algol derivatives, it can take some getting used to. Likely less than people often think, but it's still a shift. And it's hardly the point of the submission as a whole.

Aliens on the other hand isn't relative at all, it's objectively the best film in the franchise.

It seems that some people really really love syntacticfull languages rather than uniform symbolic/functional notation.

Considering all the mainstream languages, some would argue that most people prefer them.

That would be like saying most people prefer VHS to Beta.

Systems don't always/often win because they're the best or most liked.

As with political views, most people prefer the ones they first learned. Only later in life, perhaps after becoming dissatisfied with what they know, they may risk trying something very different.

My CS education was the typical Pascal/C/C++, so it wasn't decades later until I actually learned a Lispy language. But I had read enough to know that a lot of very intelligent people had loved Lisp; surely there was something to that.

Likewise, the people who grew up on Lisp probably only grudgingly moved _down_ the ladder to use some of the more popular languages ;).

to me lisp is its own problem, its value is that it gives you too much it becomes anti social. Society likes ignorance and delegation, lisps gives you the ability to have everything into one atom and mold it as you need it. But that's only useful for 1) people with that kind of mindset 2) hard problems that fall off mainstream/commercial support

Like Autolisp? Autocad (and its clones) of software used by zillions of designer, architects, ... extened by zillions of Lisp programs.

And SISCOG (http://www.siscog.eu) who built a Europe-wide transport infrastructure project in Common Lisp which was used by London Transport amongst other clients?

Is autolisp code lispy ? The few that I remember felt like qbasic in parens.

Yeah, but it's not like Lisp is a surprise to many IT people. It's old, it has a rather enthusiastic grass root following that promotes it and various universities have at least a few courses using it.

Another thing I’m getting is that the new kids won’t like rails, and it’s being harder to hire.

I'm surprised by this, to me the language used would be a long way down the list when evaluating an employer.

Ruby is still a beautiful language, particularly for its object oriented-ness. And I think Ruby's nature, simplicity and power appeals to young people, however the resurgence of a towering Javascript/NodeJS is hard compete with. These are market forces that really matter when you're wondering what language & platform to use for your MVP. In recent surveys, Ruby & Rails has fallen to the bottom of the list among the former stars of programming languages. Python has data science & machine learning to keep it strong and new stars Elixir/Phoenix and Golang are sucking up a lot of oxygen that would otherwise go to Ruby & Rails. And let's not forget Bubble (visual app building) and the Zero Code Startup.

I really think Pedro (the blog post writer) has an excellent point about being a polyglot but trying to qualify as a Senior Engineer except I'm older and have even more platforms & languages behind me. Its a serious problem because I have the experience to easily be a Sr. Engineer or manager but my experience isn't concentrated long enough anywhere to make me useful enough to warrant the role.

Ruby also suffers from top quality JITs vs what JS has.

The only good option being JRuby, as far as I understand. I am not at all into Ruby.

Regarding being polyglot and seniority, a possible solution is to work for consulting companies that focus on multiple stacks.

MRI has just added a JIT, I know nothing about it other than that it exists.

MJIT works by taking MRI Ruby bytecode, replacing the instructions with the original C code, and compiling an entire method using GCC/LLVM.

Because MRI Ruby is a threaded VM, it's easy to replace jumping to an instruction which dispatches to the next instruction, with jumping to some native code which dispatches to the next instruction.

Right now, AFAIK, only very basic optimizations are enabled but just removing the overhead of VM dispatch between each instruction is a huge performance improvement.

Most of the time you're not using JRuby for the JIT but for threads and/or to fit into some Tomcat/other Java environment. JRuby isn't exactly fast.

JRuby is now about 3x faster than MRI, thanks to improvements in JRuby and increased support from the JVM for optimizing dynamic languages like invokedynamic.

Not so fast with Elixir. According to Indeed.co.uk's API there are 18 Ruby jobs for every Elixir job. Yes, there are 3 times as many Python jobs but Ruby isn't exactly dead.

Surprising, for me it's one of the top criteria. The languages I'm using are shaping my process of thought, like when I'm speaking a different natural language, and some are more enjoyable than others to "think" in.

Besides, some languages give you vastly different career prospects (industries you can work in, salaries/rates, environment, etc.)

For some reason, Rails seems to always have some weird commentary attached to it.

Years back, I've had people tell me they stopped using Rails because it was too hard to find good problem solvers. That didn't diminish the continued success of the framework.

As an antidote to the comment, I was recently at a Ruby meetup. Everyone was on Rails and everyone was younger.

While I'd agree with you as an entry level candidate, with experienced people, I've found the language is often the most important thing on the list. Often above salary. I would say it is more commonly number one than any other factor. I only know of a few programmers who have shifted through more than 2 languages in their careers (20+ year type careers). I don't have brilliant insight as to why. Perhaps the fear of starting at ground zero? Imposter syndrome? Feeling of throwing your past experience down the drain?

Interesting. I've used at least half a dozen languages in my career of ~15 years. Apart from a few categories of language I avoid, I'm much more interested in the working domain, team dynamics, and quality of life.

It has often been to my advantage to be willing to learn a new language for a new project or a new job, giving me opportunities I wouldn't otherwise have had. I learned Oracle PL/SQL to integrate a 7-8 figure Rails app into an accounting system when nobody else volunteered. I learned Rails to get into ecommerce. I learned PHP to become a startup's only web developer so I could write cool audio processing tech in Ruby. I learned Ruby to speed web development in my own startup. I learned Scala to get into the San Francisco market. JS. Java. Kotlin. POSIX shell. C++. C. Etc.

Unless a developer/engineer just wants to be a cog forever, I highly recommend some flexibility on language. Solving real problems is way more interesting and more useful than being a framework stickler.

It's because we don't like or approve of rails. It doesn't make business sense to us (us being a completely anecdotal group of young developers I'm friends with). There are other solutions that provide sufficiently more developer speed and safety, while also providing much better performance than any rails application.

Edit: I guess I just hang out with like minded people, but our biases shouldn't negate the fact that this is a very common feeling where I am.

To answer many of the replies:

(Most prominent -> least prominent)

Web-related: Go, Elixir, JavaScript, Python

Desktop GUI: Python, C++

Game-Dev: C#, C++

Really its a matter of how much you'll pay us, and I think we've been scarred by too many bloated Rails apps and Wordpress sites to want to work on those.

Now we work with Node.js which is much better ..... oh, wait I just ran out of disk space after running `npm install`.

What other solutions are you youngsters using then? Elixir/Phoenix? Django/Python? Node? Haskell?

Not OP but in my limited experience the MVP slot is mostly held by Django, with Node + typescript, Go and Elixir being the “safer, faster” variants “kids” are using.

Ha it's funny seeing Django in that list because back in day (10 years ago or so) before rails took over I was trying to get everyone to use Django at my job. Then rails exploded and I moved to that.

I honestly thought Django was mostly dead now.

Anecdotally Django seems less common in the job listing post every month than Rails but is still present, and the python experience is relevant to many more.

Yeah I think it has to do with there being just more python scripts floating around, and Django (or flask) make it easy to turn that random script into a proper api. Plus CS courses have now started teaching python.


What stack are you gravitating to?

Judging by listings in the month jobs posting, the vast majority of hiring companies won't look at you if you don't have immediate and long experience with their current tech stack.

Back in my late 20s, I got stuck on maintaining a legacy software system written in Ada95 (I was unemployed in 2008 and it was the only work I could find).

When that job went away, I thought I could get a job using something more modern. However, the only job I was able to land was a new development project in Ada2005 (actually it was a port from SPARC to x64 and a partial rewrite of the same legacy application I had been maintaining for 3 years).

It took me forever to find a company that would "take a chance" on me - which is when I started working with Java.

That whole experience taught me that I always need to have side projects going so I have actual work samples to show potential employers.

Ha, I was somewhat in the same position, my first job out of college was working for a NASA contractor using Ada. I programmed on the side using C for fun and was able to find a startup to take a chance on me after a few years.

And even then, experience tells me one needs to have some luck that the HR people are even willing to listen about those side projects.

Good that it worked out for you.

Yes, this is especially true at larger companies. In my case, a small company picked me up.

Why is this surprising? For years employers have included proficiency with a specific brand of tool (language) as part of their job descriptions. And in the Bay Area anyway there is often a lot of talk about being able to use any language you're comfortable with in the interview process, but in practice unless you use the language(s) in the stack you're interviewing for it's a harder sell.

I self-select myself out of jobs that have specific languages in the job listing if I can't honestly say I've used them professionally.

The language an employer uses is high on my list when evaluating a potential employer. It tells me if the company has put thought into what they want to accomplish and what technologies would best serve them in accomplishing their goals vs. choosing their technology stack based on popularity, brand loyalty, or some other subjective criteria. I typically haven't found joy in working on projects where I'm using tools that don't easily support the company's goals and vision.

I don't think it's that newcomers won't like Rails. I feel it just makes sense to go all Javascript instead since that's where the momentum is now and doing everything in one language has a lot of benefits especially when it's something as ubiquitous as javascript.

That wasn't what I was thinking about, there are good reasons to choose one stack over another once you are in a job. When you are job searching though there are a ton of criteria: salary, opportunities to learn and develop, autonomy in the role, enlightened management, realistic schedules, short commute, holidays, decent desk, chair and screen. I'd put all of those over a choice between Ruby and JavaScript.

It is always on by top list, after learnig what the application and domain is all about.

The majority of HR departments tend to suffer from myopia for anything beyond the last couple of months on the current employer.

So always take care not to land in something that will be hard to get out of.

Spotted the guy who's never used an immutable language with pattern-matching

I'm waiting for JavaScript to consume everything. https://www.destroyallsoftware.com/talks/the-birth-and-death...

This might be a stupid question and a tangent, but what does he mean by: "don’t use raw strings as constants"

Probably saying something like

  public String getDescription() {
    return “some constant”;

  public String getDescription() {
it’s a fairly weak example but you get the idea.

I really can't tell, but if I were to guess it would be the difference between something like `User.where(thing: 'foo')` and something like `Foo = 'foo'; User.where(thing: Foo)`

So you can refactor/find things that use Foo easier than 'foo'. You would probably get editor support for the constant "Foo" and not for the string "foo".

That reminds me of me after browsing HN's flavor of the month. Go -> Clojure -> Ruby -> Rust -> F# -> ...

Let me know when F# becomes flavor of the month. I'll gladly hop on that bandwagon.



Enjoy. If you want to dip your toes in the water, you can go through


for a quick introduction.

If you have background in functional languages/programming, most of it should look familiar. If you come from an OO or a procedural background, then it might take some getting used to.

But if you are looking to get a job at a windows shop, I'd stick to C# ( or VB.Net ) as that's where most of the jobs are.

I am shocked at how many situations I've been in where the engineering team has delivered a drastically over-complicated, usually micro-service-based, system. Ten engineers working for a year where I could easily see 1-2 Rails/Django devs delivering more in 2 months.

Clojure was fun overall. The not so fun part was integrating with necessary Java libraries with crazy/stereotypical abstraction patterns. Necessary because Clojure library offering isn't rich; not to knock on all the beautiful open source work that's out there.

I switched from rails to full-stack js and couldn't be happier. EmberJS + ExpressJS is so simple to set up, and than you have everything in one language and you can even share files and just use a build script to distribute them to client and server (e.g. regex)

Would have been a bit more interesting had he passed through a BEAM VM language on the way...

My career so far:

Delphi -> Microsoft stack -> Rails/Ruby -> Scala -> Clojure -> Rust

The stuff I remember well starts from Ruby. Good things:

- Ah man code is like a book.

- Lots of people were writing Ruby back then, so lots of libraries.


- Deployments in general, this was before Docker, but I prefer having single binary/jar deployments

- Slow

- Lots of runtime errors

- You need lots of tests

Scala, good:

- If using IntelliJ, the development experience is kind of slick

- Quite modern, JVM libs work

- You can go functional or OOP, whatever you wish

- Fast

- Type system helps you out, so no need for that many tests


- IntelliJ, I need my Emacs

- SBT is not very nice in the end, making a huge jar file can cause trouble sometimes

- Not the biggest fan of implicits

Clojure, good:

- Lisps are SO MUCH FUN to write

- ...especially if writing pure functions, stuff like code generators

- Emacs is good here, Cider rocks!

- JVM libs are available

- Leiningen is definitely nicer than SBT

- Everything is pretty easy and straightforward


- Concurrency and effects can cause trouble, especially if doing it through Java libraries

- Leiningen is nice, but slow. You must use Cider.

- Runtime errors, as with every dynamic language

- Again lots of tests, but at least they're fun to write

Rust, good:

- Concurrency, IO and so on. Just damn nice with Rust

- Refactoring without fear

- Cargo is the best dependency management / build tool ever

- Small binaries

- Total control of CPU/RAM, you can do pretty fast systems

- If it compiles, it usually works

- Type system is great, libraries are quite nice already

- Community feels like the good old Ruby days, but better


- Borrow checker still sometimes hurts, hopefully non-lexical lifetimes land to stable soon

- Problems with the current 0.1 tokio and futures can be pretty daunting. It's not a good idea to do super complex stuff with them yet

- Write your own libraries, especially if you started two years ago. That was fun though

I think the next thing I must grasp well is Haskell, just for the fun of it.

> Community feels like the good old Ruby days, but better

I really miss that old Ruby community. It was full of that hacker spirit, everyone trying stuff, no one to crush your creativity. Then the startup hipsters took over and came up with good practices, style guides, gems you absolutely had to know to get hired, etc.

It drove me away from Ruby, actually. It just wasn't fun anymore.

I hope it doesn't happen with Rust. I did not have so much fun writing code for a long time.

Your comment really speaks to me. I'm in the process of transitioning my career out of development altogether for the reason you mentioned getting out of Ruby. It just isn't fun anymore. I haven't been able to find a company with a good hacker-spirit since 2010. I like programming, and will probably continue with languages like Elixir and Haskell, but as far as the day-to-day work goes, I'm done.

Is that, perhaps, more to do with JS fatigue than anything related to Ruby or Rails? What I can't fathom is how every shitty little SPA has to be done in React these days.

Just go into lower level. In many companies people have message brokers that need consumers. That's a good place to introduce languages such as Scala, Rust, Haskell and so on. Try to stay away from frontend and hack away.

This definitely feels my own golden age now being able to write Rust and seeing all these great people doing interesting projects and hacking things...

The ecosystem and the community is fantastic. But I have to say, Rust is a harsh mistress. Guess I need more practise.

Rust is a very different beast indeed. The fun clearly doesn't come from the "flexibility/easiness" of the language (compared to Ruby), but really from the feeling that we're all still discovering a new paradigm, new ways to do things using its various features. Building state machines out of the type system is an example. Rust is still kind of a blank canvas just asking to be painted on.

Yes. I think the biggest thing it has going for it is that it is an advanced language offering superior guarantees, but lacks the smell of academic elitism that Haskell and Scala carry. Its a working mans tool, with a welcoming culture that disapproves of intellectual status games and too much jargon.

thoughts on rails 5? coming from rails 3 -> 4 -> 5, it solved a lot of issues for me.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact