42 comments

[ 2.4 ms ] story [ 77.8 ms ] thread
Thanks, I was looking for something just like this today!
I was glancing around and landed on the page for the flyweight pattern.[1]

It looks like `addBook` is using the spread operator, which always creates a shallow copy of the book instance properties, thus nullifying any benefits of the flyweight pattern. It also attaches extra arbitrary properties, but still assigns the result to a `book` variable. I don't think this is a great example.

[1]: https://www.patterns.dev/vanilla/flyweight-pattern/

Edit: I forgot to give you kudos for all the effort it must have taken to create all this content, and I appreciate that you're making it available for free.

Love this! Just wanted to note that I think there’s a mistake on the flyweight pattern page’s example. You’re using getting a boolean with Set.has but treating it like a Book (as if you had used Set.get). I also don’t really understand how this saves memory if you’re spreading the result into a new object, but maybe someone here can enlighten me!
The ones that actually match POSD (deep modules, small interfaces, lower complexity) and work great with plain functions are:

Module Pattern

Factory Pattern (factory functions)

Mediator / Middleware Pattern (as function pipelines)

Hooks Pattern (custom hooks, generalized)

Container / Presentational Pattern (implemented with function components + hooks)

Everything else is either neutral, UI-only, or fights POSD (Singleton, Mixin, etc.).

Patterns from that page you should treat skeptically for POSD

From Patterns.dev, for your POSD-style codebase I’d avoid or downplay:

Singleton Pattern → encourages global state and tight coupling. Patterns

Mixin Pattern → tends to increase interface surface and make dependencies opaque. Patterns

Observer Pattern → powerful, but event-based wiring can obscure data flow and increase “system complexity” (classic POSD warning). Patterns

(comment deleted)
fantastic resource! kindly add svelte design patterns too and sveltekit if you can
I wish people would stop promoting the singleton pattern: in almost every case I’ve seen, singletons are unnecessary tech debt and solve a problem that’s better solved with some form of dependency injection (and I don’t mean the XML/YAML monstrosities various frameworks force on you but rather constructor arguments or factory functions)
Yes, I have to admit my interest was piqued by the banner, and I then scrolled down, saw the first example was singletons, and closed the tab.
Great site!

I tend to advocate for people to study design patterns. Not for the purpose that you will necessarily ever use most (or even any) of these exact patterns in your software, but just that you've strengthened your mental muscle for software design in general. I encounter lots of engineers who simply aren't able to think "outside the box" when building something new.

In my senior year of college two decades ago, I needed one or two credit hours to finish up, and I signed up for a once per week software patents (as in, intellectual property, I thought) course. It turned out to be a patterns course taught by none other than Ralph Johnson and the text was his famous Gang of Four Design Patterns book. Happy accident, it turned out to be among the most professionally useful courses I ever took.

https://en.wikipedia.org/wiki/Ralph_Johnson_(computer_scient...

(comment deleted)
When overused these kind of "patterns" always lead to slow and hard to grasp code that is a nightmare to maintain.
A singleton for me is just making sure you define a single instance of something at the entry point of your app and passing it down via constructor arguments, which is normally what I do.

I understand you can override modules in tests, and if you have a version of your app that runs in a different environment where you might replace a HTTP service with a different variant (like if an RPC service that normally performs HTTP requests, but in one case maybe you want to talk to specific process, worker or window using message passing).

I really really feel using constructor args is just a lot simpler in most cases. But I think there's a reason why people don't always do this:

1. Sometimes someone is using a framework that imposes a high level of inversion of control where you don't get to control how your dependencies are initialised.

2. (this was me when I was much younger) Someone dogmatically avoided using classes and thought they could get away with just data and functions, and then they realise there is some value in some shared instance of something and then they just undermine any functional purity. Don't get me wrong, functional purity is great, but there are parts of your apps that just aren't going to be functionally pure. And you don't need to go full OOP when you use classes.

----------------------------------------

Here are some examples of what I mean.

Here's one example, where I have an app defined in web components and I define the components for each part of the app before passing them into the skeleton. It's a more simple app and I avoid some shared state by different landmark components communicating by events.

https://github.com/AKST/analysis-notebook/blob/b3f082fc63c9b...

----------------------------------------

Here's another example, this app is built of multiple command line instructions that sometimes have their own version of services (singletons) with specific configuration, sometimes they are initialised in child processes.

https://github.com/AKST/Aus-Land-Data-ETL/blob/672280a8ded69...

Because this app is doing a lot of scrapeing and I wanted to also child worker processes to do their own HTTP I was going to make a variant of my HTTP service which talked to a daemon process that tracked state for open connections to specific domains to avoid opening too many simultaneously and getting blocked by that host.

