Elixir vs Ruby: Naming conventions
How does naming files, modules, variables and functions in Elixir compare to Ruby and its convention-driven world?
First of all, I highly recommend two resources for basic information about Elixir naming conventions. Number one is a dedicated chapter in the official documentation. Even though I've browsed through Elixir docs so many times, it took a while until I've noticed this excellent, information-rich article. Also, there's an extensive, opinionated community driven style guide.
Files and modules
You can see in official naming guide that files should be named by underscored module name, just like in Ruby. But as opposed to Rails coupled with autoload, it's a convention instead of a hard rule. That's because there may come a time when such a convention must be broken for reasons hard to anticipate up front when creating it. Here's one example why: acronyms.
As Rubyist, you may feel hesitant and insecure about putting acronyms in module names. Well, you really shouldn't anymore. Without inference of module names from file names (or table names in case of Ecto), you can name your modules (and models) like
HTMLParser all day long. Elixir won't make you regret it.
Here's what you may see in some Ruby projects or gems:
lang:ruby module CodeAnalyzer module Engine module Parser module Ruby # code indented way too much end end end end
Here's how some developers used to bypass this issue in the following way:
lang:ruby module CodeAnalyzer module Engine module Parser module Ruby # code indented a bit weird end end end end
In Elixir you won't have to come up with such inventions as here modules can be nested without weird or deep indentation. Here's what will suffice:
lang:elixir defmodule CodeAnalyzer.Engine.Parser.Ruby do # nested module code end
In Ruby something like this will also work, but only if parent module was already defined. But sometimes, there's just no obvious place to define that parent module. You don't have to worry about such things in Elixir as here the whole module name is just an atom.
In Elixir, every single OTP app prefixes all of its modules with its own name. You won't find any top-level
User here. This results in a much more organized code structure throughout all parts of the app. Combine that with explicit module aliasing via the
alias directive and you'll always know what you're referring to in the code, even if only the last part of the module is used.
This convention also makes it so much easier to switch your growing project to multi-app architecture (called umbrella in Elixir/OTP dictionary).
It turns out you can also use it when naming module functions for authoring selective imports. Basically, functions such as
_this_one won't get imported by the
import directive, unless invoked with such intent in the following way:
lang:elixir import MyModule, only: [_this_one: 0]
In Ruby, there are at least two major conventions for naming methods with trailing bangs:
- Standard library uses it to mark methods that change the object in-place instead of returning new one. Someone has really tried to tame the mutability here.
- Ruby on Rails and ActiveRecord (and many others) use it to mark methods that throw instead of returning
falsewhen the operation fails.
It's really unfortunate that such ambiguity was born in Ruby, to say the least. As a result, not only you have the mutability that on its own makes it so hard to reason about anything but the simplest Ruby/Rails codebase, but now you don't even know what really to expect from those damn bang functions. It's such a mess!
It would be a little hard for Elixir to go with the first pattern, so having no other choice it went with the second... But seriously, trailing bang comes with a really clear meaning in Elixir: do you want to handle exception? It fits perfectly into the whole OTP philosophy. Basically, you should just let exceptions be exceptions and throw and let OTP restart things for you. Then, you can remove bangs and pattern match on each spot that is to be a supported negative scenario.
There's just one concern that I have. According to official docs, there's also a second convention for bangs in Elixir: the one for macros. It's basically about declaring scope-unsafe variables in macros. While writing macros is a whole separate domain and on its own this convention seems OK, I hope it won't open a door for more and more conventions that will make Elixir universe as messy as the Ruby one already is. But I guess that's a responsibility for a community to take.
Naming conventions is yet another example of how Elixir inherits (no pun intended) much of what made everyday coding in Ruby nice and pleasant. Of course, it does so while trying to improve on limitations of Ruby, even when it comes to seemingly little things, such as setting proper, fool-proof rules for naming things consistently and beautifully.
That's why I believe that at the end of the day it's those little things that make you smile at the code that you've written. And they make others understand it and so the community grows.