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.
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.
In the case of Ruby:
* most popular style guide: https://rubystyle.guide/
* automated tool for linting and enforcing code style: https://rubocop.org/
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.
That said, I am a fan of single-use private methods and think they have their place, with or without method-length rules.
If you find the defaults to be not good (which I agree), then simply update the rubocop.yml.
"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")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.
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.
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.
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.)
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)
So divergence does occur, but some languages make it a lot easier than others.
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?