[HN Gopher] Learning Ruby: Things I Like, Things I Miss from Python
       ___________________________________________________________________
        
       Learning Ruby: Things I Like, Things I Miss from Python
        
       Author : OKRHero
       Score  : 194 points
       Date   : 2021-02-16 06:50 UTC (16 hours ago)
        
 (HTM) web link (medium.com)
 (TXT) w3m dump (medium.com)
        
       | zem wrote:
       | ruby is one of my favourite languages, but there are two things I
       | wish it had done python's way - inline type annotations, and
       | imports acting as namespaces. I wouldn't have thought to miss the
       | latter before I got a day job working in python, but it really
       | does make code in larger projects a lot easier to read and
       | maintain when you can tell at a glance where every symbol is
       | defined.
        
       | LimaBearz wrote:
       | Can we talk about Ruby's laissez faire approach to syntax.
       | Ignoring all the purely technical stuff every ruby project I've
       | seen that has at least a small team behind it looks like a
       | kitchen sink. Bob does it X way, Tina does it Y way, Joe does it
       | X way. Every person does it their own way and readability becomes
       | an eyesore as the project grows.
       | 
       | If I didn't know any better I've seen teams so bad it's almost a
       | game of "look how gross this block is, now let's see how much
       | worse you can make the next one".
       | 
       | I've always found ruby very off-putting for this reason alone. I
       | don't want to inherit or work on something where everyone just
       | wants to show off. How do organizations manage this? Is it just
       | me?
        
         | hrktb wrote:
         | I'd argue the issue is better acknowledge in ruby than in many
         | other popular languages, which just assume having few options
         | means everyone can mostly code as they want from there.
         | 
         | Sister comments are pointing to rubocop which mostly makes it a
         | solved problem. I'd add that having multiple potential ways to
         | do things, and being able to also favor a specific way through
         | linting is very powerful.
         | 
         | You can actually choose the best option for your current
         | project and enforce it, while choosing a different tradeoff for
         | another project and still enforce it there. And you still can
         | opt-out of the rules on a case by case basis in a standard way.
        
         | mparramon wrote:
         | Following Rubocop's Ruby Style Guide [1] to the dot helps a lot
         | with this, and it's quite easy to enforce using the rubocop gem
         | + a GitHub action
         | 
         | [1] - https://github.com/rubocop-hq/ruby-style-guide
        
           | ljm wrote:
           | I wouldn't really go that far though, as you'll end up with a
           | codebase that the linter likes (and _appears_ clean) but is
           | still an utter nightmare to read because of all the forced
           | indirection. I 'm talking about things like: restricting
           | method length to 10 lines, restricting class length to 100
           | lines, and similar ones that prevent you writing code in a
           | certain way because the preferred approach to writing code is
           | 'out of sight, out of mind'.
           | 
           | Default Rubocop is why you have codebases where code is still
           | complicated, it's just hidden away inside single use modules
           | (concerns) or inside single-use private methods. As far as
           | I'm concerned you just can't defer the architecture of your
           | codebase to a tool like that and I'm sure it teaches more bad
           | habits than good ones.
           | 
           | Instead, decide these things for yourself and keep it in
           | check during code review or the stage before that. Maybe even
           | design rubocops of your own to help. The linter can still do
           | a good job without telling you how to handle complexity.
        
             | sodapopcan wrote:
             | Couldn't agree more. Our Rubocop is FULL of customizations
             | and the odd override too (though there is a case some
             | overrode a rule which caused massively confusing code).
             | 
             | "Single use private methods" is a little triggering to
             | me... not even rubocop's fault. I've seen the likes of this
             | before (single use):                 def
             | redirect_to_home_unless_user_present         redirect_to
             | :home unless user.present?       end
             | 
             | Why!?? (that is a rhetorical "why")
        
               | ysavir wrote:
               | That's a horrible usecase for single use private methods,
               | agreed. But I think they're an excellent way to breakdown
               | a complex chunk of code into smaller components, using
               | the method names to describe what they're doing. That
               | requires good naming, though; without good names, the
               | effort is moot.
        
               | ljm wrote:
               | I think they serve a good purpose once you realise you've
               | stepped out of OOP. You have the idea of a command, or a
               | service, and it does a bunch of procedural things as part
               | of its execution. It only has one public method, so the
               | private methods are a way to describe the steps of the
               | command.
               | 
               | But that's an architectural decision, it's not code
               | style. If rubocop tells you how to write a function then
               | you're going to see nonsense like the example in our
               | parent comment.
        
               | ysavir wrote:
               | > I think they serve a good purpose once you realise
               | you've stepped out of OOP. You have the idea of a
               | command, or a service, and it does a bunch of procedural
               | things as part of its execution. It only has one public
               | method, so the private methods are a way to describe the
               | steps of the command.
               | 
               | It doesn't have to be a step out of OOP, and their usage
               | is not limited to "Single Public Method Objects". Single
               | use methods are just a convenient and clean way to make
               | complicated code easy to digest, OOP or not.
        
             | ysavir wrote:
             | Very refreshing to see this take and I largely agree.
             | People all too often refer to linters as The Rule rather
             | than The Tool, and forget that a robot will never be able
             | to evaluate the quality of a codebase. That requires human
             | input (though Rubocop is very efficient for people that
             | want to avoid human input--often the root cause of the
             | problem).
             | 
             | That said, I am a fan of single-use private methods and
             | think they have their place, with or without method-length
             | rules.
        
             | andrewzah wrote:
             | You can configure rubocop however you want.
             | 
             | If you find the defaults to be not good (which I agree),
             | then simply update the rubocop.yml.
        
               | sodapopcan wrote:
               | In fairness, ljm was responding to a comment that
               | suggested following the style guide "to the dot".
        
               | ljm wrote:
               | That was that's exactly the point I was aiming to
               | address, thanks :)
        
         | tomthecreator wrote:
         | The solution is the same as in many other programming
         | languages: adopt a coding standard and enforce it in an
         | automated fashion, i.e. in the CI/CD pipeline. Code style then
         | becomes mostly a non-issue and not an object of discussion.
         | 
         | In the case of Ruby:
         | 
         | * most popular style guide: https://rubystyle.guide/
         | 
         | * automated tool for linting and enforcing code style:
         | https://rubocop.org/
        
           | pelasaco wrote:
           | I just would like to point out that even though that is the
           | most sane way, it comes with it owns set of problems. One of
           | them is when developers start to code to cheat the linter, or
           | they complicate the code just to "make the linter happy",
           | another is when the linting rule introduces problems/errors
           | like https://github.com/rubocop-hq/rubocop-rails/issues/418
        
             | msbarnett wrote:
             | Yeah I would never recommend relying on just a linter. The
             | linter can reduce scut work, but you always want to have at
             | minimum a thorough code review process that's looking at
             | things like "ok, we made the linter happy; but are we happy
             | with the result, or should we have just disabled the linter
             | here?" and "the linter didn't catch that we solved this
             | thing with X here but with Y&Z last time. Let's rationalize
             | our approaches and get everything on the same page"
        
           | [deleted]
        
         | crazypython wrote:
         | > Can we talk about Ruby's laissez faire approach to syntax.
         | Ignoring all the purely technical stuff every ruby project I've
         | seen that has at least a small team behind it looks like a
         | kitchen sink. Bob does it X way, Tina does it Y way, Joe does
         | it X way. Every person does it their own way and readability
         | becomes an eyesore as the project grows.
         | 
         | This is why Python has deliberately chosen to not add Ruby's
         | blocks. To make any code readable by everyone. (And to prevent
         | bad coding patterns that blocks promote, but that's besides the
         | point.)
        
         | LordHeini wrote:
         | There are multiple ways to do things in many languages. And
         | Ruby is a rather sane one.
         | 
         | The worst language in that regard is Scala by far.
         | 
         | There are the "Scala is Haskell" people where everything is a
         | pure function and written in point free style.
         | 
         | There are the ones who think Scala is just Java with type
         | inference.
         | 
         | And the everything needs to be a DSL people.
         | 
         | Or everything needs to be implicit and generic in a static
         | singleton.
         | 
         | There are a bazillion ways to write the most trivial stuff. Use
         | for comprehensions, recursion, higher order functions, or your
         | usual for each loop. If you hate your coworker use some fancy
         | type system trickery. Does not matter pick and choose.
         | 
         | Ruby is mostly confined compared to many similar languages like
         | Python.
         | 
         | I would say that even JavaScript is way more chaotic in that
         | regard.
        
         | burnthrow wrote:
         | We can talk about anything we want. Don't even have to open the
         | link. Now let me tell you what _I_ think about Ruby...
        
         | blunte wrote:
         | When it comes to showing off with Ruby, I think that only
         | occurs in two cases: creating unnecessary DSLs ( _cough_ Rspec)
         | when plain Ruby was already adequate, and doing as many chained
         | collection operations in one line as possible.
         | 
         | The former adds a layer of abstraction which is often
         | unnecessary, and the latter can go from succinct to slightly-
         | too-clever to fully obfuscated.
         | 
         | As others have mentioned, Rubocop can help keep some uniformity
         | (although some of the default rules suck, imo). And beyond
         | that, the company just has to have people who agree to be
         | decent. It will happen that developers of different skill
         | levels will write more or less readable code depending on the
         | perspective of the viewer, but that's true in all languages.
         | You _could_ write Ruby code like you would write Basic.
        
         | calessian wrote:
         | That's not exclusive to Ruby, though. Is there a language in
         | which a project of at least a small team would not diverge?
         | 
         | Devs at my current dayjob have established, in multiple rounds,
         | that it is impossible to find at least a minimum of viable
         | coding standards, where every draft has been heavily opposed.
        
           | msbarnett wrote:
           | > Devs at my current dayjob have established, in multiple
           | rounds, that it is impossible to find at least a minimum of
           | viable coding standards, where every draft has been heavily
           | opposed.
           | 
           | Tangential, but this mostly sounds like a leadership issue?
           | Ultimately software development is a team sport, and teams
           | work if and only if individuals put the team's interests
           | ahead of their own - and it's not realistic to expect a group
           | of individuals to develop that mentality by consensus. Absent
           | leadership, all you have is a bag of individuals who will
           | bicker about the bikeshed endlessly.
           | 
           | A good leader would hear that feedback and accommodate it to
           | an extent, but they also have to take the responsibility for
           | saying "alright folks, I've heard your feedback, but
           | ultimately it's more important to the team as a whole that we
           | have _something_ than it is that everybody got that
           | _something_ to be what they liked most. This _is_ arbitrary,
           | but this is what we 're running with, so it's time to fall in
           | line".
           | 
           | (This is something of a pet peeve of mine, as my team
           | occasionally has to work with an ops group that is stuck in
           | the stone age solely because they've lacked clear leadership
           | for a decade or more. Every time they meet to discuss
           | aligning and modernizing their approach ends in a lack of
           | consensus as to _how_ , and consequently in 2021 they
           | continue to manage their servers each by hand, each in their
           | own way, a collection of expensive single points of failure.
           | Literally any approach they discuss would improve on the
           | status quo, so any competent leader at this point should
           | simply roll the dice, pick one at random, and get on with the
           | task of moving forward)
        
           | gilbetron wrote:
           | While not perfect, Go has much more "developer convergence"
           | than any other language I've worked in. Ruby has numerous
           | ways to do the same thing, but one of Go's design objectives
           | was to minimize that.
           | 
           | So divergence does occur, but some languages make it a lot
           | easier than others.
        
       | viraptor wrote:
       | > Python and Ruby are really similar.
       | 
       | Yes they are, as seen in the _why's old project:
       | 
       | https://github.com/whymirror/unholy
       | 
       | > Compile Ruby to Python bytecode. And, in addition, translate
       | that bytecode back to Python source code using Decompyle
       | (included.)
        
       | breatheoften wrote:
       | ruby has enumerators which are quite a bit like pythons
       | generators -- i actually think they are easier to reason about
       | and do complicated things with -- although i learned python a
       | long time ago and haven't tried to use generators for anything in
       | a long time.
        
       | Sparkyte wrote:
       | Maybe I'm part of the hate train, but the natural progress is
       | either Python to Java or Python to Go. Ruby is alright, but not
       | scalable unless you're trying to be a startup where you can just
       | drop in a bunch of gems and make it work.
        
         | gls2ro wrote:
         | How big is big enough for you to think that Ruby is scalable:
         | 
         | Gitlab size? GitHub size? AirBnB size? Shopify size?
         | 
         | I think this is a kind of fallacy to take into consideration
         | scalability as the main metric when choosing the programming
         | language unless you are truly doing a project which has as main
         | selling point scalability.
         | 
         | I would also say that that 90% (it is just a guess based) of
         | projects started with any of these languages including Python,
         | Java, Ruby, Go, Elixir, Javascript will not reach a level where
         | scalability is truly a problem.
         | 
         | There are some things to think about when choosing a
         | programming language like:
         | 
         | 1. It is a language which has by default solutions to problems
         | I would encounter. See for example choosing Python over Ruby as
         | there are more AI libraries in Python than Ruby, or choosing
         | Elixir over Ruby if serving multiple concurent connections will
         | be at the core)
         | 
         | 2. How quick do you want to iterate the product? How many
         | libraries in your specific domain are available? ...
         | 
         | 3. Availability of developers in your area or with a specific
         | domain knowledge if needed
         | 
         | 4. How much the infrastructure cost to serve some users per
         | month
         | 
         | Also I agree with you - even if you presented it as a negative
         | trait - that if I want to just spin up a quick prototype, I
         | will almost always choose Rails. I have by default a lot of
         | gems providing a lot of functionalities out of the box: user
         | management, payments, activity feeds, image/video/audio upload
         | and processing, .... Add there some TailwindUI/Bootstrap/Bulma
         | screens and already I can have a good looking prototype to test
         | an idea without too much work.
        
           | Sparkyte wrote:
           | Coming from operations perspective Ruby is only scalable
           | because it is great for protyping. It as a deliverable can be
           | challenging due to size and complexity involved with
           | compiling gems. I'm sure it has gotten easier to deliver with
           | Kubernetes but Java and Go are much easier to ship and
           | deploy.
        
             | gls2ro wrote:
             | In my case I managed to scale Rails (a Ruby web framework)
             | both vertically (of course this is always simple with
             | resource lakes like some cloud providers are offering) and
             | horizontally.
             | 
             | For example I can spin up as much pods on GCP or dynos on
             | Heroku without doing almost any changes in my Rails app.
             | Things are even more simpler if I would use Sinatra.
             | 
             | Of course I am not asserting here that Ruby is a fit for
             | all types of web apps. But what I disagree with your
             | statement that sounds like a very general conclusion
             | stating that Ruby is not scalable.
             | 
             | So talking specifically about programming languages and not
             | frameworks, how do you scale Java or Go without operational
             | overhead?
        
             | codeduck wrote:
             | Sorry, this is false.
             | 
             | I'm a Java developer who became a Ruby developer who became
             | an SRE.
             | 
             | Ruby is far easier to deploy and use than Java or (in my
             | limited experience) Golang, specifically because it has a
             | single, mature, sane packaging system i.e. bundler.
             | 
             | Only Elixir + hex come close to the ease of packaging with
             | bundler. The only time you need to compile gems is if
             | you're shipping ones with native extensions - and there are
             | enough alternatives available that you shouldn't need to
             | use gems with native C extensions.
             | 
             | If you're running ruby on a modern web server - like puma -
             | it will scale far further than your architecture will
             | likely permit without redesign.
        
               | Sparkyte wrote:
               | I worked with both Java and Ruby. It's easier to ship
               | Java than Ruby. I've even used puma and unicorn. But when
               | you compare it to just getting a springboot build with
               | Java it's kind of pointless. Puma doesn't even matter
               | anymore especially in a K8S cluster unless the
               | application is big and tall.
        
           | codeduck wrote:
           | The biggest issue with Rails is remembering how to DISABLE
           | everything you don't want. API-based Rails is really nice.
        
         | masklinn wrote:
         | > the natural progress is either Python to Java
         | 
         | Python to java is not progression, it's criminal malfeasance.
        
           | TameAntelope wrote:
           | You'd be laughed out of every dev shop I've ever worked at
           | for suggesting that switch.
        
           | [deleted]
        
       | DarkWiiPlayer wrote:
       | > I Miss: Generators
       | 
       | Excuse me, what?! Python has stackless symmetric coroutines; Ruby
       | comes along and brings you stackful symmetric coroutines, what's
       | there to miss?
       | 
       | How is `return to_enum :each unless block` not infinitely nicer
       | than pythons yield? (and waaay more powerful as a little cherry
       | on top)
        
         | pmontra wrote:
         | No idea what that statement does and I'm a rubyist. Anyway I
         | think that Enumerator::Lazy is what one should use in most
         | cases. Quite syntax heavy compared to Python though, but what
         | is not in the very core of a language is always more verbose.
         | 
         | https://ruby-doc.org/core-3.0.0/Enumerator/Lazy.html
        
           | inopinatus wrote:
           | Hopefully you do actually recognise what that does: the
           | standard library has many methods documented to return an
           | enumerator if no block is given. This is how.
        
         | orf wrote:
         | Genuinely no idea what your statement does, but "yield 123" or
         | "yield from xyz" is infinitely more clear.
        
           | regularfry wrote:
           | I think nestling somewhere in the core of the GP's comment is
           | that Ruby also has `yield`, and using it (or related
           | functionality) is pervasive.
        
           | inopinatus wrote:
           | That's the idiom for an enumeration method called without a
           | block. It's why you can chain them, as in ary.map.with_index
           | and so forth.
        
           | vidarh wrote:
           | It creates an Enumerator (for practical purposes consider it
           | a continuation) over the method "each" unless you've passed
           | in an argument named "block", which by convention would be a
           | block argument (a closure passed after the argument list
           | effectively) that will evaluate to Proc object.
           | 
           | Consider this example:                  class Foo
           | def each            yield(42)            yield(:hello)
           | yield(:world)          end        end
           | 
           | You can now do:                  Foo.new.to_enum(:each)
           | 
           | (In this case the ":each" is redundant - to_enum defaults to
           | :each, so you could do Foo.new.to_enum) And you will get back
           | an Enumerator that supports the Enumerable interface, giving
           | you methods like map, inject, to_a (to array) etc. as a
           | facade to the original object so you can do (for example:
           | Foo.new.to_enum.to_a         => [42, :hello, :world]
           | 
           | (you can achieve a similar thing by including Enumerable into
           | the class in question, but to_enum lets you do this on
           | arbitrary objects without messing around with the class)
           | 
           | It allows any method to return something that behaves like a
           | collection class without needing to pass more than a method
           | that implements "each" by yielding each value of the
           | collection.
           | 
           | You can also do this with a bare lambda, btw., but the syntax
           | is a bit awkward as "yield" is syntactic sugar over calling
           | the "call" method over an implicit (unnamed) block argument
           | that isn't quite as transparent as you might like:
           | ob = ->(&block) { block.call(42); block.call(:hello);
           | block.call(:world) }.to_enum(:call)
           | 
           | The explicit "&block" argument is needed because "yield" has
           | lexical scope where defined. "ob" is now an enumerator that
           | supporters each, to_a, map etc.:                   ob.to_a
           | => [42, :hello, :world]
        
             | DarkWiiPlayer wrote:
             | > for practical purposes consider it a continuation
             | 
             | But it's not though. While coroutines can be used to
             | implement one-shot continuations and vice versa, they are
             | very different features and behave completely differently.
        
               | vidarh wrote:
               | While you're technically right, in terms of using it as a
               | shortcut to give superficial understanding of how
               | Enumerator functions the distinctions are largely
               | irrelevant.
        
           | DarkWiiPlayer wrote:
           | > "yield 123" or "yield from xyz" is infinitely more clear
           | 
           | It's funny because the first is exactly how you write this in
           | ruby.
        
         | diarrhea wrote:
         | Generators aren't only used in the context of coroutines.
        
           | inopinatus wrote:
           | The article misleadingly states "Ruby 3.0 has Fibers". Not
           | false, but Ruby _2.0_ had fibers, they are used for building
           | a variety of cooperative concurrency behaviours, and they go
           | back years.
        
           | DarkWiiPlayer wrote:
           | But generators literally are coroutines though
        
       | mparramon wrote:
       | I would love to find an MVC framework with the extense library
       | support of Rails but for Javascript. Being able to simply `gem
       | 'devise'` and have full authentication at your hands (including
       | views, email templates, etc) is a godsend.
       | 
       | At many places where I've worked at this paradigm is exactly what
       | is needed, but people know and want to use JS. If we choose to go
       | that way then, we need to write a lot of glue code and think
       | about setting up architecture standards, folder and naming
       | conventions, ORM support, authentication, job queues, etc; all of
       | this comes by default with Rails and makes life so much easier.
       | 
       | Lately some options seem to be gaining steam:
       | 
       | * RedwoodJS [1]: by Tom Preston-Werner (founder of Github), comes
       | with React and GraphQL baked in, pretty new project though
       | 
       | * Next.js [2]: brings a lot of nice conventions to the frontend,
       | but still some things missing on the backend side
       | 
       | * SailsJS [3]: similar to Rails, but small community
       | 
       | * Strapi [4], hapi [5] et al: plenty of backend functionality,
       | but frontend agnostic.
       | 
       | Has anybody had experience with any of these (or others) and can
       | report back?
       | 
       | [1] - https://redwoodjs.com/
       | 
       | [2] - https://nextjs.org/
       | 
       | [3] - https://sailsjs.com/
       | 
       | [4] - https://strapi.io/
       | 
       | [5] - https://hapi.dev/
        
         | busk07 wrote:
         | https://adonisjs.com <- feels the closest to rails but with
         | nodejs instead
        
         | illiriath wrote:
         | I've had good experience using Nest.JS [1] for the backend and
         | next.js for the frontend. Nest has defaults for ORM,
         | validation, task scheduling, authentication, etc, I find it a
         | lot easier to work with when compared to plain express projects
         | with tons of glue.
         | 
         | Disclaimer: I have never tried Rails
         | 
         | [1] https://nestjs.com/
        
         | claudiojulio wrote:
         | https://adonisjs.com
        
         | twic wrote:
         | It's crazy to me that every other language doesn't have a
         | Rails, literally just a clone. It has been so obviously
         | valuable, and is now so well understood.
        
           | pjmlp wrote:
           | We were using AOLServer and its clones, Vignette, Zope before
           | Rails was invented.
           | 
           | Most of the Java and .NET world eventually moved into full
           | blown CMS, with component frameworks, graphical layout
           | editors, user management and third party connectors to
           | distributed platforms.
           | 
           | For me, Rails just feels too little, with a quite slow
           | language.
        
         | berkes wrote:
         | > Being able to simply `gem 'devise'` and have full
         | authentication at your hands (including views, email templates,
         | etc) is a godsend.
         | 
         | I'm on the other end of the spectrum.
         | 
         | I mean, I see the benefit of such "roll X quick by adding a
         | gem" for a proof of concept, a demo or even an MVC.
         | 
         | But even for the latter, it breaks down rapidly. Such gems
         | bring their own libraries. They force you to "do it their way".
         | A gem like devise comes with its own addons (omniauth) that
         | paint you even quicker into a corner.
         | 
         | I've consulted and developed at many Rails codebase where most
         | of the problems were caused by years of development on top of
         | such gems. A weak, wobbly base, and the entire castle built on
         | top of that. Where 90% of the devise features were unused or
         | worked around. Where more code was present to customise and
         | work around devise-isms, AASM-weirdness, CanCan-troubles, than
         | a complete "roll your own from scratch" would have ever caused.
         | 
         | So, my "devise advise" always is: sure, use it to get your
         | product to market rapidly. But be sure to pull it out and
         | replace it with a focused, fitting architecture, sooner rather
         | than later.
         | 
         | Such gems fit very well in the problematic development-workflow
         | where one "searches for an existing gem which offers some
         | version of Foo" rather than "figuring out how we want to do
         | Foo, then look for a gem that does it our way".
        
         | alejandro_del wrote:
         | Check https://blitzjs.com/
        
         | AlphaWeaver wrote:
         | I've used 1, 2, and 4.
         | 
         | Strapi [4] is much less stable than it looks. It's pretty
         | buggy, isn't designed very well, and is just all around messy.
         | I used it in production once and never will again.
         | 
         | Next.js [2] is well designed and well maintained, but is
         | _totally_ a frontend framework. It 's not just "missing a few
         | things" on the backend side, there _is_ no backend side. Works
         | great if you already have an API (JAMStack) but can 't stand
         | alone in the way that Rails or Laravel might be able to.
         | 
         | Despite being the youngest, RedwoodJS [1] is the best option
         | here. I was actually involved pretty early on as a contributor
         | on the project before I had to step away (unrelated, personal
         | reasons) and was seriously impressed by their progress. TPW
         | runs a tight ship, and all of the devs that work on the project
         | are smart and amazing. They ship _very_ quickly and it 's
         | rapidly growing into the closest thing to a Rails competitor in
         | Javascript. I wouldn't be surprised if it takes the JS world by
         | storm in a year or two.
        
         | patates wrote:
         | https://nestjs.com/ is nice, although I'm not sure about your
         | front-end expectations.
        
         | cjohnson318 wrote:
         | I like using Django [0] with django-rest-framework [1] and dj-
         | rest-auth [2]. This gets you Rails-inspired organization, with
         | a nice abstraction for REST endpoints in general, and a
         | login/logout/registration/email verification/social auth in
         | particular. Everything is built on the very well maintained
         | allauth [3] module. Here's a good quickstart [4]. I've used
         | Ruby in production, but I like Python's "one way of doing
         | things" approach.
         | 
         | [0] - https://www.djangoproject.com/ [1] - https://www.django-
         | rest-framework.org/ [2] - https://dj-rest-
         | auth.readthedocs.io/en/latest/index.html [3] - https://django-
         | allauth.readthedocs.io/en/latest/overview.htm... [4] -
         | https://www.rootstrap.com/blog/registration-and-authenticati...
        
         | gls2ro wrote:
         | I don't have a recommendation, but here is a quick test I use
         | when looking for a replacement for Rails:
         | 
         | How quick can I create a minimum simple SaaS like application
         | that has the following functionalities:
         | 
         | - User management (Signup/Login/Reset password/Confirmation...)
         | 
         | - Simple payment processing (like have on single plan, one time
         | fee, and after paying the user will see some other default page
         | then not paying)
         | 
         | - Sending emails in HTML and plain text (for user management
         | and after payment is processed)
         | 
         | - Use slugged/permalink like URLs
         | 
         | - Processing some task in async queue
         | 
         | And so far there are a lot of alternatives out there but for me
         | Rails provides the easiest experience to have this up and
         | running quickly as there are already battle-tested gems for
         | almost all these functionalities.
        
           | andi999 wrote:
           | Just wondering: is rails transactional? What I mean how are
           | race conditions avoided if e. g. two requests are processed
           | at the same time.
        
             | ryanbrunner wrote:
             | Rails has support for database transactions, but depending
             | on what you want to do, things won't be magically
             | transactional all by themselves (that said, it's really
             | just a matter of wrapping transactional code in a block).
             | 
             | There are a couple of footguns in Rails involving
             | transactions - by default, validating uniqueness of a
             | column in Rails will be done at the app layer which is
             | prone to race conditions, and with a typical CRUD model for
             | an application, there's nothing in particular preventing
             | overwrites of model data if say, a field was present on a
             | form and two people were editing it, even if the "later"
             | editor made no changes to that particular field.
             | 
             | When multiple systems and things other than databases (or
             | multiple databases) are involved, you're generally on your
             | own as well.
        
               | pbowyer wrote:
               | It's important to say that these footguns are not
               | exclusive to Rails. I don't know a single web framework
               | which avoids them - if it exists I'd like to know so I
               | can learn how they do it.
        
               | RickSullivan wrote:
               | Elixir's Ecto has the best solution to this problem in my
               | experience--it doesn't do database validations on things
               | like uniqueness, but provides easy error handling when
               | constraints are hit.
               | 
               | It encourages you to write validations for things that
               | don't need to hit the DB, but actually uses the DB for
               | handling constraints, since it's the real source of truth
               | for data consistency.
        
               | andi999 wrote:
               | Thanks. So typical CRUD applications usually run on luck?
               | (or on not beeing unlucky if the average number of
               | request per hour is not so high).
        
               | msbarnett wrote:
               | Not really?
               | 
               | Most CRUD applications go the one extra step of putting a
               | uniqueness constraint on the column instead of just
               | relying on the app layer validation, and comparing hidden
               | time stamps on the form if multiple people might edit the
               | same row simultaneously. It's exceedingly
               | straightforward.
               | 
               | Did you really think thousands of web apps up to and
               | including GitHub and shopify were squeaking by on blind
               | luck? If basic database transactions and integrity
               | weren't doable in Rails, that'd have had to have been
               | fixed years ago.
        
               | andi999 wrote:
               | Any reference how this is best done with rails?
        
               | msbarnett wrote:
               | Depends on what you mean by "this", I suppose.
               | 
               | Most of your basic questions are covered by the starter
               | Rails Guide for ActiveRecord, eg) transactions,
               | validations, how to add a uniqueness constraint to the DB
               | in a migration
               | 
               | https://guides.rubyonrails.org/active_record_basics.html
               | 
               | The ActiveRecord Queries guide also covers some
               | mechanisms for handling row-level write contention
               | 
               | https://guides.rubyonrails.org/active_record_querying.htm
               | l#l...
        
             | brightball wrote:
             | It's got a very helpful transaction block wrapper structure
             | that allows you to do some really interesting things with
             | database transactions.
             | 
             | To be perfectly honest, one of the things that I always
             | love about Rails is exactly how good the database layer is
             | with ActiveRecord. I like using the advanced features of my
             | database, especially PostgreSQL, and I've found that it's
             | really easy to do with Rails.
             | 
             | The "Scopes" feature of active record that let's you save
             | small snippets of a query and chain them together is
             | fantastic. I've used it to create complex filter functions
             | that combine full text search and coordinate distance as
             | simple methods that can be attached to any query.
             | 
             | Being able to add a
             | `.search("fender").distance_from(location, 5, :miles)` onto
             | any query made my life so much easier.
             | 
             | It's been a few years, but I wrote about some of my
             | favorites.
             | 
             | https://www.brightball.com/articles/rails-gems-to-unlock-
             | adv...
        
             | gls2ro wrote:
             | In case of DB:
             | 
             | I think ActiveRecord has #lock (see https://api.rubyonrails
             | .org/classes/ActiveRecord/Locking/Opt...) and that together
             | with #transaction (https://api.rubyonrails.org/classes/Acti
             | veRecord/Transaction...) are enough for my use cases.
             | 
             | In case of file uploads to cloud providers: there I don't
             | know for specific method to help with race conditions when
             | transforming the resources.
        
         | nickjj wrote:
         | I think this is a very uphill battle because JS and most other
         | languages are missing a key ingredient that Rails has had for
         | 10+ years. It's having a long running large real world SAAS app
         | that Rails is extracted from.
         | 
         | That means every feature of Rails is derived from a practical
         | use case, and then it's well tested in that app before it's
         | pushed out to the community. That's tested from an API and
         | functionality point of view. By the time end users start to use
         | it, the feature has likely been running in production for 6+
         | months. Maybe even serving billions of page views through it if
         | you factor in Shopify and GitHub also running off Rails master.
         | 
         | It's all constantly in use, kept up to date and iterated on as
         | a whole.
         | 
         | Creating a starter kit / boiler plate of libraries and opinions
         | in another language / web framework is not the same. I think
         | this is why even after 10+ years there's only a handful of
         | popular Rails alternatives in other languages.
        
           | ivanhoe wrote:
           | There's plenty of JS code written out of the real needs and
           | by teams running huge projects, take just Facebook as an
           | example. Problem is that there are thousands of solutions for
           | everything, and that saturation and bad signal to noise ratio
           | makes it hard sometimes to pick the right lib (or even hear
           | about it in the first place). Rails have a huge benefit of
           | having a single team behind it, so you always have a
           | recommended way of doing things, and the whole community is
           | smaller, less libs to test, less rubbish to avoid, making the
           | experience much more linear...
        
       | pjmlp wrote:
       | > OOP all the way.
       | 
       | Python is surely OOP all the way, just ask the type system about
       | what are the metaclasses of every, single, basic, data type.
        
         | [deleted]
        
         | azangru wrote:
         | Ah, but can you call a method on a primitive such as number in
         | Python? :-)                   30.downto(0) do |n| print a[n]
         | end
        
           | maxnoe wrote:
           | There are no primitives in python.
           | 
           | Everything is an object with methods.
        
           | pjmlp wrote:
           | Since you didn't specify which method exactly,
           | print((30).to_bytes(2, byteorder='big'))
        
       | divtiwari wrote:
       | I'm currently learning Ruby as I plan to implement the 'Building
       | Git' book by James Coglan. Ruby has lot many features/syntactic
       | sugar and a weird OO model. Makes me feel quite uncomfortable.
       | Also, no good IDE except RubyMine.
        
         | cies wrote:
         | The OO model is very clean. Pretty much Smalltalk's model.
         | Wanna see weird OO, look at JS or even Python (both were not OO
         | in first version).
         | 
         | IDE's will never be really good for Ruby (or any dynamically
         | typed language), as it can only infer so much from the code.
         | Type hints in the new Ruby version will maybe change this, but
         | it is an addon so it may take a while to materialize.
         | 
         | Ruby is --to me-- a dynamically typed language with very clean
         | OO, expressive syntax, and as much FP goodness as possible
         | without sacrificing it's OO essence.
         | 
         | There is a language that has --in my view-- similar
         | fundamentals, except that it is strict/strongly typed. This
         | language is Kotlin: very clean OO, expressive syntax, and as
         | much FP goodness as possible without sacrificing it's OO
         | essence. IntelliJ is a great IDE for Kotlin and IDEs for Kotlin
         | can actually deliver (due to the static typing). And yes,
         | Kotlin is null-safe (no reason to carry this mistake around in
         | static typed langs any further).
         | 
         | Oh and the syntactic sugar in Ruby is called cocaine. :)
        
           | pansa2 wrote:
           | > _JS or even Python (both were not OO in first version)_
           | 
           | This isn't true for Python. Quoting Guido: "classes were
           | added late during Python's first year of development at CWI,
           | though well before the first public release" [0].
           | 
           | I don't think it's true for JS either - the first versions
           | didn't have classes, but I believe they already had
           | prototype-based OO.
           | 
           | [0] http://python-history.blogspot.com/2009/02/adding-
           | support-fo...
        
             | cies wrote:
             | I dont consider prototypes-based OO a real OO.
             | 
             | I thought Python had classes in the first release but not
             | to be used by library/application makers. But I was
             | probably mistaken. Thanks for setting that straight.
             | 
             | What I think is rather unidiomatic in OO langs is Pythons
             | "function/methods" like `len(x)` which is implemented as
             | `__length__()`. This is just weird/unintuitive/unidiomatic
             | OO... I do not know what Guido was on when he thought this
             | was a good idea.
        
               | pansa2 wrote:
               | > _I dont consider prototypes-based OO a real OO._
               | 
               | Fair enough - in some ways it's like half-way to an OO
               | system. Yes, it's theoretically elegant to have
               | everything be an object and not to have classes. However
               | in the vast majority of cases it seems prototype-based OO
               | is only used as a foundation upon which to build a class
               | system.
               | 
               | > _What I think is rather unidiomatic in OO langs is
               | Pythons "function/methods" like `len(x)` which is
               | implemented as `__length__()`._
               | 
               | AFAIK the reason for the double-underscore method for
               | `len` is to avoid it accidentally working for, say, a
               | Rectangle class that exposes `length` and `width`.
        
         | Toutouxc wrote:
         | Could you elaborate on the "weird OO model"? Is it the thing
         | with eigenclasses?
        
       | pansa2 wrote:
       | > _it seems that Ruby is determined to keep adding syntax._
       | 
       | With async/await, type hints, the walrus operator and now pattern
       | matching, Python's designers are just as guilty of this as
       | Ruby's.
        
         | jspash wrote:
         | It really feels like Ruby has reached the point of featuritis.
         | Nobody wants to admit that the language is "done". Well, done
         | enough.
         | 
         | None of the recent changes in Ruby are game changers for me
         | except maybe for pattern matching. But since I've fully drunk
         | the Elixir cool-aid, I won't be needed that now. Sorry Ruby. It
         | was a great ride, and I'll miss you. But it's time for me to
         | move on.
        
           | Ancapistani wrote:
           | I've used both, but am a long-time Python user and prefer it
           | for most things. It's my "first and true love of languages".
           | 
           | In Python, "format-strings" were a welcome improvement and
           | "f-strings" took me a bit to warm up to but are worth it. The
           | walrus operator, though? Sure, it's useful in a narrow case,
           | but it's extremely narrow. Likewise the pattern marching
           | stuff - it just doesn't feel like "Python" to me, and doesn't
           | seem to solve any real-world problem that's remotely close to
           | justifying the cognitive overhead.
        
           | [deleted]
        
       | pansa2 wrote:
       | I'm still amazed at how Python code with type hints looks so
       | different to Python without them. Consider the program near the
       | end of the article - if we remove the type hints:
       | 
       | - The `AstNode` abstract base class becomes completely
       | unnecessary and can be deleted.
       | 
       | - We can't use `@dataclass` and are forced to write an `__init__`
       | method for each class. But this is good, right? "Explicit is
       | better than implicit"?
       | 
       | - It's then clear that our three classes each have two methods,
       | one of which is __init__. Following the advice from "Stop Writing
       | Classes" [0], each class should just be a function (in this case,
       | a generator).
       | 
       | These changes reduce the code above "AST representation of the
       | Ruby statement" from 70 lines to 40, and simplify some of those
       | lines as well - for example, `arg_reprs = (arg.representations()
       | for arg in self.args or [])` becomes simply `arg_reprs = args`.
       | 
       | The knock-on effects of type annotations seem to be doubling the
       | complexity of the code. This is why I'm far from convinced that
       | static type checking in Python is worth the cost.
       | 
       | [0] https://www.youtube.com/watch?v=o9pEzgHorH0
        
         | josteink wrote:
         | > The knock-on effects of type annotations seem to be doubling
         | the complexity of the code. This is why I'm far from convinced
         | that static type checking in Python is worth the cost.
         | 
         | If adding types to your existing code cannot be done without
         | creating new types, that's a sign that your type-system lacks
         | expressiveness.
         | 
         | For instance C# 1 lacked a good and general way to describe
         | types for functions (that is, decoupled from a class).
         | 
         | In later C# revisions this have been greatly improved, making
         | this pain go away, and accepting type-safe function-value
         | parameters as easy as any other parameter.
         | 
         | I'm guessing Python is lagging quite a bit in that regard. That
         | doesn't mean that type-safety (when done right) isn't massively
         | useful and worth the effort 200 times over.
        
           | masklinn wrote:
           | I think you're missing their point. They're not necessarily
           | saying types are not useful, they're saying types encourages
           | a form of thinking which tends towards class-based
           | overcomplications.
           | 
           | Now this is also a very yagni view, and maybe TFAA uses the
           | structure they build for inspection before the final
           | consumption, but their point that the posted script could be
           | half the size and work the same without loss of
           | readability... is compelling.
        
             | nerdponx wrote:
             | The sentiment in Python has been trending toward using
             | "classes" (either with dataclass or attrs [1]) as
             | essentially namespaced logicless containers, and away from
             | dicts (or TypedDicts [2]) and namedtuples/NamedTuples [3])
             | for that purpose.
             | 
             | This is very different from the useless "you don't need
             | classes" classes.
             | 
             | Think of classes nowadays serving as being both traditional
             | OO classes and Scala case classes: they can be types of
             | stateful objects, or just dumb record types, which you now
             | can easily extend with logic if you do happen to need it
             | (and you often do, in nontrivial applications).
             | 
             |  _Now this is also a very yagni view, and maybe TFAA uses
             | the structure they build for inspection before the final
             | consumption, but their point that the posted script could
             | be half the size and work the same without loss of
             | readability... is compelling._
             | 
             | I find this "it would be half as long and work the same"
             | argument totally bizarre. Do you look at a basic Rust
             | program and say "it would be half as messy and look the
             | same if we did away with all the move/borrow stuff"?
             | 
             | Of course not. Yes, YAGNI is fine and all, but at least
             | recognize that there is an upside to this tradeoff. I am
             | not going to need internal state on the object, but I sure
             | as hell am going to want the type checking.
             | 
             | And yes, you could (perhaps should) use Typescript or
             | Kotlin for new web server projects instead. But some people
             | _want_ Python with types, including me.
             | 
             | I wouldn't accuse Python of being "concise" in the first
             | place, so I am happy to add some up-front verbosity in
             | exchange for a much smoother developer experience later in
             | my project, with significantly less mental overhead as my
             | IDE can do a lot of work for me.
             | 
             | 1: https://www.attrs.org/en/stable/
             | 
             | 2: https://docs.python.org/3/library/typing.html#typing.Typ
             | edDi...
             | 
             | 3: https://docs.python.org/3/library/collections.html#colle
             | ctio... and https://docs.python.org/3/library/typing.html#t
             | yping.NamedTu...
        
               | masklinn wrote:
               | > The sentiment in Python has been trending toward using
               | "classes" (either with dataclass or attrs [1])
               | 
               | That is not even remotely what is being done here. And
               | just because you _can_ do it doesn 't mean you _should_
               | or _must_ do it. Not every argument needs to be bundled
               | in its own object.
               | 
               | > I find this "it would be half as long and work the
               | same" argument totally bizarre. Do you look at a basic
               | Rust program and say "it would be half as messy and look
               | the same if we did away with all the move/borrow stuff"?
               | 
               | Again, missing the point. The point was not about the
               | typechecking but the design perspective it engenders and
               | encourages.
               | 
               | > Of course not. Yes, YAGNI is fine and all, but at least
               | recognize that there is an upside to this tradeoff.
               | 
               | Why would I? There is none in the article's snippet.
               | 
               | > I am not going to need internal state on the object,
               | but I sure as hell am going to want the type checking.
               | 
               | Type checking doesn't require classes. You can typecheck
               | a funtion.
               | 
               | > I wouldn't accuse Python of being "concise" in the
               | first place, so I am happy to add some up-front verbosity
               | in exchange for a much smoother developer experience
               | later in my project, with significantly less mental
               | overhead as my IDE can do a lot of work for me.
               | 
               | You're still missing the point entirely, but I hope
               | you're happy about all those strawmen you beat down.
        
               | nerdponx wrote:
               | _Type checking doesn 't require classes. You can
               | typecheck a funtion._
               | 
               | I am not talking about type checking a function. I am
               | talking about type checking data structures, replacing
               | this:                   record = { "a": 1, "b": 2 }
               | 
               | with this:                   record = Record(a=1, b=2)
               | 
               | The latter can be statically type checked much more
               | easily than the former.
               | 
               | This is analogous to Scala case classes and Haskell
               | records. And it can be a significant productivity
               | improvement in big codebases, because it reduces your
               | reliance on documentation and working memory. Instead, it
               | lets you depend on the type checker and IDE to provide
               | auto-completion to prevent a whole class of errors.
               | 
               | In fact, it lets you write _less_ runtime code and have
               | shorter, simpler code paths in some cases, because you
               | can mostly skip checks like  "if x is an int" (at least
               | in internal/private functions), and even skip the
               | corresponding unit tests, and let the type checker deal
               | with it instead.
               | 
               | Edit: as for the example in the article... really? This
               | is just single dispatch. What would you write instead,
               | one big if/else statement? A dict of functions to
               | dispatch to? `AstNode` is an interface, and all of these
               | subclasses implement the interface.
        
             | foxes wrote:
             | Then types in python are not expressive enough. Other
             | languages most of the time you don't even necessarily need
             | to specify the types - the compiler/interpreter can figure
             | it out and still type check the program.
        
               | nerdponx wrote:
               | Maybe, maybe not. Data classes are meant to feel like
               | Scala's case classes, or Haskell's record syntax.
               | 
               | The point is to stop passing around dicts/hashmaps/tables
               | and tuples, and to start passing around containers with a
               | known and fixed set of keys/names, and known types for
               | the values/elements.
               | 
               | I don't know of any language where the compiler
               | automatically infers field names and types, and I don't
               | know if I would ever want such a thing.
        
               | josteink wrote:
               | > I don't know of any language where the compiler
               | automatically infers field names and types, and I don't
               | know if I would ever want such a thing.
               | 
               | Typescript can do this to unannotated code, and it's
               | surprisingly useful.
        
               | nerdponx wrote:
               | I've already started checking out Typescript as a
               | general-purpose scripting language to replace Python,
               | Ruby, Perl, and to some extent Bash/Zsh. Knowing this
               | makes it even more attractive. The only annoyances I've
               | had so far are: difficult-to-search docs, and the `tsc`
               | command-line compiler is slow.
        
             | sportsracer wrote:
             | Typing in Python is, at times, at odds with the language.
             | 
             | Said another way, patterns which were once Pythonic are now
             | discouraged by the type system (or, mypy I suppose, since
             | the door is still open for other type checkers to emerge).
             | Examples:
             | 
             | - Duck typing. I was taught that's Pythonic, yet mypy
             | really only accepts nominal subtyping i.e. it infers type
             | compatibility from the type hierarchy. There is
             | typing.Protocol, but I've never encountered that in code.
             | Ruby's new type annotation language RBS introduces
             | interfaces, so maybe it'll stay true to its duck typing
             | roots.
             | 
             | - Lists with mixed types. I might as well bookmark this
             | page, because this behavior of mypy always surprises me: ht
             | tps://mypy.readthedocs.io/en/stable/common_issues.html#inv.
             | ..
             | 
             | In both cases, mypy reminds me way more of Java's type
             | system than of Python's, which is maybe why heavily typed
             | Python code often ends up looking like Java.
        
         | gjulianm wrote:
         | I don't think you can draw that conclusion from that code. I've
         | been using Python for a few years and moved some projects to
         | static type checking. It's not always worth the cost in every
         | situation, of course. For such a simple script I wouldn't add
         | type checking. But for larger projects where there are multiple
         | classes and a lot of types, I've found that the effort is very
         | much worth it: a lot of type errors are found faster (some of
         | them hadn't been caught by unit testing), the code becomes more
         | consistent and easier to read, and developing is easier if you
         | have an IDE that understands the types and gives you
         | appropriate suggestions.
         | 
         | It's also worth it in plumbing code, as it tends to be more
         | difficult to test properly and having types will catch a whole
         | class of errors.
         | 
         | The fact that the program at the bottom of the article can be
         | simplified is not an issue of typing, in fact static typing
         | could also be used in your approach without any issue.
        
         | viraptor wrote:
         | > We can't use `@dataclass` and are forced to write an
         | `__init__` method for each class. But this is good, right?
         | "Explicit is better than implicit"?
         | 
         | I don't think that applies. @dataclass is an explicit wrapper
         | with specific result and can be inspected and interacted with.
         | 
         | Implicit would be something like "when a class has more than
         | one attribute and doesn't define init or repr, add those
         | methods assuming they're wanted/needed".
         | 
         | > The `AstNode` abstract base class becomes completely
         | unnecessary and can be deleted.
         | 
         | You're mixing type annotations with class usage. You can have
         | either one without the other.
         | 
         | For example without AstNode you could still have an
         | `Expr|MethodCall|Lambda` type hint on relevant variables. Or
         | even alias that expression to AstNode without having the class
         | and inheritance.
         | 
         | Finally, classes help describe/construct the structure present
         | in "stmt". Sure, you could replace those with functions, but
         | that means your data is either reduced to untyped / string-
         | tagged lists/dicts, or you're running the exact function
         | immediately inline. It doesn't matter for this toy example, but
         | it would in any practical use of this code.
        
           | mumblemumble wrote:
           | @dataclass is one of those ones where it depends on how you
           | look at it.
           | 
           | To me, it's a great application of "explicit is better than
           | implicit." Because @dataclass explicitly indicates that this
           | class has a certain set of behavior, and is meant to be used
           | a certain way. If you manually implement exactly the same
           | behavior, you've made the individual statements more
           | explicit, but you've made the class's actual function
           | implicit. A reader needs to figure it out by reading the code
           | and working backward from there. An editor of the code needs
           | to recognize the intended behavior and not break it,
           | otherwise the understanding that other people built up by
           | reading the code may break in subtle ways.
        
         | jasfi wrote:
         | If you're going to use Python with types, then you may as well
         | use Nim. Nim has a similar syntax, but with better performance.
         | You can also interface with Python code via Nimpy.
        
           | nerdponx wrote:
           | I like Nim, but I don't think there is much of anything
           | similar between these languages _except_ the syntax.
        
           | craftinator wrote:
           | I love using Nim, it's an absolute pleasure after years of
           | fighting with Python's "I hope I got this right..." style of
           | type guessing. The biggest problem most people run into is
           | that python has a huge library ecosystem.
           | 
           | I haven't actually tried interfacing them, it's next on my
           | docket. Have you found any good learning resources for that?
        
         | BiteCode_dev wrote:
         | It's more of a design issue here. You could do away from
         | classes while keeping types by using 2 lines:
         | Expr = Callable[[str]], Callable[..., Iterable[str]]]
         | AstNode = Expr | Callable[... ], Callable[..., Iterable[str]]]
         | 
         | You then don't event need to call representation() , as each
         | node is callable. No classes needed.
         | 
         | But my instinct tells me a node will eventually have more
         | attributes and methods. Stuff to get parents, children,
         | siblings, filtering and so on.
         | 
         | So it's probably a good call to use a class for later. Also you
         | want to keep AstNode as a node could be an expression, a
         | statement or a comment.
        
           | rkangel wrote:
           | To echo the sibling - I believe the most important skill a
           | developer can have is reworking* code, and doing it readily
           | and continuously. If you can do that, then you don't ever
           | have to worry about what code MIGHT do in the future, you
           | just design it for what it DOES do and then rework it later
           | if necessary. It's quicker and the result is cleaner. Also,
           | even if you tried your hardest to predict what was going to
           | be needed, there are always things you can't predict and at
           | that point you either rework your code or have bad code.
           | 
           | * I could have said refactoring, but some people have very
           | specific definitions attached to that word so I use something
           | more general here
        
           | mumblemumble wrote:
           | > But my instinct tells me a node will eventually have more
           | attributes and methods.
           | 
           | I couldn't agree more with Jack Diederich's thoughts on that
           | sentiment, at 5:40 in the video linked above:
           | 
           | https://youtu.be/o9pEzgHorH0?t=340
           | 
           | > _Lots of times people think they might need something
           | later. You don 't. Or, you can just do it later._
        
             | BiteCode_dev wrote:
             | https://docs.python.org/3/library/ast.html
             | 
             | Experience helps with this.
             | 
             | It's a middle path as usual: naive approaches waste as much
             | time as overenginering.
        
             | nerdponx wrote:
             | YAGNI is a double-edged sword. It's almost no additional
             | overhead to write a class now if you think you might need
             | to add methods and internal state later. But it's quite a
             | bit of refactoring work if you decide _not_ to write a
             | class and then realize you need one after the fact.
             | 
             | Maybe this is an indictment of Python as a language, but I
             | think it's a case of not taking anything too literally.
             | YAGNI fetishism is like DRY fetishism all over again.
        
               | mumblemumble wrote:
               | I guess? At the same time, working in some languages
               | (Racket and C come to mind), it's surprising how long I
               | can go without anything ever becoming a class, ever.
               | Seemingly indefinitely.
               | 
               | My sense is that many of us are taught to think in terms
               | of classes and methods instead of data structures and
               | functions, so it's very easy to slip into thinking you
               | need objects in order to manage state, even when that's
               | not the simplest way to do it.
               | 
               | Strictly speaking, though, I think that the only time in
               | Python that you really have no workable alternative to
               | classes is when you also need dynamic dispatch.
        
               | Groxx wrote:
               | Personally: it's much easier to YAGNI in languages with
               | relatively-strong compile-time checks and relatively-
               | sophisticated tooling.
               | 
               | If you lose either of those, a little bit of preventative
               | work goes a loooooong way. The friction (largely due to
               | risk) of correcting things later is so large that it just
               | starts snowballing unless your team is _extremely_
               | attentive.
               | 
               | In that sense, Python is... middle of the road, with an
               | extremely broad spread. Tooling is an odd blend of
               | surprisingly good vs incapable of doing simple inference
               | / detecting all calls, and type safety depends heavily on
               | how you write things (some tactics are fairly strong,
               | many are so weak as to be useless or make things much
               | worse).
        
               | mumblemumble wrote:
               | Personally:
               | 
               | Python has a great type checker. Several, actually.
               | They're not compile time, because Python doesn't exactly
               | have a compile time, but still. 11/10, use everywhere,
               | spend less time on unit tests, be happy.
               | 
               | Second, to me, these sorts of concerns about refactoring
               | are. . . maybe not a smell, but worrisome, all the same.
               | They suggest poorly-modularized designs with no internal
               | component boundaries that might have limited the scope of
               | impact of such a change. That said, even if you do find
               | yourself needing to do a far-reaching refactor like that,
               | refactoring in a dynamic codebase isn't necessarily
               | harder, it's just different. Not being able to lean on
               | the compiler to help you boil the ocean, for example, is
               | fine, because boiling the ocean wasn't necessarily the
               | best approach in the first place. The strangler pattern
               | will help here, not just with type errors, but also with
               | other problems the compiler might not have been able to
               | help with, and will also be friendlier to the people who
               | do your code reviews.
        
       | RangerScience wrote:
       | I'm real confused about generators, but maybe I'm just missing
       | something about what Python generators do?
       | 
       | Like, aren't generators just a block of code that can produce the
       | next value, wrapped in an iterator?
       | 
       | 'Cuz like, sure, they're not front in center in Ruby programming
       | and if they are in Python I can see why one language would teach
       | you the other, but ruby blocks and yield make this pretty damn
       | easy. So what am I missing?
        
         | mjfisher wrote:
         | Not a huge amount in concrete terms of capability, but the
         | little differences add up in how the languages are used in
         | practice. Python has functions you can refer to and pass around
         | directly (as opposed to Ruby's blocks and procs), and that
         | makes functions that are also generators just a little bit more
         | friction free and more likely to be used. Python also has
         | explicit support for differentiating between generators and
         | regular functions in other parts of it's syntax (e.g. list
         | comprehensions) that tend to make people reach for generators
         | more quickly in Python than other languages.
        
           | nerdponx wrote:
           | Python's lazy, composable iterators/generators are my #1
           | favorite Python language feature and I find myself missing it
           | in every other language I use.
        
         | [deleted]
        
         | jcheng wrote:
         | Ruby has generators, but it's not just blocks and yield.
         | Generators are special because after they've yielded (in the
         | Python sense) a value, they are actually no longer on the stack
         | at all. But when their next value is requested, they pick up
         | execution right where they left off with all local variables
         | intact, as if they had never suspended execution.
         | 
         | If that doesn't make sense to you, it's definitely worth taking
         | a few minutes out to look up some tutorials on generators in
         | Python or any other language that supports them.
         | 
         | In Ruby, you can create a generator by passing a block to
         | Enumerator#new; the block's parameter is a "yielder" object
         | whose << operator behaves like Python's yield keyword. Keep in
         | mind that this block is NOT invoked once per iteration; it is
         | invoked once, it might suspend and resume though (or suspend
         | and not resume!).
         | 
         | https://stackoverflow.com/questions/35826375/enumerator-new-...
        
           | jonnytran wrote:
           | Yes, the difference is between internal iterators vs.
           | external iterators.
           | 
           | For external iterators, Ruby's Enumerator syntax is a little
           | less convenient. But I think it's a small price to pay for
           | not having 2 colors of functions [1]. In Ruby, an Enumerator
           | is just an object with methods like any other object. And
           | there are things built in to core to make working with lazy
           | enumerators just as easy as regular ones.
           | 
           | To GP's point, you rarely actually need enumerators because
           | of a block's ability to break in Ruby. It's the most useful
           | thing that's in so few languages [2]. For example, if I
           | really wanted to write an infinite loop yielding fib -- not
           | saying I would do it this way but -- the quickest way to do
           | it in Ruby, adapting the Stack Overflow example, is probably
           | more like this:                   def fib()           puts
           | "Enter function"           a = b = 1           i = 0
           | loop do             puts "Inside loop"             yield i, a
           | puts "i: #{i}, a: #{a}, b: #{b}"             a, b = b, a + b
           | i += 1           end         end
           | n_from_user_input = 5         fib do |i, x|           break
           | if i == n_from_user_input           puts "Use value: #{x}"
           | end
           | 
           | 1: https://journal.stuffwithstuff.com/2015/02/01/what-color-
           | is-...
           | 
           | 2: https://yehudakatz.com/2012/01/10/javascript-needs-blocks/
        
       | blunte wrote:
       | Thankfully it is not "OOP all the way".
       | 
       | You can write a lot of functional, or at least simply procedural
       | (modular) without ever needing to define your own class. Sure,
       | you'll be instantiating some objects implicitly or explicitly at
       | times, but you don't have to make your code be class-based.
       | 
       | You can write pure functions, although you do have to be aware of
       | space/time concerns depending on the situation. Obviously you
       | want to avoid copying huge data structures frequently. But in a
       | lot of cases, the performance cost is low, so you can respect
       | your inputs by not mutating them and just return some
       | value/collection.
        
       | buf wrote:
       | I still think Ruby on Rails is the #1 choice for launching a non-
       | static web project in 2021. The lifecycle I've seen with the 4
       | companies I've built on Rails:
       | 
       | 1. Pure vanilla rails
       | 
       | 2. Then the front end grows into an SPA as functionality becomes
       | more complex
       | 
       | 3. More microservices like search emerge into their own thing
       | 
       | By step 3, I've got a dozen engineers and things are still as
       | smooth as one engineer. Engineering is never the bottleneck. The
       | tech debt is super easy to spot. It's incredibly simple to know
       | how to scale horizontally to many more engineers.
       | 
       | Rails can get you very very far. It's not sexy, but it gets me to
       | market faster than anything out there.
        
         | andy_ppp wrote:
         | Phoenix and Elixir, if you understand the choices made, is also
         | a wonderful choice. The whole contexts thing allows you to
         | design your system as micro services before you even add in
         | network partitions, and Ecto, not being an ORM (it's a DSL for
         | writing SQL in elixir) is incredible, better than it's possible
         | to be in active record. Then there's channels, presence,
         | performance etc.
        
           | syspec wrote:
           | I want to use this, but every time I try to switch it is not
           | there yet.
           | 
           | Sure it has the core stuff, but things like ActionMailer and
           | the built in S3 support in ActiveStorage, Devise, keep me
           | going back. Too many of those things are not there... yet ...
           | with Phoenix
           | 
           | Also nothing beats active record after they added Arel which
           | turns your query into an AST and optimizes it before sending
           | it out - Ecto is not really there yet and doesn't look like
           | it will get there
        
             | andy_ppp wrote:
             | I think most packages are just as good if not better than
             | their Ruby equivalents these days (and really we can do all
             | of the things you mentioned in elixir), the active record
             | pattern is obviously easier to understand than Ecto, but if
             | you know how to write well optimised SQL you never want to
             | go back to an ORM (even if it tries to rewrite your queries
             | for you with magic).
        
               | anhari wrote:
               | ActiveRecord and the scenic gem for database views is a
               | very powerful combination. For the vast majority of
               | queries, you don't need to write optimized SQL by hand.
               | 
               | When you do, you can push that work to a version
               | controlled database view, and wrap it in a ActiveRecord
               | class interface and still get all of the ORM niceties.
        
               | andy_ppp wrote:
               | The difference here is between two different
               | philosophies:
               | 
               | 1) I want the thing to just work, it doesn't matter about
               | underlying complexity, give me the magic and the version
               | controlled database view and the ORM niceties.
               | 
               | 2) I don't trust magic as it has bitten me in the arse
               | before, let me make and be able to fix things myself as
               | I'm working at a (slightly) lower level.
               | 
               | I'm not saying either is correct but in Phoenix/Elixir
               | magic stuff tends to be kept to a minimum. One man's "for
               | the vast majority of queries" is another persons "will
               | work fine until you need to do something complex".
               | 
               | It's all trade offs and I choose mine carefully as I'm
               | sure you do.
        
           | realusername wrote:
           | What I love with Phoenix/Elixir is that there's plenty of
           | features I wouldn't bother with in a more traditional
           | language. You want a PubSub? It's just there, you can use it
           | in one line. You want a Redis? Registry is there, also in one
           | line. It's like a whole tech stack in one language.
        
             | jamil7 wrote:
             | I saw a presentation about Phoenix/Elixir that described
             | this once and it really impressed me. It's been 4 or so
             | years that I've been meaning to pick up Phoenix for a
             | project but still haven't.
        
               | vram22 wrote:
               | Was it a publicly available online presentation? If,
               | please share title or link if you can.
        
               | [deleted]
        
               | realusername wrote:
               | You can watch this small twitter demo:
               | https://www.youtube.com/watch?v=MZvmYaFkNJI. It's what
               | sold me on the technology.
        
               | vram22 wrote:
               | Thank you.
        
               | jamil7 wrote:
               | It was a slide from a conference I believe but I wouldn't
               | know how to find it - some elixir people in the thread
               | might know. It was something illustrating all the
               | external tools you'd normally reach for and how
               | Erlang/Elixir just had all of this built into the
               | language and VM.
        
               | sfusato wrote:
               | That sounds like one of Sasa Juric's talks:
               | 
               | GOTO 2019 * The Soul of Erlang and Elixir * Sasa Juric
               | 
               | https://www.youtube.com/watch?v=JvBT4XBdoUE
               | 
               | https://elixirforum.com/uploads/default/original/2X/a/a25
               | 24c... (also from Sasa Juric's book)
        
               | [deleted]
        
               | jamil7 wrote:
               | Thats it! Thank you!
        
           | nickjj wrote:
           | > Phoenix and Elixir, if you understand the choices made, is
           | also a wonderful choice
           | 
           | Until you want to integrate Stripe, BrainTree, PayPal or
           | Paddle and then you can't find an official library for
           | Elixir. Even common things like pagination isn't fully solved
           | with well maintained libraries in Elixir. Then there's things
           | like wanting to create notifications (showing unread icons,
           | sending email / sns notifications, etc.) where Rails has this
           | solved with well supported gems but there's nothing like this
           | in Elixir. There's dozens of other things too, and all of
           | this adds up to you having to be an expert in Elixir and have
           | to write all of this functionality from scratch.
           | 
           | If you're in it for the long haul and are investing years
           | into the project with a big team that's fine, but if you're a
           | solo developer building an app and want to focus on writing
           | your app's features, in my opinion Phoenix can't really be
           | compared to Rails. Especially with Hotwire Turbo being
           | recently announced, now you can get all of those awesome real
           | time features into a Rails app quite easily (IMO even easier
           | than Live View).
           | 
           | I started a SAAS app with Phoenix a while back and ultimately
           | backed out of it because all of the above. It just felt like
           | I was in a perpetual state of having to write libraries
           | instead of my application. It was one of those things where I
           | got super excited initially but after 2 years of trying,
           | waiting for certain features to come, etc. it ended up not
           | being for me.
           | 
           | I have no regrets tho because I learned a really big lesson
           | from that, which is if you want to build something now, use
           | whatever tech stack you know right now and if you're very
           | much inclined to deviate from that and learn something new
           | then pick something based on what exists today in that tech
           | stack. Not what's "on the horizon" or "coming soon". You
           | could set yourself up for waiting forever, or if that feature
           | you wanted in the future does come out it could be way
           | different than what was originally claimed and now it feels
           | like you've delayed what you wanted to do for so long for
           | nothing.
           | 
           | In other words, nothing is ever going to be perfect. Just
           | pick something and build it. Also trust what folks do, not
           | what they say.
        
             | andy_ppp wrote:
             | I think often the things that don't exist are not there for
             | good reasons... using Stripe's api for example from a
             | module is pretty trivial in my experience, it's just HTTP
             | and you don't need to be super clever about it. What
             | specifically are you struggling to paginate, I've never
             | found that difficult to implement even with quite clever
             | options. Again notifications doesn't sound particularly
             | difficult and I don't see why I'd want to rely on some
             | complex gem that does every option when I don't need them.
             | Elixir+Phoenix I agree is definitely lower level but there
             | are things it does much better than Ruby on Rails. I'd
             | rather not have to rely on gems and write the simplest
             | implementation specifically for my use case. It's a trade
             | off, but one I'm much happier with.
        
               | nickjj wrote:
               | > I think often the things that don't exist are not there
               | for good reasons... using Stripe's api for example from a
               | module is pretty trivial in my experience, it's just HTTP
               | and you don't need to be super clever about it.
               | 
               | It's way more involved than inserting an auth token
               | header into an HTTP request and calling some API
               | endpoint.
               | 
               | For example, what about verifying webhooks? The official
               | libraries for Stripe (Python, Ruby, Node, PHP, Go, JS,
               | etc.) deal with this for you.
               | 
               | But with Elixir, you're on your own. This is very low
               | level code to have to deal with and it's extremely
               | important you get it right.
               | 
               | You're left having to parse Stripe's specification on
               | this and then implement the code yourself in Elixir. It's
               | so tricky and involved that the Dashbit company (the
               | creator of Elixir and members of the core team work
               | there) wrote a blog post on it at
               | https://dashbit.co/blog/how-we-verify-webhooks.
               | 
               | But before a few months ago that blog post didn't exist.
               | Also this isn't the only thing you'll have to do yourself
               | when it comes to interacting with Stripe using Elixir.
               | 
               | Then you'll have to do similar things for other payment
               | providers all which are different in a lot of ways, but
               | with Rails you have the combination of having official
               | Ruby clients from those payment providers and even the
               | Pay gem which lets you support payments from multiple
               | providers. That could easily be a few months of dev time
               | just for that abstraction alone if you had to go about
               | that from scratch and your implementation wouldn't have
               | any track record until you start using it and ironing out
               | the bugs from real world experience.
               | 
               | > Again notifications doesn't sound particularly
               | difficult and I don't see why I'd want to rely on some
               | complex gem that does every option when I don't need them
               | 
               | Don't take this the wrong way but this seems to be the
               | mindset of almost everyone I chatted with when it comes
               | to Elixir. When someone asks how to do something, the
               | answer is it's trivial or easy to implement but there's
               | rarely any examples posted on how to do it and almost
               | never in a production ready way.
               | 
               | In my mind trivial or easy means I can sit down in maybe
               | a few hours or a day and write a production ready
               | solution, complete with tests and have it work exactly
               | how I want without running into any major roadblocks.
               | 
               | I'd be curious to see how you would implement
               | https://github.com/excid3/noticed or
               | https://github.com/pay-rails/pay in Elixir / Phoenix.
               | Based on your responses of saying these things are easy
               | I'm guessing you've written large apps with Phoenix where
               | you've developed features like this in a production app?
               | It would be fantastic if you could post some code
               | examples or a blog post on how you went about this. Not
               | just to answer my specific question but I'm sure the
               | community would appreciate having concrete examples of
               | how it's done. This way maybe more folks would use the
               | framework.
        
               | andy_ppp wrote:
               | Here's some links by way of an answer for you...
               | 
               | Stripe, including webhooks support, actively developed:
               | https://github.com/code-corps/stripity_stripe
               | 
               | Global pay solution that supports everything: they are
               | all a bit crap you're right, the best I've found is
               | https://github.com/aviabird/gringotts and ex_money really
               | is amazing that integrates with it (I would suggest it's
               | better than the equivalent ruby gem). To be fair I'm not
               | sure I'd want to use the pay gem with anything complex as
               | you need to be able to use the specific quirks of each
               | API properly (for example it can't pause subscriptions on
               | stripe right now even though stripe supports this:
               | https://github.com/pay-
               | rails/pay/blob/master/lib/pay/stripe/...).
               | 
               | You're also right about noticed, after looking into it
               | more it would be worth building for elixir for sure.
               | Ravenx represents a start but it's unmaintained and
               | doesn't have a huge number of strategies. It depends on
               | how much I needed to do notifications like this. For the
               | apps that I've built we've just needed database and
               | grouped emails sent once per day, no need for texts or
               | slack etc. There's no reason these couldn't be added
               | fairly simply but I agree noticed is very neat. It's
               | definitely a few weeks work to roll your own from scratch
               | so to be honest I'd probably just integrate with Twilio
               | and just pay for someone else to handle this for me.
        
               | nickjj wrote:
               | Thanks.
               | 
               | > Stripe, including webhooks support, actively developed
               | 
               | I've looked into Stripity Stripe. For some time it was
               | unmaintained and ended up getting taken over by another
               | maintainer. It's also not as comprehensive as the
               | official Stripe libraries. There's also a very big
               | difference in using an official Stripe library and hoping
               | for the best with a random one someone developed. Just
               | skimming the code base it looks like the Checkout module
               | is missing features that exist in the official Stripe
               | library in every other supported language.
               | 
               | According to the README file for Stripity Stripe it's
               | also using Stripe's API version from 2019. There have
               | been multiple major API updates since then, and there's
               | been an open issue since November 2020 to add support for
               | newer API versions with no replies. Personally I would be
               | using one of those major features too.
               | 
               | And this really is the point I'm trying to drive home.
               | With Ruby, Python, Go, PHP, Node, Java and .NET these are
               | problems you don't even need to think about. You just
               | pick the payment provider's official SDK and start coding
               | immediately, often times there's also an abundance of
               | resources to implement the billing code itself into your
               | app too through blog posts, official docs, YouTube
               | videos, and even paid products like
               | https://spark.laravel.com/. Stuff that makes integrating
               | billing into your app (through Stripe, BrainTree and
               | Paddle) being something you get done in 1 day instead of
               | 3 months.
               | 
               | With Elixir it becomes weeks of comprehensive research,
               | evaluating questionable libraries, opening PRs, and
               | becoming a full time library developer just to get to the
               | point where you could even maybe begin to start accepting
               | payments with just Stripe. Then you have to repeat the
               | whole process for PayPal, BrainTree and / or Paddle.
               | These are all super popular payment providers.
               | 
               | > the best I've found is
               | https://github.com/aviabird/gringotts
               | 
               | I asked the Gringotts developers if they would be
               | supporting PayPal about 5 hours after they announced the
               | project ~3 years ago. He said it was coming and to stay
               | tuned. It's now ~3 years later and PayPal support isn't
               | there. Neither is BrainTree or Paddle. Here's the open
               | issue for PayPal support from 2018 (not by me, I asked on
               | another site)
               | https://github.com/aviabird/gringotts/issues/114. The
               | Stripe integration is also missing a ton and hasn't been
               | touched since 2018.
               | 
               | By the way, the Pay gem is really good. It's a smart
               | abstraction and supports a ton of different subscription
               | / 1 off payment use cases. Even complex ones like the
               | type of app I was building.
               | 
               | > It's definitely a few weeks work to roll your own from
               | scratch so to be honest I'd probably just integrate with
               | Twilio and just pay for someone else to handle this for
               | me.
               | 
               | Twilio ends up being 1 potential delivery method, it's
               | not really someone you pay to solve the problem for you.
               | 
               | There's wanting to show notification in the app over
               | websockets, saving them into a database, emailing them
               | out only if they are unread, maybe sending a text through
               | Twilio, Slack and other providers.
               | 
               | The noticed gem handles all of this for you (and supports
               | Twilio too).
               | 
               | Notifications in general is another example where other
               | frameworks have this solved in very good ways, but it
               | becomes another example where you have to stop developing
               | your app and start developing a notification library with
               | Elixir.
               | 
               | At this point we've only talked about payments and
               | notifications too. There's lots of other examples and
               | these are all things to think about if your goal is to
               | build an application.
               | 
               | Now don't get me wrong, it still takes a lot of custom
               | code and effort to build an app with Rails but the time
               | spent isn't on low level generic problems. It's letting
               | you focus on your custom application's business logic.
               | 
               | And with less opinionated frameworks like Flask you can
               | lean on Python being extremely well supported by almost
               | every service that offers an SDK and also being able to
               | find solutions to nearly every web dev problem online in
               | some form or another (blog posts, youtube, stack
               | overflow, etc.). That goes a long ways because at least
               | you're not starting from scratch.
        
         | tomduncalf wrote:
         | This is interesting - I'm evaluating what to use for the
         | backend of a side project right now after having focussed more
         | on front-end/apps for the last few years, so I'm catching up on
         | the latest progress in this area. Interesting to see that Rails
         | is still often considered the best option, seems like they must
         | be doing something right.
         | 
         | Ideally in my mind, I'd like to use JS (well, TS), because it's
         | the language I work in most of the time these days... but I can
         | pick up different languages as needed, that's not a major issue
         | and I've done some Ruby way back when - more important to me is
         | making the development of the backend as simple and as quick as
         | possible (the less code the better!), and that it will run
         | reliably. A good selection of well-tested off-the-shelf modules
         | to integrate other services is always a good thing. It seems
         | like Rails is well ahead of any JS-based solution in these
         | regards, from what I can see.
         | 
         | I had been evaluating PostGraphile, which appeals because of
         | the "low-code" approach, but I guess I'm a little concerned
         | about doing most of the logic in database functions, because
         | I've worked on a system like that years ago and found things
         | like testing and debugging pretty painful. If anyone has
         | evaulated PostGraphile (or Hasura etc) vs. Rails, I'd love to
         | hear your thoughts (or equally, any other solutions to check
         | out!).
        
         | capableweb wrote:
         | Replace Rails with any framework and you'll have the same
         | amount of success. I've done the exact same thing across Ruby,
         | PHP, Golang and tons of other frameworks/languages and never
         | had problems if following those steps.
         | 
         | Problems usually come from when teams start with point 3 and 2
         | when in reality they should stay lean in the beginning.
        
           | TylerE wrote:
           | I have seen multiple PHP projects at multiple companies go
           | totally pear-shaped. I refuse to work at companies that use
           | PHP anymore.
        
             | capableweb wrote:
             | Ok? I've seen projects go pear-shaped in probably 20
             | different programming languages at this point, and I guess
             | after a certain point of experience, you start to realize
             | that failures seldom have to do with the programming
             | languages themselves, and more about the people actually
             | writing and managing the code (and everything else around
             | development).
        
               | TylerE wrote:
               | When the 3 biggest disaster projects I've ever had to
               | touch were all in PHP, I choose to believe that is a
               | trend.
               | 
               | Yes, you can write bad code in any language.
               | 
               | PHP makes it hard NOT to. The footguns are all sitting
               | out in the open, loaded, and the safeties are off.
        
               | capableweb wrote:
               | Well, if you're not interested in factual discussions and
               | you simply "choose to believe trends", all bets are off
               | since you're pulling in your feelings about a programming
               | language into your arguments. Hope all goes well for you
               | anyway!
               | 
               | As a counter-point, the biggest disasters I've seen has
               | been involving either C++ or C#, but in the end I think
               | the reason they were disasters were because of the
               | programmers (and their ego) rather than the programming
               | languages they worked with. C++ is plenty open and
               | without safeties, but that doesn't mean you have to use
               | it like that.
        
             | herbst wrote:
             | Same experience. I've seen nice PHP projects on the
             | internet, but not yet in RL. I know its possible, i guess
             | its just that the language does not reward you for
             | corecctness.
        
               | jake_morrison wrote:
               | PHP used to actually have a performance penalty for
               | splitting things up into modules and using object
               | oriented design, because it meant loading and parsing
               | more files on each request.
               | 
               | I was trying to do that on my first big PHP project, and
               | got called out. The architect said "your code is too
               | good, PHP is for writing crap". If you are going to write
               | good code, you should do it in another language.
               | 
               | Symfony framework is a great example of a PHP framework
               | written by Java programmers. All the same verbosity as
               | Java, but without the benefits of Java.
        
           | brightball wrote:
           | This argument has been made for years and I've yet to see it
           | really be true.
           | 
           | You can make anything with anything, but Rails is the closest
           | thing to an Aspect Oriented Programming environment that I've
           | seen in the wild. It's not easy to replicate that type of
           | ecosystem with any other framework. I've spent ample time on
           | teams using Go, Python, PHP, Java and C#.
           | 
           | There's just no comparison vs what a small Ruby team can
           | accomplish.
        
             | bestinterest wrote:
             | I really agree with this statement. I've never seen
             | anything like Rails in Java/JavaScript land. In Java
             | there's SpringBoot but its nowhere near a cohesive package
             | with a strong online community behind it like Rails. It's
             | more of a collection of libs put together with a good
             | enough attitude.
        
           | bambataa wrote:
           | What Rails-equivalent framework does Golang have?
        
             | LordHeini wrote:
             | I found Revel to be quite similar.
             | 
             | I would say it is as productive as rails too.
             | 
             | There is no ActiveRecord and way way less magic which slows
             | (initial) development speed but it pays off in less bugs to
             | fix.
             | 
             | So overall development speed is roughly the same. But with
             | Go you gain co routines and nice performance which can make
             | life a lot easier.
             | 
             | Gos type system, while somewhat poor, still helps too.
             | 
             | The worst part of the whole thing is actually the weird Go
             | std lib templating.
        
             | enumjorge wrote:
             | Same question but for Node.
        
               | capableweb wrote:
               | NodeJS has even more "batteries included" frameworks than
               | Golang. Sails, Hapi, NestJS and Adonis are probably the
               | most popular right now, at least from my viewpoint
               | (traditional SaaS web development)
        
             | capableweb wrote:
             | I myself usually opt for the "small set of composable
             | libraries" way of development nowadays, but I've worked
             | with teams using Buffalo and Beego which kind of provides a
             | "batteries included" approach that Rails follows too.
        
           | regularfry wrote:
           | Constant factors vary framework to framework. I can't say
           | I've ever seen anyone move as fast on Symfony as on Rails.
        
             | capableweb wrote:
             | Symfony might be the odd one of the bunch.
             | 
             | Jokes aside, Laravel is probably a more likely contender to
             | provide a Rails experience for PHP.
             | 
             | And it's easy to get clouded by anecdotal data, I'm sure
             | I'm biased as well. But after some time in the industry, it
             | matters more who the person is that is using the framework,
             | than the framework itself. If you put an equally skilled
             | developer with the same amount of experience in either
             | Symfony or Rails, I'm sure they can be as productive as the
             | other. But then skilled developers tend to gravitate
             | towards Rails more than Symfony, so hard to judge in the
             | real world.
        
         | sedeki wrote:
         | I've thought about learning Ruby (and Rails), being a Python
         | guy myself. Not sure if it is worth the effort.
        
           | Toutouxc wrote:
           | Give it a try. The languages are actually very similar, on
           | roughly the same level of "high-low-level-ness", they both
           | come batteries included, both are very mature and refined.
           | 
           | Some things you may find nice are: the elegance and ease of
           | use of blocks instead of explicitly declared lambdas, the
           | standard library full of nicely named methods with aliases
           | for the most used ones (e.g. array.map / array.collect do the
           | same thing, some people hate this), the {do,if,case,loop}-end
           | syntax that doesn't fuck your code up when you just quickly
           | comment these parts out without reindenting the rest, and if
           | you're into OOP (as in OOP, not explosion-in-the-factory-
           | factory), then of course the hardcore OOP-ness.
        
           | syspec wrote:
           | Try rails you won't look back
        
             | mehphp wrote:
             | I tried (am trying at current company) and still don't like
             | it. I completely see the appeal for getting an idea up and
             | running quickly, it absolutely delivers on that.
             | 
             | However, I joined a rails company about a year ago and the
             | codebase is just a mess at this scale. I find it annoying
             | that in order to know where dependencies are coming from, I
             | can't just go to the top of the file and see what's
             | imported. I have to know how rails injects it and then
             | track it down from there.
             | 
             | Don't get me wrong, there are issues with django, Go, etc..
             | but for the most part I can jump in and track down what's
             | happening much easier.
             | 
             | Now, Ruby itself isn't bad at all.
        
               | awj wrote:
               | Yeah, that's gotta be one of my biggest complaints after
               | years of working with Rails. Eventually you sort-of
               | memorize the conventions and can relatively accurately
               | guess where a file lives, until someone decides to get
               | clever and put things in a weird place.
               | 
               | It also implicitly discourages you from asking yourself
               | if you _should_ be accessing the thing you are. IMO a lot
               | of the tight coupling in Rails codebases begins with
               | being able to grab literally anything and use it with no
               | one the wiser unless the read that specific line of code.
        
               | vidarh wrote:
               | I love Ruby. Have used it for 15 years, but I can't stand
               | Rails. It's just massive overkill for most things.
               | 
               | I prefer to start with Sinatra + Sequel for web projects.
               | Sometimes Padrino + Sequel. As your project grows, sure,
               | you'll pull in many things similar to Rails, and probably
               | pull in some projects that started out with Rails too,
               | but it allows you to be much explicit about which
               | dependencies you pull in, why, and to limited where it
               | gets pulled in.
        
           | cies wrote:
           | I'd go with something that has static/strong types when
           | picking a new tool. Not saying I dont love Ruby, but when the
           | codebase/team becomes large i've always regretted not having
           | strong type safety. Today I'd go with Kotlin.
        
           | forgingahead wrote:
           | Depends on your use-cases - Rails is unparalleled in my
           | opinion for getting a web app up and running, testing startup
           | ideas, and building quickly in response to customer feedback.
           | 
           | I've been using Python recently as I've done more ML work,
           | and I do prefer Ruby (though the syntaxes are similar and you
           | won't have any difficulty).
           | 
           | If you learn for Rails, start with 5.2.3, webpacker and the
           | other black magic in Rails 6 will frustrate you and currently
           | eliminates a lot of the ease of getting started and building
           | relatively sophisticated web apps quickly.
        
             | 20thCB wrote:
             | "If you learn for Rails, start with 5.2.3, webpacker and
             | the other black magic in Rails 6 will frustrate you and
             | currently eliminates a lot of the ease of getting started
             | and building relatively sophisticated web apps quickly."
             | 
             | Anyone else agree with this?
        
               | buf wrote:
               | Nope.
               | 
               | Webpacker is here to stay and takes about 5 minutes to
               | understand. Might as well take advantage of it so you can
               | quickly add complex front end components via React or
               | whatever flavor of FE you prefer.
        
               | adsteel_ wrote:
               | I agree, webpacker makes learning Rails harder. It's a
               | shame. But learning on a non current version is a short
               | term solution. A good tutorial, like Michael Hartl's,
               | will get you over that hump.
        
               | zeku wrote:
               | I'm learning rails 6 right now for a work project, and
               | webpacker is just broken so I can't even get hello world
               | to work.
        
         | pjmlp wrote:
         | I don't know, I just keep using Java and .NET, alongside their
         | top of the line JIT/AOT compilers and GC implementations.
        
         | flippinburgers wrote:
         | The single most important part of rails is the orm. That,
         | honestly, seems to be the "thing" that people are actually
         | getting excited about when they gush over rails. A close second
         | would be rspec. Finally, in production, it can be very helpful
         | to be able to inspect the state of the db using the rails
         | console (as long as the developer is wise enough to sandbox
         | their actions).
         | 
         | I find the tooling (I use vim with solargraph) to be not only
         | underwhelming but frustrating. No I am not willing to use a
         | different editor. Rails code is not at all discoverable because
         | I cannot follow method calls/objects half of the time. In order
         | to alleviate this, I suppose, rails tries very hard to
         | standardize things. Ok. Fine.
         | 
         | The ease of creating model relationships can also produce a
         | nasty set of relationships between tables if you are not
         | careful. One project I worked on used the fast_jsonapi where
         | one table had relationships spanning most of the system.
         | Dealing with that was borderline ridiculous.
        
       | dialamac wrote:
       | > the language designs are so similar that I could just as well
       | imagine a world where Python is the web development lingua
       | franca, and Ruby has all the machine learning libraries.
       | 
       | Well aside from the startling implication that Ruby is a web
       | development "lingua Franca."...the latter statement is
       | reasonable, as it turns out language design isn't actually that
       | important here. But the former is pretty far off the mark. I
       | mean, Ruby doesn't even have first class functions and is very
       | strongly smalltalkish in its OO purism, it has mutable strings
       | a-la Perl. The async story is obviously quite different. Python
       | has a much more complicated interpreter, which has contributed to
       | it being more difficult to get even simple optimizations that are
       | done in Ruby. They're really only similar in the most superficial
       | sense... in the same way that all current dynamic interpreted
       | languages will do certain things similarly.
        
         | viraptor wrote:
         | > it has mutable strings a-la Perl
         | 
         | Yes, but they're not popular these days. Ruby 3 almost
         | defaulted to frozen-strings by default (I wish it had) and a
         | lot of the ecosystem moved to that style resulting in some nice
         | memory saving. Rails enforces it on its code for example:
         | https://github.com/rails/rails/blob/0f09dfca363410f51f6f6078...
        
           | mbell wrote:
           | You are talking about string _literals_, there is no motion
           | nor interest I'm aware of in making all strings immutable in
           | Ruby, thankfully.
        
         | capableweb wrote:
         | While I agree with most of your comment, "Ruby doesn't even
         | have first class functions" sticks out. Not because it's not
         | true, but because it doesn't really matter as Procs are first
         | class in Ruby and can be used in all the same ways, so it
         | doesn't really affect things except introducing something you
         | might need to learn coming from JS or any other language where
         | functions are first class.
        
           | jablan wrote:
           | >it doesn't really matter
           | 
           | This way can be argued for almost every language, as they
           | almost all allow some form of passing a piece of code to a
           | function. The difference is what matters - whether that's
           | elegant enough to be widely used or not. Ruby is, frankly,
           | somewhere in between.
        
         | Toutouxc wrote:
         | > Ruby doesn't even have first class functions
         | 
         | I'm genuinely curious what is it that you miss in Ruby
         | regarding functions (my Ruby is pretty OOPish and I try to
         | avoid functional magic).
         | 
         | AFAIK you can do stuff like currying/partial application with
         | lambdas and you can get a hold of any method by name and use it
         | as a lambda, pass it to other functions, use it as a block etc.
         | Is there something we're missing compared to, say, JavaScript?
        
           | pmontra wrote:
           | You can pass a function as argument using its name in Python
           | and then call it. You must pass it as a symbol in Ruby and
           | then send to it.                 def f(x):         return x
           | def g(fn, x):         return fn(x)            g(f, 1)
        
             | inopinatus wrote:
             | Ruby can definitely pass around several varieties of
             | closure and related constructs, including procs, blocks,
             | lambdas, bindings, continuations, fibers, and both bound
             | and unbound methods.
             | 
             | Whether we should is another matter, and the syntax and
             | idioms certainly lean towards preferring a symbolic late
             | binding, but the language is multi-paradigm, and one may
             | write purely functional Ruby if desired, immutable values
             | and all.
        
             | burlesona wrote:
             | The exact same thing in Ruby:                   f = ->(x) {
             | x }              g = ->(fn, x) { fn[x] }
             | g[f,1]
        
             | jonnytran wrote:
             | > You must pass it as a symbol in Ruby and then send to it.
             | 
             | This is just plain wrong. You can absolutely do this, as
             | other commenters have pointed out.
             | 
             | It is common to see Ruby code passing around a symbol and
             | sending it, but my guess as to why this pattern became
             | common is that it can be serialized into plaintext like
             | YAML, stored in a database, and called later, like for a
             | background job in a web app. But there's no need to do it
             | this way.
             | 
             | Another reason you don't tend to see it this way in Ruby is
             | because Ruby optimizes for the common case: calling methods
             | [1]. Because of this, you can easily create very concise
             | DSLs in Ruby, which would never be quite as clean in
             | Python.
             | 
             | 1: https://yehudakatz.com/2010/02/21/ruby-is-not-a-
             | callable-ori...
        
             | Toutouxc wrote:
             | No, passing a symbol and sending is a different mechanism.
             | 
             | The send method is basically the same thing as calling a
             | method by name, as in "obj.foo" == "obj.send(:foo)". If you
             | only pass a symbol into your "caller" method, the symbol
             | goes through the normal lookup: does the receiving object
             | respond to this? If yes, then that implementation (at that
             | exact moment) is called, if not, you get a method_missing.
             | 
             | You're right that that's not how you do first-class
             | functions in ruby. Your example in ruby would be:
             | def get_and_call(fn, x)         fn.call(x)       end
             | def upcasinator_method(str)         str.upcase       end
             | upcasinator_lambda = lambda {|str| str.upcase}
             | get_and_call(upcasinator_lambda, 'foobar')       =>
             | "FOOBAR"
             | get_and_call(method(:upcasinator_method), 'foobar')
             | => "FOOBAR"
             | 
             | As you can see there are two ways to do that -- either you
             | create a Proc (a lambda if you care about arity), which is
             | the first class function in Ruby, and you call that, or you
             | define a method (but that's a method, an OOP concept, a
             | procedure that implicitly operates on an object), you get a
             | hold of it using the "method" method and then you call it.
        
         | sedeki wrote:
         | Wait, Ruby doesn't have first class functions?
         | 
         | I'm pretty sure it does and that it is full-fledged, compared
         | to Python's intentionally restricted `lambda` syntax.
        
           | capableweb wrote:
           | Nope! You can't pass functions, return them from other
           | methods and so on, so it's not first class.
           | 
           | What you do have in Ruby is Proc, that can be used kind of
           | like first class functions, so you don't really miss out on
           | much.
        
             | burlesona wrote:
             | This is incorrect. If you want an anonymous function (with
             | no object context) you can write a lambda, store it to a
             | value, make other lambdas that call it and return yet
             | another lambda etc.
             | 
             | You can do the exact same thing with object methods if you
             | want, eg. my_puts = Kernel.method(:puts).
             | 
             | You can even do this in one line and stop the auto
             | parenthesis behavior if you want.
             | 
             | foo = method def foo ... end
             | 
             | Now foo will reference the method and you have to use foo()
             | to call it.
             | 
             | The reason that people don't do a bunch of that in Ruby is
             | that the support for anonymous blocks is so convenient That
             | there aren't that many cases where you really need
             | anonymous functions detached from any object.
        
             | syspec wrote:
             | Ah, so it's the same then
        
               | feanaro wrote:
               | Not really the same since lambda in Python is just a
               | syntactic variant that is restricted. A function defined
               | using `def` can be passed around without restrictions,
               | which you simply cannot do in Ruby.
        
               | burlesona wrote:
               | There is no practical difference in Python functions and
               | Ruby lambdas.
        
               | zwp wrote:
               | What are the restrictions on passing around a ruby
               | `Method` object?
               | 
               | (Please see my sibling comment for example).
        
             | zwp wrote:
             | Maybe I misunderstand your use of the word "function" here,
             | but in addition to Proc/lambda you can certainly reference
             | a method, pass that around, return it, call it?
             | 
             | See:                 * https://ruby-
             | doc.org/core-2.7.1/Method.html       * https://ruby-
             | doc.org/core-2.7.1/Object.html#method-i-method
             | $ irb         2.3.3 :001 > p = Kernel.method(:puts)
             | => #<Method: Kernel.puts>          2.3.3 :002 > p.call
             | "Hello"         Hello          => nil          2.3.3 :003 >
             | def run_a_method(meth, *args)         2.3.3 :004?>
             | meth.call(*args)         2.3.3 :005?> end          =>
             | :run_a_method          2.3.3 :006 > run_a_method(p, "Hi!")
             | Hi!          => nil          2.3.3 :007 >
             | 
             | ps. this also works with the "to_proc" operator (&) so you
             | can do this:                   [1, 2,
             | 3].each(&Kernel.method(:puts))
             | 
             | (but I wouldn't necessarily recommend this since it's not
             | very readable, at least to my eyes).
        
               | jablan wrote:
               | Yeah, and that's not what first-class usually means. I
               | simply can't say "my_method = Kernel.puts" the same way I
               | can say "my_class = Kernel". Ruby authors seemed to want
               | to make parentheses optional by any price, and that
               | killed the opportunity to make methods first class.
        
               | zwp wrote:
               | So the functionality is equivalent, but ruby's syntax is
               | slightly longer. Is there something other than ruby's
               | slightly longer syntax that you find lacking?
               | 
               | Ruby's Method class matches the design "everything is an
               | object" (strings, numbers, modules & classes
               | themselves...). Are regular expressions first class in
               | ruby? There is top-level syntactic sugar for REs (/.../)
               | but REs are just instances of `Regexp`. It's just syntax.
               | I don't think you can argue Python REs are first class: I
               | have to drag in `re` and then `re.compile()` a string
               | (and my editor won't know to syntax highlight metachars
               | or interpolation). I use REs more often than method
               | references in either language...
               | 
               | But I'm not trying to "whataboutwhataboutwhatabout" here.
               | I'm trying to say there's a difference of philosophy.
               | Ruby Methods are just more objects (Method<Object). They
               | aren't some sort of blessed type, just like everything
               | else. Everything descends from Object. Kernel is just a
               | module. Method is just a class. And so on. In that
               | context, what does it mean to be "first class"? Objects
               | are first class and methods are objects. QED. No?
               | 
               | (FWIW I agree on ruby's optional parens, too much
               | inconsistency for a few less lit pixels on the screen).
        
               | orf wrote:
               | > and my editor won't know to syntax highlight metachars
               | or interpolation
               | 
               | IntelliJ editors do this and it's insanely useful. Works
               | with SQL, regex and any other language.
        
               | jablan wrote:
               | Sorry, Ruby methods are not objects. They can surely be
               | wrapped in objects of class Method, but this feature is
               | used quite rarely. Whether this is due to the syntax, the
               | confusion regarding the evaluation scope, or something
               | else, I have no idea.
        
               | zwp wrote:
               | Yeah, you got it, see my reply to capableweb. Thanks!
        
               | vidarh wrote:
               | The only way of obtaining a method in Ruby returns it as
               | an object. Whether or not they "are" an object is thus
               | entirely an implementation detail.
               | 
               | I think part of the confuction here is that "ob.puts" for
               | example, does not directly access a method. It denotes
               | passing a message to "ob". That message may or may not be
               | routed to a "puts" method.
               | 
               | But at _no point_ does Ruby allow you to get hold of any
               | kind of reference to a method that is anything but an
               | object.
        
               | capableweb wrote:
               | > In that context, what does it mean to be "first class"
               | 
               | It's easy. First, what does it mean to be "first class"?
               | 
               | > a first-class citizen (also type, object, entity, or
               | value) in a given programming language is an entity which
               | supports all the operations generally available to other
               | entities. These operations typically include being passed
               | as an argument, returned from a function, modified, and
               | assigned to a variable
               | 
               | Secondly, how does that apply to functions?
               | 
               | > [First class functions] means the language supports
               | passing functions as arguments to other functions,
               | returning them as the values from other functions, and
               | assigning them to variables or storing them in data
               | structures
               | 
               | In what way doesn't Ruby have first class functions? This
               | article explains it better:
               | https://codequizzes.wordpress.com/2014/05/19/ruby-
               | methods-ar...
               | 
               | In practice, because of Proc, you can use Proc wherever
               | you'd use functions and get the same effect in practice,
               | so this is not a big issue. But just because Ruby has
               | Procs, doesn't mean it has first class functions, which
               | is a specific concept.
        
               | [deleted]
        
               | zwp wrote:
               | Thanks, I had also looked for a definition. Instances of
               | Method fulfill both of the cited criteria -- example in
               | this thread, above. Also: Method != Proc, but the
               | article's comments help.
               | 
               | I, aha, notice this:                   $ irb
               | 2.3.3 :001 > Kernel.method(:puts).object_id          =>
               | 47165335040720          2.3.3 :002 >
               | Kernel.method(:puts).object_id          => 47165334993360
               | # not the same!         2.3.3 :003 >
               | 
               | That is: #method doesn't return _the_ Kernel.puts method,
               | it returns _a new Kernel.puts Method instance on each
               | invocation_. So the returned object from Kernel#method
               | isn 't the "real" (first-class) method object.
               | 
               | I feel like I'm a lot closer but I'd still like a better
               | definition :)
        
               | Toutouxc wrote:
               | Isn't the main issue that Ruby simply doesn't have
               | functions? Ruby is pure OOP, and there is no way to
               | define a method without the implicit 'self' argument.
               | Maybe the reasoning was that methods themselves aren't
               | really meant to be passed around. Because in today's
               | idiomatic Ruby, they mostly aren't -- the late binding
               | behavior of 'send' feels much more Rubyish.
        
               | indeyets wrote:
               | Exactly. And Ruby methods are not even "functions with
               | self" they are message-handlers. Like in Smalltalk or
               | Objective-C.
               | 
               | Different heritage
        
               | burlesona wrote:
               | You can literally write my_method = Kernel.method(:puts)
               | and it'll give you the method to do what you wish with.
               | 
               | As far as defining anonymous functions, literally all you
               | have to do is:
               | 
               | my_func = -> (input) { output }
               | 
               | That makes a "lambda" which is literally just a function
               | you can pass around anonymously.
               | 
               | You call it via either my_func.call(input) or simply
               | my_func[input].
               | 
               | That capability is used all over the place.
               | 
               | Ruby absolutely has first class functions, the only thing
               | you can say is it the syntax is slightly different if you
               | want to use them anonymously (with no object context).
        
               | jablan wrote:
               | > my_method = Kernel.method(:puts)
               | 
               | That's exactly what being first-class is not. You tell
               | Kernel to wrap you a method called puts into an object of
               | class Method, rather than assigning a method to a
               | variable. You don't have to look further than JS for a
               | counterexample.
        
               | inopinatus wrote:
               | First-class refers to the existence of the language
               | construct, not whether or not you like the syntax.
        
               | jonnytran wrote:
               | This is clearly a misunderstanding of what a first-class
               | function is. If it doesn't meet your criteria in the
               | colloquial sense of "first-class", that's one thing. But
               | in computer science and programming in general, "first-
               | class function" has a very specific meaning [1], and Ruby
               | absolutely meets the criteria.
               | 
               | The other bit of confusion is that the actual syntax in
               | Ruby optimizes for the common case, which is calling
               | methods. Yehuda Katz wrote a good article explaining this
               | [2]. Among other things, it allows you to create very
               | concise DSLs in Ruby that would never be quite as clean
               | in Python.
               | 
               | 1: https://en.wikipedia.org/wiki/First-class_function
               | 
               | 2: https://yehudakatz.com/2010/02/21/ruby-is-not-a-
               | callable-ori...
        
               | burlesona wrote:
               | Ruby has two kinds of functions. Methods are functions
               | attached to objects. Lambdas are functions that are not.
               | Ruby lambdas are exactly like javascript arrow functions,
               | or python functions.
               | 
               | There's a _reason_ for the difference, since it
               | determines the implicit self, which is a meaningful part
               | of how Ruby works. Because that difference is meaningful
               | it is reflected in the syntax. `lambda`, or the shorthand
               | `->` creates a function not attached to an object, and
               | `def` creates a function and attaches it to the calling
               | context (making it a method).
               | 
               | Perhaps you don't like this, that's fine. But to say
               | "nope I want my methods and functions to be
               | interchangeable and not to be called lambdas" is purely a
               | subjective argument.
               | 
               | Objectively, Ruby has first-class functions that can be
               | assigned to variables and passes around and returned from
               | other function calls etc etc. They're just called
               | lambdas.
        
       | jaimex2 wrote:
       | Ruby - a language that is made for developer happiness and
       | productivity.
       | 
       | It had me at happiness.
        
       ___________________________________________________________________
       (page generated 2021-02-16 23:03 UTC)