10 Elixir gotchas

No, I’ve not gone to the click-baiters (“10 tips that will change your life today!!!”), but I chose to limit myself to just 10 so that I don’t pull what is considered a “Tobi” and spend days writing a blog post so huge no one wants to read it anyhow. I’ll write a follow-up post with more ๐Ÿ™‚

Anyhow, what are “gotchas”? For this purpose I’d define them as “slightly confusing or irritating behavior, prone to lead to errors especially when you’re new to Elixir“. There are good reasons for many of these, however some of them are also more arcane. Running on the great basis that Erlang built is probably Elixir’s biggest asset – it gives us a lot of functionality and properties that helps Elixir thrive especially in the modern multi-core and multi-node environment. However, Erlang also comes with its fair share of baggage as a programming language conceived almost 40 years ago. Erlang’s focus on backwards compatibility also means many of these decisions still live on today.

The list is brought to you by:

  1. My own experience learning Elixir
  2. Me teaching Elixir to folks both at Liefery and at Remote over the years
  3. Horrifying discoveries in production code

Apologies for the lack of syntax highlighting, but wordpress borked the last way that was working for elixir and I didn’t want to yak shave this too far. I hope that you can sill enjoy them and may learn from them!

1. A list of numbers becomes text in iex

Let’s start with an oldie but goldie that pretty much every beginner book tells you about: Why does this random list of integers print out as text?

iex> [69, 108, 105, 120, 105, 114]
~c"Elixir"

This is because charlists, denoted by ~c"text" or 'text', are actually just that – a list of integers. So iex literally can’t tell the difference and does its own best guess work: it checks if the integers are in a range between 0 and 127 and will then print it as text.

You can also show that it literally is a list by using Enum functions on it:

iex> Enum.map(~c"elixir", fn integer -> integer + 100 end)
[201, 208, 205, 220, 205, 214]
iex> Enum.map(~c"elixir", fn integer -> integer - 32 end)
~c"ELIXIR"

The first line changes the integers to be outside of the printable range with +100 so iex prints it as a list of integers again. The second one just uses the knowledge that the difference between lower case and upper case letters in ASCII is 32 to transform it.

2. Charlists vs. Strings

All this brings us to the following questions: Why do we even have charlists and strings in elixir? What’s the difference between them? When do I use which one? Great question! It’s a source of a lot of confusion, esp. since in most languages single and double-quoted strings only sport minor differences – in Elixir they are backed by entirely different data structures. While single-quoted strings are just a list of integers, double-quoted strings are UTF-8 encoded binaries and so resemble strings you are used to in most modern programming languages.

As a rule of thumb, use strings aka double-quotes ("string"). The major use case for charlists / 'charlists'/~c"charlists" is interfacing with erlang or erlang libraries. Or in the words of the documentation:

In practice, you will not come across them often, only in specific scenarios such as interfacing with older Erlang libraries that do not accept binaries as arguments.

The other mystery here are the 2 different syntaxes in use for charlists – single quotes ('charlist') vs. the ~c"charlist" sigil. That’s a rather recent development, it was changed in Elixir 1.15, after some discussion. The reason for this is what I mentioned initially – it caused a lot of confusion:

In many languages, 'foobar' is equivalent to "foobar", thatโ€™s not the case in Elixir and we believe it leads to confusion.

So, it’s now less confusing but still confusing – which is why it made this list.

3. %{} matches any Map

Pattern matching is one of Elixir’s chief features! You can see it utilized frequently, for instance in a recursive function where we want to end recursion on empty list:

def list([]) do
  IO.puts("list is empty")
end

That works flawlessly, however if you try the same with a map it’ll always match – no matter the map:

def map(%{}) do
  IO.puts("Empty map or is it?")
end
iex> map(%{not: "empty"})
Empty map or is it?

The reason is simple – pattern matches on lists and maps just work different. In a list we’re looking for an exact match of the elements, whereas for maps it is basically checked if the structure is included:

iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex> [a, b, c] = [1, 2, 3, 4]
** (MatchError) no match of right hand side value: [1, 2, 3, 4]
iex> [] = [1, 2, 3]
** (MatchError) no match of right hand side value: [1, 2, 3]
iex> %{action: action} = %{action: "learn"}
%{action: "learn"}
iex> %{action: action} = %{action: "learn", more: "can be", provided: true}
%{
  more: "can be",
  action: "learn",
  provided: true
}
iex> %{} = %{action: "learn", more: "can be", provided: true}
%{
  more: "can be",
  action: "learn",
  provided: true
}
iex> %{action: action} = %{no_action: "sad"}
** (MatchError) no match of right hand side value: %{no_action: "sad"}

If you do want to execute a function only when given an empty map you can use either of the following guards: map_size(map) == 0 or map == %{} – which also showcases the difference between the match (=) and equality (==) operators. One full example from the docs:

def empty_map?(map) when map_size(map) == 0, do: true
def empty_map?(map) when is_map(map), do: false

4. Structs are Maps

While we’re in the topic of maps let’s talk about structs! We can easily create and use a struct:

iex> defmodule Human do
...> defstruct [:name, :age]
...> end
iex> tobi = %Human{name: "Tobi", age: 34}
%Human{name: "Tobi", age: 34}

It gets more interesting around pattern matching again, let’s try %{} from the previous section:

iex> %{} = %Human{name: "Tobi", age: 34}
%Human{name: "Tobi", age: 34}
iex> is_map(%Human{name: "Tobi", age: 34})
true

It matches and it is a map! Structs are nothing more than special maps with a __struct__ key that tells it which struct it is. It gets even weirder when you know that some of the built-in data types are structs and hence maps:

iex> is_map(1..10)
true
iex> is_map(Date.utc_today())
true
iex> is_map(~r/elixir/)
true

We can see their map nature more easily in an example! With a bit of meddling we can also tell IO.inspect to not print a prettified version showing us the real map underneath:

