This is the second part of my blog post series on code style. The first part can be read here.

7. State and lifecycle

We struggled a lot with reconciling state with library code. In our backend, state usually means caches, or in general, side effects like creating an actor in a constructor etc. It always seemed like we had to trade encapsulation for a clear distinction between library and client code, or the other way round. Why should any component be aware of a service using a cache internally, other than the service itself? Why should a library call have side effects? I feel that both questions are valid, and to be honest we are yet to find a reassuring answer to both of these. We decided to keep state in the library, instead of abstracting it away somehow, and implementing it in the application, but it feels wrong. Also some of these stateful components must have been singletons, and there was no programmatic way to enforce this, if dependencies had to be passed to the same component (therefore it couldn't really be defined as object). I guess the answer to that problem is that not all service code (and therefore not all business logic) can be library code, some of it can only be implemented in the application.

8. Type safety

First of all, everything this guy says here.

how can we even write code without statically reasoning about it first? A maintainer must construct a model in their heads [...] Types are a powerful tool for clarifying thoughts, and designing correct software [...] Essentially anything that can be cleanly and obviously known about the code up-front belongs in a type.

Types are an essential part of the mental model of the problem at hand, and Scala has a super-sophisticated type system, which contributes enormously to the expressiveness of the language. (It's actually Turing complete, by accident!, and as Bodil Stokke joked about it, it may become sentient in the next version. Ok, it's not as expressive as Idris for example, where you can define types like "the second element of a five element list", but still, amazing things can be done to make the code clear, self-explanatory and correct.)

Advertisement

Advertisement

Our current project with my colleague is to refactor all the template rendering code in Kinja, and template rendering is the frontier of the properly typed and the "stringly typed" universes. Templates only take strings, and developers with a dynamic typing background tend not to push back strong enough, so that all this mess doesn't leak into Scala code. Some examples of bad practice I've seen include a domain model where models were merely wrappers around Map[String, Any] instances, the overuse of Any etc. These are things that should be avoided at all cost.

9. Naming

Obviously people with Java background use verbose names and CamelCase. I'm pretty sure a Java person would feel an urge to rewrite that typeclass example I used earlier, so that Value[T] becomes ValueTypeClass[T], the constructors are called NumExpr, AddExpr and SubExpr, and the value function is getExprValue() or calculateExprValue(). I think, except maybe for the special case of methods with side effects, there's no need to include such extra verbs in function names. It's so much cleaner not to have the get- and is- prefixes in every function name. A function returns a value, that's what it does, so there's no need to further emphasise it. Also there is no need to use full sentences as class, value or function names, because types add to expressiveness so much, that it makes those long, explanatory names obsolete.

could be written as

Most of the time single words do the job, and short, single words, without the use of camel case make the code more readable.

10. Comments

The other thing I kinda have a strong opinion about are comments in code. It's probably a cliche to say that if you need to comment your code, it most likely sucks, but nonetheless it can be true. Comments are a bunch of characters that don't do anything, don't add to the functionality, and try to explain things that should be explained by the code itself. They turn short, concise source code into something longer, that is harder to read, and is cluttered with noise and garbage. Not to mention the maintenance overhead to keep code and its explanation in sync. And that's the most problematic part.

Advertisement

Advertisement

A couple of weeks ago I added a class to our kinja-common library, that contains base classes of our framework, and utilities etc. I provided scaladoc comments to public methods and the class itself, where I explained what kind of configuration is required by this class. Since then I was only asked to help with problems due to missing configuration. Also, the very first contributor, adding extra functionality to this class, forgot to update the comment that explains what the class does and how. It seems to me that nobody reads these comments and nobody maintains them. And in my opinion an outdated, misleading comment is worse than not having comments at all. Not only because it lies, but also because it takes up space.

So I would say people should only comment their code, if they are disciplined enough and 100% up to maintaining these comments in the future. Scaladoc is a great tool if used properly, but otherwise it can be a waste of time and energy.

11. Implicits

Teams tend to impose constraints on the usage of implicits, especially when it comes to implicit conversions, and it kinda makes sense. Implicit conversions don't make the code more readable, quite the opposite is true actually. But implicits are crucial to the typeclass pattern, they are the means of ad-hoc polymorphism in Scala. Also using implicit parameters is another method of dependency injection, which is a very common pattern in libraries, including the standard library. Just look at all those methods of Future expecting an ExecutionContext to be in implicit scope. So in my opinion these two patterns definitely justify the use of implicits. What I'm trying to get at is that using implicits in a systematic way, as part of recognisable patterns, contributes a lot to writing better Scala code, while arbitrary implicit conversions here and there can be very confusing.

12. Pattern matching

Pattern matching really shines when used with algebraic datatypes, but it can be used very efficiently in a lot of other cases. Actually the question is more like when not to use this feature. One case when I feel it is an abuse is matching on booleans - but it might be just me. To me booleans and if statements just go together, hand in hand, and something like

instead of

feels disturbing (even if the branches are not as simple as a single function call).

13. Default parameter values

Yet another very powerful Scala feature, but when refactoring large codebases, it is always possible to come across nasty use cases of even the most powerful language feature. For example using default parameter values in case class definitions is such a case:

These default values are business logic, and normally do not belong to a model. There is probably a good reason why this particular class is written this way, but in general this should be avoided in my opinion.

14. Anonymous functions

It's just a formatting issue, and I'm not sure if there is a real canonical way, but I think teams need to agree on one way of formatting anonymous functions. Our practice is something like

I've seen other people (or maybe not people but their IDEs) doing things like

and it seemed odd. The reason I think there needs to be a formatting convention is because it takes let's say two seconds to read and comprehend something familiar looking, and 20 seconds to read and comprehend something that is formatted in an unexpected fashion, and these seconds add up. Especially that we usually spend more time reading code than writing, when working in teams.

15. Organizing imports

This is another minor formatting detail, but it can quickly grow very annoying. I guess there's a division on this between people using IDEs and people using text editors. This is what an IDE does with imports:

and this is how it should be pushed to Github in my opinion:

Also using these grouped imports like import com.foo.bar.{ Thing, Stuff } might be tempting, but it makes both version tracking, and moving things between packages harder. When using these, it is almost impossible to grep on a particular import, as it can take endless number of forms, by being grouped with other things in all possible permutations. (Also there's no need to import things like scala.Some, or anything from scala._ - I guess that is being added by some IDE.)

16. Constants

I don't think it makes much sense to highlight constants in Scala, e.g. by using all uppercase names, or any other funny formatting. Everything is a val anyway, so what's the point?

Advertisement

I tried to come up with points that are usually not covered by canonical documents on Scala code style. Because of course it is probably the best to just simply adopt style guides like the Scala Style Guide or Twitter's Effective Scala, and build on top of these.

In my opinion it is crucial to have written conventions, a common ground, as programming became inherently a team sport, played by heterogeneous teams consisting of members with different backgrounds and level of experience, and these people need to be able to efficiently work on each other's code, without spending their days trying to decipher it. Especially that Scala is the kind of language, which is so generic and multi-faceted, and almost anything can be done in so many different ways, that teams can benefit from defining their own subset of language elements, or idioms, so that team members can really read and speak the same tongue. And what these conventions actually are is less important than the fact of having a convention, and being consistent.

Please share your opinion on the above points in comments, or any additional points that I've missed, as I'm pretty sure this list can be continued endlessly.