Pro Yearly is on sale from $80 to $50! »

ElixirConf.EU 2016: Credo - Analysing ASTs for Fun and Profit

ElixirConf.EU 2016: Credo - Analysing ASTs for Fun and Profit

Today's developer tools are mostly used in a very technocratic fashion where there's success and failure, 1 and 0. Either a tool thinks you're totally wrong or you're 100% right. You get shouted at or praised. There is no middle ground. Except there is. Credo, the first teaching code linter for Elixir, puts its focus on the in-between: How errors can be opportunities for learning. How AST analysis can be fun and educational. And how one can combine these features into a professional tool that gives straight results while still treating its users as human beings.

Faf649a95c7b996e7b7071f85277b3e2?s=128

René Föhring

May 11, 2016
Tweet

Transcript

  1. CREDO Analysing ASTs for Fun and Profit René Föhring, @rrrene

  2. @rrrene

  3. @rrrene read in pirate voice

  4. @rrrene read in pirate voice |> java |> ruby |>

    elixir
  5. @rrrene read in pirate voice |> java |> ruby |>

    elixir www.neopoly.com/jobs
  6. 2014 inch-ci.org Tool for evaluating inline code documentation

  7. 2015 elixirstatus.com Tool for community announcements

  8. Code 2016 Docs 2014 inch-ci.org Community 2015 elixirstatus.com Credo Today‘s

    topic!
  9. Excursion: What is static analysis?

  10. 11 def runApiCall(list, mode \\ nil) do 12 options =

    %{} 13 list |> IO.inspect 14 API.call(list, mode) 15 end $ mix compile example.ex:11: warning: default arguments in run/1 are never used example.ex:12: warning: variable options is unused
  11. 11 def runApiCall(list, mode \\ nil) do 12 options =

    %{} 13 list |> IO.inspect 14 API.call(list, mode) 15 end $ mix compile example.ex:11: warning: default arguments in run/2 are never used example.ex:12: warning: variable options is unused
  12. abstract syntax tree (AST) {<operation>, <meta>, <arguments>}

  13. abstract syntax tree (AST) {<operation>, <meta>, <arguments>} # 1 +

    2 {:+, [line: 1], [1, 2]}
  14. abstract syntax tree (AST) {<operation>, <meta>, <arguments>} # 1 +

    2 {:+, [line: 1], [1, 2]} # 42 |> inspect() {:|>, [line: 1], [42, {:inspect, [line: 1], []}]}
  15. abstract syntax tree (AST) {<operation>, <meta>, <arguments>} # 1 +

    2 {:+, [line: 1], [1, 2]} # 42 |> inspect() {:|>, [line: 1], [42, {:inspect, [line: 1], []}]} # try it yourself Code.string_to_quoted!
  16. Ruby - RuboCop Python - pylint JavaScript - ESLint Elixir

    - ?
  17. Ruby - RuboCop Python - pylint JavaScript - ESLint Elixir

    - Dogma
  18. $ mix dogma Inspecting 78 files. .X....XXX.XX.XX.XXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXX 78 files, 624

    errors! == lib/plug/adapters/cowboy/conn.ex == 92: CommentFormat: Comments should start with a single space 82: CommentFormat: Comments should start with a single space 121: FunctionArity: Arity of `parse_multipart` should be less than 4 (was 5). 117: FunctionArity: Arity of `parse_multipart` should be less than 4 (was 5). 38: FunctionArity: Arity of `send_file` should be less than 4 (was 6). 47: LineLength: Line length should not exceed 80 chars (was 92). 49: LineLength: Line length should not exceed 80 chars (was 97). Dogma v0.1.5 analysing Plug v1.2.0-dev (output abbreviated)
  19. (╯°□°)╯︵ ┻━┻ But what is the problem?

  20. Elixir’s recent rise from totally unknown to still definitely unknown

    but mentioned in hushed tones Clark Kampfe in “Elixir is not Ruby” https://zeroclarkthirty.com/2015-11-01-elixir-is-not-ruby.html
  21. What‘s the newcomer experience?

  22. None
  23. Let‘s create a mentoring tool.

  24. 11 def runApiCall(list, mode \\ nil) do 12 options =

    %{} 13 list |> IO.inspect 14 API.call(list, mode) 15 end
  25. 11 def runApiCall(list, mode \\ nil) do 12 options =

    %{} 13 list |> IO.inspect 14 API.call(list, mode) 15 end breaks convention of underscored names outputs to STDOUT, is slow
  26. 11 def runApiCall(list, mode \\ nil) do 12 options =

    %{} 13 list |> IO.inspect 14 API.call(list, mode) 15 end breaks convention of underscored names outputs to STDOUT, is slow
  27. First, we need to categorize issues.

  28. readability

  29. readability software design

  30. readability software design refactoring opportunities

  31. readability software design refactoring opportunities consistency checks

  32. readability software design refactoring opportunities consistency checks warnings

  33. 1 defmodule ReadabilityExample do 2 @githubBaseURL "https://github.com/" 3 @github_sample_url "https://github.com/rrrene/

    credo/blob/master/lib/credo/check/consistency/ line_endings/unix.ex" 4 end
  34. 1 defmodule ReadabilityExample do 2 @githubBaseURL "https://github.com/" 3 @github_sample_url "https://github.com/rrrene/

    credo/blob/master/lib/credo/check/consistency/ line_endings/unix.ex" 4 end breaks convention of underscored names exceeds maximum characters per line
  35. Next, issues need to be prioritized.

  36. issues ordered by priority 20 12 11 6 5 1

    -1 -2 -5 -10 -11 -100
  37. issues ordered by priority 20 12 11 6 5 1

    -1 -2 -5 -10 -11 -100 Readability: max line length Warning: IO.inspect Readability: camelCase function name
  38. issues ordered by priority 20 12 11 6 5 1

    -1 -2 -5 -10 -11 -100 (regular) Readability: max line length Readability: camelCase function name Warning: IO.inspect
  39. issues ordered by priority 20 12 11 6 5 1

    -1 -2 -5 -10 -11 -100 Readability: max line length Readability: camelCase function name Warning: IO.inspect (strict)
  40. readability software design refactoring opportunities consistency checks warnings

  41. {1, "string", true} # two ways how to write tuples

    # both great, if applied consistently { 1, "string", true }
  42. {1, "string", true} # two ways how to write tuples

    # both great, if applied consistently { 1, "string", true } without spaces inside the tuple
  43. {1, "string", true} # two ways how to write tuples

    # both great, if applied consistently { 1, "string", true } with spaces inside the tuple without spaces inside the tuple
  44. defmodule BadHTTPHeaderError do defexception [:message] end defmodule UserRequestError do defexception

    [:message] end
  45. defmodule BadHTTPHeaderError do defexception [:message] end defmodule UserRequestError do defexception

    [:message] end consistent suffix "Error"
  46. defmodule BadHTTPHeaderException do defexception [:message] end defmodule UserRequestException do defexception

    [:message] end consistent suffix „Exception"
  47. defmodule InvalidHTTPHeader do defexception [:message] end defmodule InvalidUserRequest do defexception

    [:message] end consistent prefix „Invalid"
  48. fails consistency check defmodule InvalidHeader do defexception [:message] end defmodule

    UserRequestFailed do defexception [:message] end no consistent prefix or suffix
  49. $ # add credo to mix.exs $ mix deps.get $

    mix credo
  50. None
  51. None
  52. $ mix credo lib/plug/static.ex:90:13

  53. None
  54. Where do we go from here?

  55. Explanations

  56. Explanations Style Guide

  57. Explanations Style Guide Editor Support

  58. Explanations Style Guide Editor Support Custom Configuration

  59. Explanations Style Guide Editor Support Custom Configuration @lint attributes

  60. Explanations Style Guide Editor Support Custom Configuration @lint attributes Custom

    Checks are coming!
  61. Explanations Style Guide Editor Support Custom Configuration @lint attributes Custom

    Checks are coming! Creating a Toolbox, not a Linter!
  62. final thoughts

  63. Questions & Alchemists

  64. # APPENDIX Credo – A static code analysis tool with

    a focus on code consistency and teaching. https://hex.pm/packages/credo Dogma – A code style linter for Elixir, powered by shame. https://hex.pm/packages/dogma Clark Kampfe in “Elixir is not Ruby” https://zeroclarkthirty.com/2015-11-01-elixir-is-not-ruby.html Inch CI – Lint your docs http://inch-ci.org ElixirStatus – community announcements http://elixirstatus.com HexFaktor – monitor your deps http://beta.hexfaktor.org