Keynote: Gang of None? Design Patterns in Elixir - José Valim | ElixirConf EU 2024

One of my favorite talks! Seriously! Here you have my graphic recording! :slight_smile:

graphic-recordings

11 Likes

What a joyful thread with @russolsen, plenty of feedback, and Carlo’s sketch!

I missed this one, added to the list!

I think “don’t really work” is a bit too harsh. I ended-up with 10 out of 23 which I think is higher than most people would estimate (and OO languages would certainly end-up with a bit more).

In any case, while several patterns do not apply to Elixir, there are important lessons in learning why. Rather we like it or not, most Elixir users came from mutable/OO languages and this discussion can help highlight differences, which I tried to focus on.

For example, even by “modern standards”, the flyweight pattern is now a language construct in Java, thanks to records, but that’s still an explicit choice there and not many may realize it happens automatically and for free in Elixir. There is a question if flyweight is still a design pattern in Java now that records were introduced, and one could argue that yes, because the main problem of allocating too many duplicated objects certainly did not disappear with the introduction of records.

And if you take more established patterns, such as observer and singleton, the fact in Elixir they are about processes, means you open up to interesting discussions about distributed observers and global singletons, which I could not explore in length during the talk!

That’s curious because I would not include those in the discussions about design patterns, but rather anti-patterns and refactorings (where the 10+ arguments and its possible refactorings are already listed in Elixir docs). I haven’t given much thought yet about where the cross-over from refactoring to design pattern happen though.

The truth is that the average Phoenix web app needs only tasks and supervisors, everything else is used and put in place by Phoenix and the rest of the ecosystem.

7 Likes

That’s the trade-off here. If it reappears a lot, you might as well give it a name. The downside is that there is no guarantee naming things will help. In the talk I mentioned both Facade and Mediator, which would fit under “naming things that often reappear” and not adding much beyond that, and to me they are opposing examples: most know what Facade means and therefore it is a useful term but I don’t think if people would immediately know what a mediator means (which I tried to poke fun at the end). So is there value in naming things, even if most of the benefit is in the name itself?

Inheriting a class is only a blueprint after you introduce all of the complexity to manage inheritance, such as final, public vs protected/private, etc. The fact you have specific keywords to manage it means they work at a different encapsulation level and my personal take (from a language design perspective) is that the complexity they introduce is not worth it (and there are still other problems to solve). Outside of that, the design patterns book really lay down the arguments here, so I’d recommend double checking there. I believe it is in the first chapters.

Embedding in Go doesn’t break encapsulation boundaries (nor introduce new ones) and interfaces keep the definition of data and behaviour separate, so it avoids the downsides typically associated to inheritance (afaik).

Many people would say interface-style polymorphism is an OO feature, even though the proposal to introduce them to Haskell was written 35 years ago! I wonder if we will see an attempt to revise what OO means as languages continue to break its properties apart.

2 Likes

This sentence left an impression on me when I read it last night but I decided to have a night of sleep before commenting on it. Upon reflection, I think I agree with the sentiment.

While the book explains what “design patterns” are, the phrase suggests, at least to my non-native ears, a collection of building blocks for designing software, which sounds prescriptive, while in practice they are possible solutions to recurring problems. This misconception could also be why many people focus on translating solutions between languages rather than discussing the underlying problems. They put the solution before the problem and the name is not necessarily helping circumvent that.

It would certainly be a shame to see everything that you can express in Elixir (or other languages) to be reduced to a collection of pre-approved templates.

5 Likes

I would say that whenever a talk about an anti-patters takes place, it would be nice to have a “pro-pattern” to counter it. The usage of anti-patterns usually does not appear out of nowhere, but from trying to deal with a complex problem. And to be honest remedy outlined in the anti-pattern section in the docs does not always apply well - sometimes you don’t really have any natural groupings of params, and creating fake groupings is probably worse than just a long parameter list.

I would also add here that GoF builder pattern is kind of addressing very similar problem: instead of initializing an object with one huge constructor call, one does it step by step. I have to admit that I sometimes do that in Elixir too for some very complex business operations, from the lack of alternative ideas. So I would say that design patters are not necessarily very far from the problems like this.

