Scala code that will stand the test of time

We use Scala quite a lot these days, and it's probably our language of choice for most backend projects. But when I look back at some of our early Scala code, from about 2 years ago, some of it is so incomprensible that it's pretty much a write off. You can probably write unmaintainable code in any language, of course, but Scala has a reputation for giving you plenty of rope to hang yourself with.

This answer on a Stack Exchange question about what Java developers think of Scala sums it up pretty well:

Well, I think Scala is too complex. It feels like C++ in that it has a myriad of different ways of doing things. Some would call this "richness", "expressiveness" or "power", but your mileage may vary. In many real world projects you would have to artificially limit what language features you're going to use and what not, so that all developers involved could speak the same subset of the language.

Those of you who have been at this a bit longer than me might remember also David Pollak’s post “Yes, Virginia, Scala is hard”:

Scala tries to be too many things. This means that you can code it like Java which is a curse and a blessing, but I think over the long term, it's a curse. It means there are too many debates about OO vs. FP. These kind of decisions/debates are okay if you're a small team, but they really suck when you're trying to teach Java developers to write Scala when they don't really want to learn.

Scala is complicated, but it's also powerful. I think we've got a pretty good process around our Scala code now. We're moving towards a more idiomatic way of writing Scala and we can onboard new Scala developers fairly quickly (most of our developers have no Scala experience before they start here).

Common sense

If you're not sure if what you're doing is the correct way, ask yourself – is your code readable, testable, and maintainable? Believe it or not, this simple test will get you 90% of the way from writing Scala that no one understands to writing good code that should stand the test of time.

You can write something like this if you want, and it will probably compile:

thing.map(kv => (kv._1, kv._2 * 2)).filter(kv => kv._1 > 2).map(kv => kv._1 + kv._2).toSeq.map(_._2)  

However, as Ian Malcolm would say:

They were preoccupied with whether or not they could, they didn't stop to think if they should.

Scala can be so effortlessly powerful – and I'm thinking of the collections API in particular – that you can cram an incredible amount of logic into very few characters. That's kind of cool, and probably even feels a bit liberating at first. But then you go back and look at some code you wrote two months ago and you have literally no idea what it does.

Dependency injection

One problem you might find when learning Scala – or really, functional programming in general – is a paucity of information on how to construct large functional programs. They always seem to say "you split everything up into small functions". OK, great – now I've got 1,000 functions, where do I put them?

I'm giving a lot of the credit for how our code is organised now to depdendency injection. Our code quality improved rapidly after we introduced Google Guice.

It might seem slightly odd to praise a design pattern that's often associated with object orientation when you're talking about learning functional programming. But dependency injection in Scala can actually help you write code in a far more modular and functional style. Roughly speaking, it goes a bit like this:

  • You write a bit of code than you think could be extracted to a (pure) function
  • Stick it as a function in a new class, inject that where you are
  • Call the function

It requires a very subtle shift in thinking, away from the noun-as-object style towards a more modular style: classes become a way of organising and testing related groups of functions.

You can even argue dependency injection is a actually pretty similar to partially applying a function anyway.

Code style

Code style is fairly important when working in teams in just about any language. It helps to have an agreed set of rules you can refer to when you're not sure what to do, and probably helps to avoid arguments. For Scala, when in doubt, ask: What Would Twitter Do?

We lean on Twitter's Effective Scala. It's a very well written guide, which despite its comparative brevity, covers pretty much all the interesting stuff you need. We also consult the scala lang style guide, which Twitter's guide builds on top of.

It you can set up automated linters to enforce your rules, great! We have Scalastyle integreated with our code review process. In practice, it's better at stuff like whitespace errors that it is about helping you write Scala in a more idiomatic way. But it's better than nothing.

Code review

Code reviews are good for lots of reasons, but one place they really shine is when you're introducing people to a new language. Pretty much every single code change we make is reviewed by at least one other person. We started out using pull requests on Github, and these days we use the Differential in Phabricator.

Phabricator is a bit underdocumented, and some bits of it defintely have a work-in-progress feel to them. However its code review tools are very powerful, and if they fit your workflow, may well feel like a sigificant step forward from standard pull requests.

We can see from code review that it doesn't take our engineers that long these days for developers to get up to speed on Scala. For the first couple of weeks they may need a bit of help, and may find links to Effective Scala dotted around their diffs. But after a few weeks pretty much everyone has got the basics down.