iex> tobi = %Human{name: "Tobi", age: 34}
%Human{name: "Tobi", age: 34}
iex> tobi.__struct__
Human
iex> Map.keys(tobi)
[:name, :__struct__, :age]
iex> Map.values(tobi)
["Tobi", Human, 34]
iex> IO.inspect(tobi, structs: false)
%{name: "Tobi", __struct__: Human, age: 34}
%Human{name: "Tobi", age: 34}
iex> IO.inspect(1..10, structs: false)
%{first: 1, last: 10, step: 1, __struct__: Range}
1..10

Now, you might think this all doesn’t matter too much. But it does! Be aware that every pattern match on a map might also match on a struct with the same keys, so will every is_map check. And yes, dates and ranges may match as well as shown above:

iex> %{first: number} = 1..10
1..10
iex> number
1

I have seen bugs in code that first matched on something being a map and only later matched on specific structs. So, instead of the struct specific code the more general map code was run – and hence another hard to track down bug was born.

In order to combat this, the Elixir team introduced a new is_non_struct_map/1 guard.

5. Structs don’t implement Access

So, I just told you that structs are just maps. But then you try to use random key access via [] on them and you are confused again:

iex(18)> tobi[:age]
** (UndefinedFunctionError) function Human.fetch/2 is undefined (Human does not implement the Access behaviour

You can use the "struct.field" syntax to access struct fields. You can also use Access.key!/1 to access struct fields dynamically inside get_in/put_in/update_in)
    Human.fetch(%Human{name: "Tobi", age: 34}, :age)
    (elixir 1.16.0-rc.1) lib/access.ex:309: Access.get/3
    iex:18: (file)
iex(18)> tobi.age
34
iex(19)> map_tobi = %{name: "Tobi", age: 34}
%{name: "Tobi", age: 34}
iex(20)> map_tobi[:age]
34
iex(21)> map_tobi.age
34

As usual, elixir is amazing and already tells us that the problem is that the struct doesn’t implement the Access behaviour. As structs have predefined keys, you should use the dot-syntax of struct.key to access them. However, since sometimes you do still want to randomly access struct keys you can use the fact that structs are still just maps to your advantage using functions like Map.get/3:

iex(22)> attribute = :age
:age
iex(23)> Map.get(tobi, attribute)
34

You can also take it further than that and use get_in/2. It doesn’t work in a plain attempt, but can work thanks to Access.key/2:

iex(25)> get_in(tobi, [attribute])
** (UndefinedFunctionError) function Human.fetch/2 is undefined (Human does not implement the Access behaviour
# etc....
iex(25)> get_in(tobi, [Access.key(attribute)])
34

Be mindful to only use these if you really do need random key access on structs. Otherwise there are many other ways, such as good old plain dot-based access or pattern matching even.

6. Keyword lists are a bit awkward as options and in pattern matches

Another somewhat special data structure in elixir are keyword lists. Again, these are backed by “syntactic sugar” on top of lists. A keyword list is a list of 2 element tuples, where the first element is an atom.

iex> [{:option, true}] == [option: true]
true

When you call functions with keywordlists as the last argument you can even omit the brackets as seen before when we wrote IO.inspect(tobi, structs: false)structs: false is a keyword list here. These properties make it the default data structure for passing along options to functions in Elixir.

However, since it’s a list the order matters here (and keys can be duplicated!) which often isn’t what you want for options: order usually doesn’t matter and duplicated options should not be a thing. It’s great for DSLs such as ecto, but when used as options it means it’s hard to pattern match on them. Let’s check out the following function:

def option(warning: true) do
  IO.puts "warning!"
end

def option(_anything) do
  IO.puts "No warning!"
end

It only matches when our options are exactly warning: true – any additional data makes it a different list and hence fails the pattern match:

iex> option warning: true
warning!
iex> option warning: true, more: true
No warning!
iex> option more: true, warning: true
No warning!

It’s an issue I struggled with early in my Elixir days. There are plenty of solutions for this. What I do in benchee is accept the options as a keyword list but internally convert it to a map (well, actually a struct even!). So, internally I can work with a nice structure that is easy to pattern match, but preserves the nice & idiomatic interface.

You can also use Keyword.get/3 to get the value of whatever option you’re looking for. You can also use Keyword.validate/2 to make sure only well known options are supplied and that you provide good defaults – hat tip to Vinicius.

7. Everything can be compared to Everything

Another surprise might be that you can compare literally every elixir term with one another without raising an exception:

iex> nil < 8
false
iex> 8 < "hello"
true
iex> {1, 2} < ["a"]
true

Most people would probably expect this to raise an error as it does in many other languages. It doesn’t, as Elixir does structural comparisons and follows Erlang’s term ordering which basically gives all terms a predetermined order:

number < atom < reference < function < port < pid < tuple < map < list < bitstring

Why is it done like this?

This means comparisons in Elixir are structural, as it has the goal of comparing data types as efficiently as possible to create flexible and performant data structures.

All in all being able to compare everything to everything may sound mildly annoying but can also lead to some really bad bugs. In a conditional, this will just silently run the wrong code:

iex> maximum = "100" # forgot to parse
"100"
iex> if 9999 < maximum, do: "you pass"
"you pass"

I have seen similar bugs in production code bases, esp. since nil also doesn’t raise and is more likely to slip through. Thankfully, if you try to compare structs elixir issues a warning these days:

iex> %Human{} > nil
warning: invalid comparison with struct literal %Human{}. Comparison operators (>, <, >=, <=, min, and max) perform structural and not semantic comparison. Comparing with a struct literal is unlikely to give a meaningful result. Struct modules typically define a compare/2 function that can be used for semantic comparison
โ””โ”€ iex:6

true

It’s note-worthy that this gotcha and the next one are currently already being addressed at Elixir targeted for the 1.17 release, thanks to the introduction of the type system. Beyond that, sabiwara also wrote the micro library cmp to take care of the problem.

8. Proper Date comparisons

Speaking of which, how do you compare dates?

iex(18)> ~D[2024-05-01] > ~D[2024-05-02]
warning: invalid comparison with struct literal ~D[2024-05-01]. Comparison operators (>, <, >=, <=, min, and max) perform structural and not semantic comparison. Comparing with a struct literal is unlikely to give a meaningful result. Struct modules typically define a compare/2 function that can be used for semantic comparison
โ””โ”€ iex:18
false

Whoops, there is that warning again! Obviously, we shouldn’t compare them like this – but it still works, and might even produce the correct result by accident slipping through tests!

The correct way to compare dates is Date.compare/2 and friends:

iex> Date.compare(~D[2024-05-01], ~D[2024-05-02])
:lt

Again, you may be surprised how often this has snuck past someone.

9. nil["something"] is valid and returns nil

Another surprise may be this:

iex> nil["something"]
nil

Of course, you’d never write it like this but if a nil value had gotten past you and was in your map variable there’d be no way to tell:

iex> map = nil
nil
iex> map["something"]
nil

Which, can be very dangerous. Why is it like this? So that you can use [] to safely access nested values:

iex> map = %{a: %{b: :c}}
%{a: %{b: :c}}
iex> map[:a][:b]
:c
iex> map[:d][:b]
nil

In that last example map[:d] returns nil and then nil[:b] evaluates to nil again without crashing. If you wanted to assure that the keys are there, you got a lot of possibilities but one of them is pattern matching:

iex> %{a: %{b: value}} = map
%{a: %{b: :c}}
iex> value
:c
iex> %{d: %{b: value}} = map
** (MatchError) no match of right hand side value: %{a: %{b: :c}}

10. How to use constants

Another question that’s common among Elixir newcomers is: “Cool, so how do I define constants?” and the answer is… there are no real constants in Elixir/Erlang. The best workaround we have are module attributes. However, they are not visible to the outside by default so you have to provide a function to access them:

defmodule Constants do
  @my_constant "super constant"
  def my_constant do
    @my_constant
  end
end
iex> Constants.my_constant()
"super constant"

That works, however one unfortunate thing about module attributes is that they aren’t… you know, truly constant. You can redefine a module attribute later on in a module without any warning and if you then use it again below the new definition – with its value will have changed:

defmodule Constants do
  @my_constant "super constant"
  def my_constant do
    @my_constant
  end

  @my_constant "ch-ch-changes!"
  def my_constant_again do
    @my_constant
  end
end
iex> Constants.my_constant_again()
"ch-ch-changes!"
iex> Constants.my_constant()
"super constant"

Interestingly, the value is not changed retroactively so my_constant/0 still returns the original value (and is a true constant in that sense). But it can change throughout the module, which is necessary for other use cases of module attributes. So, if you accesses it in a function and someone happened to define it again with a newer value above, you may be in for a bad time.

Hence, I whole-heartedly agree with my friend Michaล‚ here:

It’s also worth nothing that you don’t need module attributes – you can also just define a function that returns a constant value:

def my_other_constant do
  "This is cool as well"
end

In many cases, the compiler is smart enough to realize it’s a constant value (even with some operations applied) and so you won’t suffer a performance penalty for this. However, there are cases where it doesn’t work (f.ex. reading a file) and certain guards require module attributes (f.ex. around enum checking). Hat tip to discussing this with Josรฉ.

To help with this, hauleth has also created a new miny library called defconst.

Closing

Hope you enjoyed these gotchas and they helped you! What gotchas are missing? Let me know in the comments or elsewhere and I’ll try to cover them in future editions – I still got ~10 on my TODO list so far though ๐Ÿ˜…

It’s also worth mentioning that Elixir is well aware of a lot of these – if you follow the links I posted, they will frequently send you to Elixir’s own documentation explaining these. From the early days, there have also already been quite some improvements and more warnings emitted to help you. As Elixir is amazing, and cares a lot about the developer experience.

If you enjoyed this post and think “Working with Tobi may be cool!” – you’re in luck as I’m still looking for a job – so give me a shout, will ya? ๐Ÿ’š

Update 1 (2024-05-02):

Update 2 (2024-05-04)

Update 3 (2024-05-11)

Reexamining FizzBuzz Step by Step – and allowing for more varied rules

Last week I found myself at my old RailsGirls/code curious project group the rubycorns coaching a beginner through the FizzBuzz coding challenge. It was a lot of fun and I found myself itching to implement it again myself as I came up with some ideas about a nice solution given a requirement for arbitrary or changing rules to the game.

I’ve also been working on blog posts helping people interview processes, the next of which will be about Technical Challenges/Code challenges (due to be published tomorrow! edit: Published now!). This is a little extension for that blog post, as an example of going through and improving a coding challenge.

To be clear, I donโ€™t endorse FizzBuzz as a coding challenge. In my opinion something closer to your domain is much more valuable. However, it is (probably) the most well known coding challenge so I wanted to examine it a bit. It is also deceptively simple, and so deserves some consideration.

So, in this blog post let’s start with what FizzBuzz is and then let’s iteratively go through writing and improving it. Towards the end we’ll also talk about possible extensions of the task and how to deal with them. The examples here will be in Ruby, but are easy to transfer to any other programming language. If you’re only here for the code, you can check out the repo.

The FizzBuzz Challenge

The challenge, inspired by a children’s game, originated from the blog post “Using FizzBuzz to Find Developers who Grok Coding” by Imran Ghory back in 2007 – the intent being to come up with a question as simple as possible to check if people can write code.

The problem goes as follows:

Write a program that prints the numbers from 1 to 100. But for multiples of three print โ€œFizzโ€ instead of the number and for the multiples of five print โ€œBuzzโ€. For numbers which are multiples of both three and five print โ€œFizzBuzzโ€.

Simple enough, right? Well, I think it actually checks for some interesting properties and is a good basis for a conversation. To get started, let’s check out a basic solution.

Basic Solution

1.upto(100) do |number|
if (number % 3 == 0) && (number % 5 == 0)
puts "FizzBuzz"
elsif number % 3 == 0
puts "Fizz"
elsif number % 5 == 0
puts "Buzz"
else
puts number
end
end

That one does the job perfectly fine. It prints out the numbers as requested. It also helps to illustrate some of the difficulties with the challenge:

First off, printing out the results is interesting as it is notoriously hard to test (while possible given the correct helpers). It should push a good programmer towards separating the output concern from the business logic concern. So, a good solution should usually feature a separate function fizz_buzz(number) that given any number number either returns the number itself or โ€FizzBuzzโ€ etc. according to the rules. This is wonderfully easy to test, but many struggle initially to make that separation of concerns. We’ll get to that in a second.

What I donโ€™t like about the challenge is that it requires knowledge about the modulo operator, as it isnโ€™t commonly used, but you need it to check whether a number is actually divisible. Anyhow, there will be a lot of code like: number % 3 == 0. To avoid repetition and instead speak in the language of the domain itโ€™s much better to extract this functionality into a function divisible_by?(number, divisor). Makes the code read nicer and removes inherent duplication.

Speaking of the division, the order in which you check the conditions (namely, being divisible by 3 and 5, or 15, before the individual checks) is crucial for the program to work and Iโ€™ve seen more than one senior engineer stumble upon this.

With that in mind, let’s improve the challenge!

We need to go back

First off, while the previous solution is “perfectly” fine, I’d probably never write it as it’s hard to test – it’s just a script to run and everything is printed to the console. And since I’m a TDD kind of person, that won’t do! I usually start, as teased before, by just implementing a function fizz_buzz(number) – that’s the core of the business logic. The iteration and printing out are just secondary aspects to me, so let’s start there:

module FizzBuzz
module_function
def fizz_buzz(number)
if (number % 3 == 0) && (number % 5 == 0)
"FizzBuzz"
elsif number % 3 == 0
"Fizz"
elsif number % 5 == 0
"Buzz"
else
number
end
end
end
view raw fizz_buzz.rb hosted with ❤ by GitHub
RSpec.describe FizzBuzz do
describe ".fizz_buzz" do
expected = {
1 => 1,
2 => 2,
3 => "Fizz",
4 => 4,
5 => "Buzz",
6 => "Fizz",
11 => 11,
15 => "FizzBuzz",
20 => "Buzz",
60 => "FizzBuzz",
98 => 98,
99 => "Fizz",
100 => "Buzz"
}
expected.each do |input, output|
it "for #{input} expect #{output}" do
expect(FizzBuzz.fizz_buzz(input)).to eq output
end
end
end
end

Much better, and it’s tested! You may think that the test generation from the hash is overdone, but I love how easy it is to adjust and modify test cases. No ceremony, I just add an input and an expected output. Also, yes – tests. When solving a coding challenge tests should usually be a part of it unless you’re explicitly told not to. Testing is an integral skill after all.

Ok, let’s make it a full solution.

Full FizzBuzz

Honestly, all that is required to turn it into a full FizzBuzz solution is a simple loop and output. What is a bit fancier is the integration test I added to go along with it:

module FizzBuzz
# …
def run
1.upto(100) do |number|
puts fizz_buzz(number)
end
end
end
view raw runner.rb hosted with ❤ by GitHub
describe ".run" do
full_fizz_buzz = <<~FIZZY
1
2
Fizz
.. much more …
98
Fizz
Buzz
FIZZY
it "does a full run integration style" do
expect { FizzBuzz.run() }.to output(full_fizz_buzz).to_stdout
end
end
view raw runner_spec.rb hosted with ❤ by GitHub

Simple isn’t it? You may argue that the integration test is too much, but when I can write an integration test as easy as this I prefer to do it. When I write code that generates files, like PAIN XML, I also love to have a full test that makes sure when given the same inputs we get the same outputs. This has the helpful side effect that even minor changes become very apparent in the pull request diff.

Anyhow, the other thing that we see is that our separation of concerns with the fizz_buzz(number) function pushed us to here is that there is only a single puts statement. We separated the output concern from the business logic. Should we want to change the output – perhaps it should be uploaded to an SFTP sever – then there is a single point for us to adjust that in. Of course we could also use dependency injection to make the method of delivery easier to change, but without a hint that we might need it this is likely overdone. Especially since I’d still want to keep the full integration test we just wrote, programs have a tendency to break in the most unanticipated ways.

Keeping up with the Domain

The other thing I complained about initially was the usage of the modulo operator. Truth be told, I only wrote the initial version like this for demonstration purposes – that one has to go. I don’t want to think about what “modulo a number equals 0” means. Whether or not something is divisible is something I understand and can work with. It also removes a fair bit of duplication:

module FizzBuzz
module_function
def fizz_buzz(number)
fizz = divisible_by?(number, 3)
buzz = divisible_by?(number, 5)
if fizz && buzz
"FizzBuzz"
elsif fizz
"Fizz"
elsif buzz
"Buzz"
else
number
end
end
def run
1.upto(100) do |number|
puts fizz_buzz(number)
end
end
def divisible_by?(number, divisor)
number % divisor == 0
end
end
view raw fizz_buzz.rb hosted with ❤ by GitHub

Much better! There is a small optimization here, where we only check the divisibility twice instead of 4 times. It doesn’t fully matter, but when I see the exact same code being run twice in a method and I can remove it without impacting readability I love to do it.

I consider this a good solution. However, now is where the fun of many coding challenges starts – what extension points are there?

Extension Points

Many coding challenges have potential extension points baked in. Some extra features, or, what I almost prefer dealing with, uncertainty and how that might affect software design.

How certain are we that we need the first 100 numbers? How certain are we that we want to print it out on the console? Is there a possibility that weโ€™ll get a 3rd number like 7 and how would that work? How certain are we that itโ€™s the numbers 3, 5 and the words Fizz and Buzz?

Depending on the answers to these question you could go, adjust the challenge to make these easier to change. And with answers I don’t mean you making them up, but in case of a live coding challenge you talking to your interviewers to see what they think. A common case for instance would be that the numbers and strings may change while weโ€™re certain it will be 2 numbers and 2 distinct strings – “product is still trying to figure out the exact numbers and text and they might change in the future as we’re experimenting in the space”.

Let’s run with that for now – we think it will always be 2 numbers but we’re not sure what 2 numbers and we’re also not sure about Fizz and Buzz. What do we do now?

Going Constants

One of the easiest solutions to this is to extract the relevant values to constants or even into a config.

module FizzBuzz
module_function
FIZZ_NUMBER = 3
FIZZ_TEXT = "Fizz"
BUZZ_NUMBER = 5
BUZZ_TEXT = "Buzz"
FIZZ_BUZZ_TEXT = FIZZ_TEXT + BUZZ_TEXT
def fizz_buzz(number)
fizz = divisible_by?(number, FIZZ_NUMBER)
buzz = divisible_by?(number, BUZZ_NUMBER)
if fizz && buzz
FIZZ_BUZZ_TEXT
elsif fizz
FIZZ_TEXT
elsif buzz
BUZZ_TEXT
else
number
end
end
def run
1.upto(100) do |number|
puts fizz_buzz(number)
end
end
def divisible_by?(number, divisor)
number % divisor == 0
end
end
view raw fizz_buzz.rb hosted with ❤ by GitHub

Right, so that got a lot longer and frankly also a bit more confusing. However, it is now immediately apparent where to change the values. That said, we also kept the “FizzBuzz” naming for the constants which may get extra confusing if we changed the text to something like “Zazz”. It’s always a tradeoff, the previous version was definitely more readable. We did get rid of “Magic numbers” and “Magic Strings”. Sadly, due to the nature of the challenge, they are also still very magical as there is no inherent reasoning to them ๐Ÿ˜…

Something bugs me with this solution though, and that’s the reason why I actually went and implemented it myself again (and wrote this post): There is an obvious relation between FIZZ_NUMBER and FIZZ_TEXT but from a code point of view they are completely separate data structures only held together by naming conventions and their usage together.

Working With Rules

Ideally we’d want a data structure to hold both the text to be outputted and the number that triggers it together. There’s a gazillion ways you could go about this. You could simply use maps, arrays or tuples to hold that data together. As we’re doing Ruby right now, I decided to create an object that holds the rule and can apply itself to a rule – either returning its configured text or nil.

module FizzBuzz
module_function
class Rule
def initialize(output, applicalbe_divisible_by)
@output = output
@applicalbe_divisible_by = applicalbe_divisible_by
end
def apply(number)
@output if divisible_by?(number, @applicalbe_divisible_by)
end
def divisible_by?(number, divisor)
number % divisor == 0
end
end
DEFAULT_RULES = [
Rule.new("Fizz", 3),
Rule.new("Buzz", 5)
].freeze
def fizz_buzz(number, rules = DEFAULT_RULES)
applied_rules = rules.filter_map { |rule| rule.apply(number) }
if applied_rules.any?
applied_rules.join
else
number
end
end
def run(rules = DEFAULT_RULES)
1.upto(100) do |number|
puts fizz_buzz(number, rules)
end
end
end
view raw fizz_buzz.rb hosted with ❤ by GitHub

Now, that’s quite different! Does it work? Well, yes – and the beauty of it is that so far we haven’t altered our API at all so all of these were internal refactorings that work with exactly the same set of tests. Yes, technically this adds additional optional parameters, we’ll get to these later ๐Ÿ˜‰

The most interesting thing about this is that the rule of “if it is divisible by both do X” is gone. Turns out, that rule isn’t really needed:

  • if it is divisible by 3 add “Fizz” to the output
  • if it is divisible by 5 add “Buzz” to the output
  • if it is divisible by neither, just print out the number itself

That works just as well. It means that the order in which we store rules in our DEFAULT_RULES array matters (so we don’t end up with “BuzzFizz”). Of course we need to verify this with our stakeholders/interviewers, but let’s say they agree here. Now, can we allow for even more flexibility with the rules?

Flexible Rules

Now our “stakeholders” might come back and say well, you know what we’re not so sure about just having 2 numbers and their respective outputs. It may be more, it may be less! The good news? This already completely works with our implementation above. However, we should still test it. You can take a look at all the test I wrote over here, I’ll just post the tests here for adding a 3rd rule: 7 and Zazz!

context "with Zazz into the equation" do
zazz_rules = [
FizzBuzz::Rule.new("Fizz", 3),
FizzBuzz::Rule.new("Buzz", 5),
FizzBuzz::Rule.new("Zazz", 7)
]
expected = {
1 => 1,
3 => "Fizz",
5 => "Buzz",
7 => "Zazz",
15 => "FizzBuzz",
21 => "FizzZazz",
35 => "BuzzZazz",
105 => "FizzBuzzZazz"
}
expected.each do |input, output|
it "for #{input} expect #{output}" do
expect(described_class.fizz_buzz(input, zazz_rules)).to eq output
end
end
end
view raw zazz_spec.rb hosted with ❤ by GitHub

As per usual, testing all the different edge cases here is a fun exercise:

  • What is the behavior if no rules are passed?
  • What happens if the exact same rule is applied twice?
  • Can I change the order to make it “BuzzFizz”?

What I also like about this solution is that the rules aren’t actually hard coded but are injected from the outside. That allows us to easily test our system against many different rule configurations. It also allows us to run many different rule configurations in the same system – each user of our FizzBuzz platform could have their own settings for FizzBuzz!

Closing Out

That’s quite a bit of changes we went through here. I want to stress, that you shouldn’t start with the more complex solution as it is definitely harder to understand. You Ain’t Gonna Need It.However, if there are actual additional requirements or credible uncertainty it might be worth it. I ended up implementing it because I wanted to translate that relationship between “3” and “Fizz” explicitly into a data structure, as it feels like an inherent part of the domain. The other properties of it were almost just a by-product.

You can check out the whole code on github and let me know what you think.

Of course, we haven’t handled all extension points here:

  • The range of 1 to 100 is still hard coded, however that is easily remedied if necessary (by passing the range in as a parameter)
  • Our choice of the implementation of Rule hard coded the assumption that rules only ever check the divisibility by a number. We couldn’t easily produce a rule that says “for every odd number output ‘Odd'”. Instead of providing Rule with just a number, we could instead use an anonymous function to make it even more configurable. However, don’t ever forget you ain’t gonna need it – even in a coding challenge. Overengineering is also often bad, hence the conversation with your stakeholders/interviewers is important as for what are “sensible” extension points.
  • The output mechanism is still just puts, as mentioned earlier dependency injecting an object with a specific output method instead would make that more configurable and quite easily so.

Naturally, naming is important and could see some improvement here. However, with completely made up challenges having good naming becomes nigh impossible.

Anyhow, I hope you enjoyed this little journey through FizzBuzz and that it may have been helpful to you. I certainly enjoyed writing that solution ๐Ÿ˜

Edit: Earlier versions of the code samples featured bitwise-and (&) instead of the proper and operator (&&) – both work in this context (hence the tests passed) but you should definitely be using &&. Thanks to my friend Jesse Herrick for pointing that out. Yes the featured image is still wrong. I won’t fix it.

Open Source isn’t just about code – other ways in which you can contribute!

Talking to developers and reading about open source I often get the feeling that the general notion is that open source is just about code and commits. Put another way, “If you don’t make commits for a project you are not contributing to it”. Or so they say. That notion is far from the truth in my eyes. Let me tell you why.

Sure, code is what ultimately ships and has a direct impact on the users of an open source project, so yes commits and code are important. But it’s by no means the only way you may contribute to a project. Projects mostly are a whole eco system, which is about more than just code. Here are a couple of other ways you may contribute to a project.

Report issues

If maintainers don’t know about issues they can not fix them. Therefore, it is crucial that you report issues you encounter and not just abandon using the project or only build a workaround. Most projects are happy to receive issue reports. Don’t take reporting issues lightly either, often a substantial amount of time goes into writing a good issue report. Ideally an issue report contains code to reproduce the issue, information about the expected outcome and the actual outcome, system information, version information and maybe a stack trace or similar artifacts. I also like to include a little note of appreciation for the maintainers, but that’s optional. Keep in mind that issues don’t have to be about bugs – they may also be about possible improvements or desired features. Github even acknowledges the importance of issues by giving you contribution points for opened issues – yay!

Write Documentation

Documentation is extremely important but often lacking, as a lot of people really don’t enjoy writing it. It’s a great way to help a project out by making it easier for other people to get into. Also if you found it hard to get into a project, try improving the documentation so the next person will have it easier than you did. I actually have commits on ruby – they are all documentation commits ๐Ÿ™‚

Improve the website

Many open source projects have their own websites. Sometimes the information is outdated and sometimes it’s just plain ugly. I remember the old shoes website – it was really ugly and looked dead (at least that were my thoughts when I first saw it). But look at it now! It looks nice and presentable. And most of it is thanks to wpp – he has never pushed a commit to shoes (that I am aware of), but this certainly was a great contribution for shoes.

Offer to help with art/design

A lot of projects would love to have their logo updated, get some illustrations for their website or similar thing. So if design or illustration is your thing, maybe go to your favorite project and ask them if they want some help with that? I know I’d be more than happy about that!

Trying out preview versions

Developers need feedback if their software works. Therefore, alpha, preview or release candidate releases are made often. Go grab one of those and try it out. If everything works – great, you just made sure it works on your system! If you find a bug – report it! That’s a great help for the project.

Weigh in on Discussions

Sometimes there are discussions about API changes or ways an implementation could be improved (among other things). Comments are very welcome there, maintainers want the input of their users. I once spent a whole day discussing some architectural issues I identified in a project. It was fun. Other work might be setting up a road map – Eric Watson did that for Shoes 4 one day. He’s a great coder, but that road map helped the project more than any code he could have written in a similar time frame. His road map went on to be a very helpful guidance and reference point.

Answer Questions

Questions about a project pop up all around the place. Be it stackoverflow or just the project’s issue tracker. By answering them you help other people to get a better experience with the project overall. Also don’t forget that a question might hint at a problem with the project. Maybe the documentation for this part could be improved or there is a common task that might be automated or deserves a better API? Maybe you could jump in to do this?

Give a presentation about a project

There are many great projects out there, but developers may only adopt them if they know about them!ย If you really like a project, consider giving a talk about it at a local user group or handing in a talk for a conference. This way the adoption of the project may be increased, bringing more people to the project making it a better and more stable product overall – benefiting everyone.

Closing

If you already have done any of the above: thank you! You contributed to open source. Keep doing that if you like, if not give it a shot. If you want to get started on contributing to open source, this post of mine might come in handy. Personally contributing to open source has been an amazing journey for me so far. I enjoy it very much and have made quite some friends that way :).