Yes, I very much agree with this part. It is important to show and remind people that it is not the best way to try to just transfer concepts from other languages (especially OOP->FP) to Elixir, because it leads to poor results. And this was great talk on that. Perhaps because I feel I am kind of past that phase (of transferring concepts), the part about particular patterns was less exciting for me, but of course mileages may vary here.

2 Likes

I think there is an important distinction between the basic ideas of design patterns and the 20 something patterns described in the GoF book. The GoF patterns are now decades old and yes, they are mired in the problems people were dealing with in the 90s. In fact I would argue that some of them were obsolete almost immediately.

On the other hand, the generic idea of a distinct just beyond what the language gives you for free solution to a common problem is as valid today as it was back then. But, given the insanity that we both seem to have experienced (Interviewer: What is your favorite pattern? Me: Plaid.), perhaps it’s time to pick another name.

2 Likes

José,

First, Hello! It’s been a long time.

It would certainly be a shame to see everything that you can express in Elixir (or other languages) to be reduced to a collection of pre-approved templates.

Yes! I think there is always this natural desire in programming (and probably everything else in life) to reduce what is a twisty, difficult problem to a set of rules. The moment people start thinking of some set of patterns as the only pre-approved templates for all code is where things go bad.

We need more tools, not more rules.

4 Likes

I think I am lucky (or maybe just too stubborn/independently minded) because my experience was the complete opposite of people like Dimi’s:

When I read your book I didn’t take it as a prescriptive way to go about things, more a, here’s a set of tools that you can use if you feel it’s a good fit for your needs.

To be honest, I am not sure why people would shoehorn them into projects if they weren’t really a good fit, did the original GOF book (or others that followed) push people into doing that? I know Design Patterns in Ruby definitely didn’t, but no idea what the other books were preaching…

Somewhere in the book I talk about an adapter that uses a factory method to get a proxy to the builder which creates a command and how insane that would be in all but the most extreme circumstances. But I didn’t make that crazy chain of patterns up, it was the way you added a new feature to an actual system that had I worked on.

Glad you missed the insanity but I think you can understand how people who suffered thru it react now.

3 Likes

This is just the way it works for most people, unfortunately. I think programming is ultimately hard and when mere mortal humans are collaborating on it—especially in very large companies—many find it easier to dogmatically follow rules as opposed to having to justify why a certain situations might need independent consideration. Programming “principles” (which I think is a terrible name) are even worse as many just read the name and assume they know what it means without reading anything about it.

4 Likes

This ties in nicely with things I’ve learned from Dave Thomas and Andy Hunt about the Dreyfus model of skill acquisition. Someone who is new to a skill (such as how to organize a codebase) relies on black-n-white “rules”. As they move toward more of an intermediate knowledge, they acquire experience learning when to “break the rules”. And then at mastery of the skill, they’re almost forget “the rules” exist since it’s all intuitive to them at that point.

3 Likes

I think I remember that bit :lol:

Tbh (as I recall) I’d only ever heard positive things about the book and how pivotal it was, and that, combined with what I learnt from your your book (which essentially just highlighted they were tools available for us should we need them) left me with an overall positive view. But that was based on what I learnt 10 years ago, and so I guess as with most things if you give something long enough it will be subject to abuse or becoming outdated.

Admittedly, when I first posted in this thread I thought José’s talk was going to be more about patterns in OOP compared to patterns in FP (as per the graphic I included, i.e. that everything’s a function?). Unfortunately I didn’t get to finish José’s talk as I got side-tracked.

Yeah I think this is true and something I have been shielded from. I can totally understand how much of a nightmare it might be working for some of these companies, though can also see from their viewpoint that they would want to try and ensure things can be done as sustainably as possible…

1 Like

“Learn all the rules well so you know exactly when to break them” is something I heard from a good amount of professionals in different areas.

3 Likes

Yeah but they weren’t as much sustainable as they were kind of a standard so people can just read your code, tweak it a little bit and move on with life. Which I usually endorse and it made sense in bigger teams.

