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)

Mastery comes from failure

In software development, and many other disciplines, people strive for mastery – you want to get better to be great something. For some reason failure is often seen as the opposite of mastery – after all masters don’t fail – or do they?

We often see talks and read great blog posts about all these great achievements of amazing people but almost never about major failures. But, how are failures important and useful? Well, let me tell you a little story from back when I was teaching an introductory web development course:

We were in the practice stage and I was doing my usual rounds looking at what the students did and helped them to resolve their problems. After glancing at a screen and saying that the problem could be resolved by running the migrations the student looked at me with disbelief and said:

Tobi, how can you just look at the screen and see what’s wrong in the matter of seconds? I’ve been trying to fix this for almost 15 minutes!

My reply was simple:

I’ve  probably made the same mistake a thousand times – I learned to recognize it.

And that’s what this post is about and where a lot of mastery comes from in my mind – from failure. We learn as we fail. What is one of the biggest differences between a novice and a master? Well, quite simply the master has failed many more times than the novice has ever tried. Through these failures the now master learned and achieved a great level of expertise – but you don’t see these past failures now. This is shown quite nicely in this excellent web-comic:

befriendswithfailure0005
“Be Friends with Failure” taken (with permission) from doodlealley. It focuses on art, but I believe it applies to programming just as much and I recommend reading the whole comic  🙂

For this to work properly we can’t program/work by coincidence though – when something goes wrong we must take the right measurements to determine why it failed and what we could do better the next time around. Post-mortems are relatively popular for bigger failures, in a post-mortem you identify the root cause of a problem, show how it lead to the observed misbehavior of the system and ideally identify steps to prevent such faults in the future. For service providers they are often even shared publicly.

I think we should always do our own little “post-mortems” – there doesn’t have to be a big service outage, data leak or whatever. Why did this ticket take longer than expected? Which assumptions we had were wrong? Could we change anything in our process to identify potential problems earlier on in the future? This interaction with my co-worker didn’t go as well as it could have been, how could we communicate better and more effectively? This piece of code is hard to deal with – what makes it hard to deal with, how could it be made easier?

Of course, we can’t only learn from our own failures (although those tend to work best!) but from mistakes of others as well – as we observe their situation and the impact it had and see what could have been done better, what they learned from it and what we can ultimately learn from it.

Therein lies the crux though – people are often afraid to share their failings. When nobody shares their failures – how are we supposed to learn? Often it seems that admitting to failure is a “sign of weakness” that you are not good enough and you’d rather be silent about it. Maybe you tell your closes friends about it, but no one else should know that YOU made a mistake! Deplorable!

I think we should rather be more open about it, share failures, share learnings and get better as a group.

This was signified for me as a couple of years back a friend asked me if it was ok to give a talk (I organize a local user group) about some mistakes made in a recent project. I thought it was a great idea to share these mistakes along with some learnings with everyone else. My friend seemed concerned what the others might think, after all it is not common to share stories like this. We had the talk at a meetup and it was a great success, it made me wonder though – how many people are out there that have great stories of failures and learnings to share but decide not to share them?

I’ve heard whispers of some few meetups (or even conferences?!) that focus on failure stories but they don’t seem to have reached the mainstream. I’d love to hear more failure stories! Tried Microservices/GraphQL/Elm/Elixir/Docker/React/HypeXY in your project and it all blew up? Tell me about it! Your Rails monolith basically exploded? Tell me more! You had an hour long outage due to a simple problem a linter could have detected? You have my attention!

What I’m saying is: Please go ahead and share your failures! Sharing them you learn more about them as you need to articulate and analyze, everyone else benefits, learns something and might have some input. Last but not least people see that mistakes happen, it demystifies this image we have of these great people who never make a mistake and who just always were great and instead shows us where they are coming from and what’s still happening to them.

My Failures + Lessons learned

