MrDoops
What are sort of smells do you tend to find in Elixir code?
I’ll be deploying my first non-hobby Elixir application in the next few weeks, and I’ll be honest - some of it smells.
For those of you who’ve spent more time with the language, what are some code smells you’ve picked up over the years? What sort of techniques or approaches have you used to refactor those smelly concoctions?
Make it work, make it right, make it fast.
– Kent Beck
I feel like I can make it “work”, but in your opinion what does that workflow of making it “right” look like?
Most Liked
dimitarvp
That is entirely up to your business requirements and what makes sense there. Let me rewrite your function:
def get_customer(customer_id) when is_integer(customer_id) do
"/customers/" <> customer_id
|> get()
|> handle_3rd_party_api_response()
end
defp handle_3rd_party_api_response({:ok, %Tesla.Env{status: 200, body: body}}), do: {:ok, body}
defp handle_3rd_party_api_response({:ok, %Tesla.Env{body: body}}), do: {:error, body}
defp handle_3rd_party_api_response({:error, _} = other), do: other
Couple of points here:
-
Breaking down local functions is very cheap. The compiler will freely inline things it feels belong together; it might internally merge
get_customerwithhandle_3rd_party_api_responseso it looks like your original function BUT your code is more readable and the more complex internal representation is not your concern. -
You can enforce all primitive types in function guards (notice the
when is_integer(customer_id)in the function head). -
Please note that passing around ok / error tuples should not last forever, obviously. However they help immensely in managing your internal flow of logic and actions. In my apps these tuples eventually get fed to the serializers that are supposed to send responses back to the client caller (API consumer or a browser) and from then on the responsibility on how to process a success or an error lies with them.
You say:
Do not do that. Every function that can return an error tuple, you handle that somewhere. No exceptions. We all know how prototypes end up as being 10-year old mastodons full of buggy legacy code, right?
There is an Elixir library for JSON schema validation and I can link it if you like, however it’s my observation that most people only need mandatory keys enforcement and type checking of a few values. Again, you can use pattern matching in functions heads:
defp validate_3rd_party_api_response({:ok, %{data: %{records: records, endpoint: url}, page: page, count: count}} = response) when is_list(records), do: response
…etc. You can deconstruct to infinity and enforce a contract right when calling your function that is supposed to consume externally provided data. If any part of the structure is not okay, this should [again] be caught by your integration tests pretty early.
Does that help? I’ll be happy to address additional concerns. Just say what you are uncomfortable with.
fmcgeough
I’d recommend using credo as well (if you aren’t already using it). It may point out some things that you hadn’t thought of. It was helpful for me when I started and its been useful in bringing in new developers to make them more self-aware of the type of code they are writing. GitHub - rrrene/credo: A static code analysis tool for the Elixir language with a focus on code consistency and teaching. · GitHub
OvermindDL1
For note, the whole {:ok, value}/{:error, reason} tuples are just monads, a good monad pipeline (like |>, but like the exceptional library uses of ~>) makes it significantly easier to read and use (and if elixir had it built in then such success tuples would no doubt be a lot more used in the ecosystem since they’d be so simple).
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