Updating the code that uses HTTP will be effortless, because it will continue to conform to the same API. I know this because I've already done this multiple times with HTTP scrapping in the app.

https://github.com/AKST/Aus-Land-Data-ETL/blob/672280a8ded69...

This resource aged surprisingly well. Still one of the clearest ways to understand common frontend patterns.
This is a nicely laid out collection of tutorials, but I'm sad that collections like this have drifted away from the very deliberate structure that A Pattern Language introduced. While patterns weren't invented by Alexander and co, they did inspire a lot of what we see in tech these days, inherited via design patterns etc.

In A Pattern Language, each pattern is hyperlinked to other patterns in a kind of hierarchy, where larger patterns (like "house cluster") are broken down into smaller constituent parts ("main entrance") all the way to quite granular details ("low doorway").

This is because you can't just take a pattern on its own; it forms part of a larger whole.

Tech pattern books often focus on small recurring structures (e.g. "command pattern" from this site), but not how they help create some larger pattern, or in the other direction, what are the smaller patterns that help create them.

This sounds like a lot of hard work of course, which is why people don't do it.* I would love to see this accomplished though. If only I had an extra 36 hours in each day.

One thing that A Pattern Language is also great at is motivating each pattern with a specific problem or symptom. This site seems to do a decent job at that, though some problems seem... kind of weakly motivated. For example, the above mentioned "command pattern" is motivated by "what if we need to rename a method?" which... is pretty weak tbh.

*EDIT: also because fitting patterns into a whole, maybe unavoidably, will promote a perspective, or a way of building a whole system. A pattern book for web applications written from an HTMX point of view would be a very different book to one written from a React slant. Maybe one pattern language can accommodate both sets of technologies, or maybe not.

If you're trying to connect Alexander to software patterns like GOF, it's important to include Gabriel's "Patterns of Software" [1] as the first genuine attempt to apply Alexandrian patterns to software. It also introduces the first and probably the best takedown of OO inheritance as not reuse but instead a form of _compression_ that is in many ways worse than just copy-and-paste.

1 - https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf

I find that the more senior you become, the less you rely on software design patterns.

Juniors often think that learning design patterns is some kind of career hack which will allow them to skip ahead a decade of experience... There is some utility in many design patterns but the problem is that juniors often miss the nuance and the motivation behind the patterns and they tend to misuse them; often creating more complexity than they would have created had they not used design patterns.

There are situations where design patterns may be useful but they're much more rare than most people think. A lot of juniors seem to think that every problem requires them to apply a design pattern from their toolbox. They try to frame every problem within the constraints of design patterns that they know. This can create problems.

In fact, there are many coding patterns which are far simpler than 'design patterns' and much more useful, but nobody talks about them because they're too trivial to discuss. For example, I've seen people write code which relies heavily on design patterns but then that same code uses an O(n^2) nested loop to find items that are common between two arrays. There is a simple 'pattern' you can use to store the items of the first array in a Set or HashMap and then finding common items is O(n) because Set and HashMap lookups are O(1)... Very useful pattern but I don't believe it has a name. I use it literally ALL the time. The idea of storing intermediate state in some kind of HashMap is a game-changer IMO but there's no name for that pattern of coding. In general, knowing what data structure is most appropriate for various scenarios is a game changer... But still, don't abuse. Basic data structures like arrays are fine most of the time.

Anyway, it's good to absorb the wisdom behind some design patterns but you should not try to fit every problem to them and dont say stuff like "For this problem, I applied Design Pattern X" - If you do, senior engineers will know you're a junior. If you use a design pattern correctly, it will probably not look exactly like in the textbook. It's kind of hard to give it a name. It may be a mix of patterns. It's the principle that counts. Reality is too complex for rigid design patterns.

On the other hand, some design patterns are too common and obvious to deserve a name. For example, the factory pattern is super common... I use it all the time but it's so basic and obvious that I will sound like a total noob if I go around calling my function "socket factory pattern"... I will call it "Utility function to obtain a socket" or something. I will never refer to it as a factory pattern, it's a bit cringe.

I agree with the first half as it echoes my experience, but the second half hasn't been my exp.

To talk about design patterns as not as useful, only to then mention big O notation seems strange to me unless you are in a context where performance is critical?

Worrying about O(n) IME is far less important than choosing the right architectural pattern. O(n) issues are usually observable via metrics or QA and are typically straightforward to fix. By contrast, recognising that your pattern choice is wrong is harder (since it manifests during dev rather than in prod) and takes more effort to rectify.

I also disagree that patterns don't deserve a name. I have found it very useful when discussing with both seniors and juniors to have a common name for a pattern being described. Seniors known instantly and it can be helpful to have a resource to point juniors to if they aren't familiar. I have also found it useful when English isn't the first language.

