Looking for Some Best Practices for Structuring Elixir Projects?

Hi,

I am relatively new to Elixir and have been thoroughly enjoying its functional approach and concurrency model. However, as my projects grow,… I have started wondering about the best ways to structure them for maintainability and scalability.

Folder Structure :- How do you organize your contexts, schemas and modules; ??
Contexts: When does it make sense to split or consolidate contexts: ??
Testing :- Any tips for keeping test files and helpers well-organized: ??
Scaling: What should I consider early on to prepare for future growth?

I have seen mixed opinions on breaking contexts into smaller apps using Umbrella projects vs. keeping everything in a single app. What works for you: ?? Any real-world examples or resources are highly appreciated !!

I have also gone through this thread https://elixirforum.com/t/best-practice-for-directory-structure-of-a-larger-application-workday but still looking forward to hearing from this awesome community.

Thanks in advance for sharing your expertise !!

At one big project I worked on, it had the rule of following folder structure with module name 1:1, it makes files easy to find when you have 20k+ source files. I would say generally to try to stick to this rule, but it’s not a big deal if you don’t do it for all files.

Contexts can vary a lot from project to project, there is no rule of thumb generally. The only advice I can give is to avoid grouping modules by their type, for example having a /models folder that contains all the models from the project is not the best approach, instead have them grouped by context, for example accounts/user_schema.ex.

For unit tests, always follow the structure of the original codebase, as it makes it simple to find the tests. Helpers and other stuff it’s up to you, usually for a phoenix project it’s located in test/support, it’s a great default that worked for me so far.

This should never be a concern to begin with unless you are building some low-level stuff. If you are using libraries like phoenix, its design already solves that problem for you.

The only trap for new people is understanding how processes work if you are defining your own GenServers or Agents. The mailbox of process is processed sequentially, so you need to be aware that you can flood the mailbox if that process receives more messages than it can process.

For overall code organization, I would strongly recommend using Boundary and follow its philosophy of organizing your codebase into private and public APIs. It not only draws a clear separation of your functionality, but makes your system easier to reason and test once it grows.

3 Likes