How to get started with contributing to open source

I often see people who really want to contribute to open source projects. And that’s great! But often it’s not easy to get started. “What project should I contribute to?”, “What can I do on that project?” and “How does contributing work?” are common questions. It can be scary. I found myself in the same place around 3 years ago. Since then I contributed to a variety of open source projects and am heavily involved in a few (including my own projects). This post is here to answer these questions, give you some help, guidance and share experience.

Step 1 – Find a project

“Where to start?” “Which project can I contribute to?” These are usual questions. The most important principle here is “Scratch your own itch!” – work on a project that you use and that matters to you. There is no point in investing energy into something you don’t care about. Even better if you have a specific issue with a project: some odd behavior, a bug that you work around, a sub optimal API, lack of documentation… you name it. If you use some projects you’ll find a couple of those. I guarantee it – nothing is perfect.

Also try to see if the project isn’t too far off your comfort zone – e.g. if you are a ruby programmer and don’t know much C then contributing to a gem which is primarily a C-extension is probably not the best idea.

Have a look at how active the project is. You’ll want to contribute to a project that is still actively developed and where the maintainers react to issues and pull requests. For this you can look at a couple of things: When was the latest commit made? Look at the recent pull requests and issues: Did anyone comment on them? Is there a crazy amount of open pull requests, some of them like half a year old without activity?