Of course a blog post like this would feel empty, hollow and wrong without sharing a couple of my own failures or some that I observed and that shaped me. These are not detailed post-mortems but rather short bullet points of a failure/mistake and what I learned from it. Of course, these sound general but are also ultimately situational and more nuanced than this but are kept like this in favor of brevity – so please keep that in mind.

  • Reading a whole Ruby book from start to finish without doing any exercise taught me that this won’t teach me a programming language and that I can’t even write a basic program afterwards so I really should listen to the author and do the exercises
  • Trying to send a secret encryption key as a parameter through GET while working under pressure taught me that this is a bad idea (parameter is in the URL —> URL is not encrypted –> security FAIL) , that working under pressure indeed makes me worse and that I’d never miss a code review again, as this was thankfully caught during our code review
  • Finally diving into meta programming after regarding the topic as too magic for too long, I learned that I can learn almost anything and getting into it is mostly faster than I think – it’s the fear of it that keeps you away for too long
  • Overusing meta programming taught me that I should seek the simplest workable solution first and only reach for meta programming as a last resort as it is easy to build a harder to maintain and understand than necessary code base – sometimes it’s even better to have some duplication than that meta programming
  • Overusing meta programming also taught me about the negative performance implications especially if methods are called often
  • Being lied to in an interview taught me not to ask “Do you do TDD?” but rather “How do you work?”
  • Doing too much in my free time taught me that I should say “No” some times and that a “No” can be a “Yes” to yourself
  • Working on a huge Rails application taught me the dangers of fat models and all their validations, callbacks etc.
  • Letting a client push in more features late in the process of a feature taught me the value of splitting up tickets, finishing smaller work packages and again decisively saying “No!”
  • Feeling very uncomfortable in situations and not speaking up because I thought I was the only one affected taught me that when this is the case, chances are I’m mostly not the only one and others are affected way more so I should speak up
  • Having a Code of Conduct violation at one of my meetups showed me that I should pro actively inform all speakers about the CoC weeks before the talks in our communication and not just have it on the meetup page
  • Blindly sticking to practices and failing with it taught me to always keep an open mind and question what I’m doing and why I’m doing it
  • Doing two talks in the same week (while being wobbly unprepared for the second) taught me that when I do that again none of them can be original
  • Working in a project started with micro services and an inexperienced team showed me the overhead involved and how wrongly sliced services can be worse than any monolith
  • Building my first bigger project (in university, thankfully) in a team and completely messing it up at first showed me the value of design patterns
  • Skipping acceptance testing in a (university) project and then having the live demo error out on something we could have only caught in acceptance/end-to-end testing showed me how important those tests really are
  • Writing too many acceptance/end-to-end tests clarified  to me how tests should really be written on the right level of the testing pyramid in order to save test execution time, test writing time and test refactoring time
  • Seeing how I get less effective when panic strikes during production problems and how panic spreads to the rest of the team highlighted the importance of staying calm and collected especially during urgent problems
  • Also during urgent problems it is especially important to delegate and trust my co-workers, no single person can handle and fix all that – it’s a team effort
  • Accidentally breaking a crucial algorithm in edge cases (while fixing another bug) made me really appreciate our internal and external fallbacks/other algorithms and options so that the system was still operational
  • Working with overly (performance) optimized code showed me that premature optimization truly is the root of all evil, and the enemy of readability – measure and monitor where those performance bottle necks and hot spots are and only then go ahead and look for performance improvements there!
  • Using only variable names like a, b, c, d (wayyy back when I started programming) and then not being able to understand how my program worked a week later and having to completely rewrite it (couple of days before the hand in of the competition…) forever engraved the importance of readable and understandable names into my brain
  • Giving a talk that had a lot of information that I found interesting but ultimately wasn’t crucial for the understanding of the main topic taught me to cut down on additional content and streamline the experience towards the learning goals of the presentation
  • Working in a team where people yelled at each other taught me that I don’t want to deal with behavior like this and that intervention is hard – often it’s best to leave the room and let the situation cool down
  • Being in many different situations failing to act in a good way taught me that every situation is unique and that you can’t always act based on your previous experience or advice
  • Trying to contribute to an open source project for the first time and never hearing back from the maintainers and ultimately having my patch rejected half a year after I asked if this was cool to work on showed me the value of timely clear communication especially to support open source newcomers and keep their spirits high
  • Just recently I failed at creating a proper API for my Elixir benchmarking library, used a map for configuration and passed it in as an optional first argument (ouch!) and the main data structure was a list of two-tuples instead of a map as the second argument – gladly fixed in the latest release
  • probably a thousand more but that I can’t think of right now 😉

Closing

befriendswithfailure0010
“Be Friends with Failure” taken (with permission) from doodlealley.

We can also look at this from another angle – when we’re not failing then we’re probably doing things that we’re already good at and not something new where we’re learning and growing. There’s nothing wrong with doing something you’re good – but when you venture out to learn something new failure is part of the game, at least in the small.

