How to write Elixir code in a way that makes it easy to refactor, without emulating a type system with unit tests?

Thanks for the insight!

For cases like this, definitely serializing things at the boundaries of your system helps, I just use ecto structs for that (embedded schemas), and try and do the Parse Dont Validate approach.

I agree types could be helpful if you change the return type of a function. But in reality, greping for the function name hasn’t yet been an issue for me, though it is not as slick I agree!

1 Like

I tried a bit of Haskell years ago but got disappointed by conceptual inconsistencies and unreliable tooling. It just didn’t seem to work very well.

In Elixir/Erlang things just work. I’ve had issues with third-party libraries but with the core libraries and tooling all issues I’ve had where from my own ignorance and I could fix them after reasonable investigations.

Like any curious programmer I’ve read stories about how types are great but if you can’t have basic stuff working it seems to lack demonstrable practicality.

Maybe it got better since or with more perseverance I could have worked around these limitations at the time. But all these hours spent learning foreign concepts didn’t seem to pay off.

1 Like

For what it’s worth, I gave up on Haskell after the second weekend. Too much pain in what should be dead-easy tooling (Elixir’s mix is a perfect example for a good and easy tool). Not to mention all the different string types. The whole thing is kind of in your way instead of helping you.

Moved to Rust and haven’t regretted since.

I still like OCaml more but I’ll revisit it after they add the multicore support.

2 Likes

Making this easier is one of the main driving forces behind the TypeCheck library (that I am building; shameless self-promition). The idea is that it creates function contracts (and documentation and property-test generators) from your typespecs, so they are now actually enforced.

4 Likes

When I started working with Norm, TypeCheck hadn’t yet been released (or at least not announced yet on this forum). I’m curious to compare the two when I return to Elixir programming in the coming weeks.

1 Like

I used Norm throughout my codebase and then recently switched all of my specs to TypeCheck. They are both great, but TypeCheck is closer to a drop-in replacement for typespecs. It’s mostly a matter of changing @spec to @spec! (and @type to @type!) to get runtime checks. It optimizes what it can and also generates @spec/@type so Dialyzer can be used with it if desired. It also adds the ability to include your own custom checks with no limits on the Elixir code that can be used (those bits just won’t get optimized). It’s really nice having type documentation that needs to be up to date to run and reads as nicely to me as regular typespecs.

3 Likes

Great question!

I’d say that this is a semi-technical question and almost wholly language agnostic. It’s related to code design as such, which steam from understanding your domain.
There tons of classic books on this topic: Clean Code, Test-Driven Development By Example, and Understanding the Four Rules of Simple Design from what I’ve read.

A good video I can recommend is https://www.youtube.com/watch?v=EZ05e7EMOLM.

Few most straightforward rules that I try to stick at all times are:

  1. Create abstractions around domain concepts rather than implementation details. The heuristic here is to figure out what will change and what’s not going to change. I usually try to figure out that immanent part of the produce (let’s call it “core”) and ensure that as much its implementation as possible adheres to SOLID principles.
  2. Refactor right after implementation. Let’s say do as much TDD as possible.

In my experience, this is a vital basis of a refactorable code, but I never worked with statically typed languages.

1 Like

If you’re interested in the described topics, please check the other thread Pairing on software Katas that I just created.