Another advice: Try not to contribute code to a really big project (think: rails) straight away, especially if you are a beginner. Those projects are… well… big. It’s often hard to find the right place where a fix should occur. Familiarizing yourself with the code base unfortunately takes a lot longer as well. Also due to their nature, they often have a lot more issues and pull requests that the maintainers have to take care of. Often that results in longer feedback cycles, somebody else trying to fix the same problem and it diminishes the chance of making “a real impact”. It’s not the ideal scenario for first time contributors in my opinion. With small to medium-sized projects I often experienced that maintainers are a lot happier to get contributions and faster to respond.

Step 2 – What to work on?

If you’ve got an itch you want to scratch you are already mostly ready to go. But first make sure that the problem persists even in the latest release of the software (even better on master). Then search the issue tracker of the project to check if the fault has already been reported. If not, report an issue (or feature request) to see if this actually is a problem/a wanted feature. In the best case you provide a small sample script demonstrating the error along with an expected behavior from your side. You can already mention that you’d like to work on this.

If you don’t have an itch to scratch but a project you’d love to help out: have a look at the issue tracker. Some projects feature tags/categories for newcomer friendly issues – check those out first. Otherwise look for something that seems appealing to you and not too complex.

I want to stress that contributions aren’t limited to fixing bugs and implementing new features. Helping out with documentation is very valuable, that’s how quite some contributors get started. Also writing tests to increase test coverage or refactoring code (hint: some projects use Code Climate – you can check out code smells there) to remove duplication/make it more readable is often very welcome. I especially recommend the latter two as to do this you have to understand the code and therefore get a good first view of the code base without having to add anything.

