What follows is the first section of five in a list of Scala best practices I compiled for my colleagues and peers. Most of this comes from the great joy (pain) I have experienced from dealing with other people's code. :-)

Defining a list of best practices is always hard. In the end we are defining a protocol of communication. Therefore I chose to use keywords defined in RFC 2119, to differentiate between rules that should never be broken and those that can be broken if you know what you're doing.

This list is also far from complete. But it should be a great place to start.

My final and perhaps most important disclaimer. This should be the first rule of any document that describes best practices.

0.1. THOU MUST NOT follow advice blindly

Always try to understand the reasons behind a rule, don't cargo-cult. Following advice blindly leads to the worst hairballs imaginable.

1. Hygienic Rules

These are general purpose hygienic rules that transcend the language or platform rules. Programming language is a form of communication, targeting computer systems, but also your colleagues and your future self. Respecting these rules is just like washing your hands after going to the bathroom.

1.1. Thou should enforce a reasonable line length

There's a whole science on typography which says that people lose their focus when a line of text is too wide. A long line makes it difficult to gauge where the line starts or ends. It makes it difficult to scan for important details. It also creates unrequired eye strain as one’s eyes have to move too far from the right to left when moving to a new line.

In typography, the optimal line length is considered to be somewhere between 50 and 70 chars. In programming, we've got indentation, so it's not feasible to impose a 60 chars length for lines. 80 chars is usually acceptable, but this is a Scala best practices article and in Scala, because we use a lot of closures, and we may use long and descriptive names for functions and classes 80 chars is not going to do the trick. We need more.

On the other hand, 120 chars, as IntelliJ IDEA is configured by default may be too wide. I know that we've got 16:9 wide monitors, but this doesn't fix the readability/eye strain issue, and with shorter lines we can put these wide monitors to good use when doing side-by-side diffs. Additionally, with longer lines it takes more effort to notice important details that happen at the end of those lines.

So as a balance for Scala strive for 80 chars as the soft limit and if it gets ugly, then 100 chars is enough, except for... function signatures, which can get really ugly if limited.

On the other hand, anything that goes beyond 120 chars is an abomination.

1.2. Thou must not rely on a SBT or IDE plugin to do the formatting for you

IDEs and SBT plugins can be of great help, however if you're thinking about using one to automatically format your code, beware.

Because it is an NP-Complete problem (i.e. it requires human-like intelligence), you won’t find a plugin that is able to infer the developer’s intent. The purpose of proper indentation and formatting isn't too dogmatically follow some rigid rules of formatting. It is to make code more logical, more readable, and more approachable. Indentation is an art form, and it is in the developer's job description to make sure that his art/code doesn’t suck.

So automated means are fine, but be careful to not ruin other people's carefully formatted code, otherwise I'll slap you in prose.

Lets think about what I said —if the line is too long, how is a plugin supposed to break it? Lets talk about this line (real code):

val dp = new DispatchPlan(new Set(filteredAssets), start = startDate, end = endDate, product, scheduleMap, availabilityMap, Set(activationIntervals.get), contractRepository, priceRepository)

In most cases, a plugin will just do truncation and I've seen a lot of these in practice:

val dp = new DispatchPlan(Set(filteredAssets), start =
	  startDate, end = endDate, product, scheduleMap, availabilityMap,
	  Set(activationIntervals), contractRepository, priceRepository)

That's not readable, is it? Seriously, it looks like barf. And that's exactly the kind of output I see coming from people relying on plugins to work. We could also have this version:

val dp = new DispatchPlan(
	Set(filteredAssets),
	startDate,
	endDate,
	product,
	scheduleMap,
	availabilityMap,
	Set(activationIntervals),
	contractRepository,
	priceRepository
)

It looks much better. But the truth is, this isn't so good in other instances. Like say we've got a line that we want to break:

val result = service.something(param1, param2, param3, param4).map(transform)

No matter how you deal with it placing those parameters on their own line is awful:

// awful because that transform call is not visible
val result = service.something(
	param1,
	param2,
	param3,
	param4).map(transform)
// awful because it breaks the logical flow
val result = service.something(
	param1,
	param2,
	param3,
	param4
	).map(transform)

This would be much better:

val result = service
	.something(param1, param2, param3, param4)
	.map(transform)

Now that's better, isn't it? Of course, sometimes that call is so long that this doesn't cut it. So you need to resort to a temporary value of some sort, e.g.:

val result = {
	val instance =
		object.something(
			myAwesomeParam1,
			otherParam2,
			someSeriousParam3,
			anEvenMoreSoParam4,
			lonelyParam5,
			catchSomeFn6,
			startDate7
		)
	for (x <- instance) yield
		transform(x)
}

Of course, sometimes the code is so disgusting that you need to get into refactoring - as in, maybe too many parameters are too much for a function ;-)

And as of now we are talking strictly about line lengths - once we get into other issues, things get even more complicated. You won't find a plugin that can do this analysis and that can make the right decision for you. All the more reason you can’t let a SBT or IDE plugin do all of the formatting for you.

1.3. Thou should break long functions

Ideally functions should only be a couple of lines long. If the lines get too big, then we need to break them into smaller functions and give them a name.

Note that in Scala we don't necessarily have to make such intermediate functions available in other scopes, the purpose here is to primarily aid readability, so in Scala we can do inner-functions to break logic into pieces.

1.4. Thou MUST NOT introduce spelling errors in names and comments

Spelling errors are freakishly annoying, interrupting a reader's flow. Use a spell-checker. Intelligent IDEs have built-in spell-checkers. Note the underlined spelling warnings and fix them.

1.5. Names MUST be meaningful

"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton

We've got three guidelines here:

give descriptive names, but don't go overboard, four words is a little too much already you can be terse in naming if the type / purpose can be easily inferred from the immediate context, or if there's already an established convention if going the descriptive route, don't use bullshit words that are meaningless

For example this is acceptable:

for (p <- people) yield
  transformed(p)

We can see that p is a person from the immediate context, so a short one letter name is OK. This is also acceptable because i is an established convention to use as an index:

for (i <- 0 until limit) yield ???

This is in general not acceptable, because usually with tuples the naming of the collection doesn't reflect well what's contained (if you haven't given those elements a name, then as a consequence the collection itself is going to have a bad name):

someCollection.map(_._2)

Implicit parameters on the other hand are OK with short names, because being passed implicitly, we don't care about them unless they are missing:

def query(id: Long)(implicit ec: ExecutionContext, c: WSClient): Future[Response]

This is not acceptable because the name is utterly meaningless, even if there's a clear attempt at being descriptive:

def processItems(people: Seq[Person]) = ???

It's not acceptable because the naming of this function indicates a side-effect (process is a verb indicating a command), yet it doesn't describe what we are doing with those people. The Items suffix is meaningless, because we might have said processThingy, processRows, processStuff and it would still say exactly the same thing - nothing. It also increases visual clutter, more words equal more text equal more noise equal harder to read.

Properly chosen descriptive names - good. Bullshit names - bad.

Closing

Hygenic rules might seem elementary but at the end of the day they are one of the most vital and overlooked pieces of becoming a great programmer. In the next article we will get into specific Scala language rules.

Stay tuned for more