I guess what I’m saying is – look at failures as an opportunity to improve. For you, your team, your friends and potential listeners. Analyze them. What could have prevented this? How could this have been handled better? Could the impact have been smaller? I mean this in the small (“I’ve been trying to fix this for the past hour, but the fault was over here in this other file”), in the big (“Damn, we just leaked customer secrets”) and everywhere in between.

We all make mistakes. Yes, even our idols – we sadly don’t talk about them as much. What’s important in my opinion is not that we made a mistake, but how we handle it and how we learn from it. I’d like us to be more open about it and share these stories so that others can avoid falling into the same trap.

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 🙂

Updated Resource section

Hi everyone,

just a quick note I updated my beloved Resource section with some quite nice new resources. I also restructured it so that Ruby and Rails got some distinct sections.

When I started this blog sharing resources for learning how to program was pretty important to me and it still is now. I love both teaching and learning. I hope that these resources can help you to learn how to program, teach someone how to program or improve your own skills. But it’s not only programming there, my favorite topic “agile software development” is also mentioned!

Most of the resources are free so go ahead and check them out.

Enjoy and have fun!

Tobi

Slides: Where to go from here (Ruby Beginners)

Hi everyone,

I just gave a lightning talk at RailsGirls Berlin about places where you can continue to learn about Ruby and Programming – especially for beginners. The slides got links to the mentioned content. So here they are:

Where to go from here (pdf slides)

Where to go from here (slideshare, here you can also download the .odp)

If you got any questions or something, please feel free to contact me (comment here or see the about section).

Enjoy coding!

Tobi

Pragmatic Thinking & Learning by Andy Hunt

“Pragmatic Thinking & Learning” is a book full of knowledge, and that is also what it is about: knowledge. More precisely it is about your knowledge, how you can improve your skills and how to fully leverage your brain. The book is the easily understandable round-up of knowledge about the brain, learning and other interesting information of high interest to knowledge workers, that I always wanted to read. And don’t be afraid – the book isn’t just dumping knowledge on you. As it is common for books of the pragmatic bookshelf, it does so in a very understandable way and is spiced up with a lot of good humour.

This book is really good. How good? When I studied at Mendicant University I recommended this book to a fellow student on the mailing list, as we were discussing about stepping away from the computer in order to solve problems. Within some hours 3 other people agreed that is a great book and definitely worth the read. It was a small course with a handful of participants and some mentors (I’ll write a post about Mendicant University soon) so the “return rate” really impressed me, but to me the book deserves all the praise.

So who wrote this book? It was Andrew (Andy) Hunt, one of the founders of my favourite bookshelf, one of the authors of the Manifesto for Agile Software Development and author/co-author of other great books like the famous “The Pragmatic Programmer”.

Content

The book starts with a chapter mainly about the Dreyfus Model, a model that helps you assess your own skills explaining the differences between a beginner and an expert. Andy also explains this model shortly in a free PragPub article.

The next chapter is all about the brain, it explains how your brain works, what L-Mode and R-Mode are and why it would be a good idea to use the latter one more.

The following chapter was the most interesting chapter of the book to me. This chapter explains how to really leverage your brain and activate all its computational power. It is elaborated why you should try to involve as many senses as possible when trying to solve a hard problem, why metaphors and humour greatly help and why sometimes all you need to do is take a walk.

The chapter after this one is called “Debug your mind” and talks about things that basically can go wrong or in your way, like cognitive biases, generational affinity, the bad effect of pressure or how humans sometimes use reptilian approaches in order to deal with life’s challenges. The main point here is to be aware of those “bugs” in your mind so you can fix them.

The next chapter is about learning, how you learn best, why teaching greatly helps, the different learner types, mind maps and how you should really read books.

The following chapter is about gaining experience, an important part of learning. For me the biggest takeaway was “Build to learn, not learn to build.”- but there is so much more in this chapter.

The last “real” chapter is concerned with managing focus, it contains a lot of tips and tricks on how to maintain your focus and how to focus better. It also explains exactly how bad loosing your focus is for your productivity. The book wraps up nicely with some pages that nicely recapitulate the book and help you where to get started now and what to do with all these new ideas.

What makes this book special?