Make sure to have a look at the README of the project. It often highlights how to figure out what to work on and which kinds of contribution are welcome.

Step 3 – Get your changes in!

Mostly you should make sure that there is an issue for what you want to work on (exceptions include small refactorings). In that issue comment that you would like to work on the problem, maybe outline a strategy to solve it if you already have one and ask for pointers and advice how to go about that issue.

Also look at the README – a lot of projects mention how they’d like contributions to be handled. Also they might have references to style guides or guidelines such as “provide test cases that fail”. A lot of them will also have instructions like these (at least on the most popular platform these days, github):

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Forking is basically creating your own copy of the repository which you can write to. Creating a Pull Request is like saying: “Hey, I made these changes do you want to have them?” In a Pull Request the changes are discussed, commentedย  and it’s basically the way to get your changes in. I recommend opening pull requests early – as soon as the basing building blocks are standing. The feature doesn’t need to work yet. This way you can get valuable feedback about whether this is the right approach as well as hints and tips leading to the right solution.

Closing Notes

Have fun contributing to open source! There are a lot of nice people out there happy to get contributions of any form. And I can tell you – it’s a great feeling to know you made something better for a lot of people (including yourself!). You might have seen an occasional story about big discussions or fights in pull requests/issues. Please don’t let that scare you. I can tell you that from my experience these are the exception not the rule. Most people in open source are really nice and work together for a common goal. Two and a half years ago I started contributing to the Shoes project. To this day this is the nicest online community I ever met with a lot of really helpful and polite people around! I stayed with the project ever since – gradually increasing my contributions.