Case in point: in Java the Visitor pattern is basically a plain old recursive traversal of a data structure + passing a callback to be called on each node (more or less, I am simplifying).

In every language supporting closures, Elixir included, this is trivial; in Java back then you had to make an interface (== contract) for the tree node, then implement it in a class, then introduce sub-classes (thus: inheritance) for other nodes you might need, which “naturally” led to dependency inversion and dependency injection because just doing new MyClass() is too easy right? We don’t want easy (I am being salty and sarcastic here – DR / DI had valid usages but they were definitely way overused).

And eventually you ended up with something like 13 Java files in 3 separate packages just to do something that you can in Elixir with Enum.reduce + a touch of recursion here and there (maximum 30 lines of code in total).

It was crazy, man. You could ideate your solution in minutes and then you needed hours, often days, just to put it in a form the computer will like. Nowadays I can sketch almost any idea in Elixir or Golang in literal minutes.

1 Like

Isn’t all of this more fun than solving business issues and improving the product? :grin:

1 Like

This both (kind of) ties in with my experience but also contradicts something you often hear people say, that your first projects are usually your worst then you get better over time.

I think my first project was actually one of my best - because everything from all the books I read was fresh in my mind. All the best practices, all the correct disciplines, all the little hacks. I noticed that sometime later when I had to work on my second app (which admittedly needed to be up pretty sharpish and wasn’t one that was very inspiring) I was incredibly lazy, taking all the shortcuts and just being very undisciplined. I caught myself doing similar on my third app so after bit of refactoring decided to nip that in the bud because by that time I had learnt that I’m the one who’s going to have to maintain it! :lol:

I don’t consider myself at that point yet, but I wonder, is it the same as it is other things, such as where when you’ve been doing something a long time (e.g driving) you often pick up bad habits (which are often derived from shortcuts) but maybe in programming some of them could be advantageous? Or not? Maybe that’s actually what lead managers to realise what I discovered, that you need to be disciplined and follow certain paths or rules or you may just end up hurting yourself in the long run?

I love Jose humbly highlighting how the [in]famous Scott Wlaschin slide (about how everything just translates to functions in FP) was taken out of context and calmly proceeds to show that it’s still basically true (for the most part) by the end of the presentation lol (sorry not sorry :laughing:).

2 Likes

This is interesting. I have tended to observe the exact opposite (assuming we are on the same page in terms of their definition). “Principles” being generally more abstract are difficult to use without comprehending their value first, whereas a “pattern” can be memorized as a particular set of steps and (mis)applied relatively easily.

My favorite example, I find that “YAGNI” is actually resisted by a lot of developers who tend to think they have earned their senior level pay when they have managed to abstract a problem into a shape that fits their preferred pattern, meanwhile the code is exponentially harder to understand when it turns out the parameters have changed a few weeks later.

1 Like

Ya, by “principles” I mean things like YAGNI, DRY, Rule of 3, Law of Demeter, etc. It’s funny you mention YAGNI because it is the only one I can’t really imagine a good time that it shouldn’t be followed. Not that I’ve sat and thought through them all (I don’t even know them all) but it’s one I get dogmatic about. YAGNI not only applies to unnecessary abstractions but also to writing code “just in case it’s needed later.”

I haven’t had any convos around design patterns in a long time. My last run-in with someone around them was a few years ago at this point and I can’t remember what it was, but they were naively insisting that a problem fit a certain pattern. While they weren’t totally wrong, they also weren’t right. Of course me just saying this without any details isn’t exactly a helpful story (who knows, maybe I was in the wrong but I don’t think so :sweat_smile:), but I’m sorry it’s been a very long time.

1 Like

I’ve been using (in love with) Elixir for almost 7 years now. Before that every minute of OO-programming has been a waste of time. Watching this presentation is like remembering the stone age. Having to deal with ridiculous problems. It’s hard to even imagine nowadays. Jose is an absolute genius. Tnx for bringing Erlang to “the masses”.

7 Likes