This book is special to me as it is a very good summary of interesting facts and methodologies concerning thinking and learning for knowledge workers. It introduces you to all these concepts in a highly comprehensive way. I feel like this book gave me the basic knowledge of this topic. Moreover I believe that, if it wasn’t for this book, I would have had to read at least 5 other books in order to get this level of knowledge. Furthermore it is a really enjoyable read, as the author himself highlights the importance of humour. It really lightens it up quite a bit. And I just love things like: “you’ve read The Pragmatic Programmer.” With a footnote that says: “If you haven’t, run, don’t walk, to the bookstore and buy a copy. Seriously.”

Choice Bits

Choice Bits is about briefly introducing my favourite ideas from the book, but with this book it is extra difficult as it contains so many ideas, that I’ll really have to pull myself together.

Build to learn, not learn to build

Have you ever read a whole book about a new programming language without using it? I have. I read a whole book and then I wanted to start to program in that new language. I didn’t get very far, I couldn’t remember much. I was making little stupid mistakes all over the place. In order to really learn how to use a technology you have got to start using that technology – reading a book will help but not make you an expert.

L-Mode and R-Mode

Those are the 2 “operation modes” of your brain, L-Mode stands for linear mode and R-Mode stands for rich mode. Linear mode is used for logical thinking, speaking and is mostly used. R-Mode on the other hand isn’t rational and verbal, however it has more “processing power” and is under used. Unfortunately only one of those 2 modes can be active at a time.

A good example is when you try to remember something but you just can’t come up with it. But hours later, without thinking about it, you suddenly remember it but the thought is very hard to verbalise. That’s your R-Mode that has digged up the information you desired.

Another story, taken from the book, I always tell when explaining this concept to friends is the story of Elias Howe, who tried to invent a lockstitch sewing machine. One night he had a terrible nightmare, where cannibals were about to eat him. They poked him with their spears. However these spears looked a bit odd… they had holes close to the tip of the spear. Holes like with normal sewing needles, just at the other end. When thinking about the dream he realised, that this was exactly what he needed to do in order to make his lockstitch sewing machine work. And whom does he have to thank for this? His R-Mode, it had the answer all along but didn’t know how to verbalise it, so it tried to get some attention with a cruel nightmare.

Context is king

Context is always king, at least when you are more than a beginner. It is suggested that beginners in a particular skill need context-free rules, whereas on your way to expert you get to value the context more and more. This is also part of the Dreyfus model.

Attractive Works Better

The book relates to a study where people are presented two user interfaces. Both have the same functionality and workflow, but one looks good whereas the other one looks ugly. The subjects found the beautiful interface much easier to work with. Being happy (or visually pleased) actually allows you to bring more of your brain processing power online – which (of course) facilitates learning and working.

Personal Impact

This book has helped me a great deal in being more effective when working and knowing about the fact that I still “work” even when I’m not sitting in front of my keyboard. I really began to value a good walk when working on a hard problem – often times my R-Mode helped me with a great solution.

Also I feel a lot smarter after I read this book, because now I can explain many things and I have numerous new ways of solving problems. Not just stepping away from the keyboard. I now know that I should try to make of as many senses as possible when trying to solve a difficult problem. And of course it feels good to know why teaching helps so much when learning something. I have noticed this effect many times, but now I can explain it.

I now recognise how bad losing my focus is, I am more aware of cognitive biases, I have started to effectively using my virtual desktops and now I think twice about interrupting someone’s work or letting someone  interrupt my work as I know that I will use a lot of context and a lot of time.

Conclusion

I am highly recommending this book to everyone, who makes a living with thinking. If you want to be more effective at your job and learn new technologies faster and better, this book is for you. It also helps you understand why somebody might have a total different view of a situation, as somebody else might have different cognitive biases or a different generational affinity.

As sometimes computer terms are used to describe concepts I mostly recommend this book to Software Engineers/Programmers or people who are at least familiar with the concept of programming. However I believe that the book is written well enough and doesn’t rely to heavily on these terms that people completely unfamiliar with programming may also benefit from it.

Most of all this books helps you understand how thinking and learning really works, which is a good thing because there is a good chance that you can improve both your thinking and your learning. The book gives you all the knowledge, techniques and tips in order to do this, but you have to do this yourself. Personally, I am looking forward to re-reading the book using the techniques the book taught me, in order to leverage the power of the book and my brain even more. So what are you waiting for? Head on over to the pragmatic bookshelf and buy this book!

Feedback

As I am still new to writing reviews I would gladly enjoy some feedback on what was good about this review, what was bad, what you felt was missing and what you felt should have been left out. Comments on what would make you come back to this blog are also appreciated. You can do so via the comments section or check out the About section for other ways to contact me.