MrDoops

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

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:

  1. Breaking down local functions is very cheap. The compiler will freely inline things it feels belong together; it might internally merge get_customer with handle_3rd_party_api_response so it looks like your original function BUT your code is more readable and the more complex internal representation is not your concern.

  2. You can enforce all primitive types in function guards (notice the when is_integer(customer_id) in the function head).

  3. 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

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

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).

Where Next?

Popular in Discussions Top

crispinb
On reading dhh’s latest The One Person Framework it strikes me that Phoenix with LiveView is already pretty much this. However, never hav...
New
mmmrrr
Just saw that dhh announced https://hotwire.dev/ Is it just me or is this essentially live view? :smiley: Although I like the “iFrame-e...
New
praveenperera
How We Replaced React with Phoenix By: Thought Bot
New
ejpcmac
I have discovered Nix last month and I am currently on my way to migrating to it—both on macOS at home and the full NixOS distrubution at...
New
rower687
Hi all, I’ve been reading a lot about the “let it crash” term and how supervising processes and the whole messaging passing make an elixi...
New
Qqwy
I would like to spark a discussion about the static access operator: .. For whom does not know: it is used in Elixir to access fields of...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
ben-pr-p
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
Owens
Hello all, I am developing a new mobile app with Flutter frontend and Phoenix backend. The mobile app has real-time task management and c...
New
griffinbyatt
Sobelow Sobelow is a security-focused static analysis tool for the Phoenix framework. For security researchers, it is a useful tool for g...
New

Other popular topics Top

hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement