[erlang-questions] Feedback on inaka/erlang_standards
Loïc Hoguin
essen@REDACTED
Mon Sep 8 23:47:20 CEST 2014
Hello,
I want to initiate a larger discussion that Twitter allows for the
inaka/erlang_standards repository. I hope you don't mind that I do it
here, Github issues is quite terrible for discussions and I feel that
talking about these things with a larger number of people can bring
better feedback.
I will take all points listed in the README at
https://github.com/inaka/erlang_standards and comment if I have
something (hopefully) interesting to say about it.
I know they are "Inaka's coding standards" but they are also used for
the Elvis coding standard checker project which would be interesting to
try and integrate as part of more people's workflow (regardless of
projects using the same standards or not, simply checking that
contributions respect it is already quite nice to have).
An example output can be seen there:
https://gist.github.com/elbrujohalcon/332db8bc343f81235462
So let me start now.
> Spaces over tabs, 2 space indentation.
Whatever floats your boat. I just want to point out here that 2-4 space
vs tab vs mixed never lead anywhere. My problem with it though is that
the Elvis tool has an option "no_tabs". That's very opinionated, instead
you should have an option that allows you to define indentation. For
example I want to know if people use spaces instead of tabs in my projects.
> Surround operators and commas with spaces.
As much as I like having these with spaces around, I have an issue with
that. I feel that the default should be "whatever Erlang is doing". For
example maps, it does "#{a => b,c => d}". The default your tool should
do is exactly what Erlang does, because you can then just copy paste
everything and not have to worry. We could extend that logic to doing
what erl_pp does but people really like their tabs/spaces/goats so
that'd probably be taking it a bit too far.
> Try to always separate PRIVATE and PUBLIC functions in groups, with
the public ones first, unless it helps readability and code discovery.
I have to disagree with that. This is what I have done for a *long time*
but I experimented and changed my mind recently.
What I do now is for *each* exported function (nice OOP language btw),
start with the exported function, then the internal functions it calls,
and then the unit tests (and performance tests). Then do the same for
the next exported function and so on. It doesn't fit all kinds of
modules (of course!) but for modules that aren't doing processes stuff
(like gen_servers) it's very nice.
For example:
https://github.com/ninenines/cowlib/blob/master/src/cow_qs.erl - if that
module was doing the old public, private, tests, it would be pretty much
unreadable. Having the unit tests right there with the code is very nice
because you can easily see what the code takes and returns.
> Don't design your system using god modules (modules that have a huge
number of functions and/or deal with very unrelated things)
Well that's fine if your problem domain is simple, but not so much
otherwise. The problem with your approach comes from being able to set a
maximum number of functions project-wide. In Cowboy for example most
modules are small, and cowboy_req is the exception. I know cowboy_req
has a lot of exported functions, I don't want a tool to tell me that.
It's meant to be this way.
> Don't write the same code in many places, use functions and variables
for that
I have for a long time been a proponent of applying DRY only when this
code exists more than 2 times, and the code is significant enough. I
care little about repeated code that's 2 lines long for example. I don't
have hard limits there but it would be nice to analyze a few projects
with different settings and find what's the most useful.
> When having lots of modules, use subdirectories for them, named with
a nice descriptive name for what that "package" does.
Sounds like you need to separate into different applications.
> Don't write spaghetti code (A list comprehension with a case inside,
or blocks with begin/end, and nested stuff)
Not what I call spaghetti code? I'm not even sure what this is about.
List comprehensions with a case or begin/end inside can be immensely
useful. Especially the begin/end ones.
> Don't use if .
I would say the opposite. ROK has already argued on that, no need to
duplicate. :-)
> Macros should be named in ALL_UPPER_CASE:
I would say "Don't use macros" instead. I would love the tool to tell me
when it sees macros that aren't ?MODULE (and a way to whitelist a few
small ones).
> Write the -spec's for your public fun's, and for private fun's when
it adds real value for documentation purposes. Define as many types as
needed.
Honestly I'd remove the comment about internal functions (wow OOP man),
they're generally not very useful, unless Dialyzer complains.
> Module comments go with %%%, function comments with %%, and code
comments with %.
Or you could stop worrying and enjoy life. Seriously that's some
nitpicking you got there. :-)
Instead of saying that you should probably worry that people *actually*
comment their code.
> Try to write functions with a small number of lines. 12 lines per
function except for integration tests is a good measure.
12, seriously? If I'm writing a game the init function or main loop
function will hardly be less than 20 lines.
> Help dialyzer and xref as much as you can, so that they can work
for you
Like with most things in life, there's an optimal middle ground. Don't
go out of your way to make tools happy. Tools are supposed to help you
not hinder you.
> When having many nested "include files", use -ifndef(HEADER_FILE_HRL)
.... -endif so they can be included in any order without conflicts.
I'll need an option that complains when -include is used (again, with
whitelist, probably never warn about file.hrl either). :-)
That's it. Hope to spark some (not too pointless) discussion.
Enjoy!
--
Loïc Hoguin
http://ninenines.eu
More information about the erlang-questions
mailing list