Open-sourcing the Blue Style Guide for Julia

We just made available the style guide that we use at Invenia.
See the Blue Style Guide at: https://github.com/invenia/BlueStyle

The style guide if of course opinionated, and may become more opinionated (and thus clearer) over time. It aims to be specific and gives many code examples.

The style guide is also tried and tested. It has come from a team of more than two dozen Julia programmers (scientists and software engineers), using Julia both for research and in production over the last three years.

We have followed the Blue style in ~100 packages, both internally and externally in our open-source repositories and in other open-source repositories primarily maintained by Invenia folk, and think this style has proven its worth.

Here’s hoping the Blue style will be useful to the wider Julia community!

27 Likes

Overall, it is best to keep the types general to start with and later optimize the using parametric types. Optimizing too early in the code design process can impact your ability to refactor the code early on.

Adding a type parameter is a (silent) performance breaking change. What used to be a concrete type Foo is no longer one, it is now Foo{T}) so people that store ::Foo in their structs will suddenly have a non-concrete type in there. Also, methods that used to were written as f(::Type{Foo}) will no longer match and have to be written as f(::Type{<:Foo}). My point is that introducing a new type parameter is not that simple of a refactoring.

If you do require the use of import then create separate groupings for import and using statements divided by a blank line:

When is import ever required? Personally, I think the import keyword can be removed completely and we just use using Example: hello everywhere and always specify module when extending.

Functions that are only intended for internal use should be marked with a leading underscore

Is this really enforced? I just looked at some random files:



etc and it doesn’t seem to be used there.

Does it also apply to internal structs?

8 Likes

When is import ever required ? Personally, I think the import keyword can be removed completely and we just use using Example: hello everywhere and always specify module when extending.

I don’t know (I don’t think it is), but we cover that case anyway :laughing: We recommended always using, and personally I agree import could go.

I’d say “inforced” is much too strong a word for most parts of the style guide :slight_smile:

Things have been added over time based on experience. That guidance was only added a few months ago (probably after that code was written)

(Also, Curt is the biggest contributor to both Mocking and the Blue style guide, so they’re only as consistent as Curt is :laughing:)

It is a recent addition to the style-guide, so older code has not been updated to follow that.

I disliked it at first but am coming round to it.

I’m curious: how did you come up with 92 characters per line?

By the way, with automatic wrapping of long lines by editors such as ST3 and Atom I don’t really see a compelling reason to worry about the length of the line of code. But I can see how that making things dependent on the environment in which the code is edited is not ideal.

3 Likes

Me too! But since we scooped it straight from the Julia CONTRIBUTING guidelines I do not really know.

Cool stuff! Any plans on turning this into a tool like https://github.com/psf/black is for python?

3 Likes

Yes! That’s the intention. In practice, I’ve no idea when (sometime in 2020 I would guess).

Pleased to see some good work on tooling for this being done e.g. https://github.com/domluna/JuliaFormatter.jl

(As a purely historical note, Eric at Invenia made a start on this 4 (four!) years ago now https://github.com/invenia/JuliaFormat.jl in Julia 0.4. Which probably predates us writing down a style guide :laughing:)

3 Likes

I wonder if there are some more Julia-specific information available about how does Julia perform in large scale production-level projects, where its easier to use another languages, what platforms and software architectures are better for it, software maintenance and so on?

1 Like

Is there official style guide for Julia? I mean, from Julia Computing

Yes. But it is certainly not all-encompassing.

I am fairly certain not, and there wouldn’t be anything more “official” about such a style guide if it existed.

5 Likes

I was originally taught the 80 rule but I’ve created my own 80-92 rule since using Julia where 80 is a softwrap and 92 is a hard wrap. I’m not sure if this comes of as just inconsistent but it certainly seems to be a more manageable way of doing things.

I guess an off-topic, but breaking or not has to be judged w.r.t exposed API. It is conceivable to expose type API with trailing type parameters being explicitly documented as an implementation detail. This would mean that API-conforming downstream code can only use the types as an upper bound. But I guess it’s not a popular idea.

Thank you for this! I might adopt it in my packages. The style guide is quite extensive and I’ve only skimmed it, but I haven’t found a single major thing I disagree with so far. Everything seems to make sense; it shows that it has been battle-tested. I like that it’s not just blindly based on another language’s / company’s style guide, and I also like that it leaves some wiggle room.

4 Likes

Hurray! I really hope some people will find this useful. It’s at least helped me get used to a consistent way of writing code without having to think about format, and mostly removed style discussions from PRs.

By the way, while I don’t know where the 92 line length originates from, Python’s Black format (which uses 88) points to this video recommending “90 ish”.

(Possibly the precedent for shorter line lengths comes from punch cards.)

1 Like

cf

2 Likes

This is a very detailed style guide. Thanks for making this public.

Most of the recommendations are very sensible, but I see the following done differently in a lot of packages.

  1. long argument lists

    # Blue Style
    function foobar(
        df::DataFrame,
        id::Symbol,
        variable::Symbol,
        value::AbstractString,
        prefix::AbstractString="",
    )
    # break then indent (note matching indentation, break before 92
    function foobar(df::DataFrame, id::Symbol, variable::Symbol, value::AbstractString,
                    prefix::AbstractString = "")
    
  2. making trailing commas mandatory various expansions (after a while, the code settles and then they are not needed)

  3. insisting on no space around = in keyword arglists/named tuples; it provides a nice visual separator

  4. not using DocStringExtensions.jl (this produces a lot of repetition in docstrings that will go out of date)

  5. insisting on return for the last value (tends to be left out in a “lispy” style)

(This is not intended as criticism, just a list of differences.)

1 Like

Actually we do use it in LibPQ.jl, and it is indeed very nice. It’s certainly compatible with the Blue style :slight_smile:

1 Like