I hope this post helps you to get started on your journey into the open source world. If there is something missing or you got more questions please feel free to add a comment.

2n edition of HU course – with blog and material

Hi everyone,

today was a great day! Today was the first day of the second edition of the course about the basics of web development I’m teaching at Humboldt University Berlin. I had a great deal of fun and am already looking forward to next week.

Anyhow, the course now has its own blog where I share presentations, homework etc. The course is held in German only, so the material is only German as well. The course is aimed at total beginners (e.g. minimum requirement: “You should know how to use a computer.”).ย  When looking through the material please keep in mind that it is not made for self-study – it is made to be presented. Also it is for real beginners and therefore sometimes makes abstractions/simplifications for the sake of productivity.

So without further ado, enjoy the blog: webanwendungsentwicklung.wordpress.com

Cheers,

Tobi

8 ways to enable workshop attendees to keep learning

Workshopping
A Rails Girls workshop (Photo credit: Kerstin Kollmann (CC) NC-ND-BY)

There are many great movements out there teaching people how to code: Rails Girls and RailsBridge among the most prominent. Those movements host free workshops with a superb coach/student ratio to get more people into coding. However there is one problem: How to keep on coding? After the workshop the attendees are mostly on their own again, but we want them to continue! How can we do that? Here are methods we use in Berlin, that I hope can help other movements, subgroups and organizations to keep students engaged.

1. Project Groups

At Rails Girls Berlin we have project groups. These groups are usually formed after the workshop on the mailing list. Someone can suggest a project to work on or just join a project group. Project groups usually are supported by one or two coaches and have 4 to 8 students. Of course there are bigger and smaller groups. You then meet (usually) every week for a couple of hours to work on the project or talk about concepts. As once a week is not a lot there often is some kind of homework.

This concept has been very successful for us. I know of 8 project groups running right now, but I’m told there are ~12 of them which is amazing. Our first project group, the RubyMonsters, have been working as a group for roughly a year now. They already finished their first app and help coach at Rails Girls Berlin events now ๐Ÿ™‚

I also heard of similar groups spontaneously forming in Poland after workshops, which made me really happy!

2. Advanced Workshops

At Rails Girls Berlin we often also let advanced applicants join the workshops (we have a workshop about every one to two months). Advanced meaning that they already visited a workshop or have other programming experience. They get their own groups and don’t follow the normal curriculum but rather work on something of their own (with the help of a coach).

We also sometimes host workshops solely for more advanced learners. I spent one such workshop with an experienced Java developer from Brazil talking about differences between the Rails and the Java world. It was a lot of fun!

3. User Groups