I do agree that seniors don't typically try to fit standard patterns to their problem in a way that a junior might, that's a fair point.

Looks great. Wish it had a table of contents. Am I missing it?
Patterns are needed for languages for which the actual underlying concept is unavailable.

For example, prototype pattern is for languages that don't have a way to express interfaces/traits etc as something that can be attached to other language entities.

Most teams don’t fail because they picked the wrong framework; they fail because they never ship enough iterations for it to matter.

Anything that reliably shortens that loop is “good tech,” even if it’s ugly, uncool, or built on last decade’s stack.

This looks like an excellent resource for interview preparation. Bookmarked.
I hate that singleton is first.

Singletons are just globals with extra steps, and have all the same problems globals have, just people (especially juniors) think they somehow are better.

In reality they’re worse because they conflate global access with enforced single instance. You almost never need to enforce a single instance. If you only want one of something, then create only one. Don’t enforce it unless it’s critical that there’s only one. Loggers are often given as an example of a “good” singleton and yet we often need multiple loggers for things like audit logs, or separating log types.

Instead of singletons, use dependency injection, use context objects, use service locators, or… just use globals and accept that they come with downsides instead of hiding behind the false sense of code quality that is a singleton.

Looks more or less like GoF? Maybe compressing files and optimizing 3rd party js files are not patterns.
Design patterns can be really helpful. In my previous job I worked on enterprise .NET applications. It made sense to use common patterns, because most applications were big and the patterns made it easier to understand unfamiliar code, within an application but also across different teams and applications. New projects looked familiar, because the same style and the same patterns were used.

Now I'm working on an old (+10 years) JS application. Similar patterns were implemented, but in this case it's not helpful at all. The code looks very corporate and Java EE style, with a ton of getters and setters (`getName() {}`, not `get name() {}`, factories, facades, adapters, etc, etc. It's usually completely unclear what the benefit of the pattern is, and code is more complicated, for instance because creating new instances of business objects is split into `Object.build` which calls `new Object`, with no guidelines at all what part of the initialization should be in `build` and what should be in the constructor.

The gist of my comment is that patterns can be useful, but usually they're overused and if you implement one without understanding why and without benefiting from faster understanding the code because the pattern is applied consistently over multiple instances, the result is worse than just implementing what you need in a readable way (YAGNI).

design patterns are a language, it's just that the programming language they're being implemented in doesn't support them natively.

e.g. observer pattern in java is what, [array of functions].forEach() in js? not worth calling that by name. another example, singletons - in Python, it's just a module (caveats apply obviously, but if we apply them, some also apply in java).

this is why designing a minimal language to make it 'simple' is misguided: you'll end up having to reinvent the design pattern language anyway. there are good reasons to design a simple language, but simple for the sake of simple is missing the point.

I would put it slightly differently: Patterns (including anti-patterns) happen whether you call them such or not. Any developer will sooner or later come up with patterns like Adapter or Builder or Composite or Iterator. In that sense, patterns are not invented, but discovered. The benefit of design patterns is to be able to communicate these discovered patterns, and to define agreed names for them, so that you don't have to describe the pattern each time you talk to another developer, but can refer to it by a well-understood name. (Or when not yet well-understood, can refer to the corresponding pattern description.) It extends the language we use to talk about software design.

The point of design patterns is less about the individual patterns, than about having "design pattern" as a general concept of coding patterns relevant to software design, that you name and describe because they keep reoccurring.

Yeah, I've seen (okay, and been responsible for) a lot of "the road to hell is paved with good intentions" due to jumping to some pattern or other because, well, it feels like the right thing to do. It makes the code cleaner at delivery time, and is usually very intuitive when the design is fresh in your mind. But IME it doesn't take long before that freshness goes away, and the next time you look at it (let alone anyone else) you find it hard to follow the logic anymore. Usually "constructing" the pattern and "executing" the pattern are in different places in the code, and there's not a straightforward way to mentally step through one without continually cross-referencing the other. At some point you wish you'd just written one long switch block that you could step through.

And that's all before new requirements that break the pattern, or other engineers that don't have time to grok the design and hack in something that looks like a switch block for their thing, which eventually takes over most of the code anyway.

I see a lot of comments about how patterns are useless from people writing toy apps or at least ones that never had to deal with really enterprise scale stuff. So for me they are like people building a shed screaming at people building sky scrapers that no one ever needs to pour so much concrete to form a foundation.

Parent comment is not like that.

People were shifted from Java to Javascript and kept the Java patterns and maybe the organization had standards requiring their use.
a lot of these are misleading, or ungrounded

see the prototype, and observable pattern

they explain the basic concept, but in the only way you would never use it in real life