ajur58
Hey everyone!
For the past weeks I’ve been using Claude Sonnet 3.5 with Aider as a “pair programming” buddy. It works fairly well, but some things have been constantly nagging me.
I’ve noticed that people are using a doc called conventions to fine-tune the code that gets written by the AI.
Inspired by this, I decided to bite the bullet and write one for myself.
To make things easier - and because I really liked PragDave’s Elixir course - I checked out the repo from the course, and told Claude to generate a conventions doc based on that. After a couple of iterations & adjustments I ended up with this version.
I’m curios if someone has their own conventions doc, what it contains, and also happy to hear your feedback.
Thanks!
# Elixir and Phoenix Best Practices
*Based on Dave Thomas' (PragDave) coding philosophy*
## Core Principles
- **Domain-Driven Design**: Organize code around business domains, not technical layers
- **Functional Core, Imperative Shell**: Pure domain logic with side effects at boundaries
- **Explicit Over Implicit**: Prefer clarity over magic
- **Composition Over Inheritance**: Build systems from small, focused components
- **Single Responsibility**: Each module and function should do one thing well
- **Easy to Change**: Design for maintainability and future change
- **Fail Fast**: Detect and handle errors as early as possible
- **YAGNI**: Don't build features until they're needed
## Project Structure
- **Context-Based Organization**: Use Phoenix contexts to define domain boundaries
lib/my_app/
accounts/ # User management domain
billing/ # Payment processing domain
catalog/ # Product catalog domain
- **API/Implementation Separation**: Public API modules delegate to implementation modules
# In MyApp.Accounts (API module)
defdelegate create_user(attrs), to: MyApp.Accounts.UserCreator
- **Boundary Enforcement**: Use tools like NimbleOptions to validate inputs at boundaries
## Coding Patterns
- **Pattern Matching**: Use pattern matching in function heads for control flow
- **Railway-Oriented Programming**: Chain operations with `with` for elegant error handling
with {:ok, user} <- find_user(id),
{:ok, updated} <- update_user(user, attrs) do
{:ok, updated}
end
- **Type Specifications**: Add typespecs to all public functions
@spec create_user(user_attrs()) :: {:ok, User.t()} | {:error, Changeset.t()}
- **Immutable Data Transformations**: Return new state rather than modifying existing state
- **Data Validation**: Validate data at boundaries using Ecto.Changeset even outside of database contexts
def validate_attrs(attrs) do
{%{}, %{name: :string, email: :string}}
|> Ecto.Changeset.cast(attrs, [:name, :email])
|> Ecto.Changeset.validate_required([:name, :email])
|> Ecto.Changeset.validate_format(:email, ~r/@/)
end
- **Result Tuples**: Return tagged tuples like `{:ok, result}` or `{:error, reason}` for operations that can fail
## Process Design
- **GenServer for State**: Use GenServers for stateful processes
- **Supervision Trees**: Design proper supervision hierarchies
- **Registry Pattern**: Use Registry for dynamic process lookup
- **Task.Supervisor**: Use for concurrent, potentially failing operations
- **Process Isolation**: Design processes to crash independently without affecting the whole system
- **Let It Crash**: Embrace the "let it crash" philosophy with proper supervision
## Phoenix Best Practices
- **LiveView-First**: Use LiveView as the primary UI technology
- **Function Components**: Use function components for reusable UI elements
- **PubSub for Real-time**: Use Phoenix PubSub for real-time features
- **Context Boundaries**: Respect context boundaries in controllers and LiveViews
- **Thin Controllers**: Keep controllers thin, delegating business logic to contexts
- **Security First**: Always consider security implications (CSRF, XSS, etc.)
## Testing Strategies
- **Test Public APIs**: Focus on testing public context APIs
- **Mox for Dependencies**: Use Mox for mocking external dependencies
- **Property-Based Testing**: Use StreamData for property-based tests
- **Test Factories**: Use ExMachina for test data creation
- **Test Readability**: Write tests that serve as documentation
- **Arrange-Act-Assert**: Structure tests with clear setup, action, and verification phases
## HTTP and API Integration
- **Req for HTTP Clients**: Use Req instead of HTTPoison or Tesla
- **Behaviours for API Clients**: Define behaviours for API clients to allow easy mocking
- **Error Handling**: Handle network failures and unexpected responses gracefully
- **Timeouts**: Always set appropriate timeouts for external calls
- **Circuit Breakers**: Use circuit breakers for critical external services
## Naming Conventions
- **Snake Case**: For variables and functions (`create_user`)
- **Verb-First Functions**: Start function names with verbs (`create_user`, not `user_create`)
- **Plural for Collections**: Use plural for collections (`users`, not `user`)
- **Consistent Terminology**: Use consistent terms throughout the codebase
- **Intention-Revealing Names**: Choose names that reveal intent, not implementation
## Documentation and Quality
- **Document Public Functions**: Add `@doc` to all public functions
- **Examples in Docs**: Include examples in documentation
- **Credo and Dialyzer**: Use for static analysis and type checking
- **Consistent Formatting**: Use `mix format` to maintain consistent code style
- **Continuous Refactoring**: Regularly improve code structure without changing behavior
- **Comments**: Write comments only when necessary. Describe why, not what it does.
## Performance Considerations
- **Avoid N+1 Queries**: Use Ecto's preloading and joins
- **Pagination**: Paginate large result sets
- **Background Jobs**: Use Oban for background processing
- **Measure First**: Profile before optimizing
- **Caching**: Apply strategic caching where appropriate
Trending in Discussions
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
New
Hey there,
It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Quite interesting article Google brought me. Didn’t find any mentions about it here.
What do you think in general? Would you use togethe...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
:warning: Security advisory: Decimal DoS vulnerability
A vulnerability has been published for decimal where very large exponents can cau...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security











Showing Posts 1 to 5- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
ajur58
Here is the file on GitHub for better readability: llm-conventions/conventions_ex.md at main · ajur58/llm-conventions · GitHub
Exadra37
I also love PragDave’s way of doing things because it aligns with what I was always doing in my previous programming language, where I was using the Resource Action Pattern, which I extended to the Domain Resource Action Pattern after learning Elixir with @PragDave. Recently, I wrote the Elixir Scribe tool to generate the code skeleton, which I also plan to improve to use message passing as PragDave does.
Now that I saw this conventions file I also need to add one to the Elixir Scribe tool, but first I will need to start using Claude.
The Elixir Scribe tool for the curious:
https://github.com/Elixir-Scribe/elixir-scribe
Pal
It was PragDaves approach that led me to Ash
arcanemachine
All that “clean code” terminology makes me want to vomit, but the tree diagram in this section does look incredibly appealing:
https://github.com/Elixir-Scribe/elixir-scribe#clean-software-architecture
dimitarvp
Same, and always has.
But I understand it’s a noble intention that was hijacked by a lot of wannabes who poisoned the well for the well-intentioned people so I try to suppress my instinctive reaction.
We actually had a friendly disagreement with Renato on this and we both agreed it’s ultimately a matter of taste. At least I maintain that having a plethora of super small files does not improve anything measurably compared to, say, a Phoenix context file (where you have
list,get_*,deleteetc. helpers).To me the important thing is the single responsibility principle and clean boundaries. How are these achieved is a technical detail that’s often not very important.