We try to encourage the attendees to attend user groups. Here it helps if they already know someone at the user group, e.g. a coach or an organizer. With us I organize the Ruby User Group Berlin and many coaches go there regularly too. Sadly we didn’t have too much success attracting learners to join us, but those that did, always told me that they liked it a lot ๐Ÿ™‚

However there is something more suitable for learners: A user group for beginners! Our friends from Open Tech School have just the thing: The Learners Meetup. This happens once every month and there are learners and coaches present. The coaches may introduce themselves and the technologies they are familiar with.ย  Then there is a talk about a basic programming topic suitable for beginners.ย  After that there is a break and people can write down topics they are interested in on cards. Then the different topics are clustered and groups for discussions about those topics are formed. I really love this format and the event.

4. Informal Meetings

Project Groups are great, but they have a drawback: A couple of hours per week at a specific time isn’t really flexible and too much for some. Some also prefer to learn on their own, but how to get help when things aren’t going that well? Well that’s what informal meetings are for. Those are meetings where anyone might drop by and work on something or ask questions. You are mostly not guaranteed to have a coach around but learners can help other learners. And mostly you can check beforehand who is there and then see if someone may help with the problem you’re having. Open Tech School has the Learners Hangout every Saturday, which is a brilliant place to go. And in the future I want to try to establish a regular schedule where you can be certain that a coach is present.

5. Show them resources

During the workshop present some good resources to deepen the knowledge of whatever you taught in the workshop. That might be websites, books or user groups to go to. I think this is most fitting at the end of the workshop. Also make sure to publish that list online so attendees can refer back to it later and don’t have to write everything down. I have such a list on this blog myself.

This might be pointing out the obvious, but it’s important nonetheless.

6. A Summer of Code

You might have already heard of Rails Girls Summer of Code – it’s a worldwide program started in Berlin. The idea is to get more woman into coding – or rather further into coding when you continued after your workshop. It’s 3 months, full-time, paid, work on open source and students get supported by coaches and mentors. It’s an amazing program. And we’re still looking for some sponsors – so if you’re willing to help and want to allow us to accept more students please donate. And thanks a ton to all our sponsors so far!!!

7. Have former attendees coach at a workshop

When a former attendee of the workshop continued to code (which we hope!) then it is a brilliant opportunity to let them coach at a workshop. This works beautifully in three ways:

  1. Motivation for attendees: Attendees love to meet someone who has been down the same path before. When we had our lovely Ruby Monsters project group coach with us for the first time a record-breaking number of 3 project groups were founded afterwards. For me that’s the primary measure of success – the people we convince to keep on coding.
  2. Affirmation for former attendees/new coaches: They see all the things that they have learned, up to the point where they can help others learn which is an awesome feeling. Moreover teaching is the ultimate learning (a topic deserving of a blog post of its own…) – explaining something forces you to really understand something and often times leads to looking at things from a different angle which greatly benefits your own learning.
  3. Better coaching: Former attendees remember pretty well, what it was like as a beginner as this hasn’t been too long ago for them. Therefore their explanations often work very well.

See – everyone wins!

I at least know that in Poland they did the same, where former attendees are now coaches which is amazing. At Rails Girls Berlin when we did this for the first time we paired our new coaches up with an experienced coach in order to have a good mix of experience levels for coaching.

8. Share your learning story at the workshop

It is extremely cool to have someone at a workshop who taught themselves how to program. They can share all their insights and tips. Attendees can really relate to a talk like this as well as the person. You might also tell them, that sometimes learning to code is hard, sometimes it’s fun but in the end it’s worth it and a lot of fun. Tell them that it’s ok to make mistakes and that even the most experienced developers resort to search engines more often than you’d think.

Our Ruby Monsters often give a lightning talk about their story of learning in a project group at our workshops. And regularly Joan Wolkerstorfer also tells her story and gives tips. You can watch her talk about this and read about it.

When talking to attendees weeks after the workshop and asking them why they kept on coding meeting someone who has done it and hearing their story is almost always mentioned first.

A lightning talk is also an ideal time to introduce git or github, as these tools enable you to cooperate with others on your project! And we all know that working together with others, building something together, is a great motivation!

Conclusion

There are lots of ways to motivate workshops attendees to keep on learning. These are ways that worked for us or that I think work. Do you have other ideas how we can help people to keep on coding? Have you tried something? I would love to get some input! As a result of initial comments this blog post already grew from 6 to 8 ways ๐Ÿ™‚

A Rails Beginner Cheat Sheet

Hi everyone,

during the last week I spent a large amount of time creating a Cheat Sheet for Rails beginners! So, here it is: http://pragtob.github.io/rails-beginner-cheatsheet/

I originally started creating this cheat sheet for my awesome Rails Girls Berlin project group. But I figured and hoped that a lot more people would be interested in this ๐Ÿ™‚

So far this cheat sheet covers the following aspects:

  • Command line basics
  • Ruby basics including (Numbers, Strings, Arrays, Hashes)
  • Rails basics including a description of the folders and commonly used commands (like starting the server)
  • Tips about using an editor
  • Information about where to get help

So also feel free to check out the repository and especially the open issues – I would love feedback on many of them! And contributions are also very welcome!

Cheers, hope it helps you + keep on learning,

Tobi

Teaching a course about web development at Humboldt University

Hello everyone,

just a quick note but I’m extremely excited to finally announce that I will be teaching a course about the basics of web development at Humboldt University Berlin. The course is meant for bachelor students who don’t study computer science. It’s a total basics course, no prior knowledge required. The course will start with basic HTML and CSS, then we’ll get into some Ruby and then some Ruby on Rails. So in short, it could be perfect to be the start of your journey into Programming. And a useful skill to have, in the spirit of this awesome video.

The course will be every week on Wednesday for 9 weeks (starting in May) for ~5 hours + breaks. The course will be held in German. The course is limited to ~14 students.

Link to the full course description (German)

If you are a student interested in the course, feel free to get in touch with me for questions etc!

In the end, a big thanks to everyone who helped making this happen! Especially Dajana ๐Ÿ™‚

Cheers,

Tobi