Where do you organize your helper functions?

Looking for recommendations on where to organize modules that just contain “helper” functions.

Some helper functions take a “socket”, some don’t. They are just utility functions used across different components and liveviews. They don’t qualify as a functional component (don’t take an assign) or a liveview component (handling events).

One option is “MyAppWeb.Helpers” and I throw them all in there. But is “helpers” the industry term for these kinds of functions?

Just curious how others organize their helper functions? Do any of you work for companies that require organizing these types of files in a specific way?

1 Like

That is by far not the best option. I used to do that too, then end up with a module with functions from all other the system that have zero common functionality, hard to navigate, even harder to refactor.

If you are using contexts to organize your business logic, then you can have small helper modules for specific contexts.

1 Like

I have one called AppWeb.Helpers with five or six generic functions in it that is imported in AppWeb.html_helpers. I have other modules e.g. AppWeb.Params that deal with a common theme.

1 Like

I spread mine out and keep them focused. A dumping ground is not a good idea as @D4no0 said.

If they are truly generic where they don’t know anything about the business logic and could be pulled into a common library (that I should make for myself one of these days stop copying them between apps), I put them in a top-level Utils module, outside of my app’s namespace. For example, a function that slugify’s some text.

If it has domain knowledge then I put it in the appropriate MyApp.<context>. If it cuts across contexts I make a new context and try and come up with an appropriate name for it. If it has domain knowledge, then it’s part of the business language and not merely a “helper” AFAIC.

The odd time I have a helper for socket-related stuff, I throw that in MyAppWeb.LiveViewHelpers. I haven’t done this in a while, though. I’ve never found any value in the various assign_* helpers I used to extract.

One thing you should not do is have anything in MyApp take a socket. I’m sure there are people who will tell you this is no big deal—I disagree. Just like in life, boundaries are important! :upside_down_face: This way of thinking is a slippery slope to compile-time dependency hell. Phoenix doesn’t set a great example by having some MyAppWeb stuff in MyApp.Application (though I understand why they do it). Saša Jurić’s aptly named Boundary library even suggests moving this out into its own MyAppApplication module (that name won’t look as lame assuming your app isn’t called MyApp :sweat_smile:)

1 Like

Thank you! This was all super helpful! @sodapopcan - I’ve been careful to keep socket-related functions in the MyAppWeb area. @D4no0 - I should have clarified that MyAppWeb.Helpers is just the folder. Then within that folder, I have modules with names that match functionality they support. Totally agree – putting a whole bunch of unrelated functions into one file (Helpers) would eventually drive me mad. :smiley:

It sounds like using the name “Helpers” is the best way to describe the functions that get used across multiple modules in AppWeb. Thank you all for helping me sanity check that.

1 Like

I have a Utils module for very generic helper functions. This module does not have any dependencies (e.g. alias) on any other module inside the app itself, but it can import functions from various libraries.

Everything else goes into a Helpers module inside its appropriate context. These helper modules often alias modules inside the same namespace.

1 Like

I like Extra as a top-level module for functions that are related to standard library modules (Extra.String, Extra.URI etc.)

I like Etc as a top-level module for other stuff (Etc.MultipartForm).

Also, since you’re asking about preferences, I really like Core instead of MyApp and Web instead of MyAppWeb.

2 Likes

On my biggest app, where I have more “helper” modules is in the Phoenix Web folder.

Here’s a little snapshot to illustrate:

We have a helpers in the _web/ root with generic helpers. But inside the live folder each view normally get’s it’s own *_helpers module too, since there is where specific components live or renderless functions.

In terms of _web, if I can abstract a functional component higher up (or I know I need to use it in other parts of the app) it goes into the components folder:

CleanShot 2024-03-22 at 11.15.10

But I also keep a helper module at the base of the lib folder, so lib/helper with lots of helper modules, some examples:

I would probably re-think some of this organisation and move things around, but this os some years of coding already and since the project grew to like 200k lines of code it’s bit daunting to go into that journey right now. Works fine. :smiley:

3 Likes

For stuff that builds around other peoples code I tend to go for MyApp.Support.ExternalLib.SomeModule, e.g. additional changeset validation functions would go into MyApp.Support.Ecto.Changeset. It’s a great pattern for all the code that’s used for “integrating with lib xyz”.

3 Likes

Ya, this is my exact rule of thumb for top a generic top-level module. My slugify example is actually a bit of an outlier. It’s generic enough but by and large it’s made up of functions that wouldn’t be out of place in the standard library.

I still wouldn’t say “helpers” is a particularly “good” name. It really has that signal of “stuff that I’m not really sure what it is.” I very much dislike how Rails codified this term. It really just means “view logic” over there but they are always using “view” to essentially mean “template” so I guess that’s where that comes from? Generally in Rails projects, though, it means, “One of several dumping grounds for methods that should probably be in non-table-backed models.” Not my favourite term, but I do use it in the web layer sometimes. Website need so much stuff to work that I do get tired of thinking of names :sweat_smile:

1 Like

Super helpful! I’m recognizing a lot of helpers that I’ve got in my own app. :grinning:. Interesting to see where you put them.

I hadn’t thought about identifying code that is specific to supporting external libraries. That’s a great idea.

Interesting. I didn’t come from Rails, so I didn’t know that’s where the term originated. When I started in Phoenix/Elixir 3 years ago, LiveViewHelpers was auto-generated into my app so I thought it was a Phoenix thing. It’s good to know that web app developers from across Rails and Phoenix would recognize Helpers (making it basically an industry term).