improving elixir code quality through static analysis and profiling
TRANSCRIPT
Improving Elixir code quality through static analysis
and profiling
A primer to Credo, Dialyzer, and Erlang profiling tools
reject apathetic code using the Elixir library credo
Credo – Elixir code linter
• github.com/rrrene/credo• Focuses on teaching and code consistency • Implements its own style guide
• If you are a Rubyist it is best described as an opinionated mix between Inch and Rubocop.
Credo’s Creed
• Be consistent in your choices – (i.e. apply the same rules everywhere)
• Care about the readability of your code – (e.g. when in doubt, spread text vertically rather
than horizontally)
• And care about easier maintenance – (avoid confusing names, etc.)
This is especially important because we are such a young community.
All the code we put out there is worth its weight in gold if it is easy to comprehend and invites people to learn and contribute.
Credo is your coding buddy
• Shows refactoring opportunities in your code – Reports TODO and FIXME
• Shows complex & duplicated code fragments • Warns you about common mistakes • Shows inconsistencies
in your naming scheme • Helps you enforce a
desired coding style
What do all those arrows (↑ ↗ → ↘ ↓) mean?
• Each issue is assigned a priority based on: – a base priority set by the config – a dynamic component based on:
• violation severity • location in the source code
• Priorities hint at the importance of each issue – ↑ highest and ↓ lowest
• By default, only issues with a positive priority (↑ ↗ →) are part of the report – Show low priority issues with mix credo --strict
Add Credo to your Travis CI build
defp deps do [ {:credo, "~> 0.3", only: [:dev, :test]} ]end
mix.exs
script: - mix test - mix credo --strict
.travis.yml
Dialyzer a static analysis tool
Dialyzer / Dialyxir What’s the difference?
• Dialyzer will analyze Erlang and other languages that compile to BEAM bytecode for the Erlang VM.
• Dialyxir provides Mix tasks to simplify use of Dialyzer in Elixir projects.
Persistent Lookup Table (PLT) is cached output from dialyzer
$ mix dialyzer.pltStarting PLT Core Build ... this will take awhile
[ :erts, :kernel, :stdlib, :crypto, :public_key ]
batteries included
mix.exsdef project do [ app: :my_app, ..., dialyzer: [plt_add_apps: [:mnesia] ] # add your own appsend
What does Dialyzer analyze?
It warns you about type mismatches! For better results, use Elixir typespecs ( @spec )
Checks if a function doesn’t terminate … and other issues that are commonly detected by static language compilers
Type specification ... is a supertype of the success typing
“Practical Type Inference Based on Success Typings” by Tobias Lindahl and Konstantinos Sagonas
TL;DR: it is a soft typing system – Incorporates subtyping – Allows for compositional, bottom-up type inference
binary_tree.ex:179: Type specification 'Elixir.Exads.DataStructures.BinarySearchTree’ :breadth_first_search(#{}) -> [any()] is a supertype of the success typing: 'Elixir.Exads.DataStructures.BinarySearchTree’ :breadth_first_search(#{}) -> [any(),...]
@spec breadth_first_search(%{}) :: list(any)
before
after@spec breadth_first_search(%{}) :: nonempty_list(any)
Add Dialyzer to your Travis CI build
def project do [ app: :my_app, ..., dialyzer: [plt_file: "./plt/.local.plt"] ]enddefp deps do [ {:dialyxir, "~> 0.3.3", only: :dev} ]end
mix.exs
before_script: - mix dialyzer.pltscript: - mix dialyzer --halt-exit-status
.travis.yml
profiling
Where are the bottlenecks in my application?
$ iex –S mixiex> :observer.start
:eflame.apply/5$ stack_to_flame.sh < stacks.out > flame.svg$ open flame.svg
$ mix profile.fprof
iex> :observer.startA GUI tool for observing an Erlang system
– system information – application supervisor trees – process information – ETS tables – Mnesia tables – contains a frontend for Erlang tracing
with module ttb.
:eflame.apply/5github.com/proger/eflame
mix profile.fprof
Profiles the given file or expression using Erlang’s fprof tool.
fprof can be useful when you want to discover the bottlenecks of a sequential code.
$ MIX_ENV=prod mix compile
$ time mix profile.fprof \
--callers=true \
--sort=own \
--details \
-e "Chess.run_test"
mix credo
mix dialyzer
:observer.start
:eflame.apply/5
mix profile.fprof