- Remove phoenix_pubsub_redis dependency from mix.exs - Update PubSub configuration to use Phoenix's native distributed capabilities - Simplify clustering logic - no longer requires Redis URL for PubSub - Phoenix PubSub automatically distributes messages across connected Erlang nodes - Benefits: lower latency, automatic failure handling, simpler deployment - Redis still available for caching and job processing when needed - Maintains backward compatibility for both clustered and non-clustered modes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
64 lines
No EOL
3.1 KiB
Markdown
64 lines
No EOL
3.1 KiB
Markdown
This is a web application written using the Phoenix web framework.
|
|
|
|
## Elixir guidelines
|
|
|
|
- Elixir lists **do not support index based access via the access syntax**
|
|
|
|
**Never do this (invalid)**:
|
|
|
|
i = 0
|
|
mylist = ["blue", "green"]
|
|
mylist[i]
|
|
|
|
Instead, **always** use `Enum.at`, pattern matching, or `List` for index based list access, ie:
|
|
|
|
i = 0
|
|
mylist = ["blue", "green"]
|
|
Enum.at(mylist, i)
|
|
|
|
- Elixir supports `if/else` but **does NOT support `if/else if` or `if/elsif`. **Never use `else if` or `elseif` in Elixir**, **always** use `cond` or `case` for multiple conditionals.
|
|
|
|
**Never do this (invalid)**:
|
|
|
|
<%= if condition do %>
|
|
...
|
|
<% else if other_condition %>
|
|
...
|
|
<% end %>
|
|
|
|
Instead **always** do this:
|
|
|
|
<%= cond do %>
|
|
<% condition -> %>
|
|
...
|
|
<% condition2 -> %>
|
|
...
|
|
<% true -> %>
|
|
...
|
|
<% end %>
|
|
|
|
- Elixir variables are immutable, but can be rebound, so for block expressions like `if`, `case`, `cond`, etc
|
|
you *must* bind the result of the expression to a variable if you want to use it and you CANNOT rebind the result inside the expression, ie:
|
|
|
|
# INVALID: we are rebinding inside the `if` and the result never gets assigned
|
|
if connected?(socket) do
|
|
socket = assign(socket, :val, val)
|
|
end
|
|
|
|
# VALID: we rebind the result of the `if` to a new variable
|
|
socket =
|
|
if connected?(socket) do
|
|
assign(socket, :val, val)
|
|
end
|
|
|
|
- Use `with` for chaining operations that return `{:ok, _}` or `{:error, _}`
|
|
- **Never** nest multiple modules in the same file as it can cause cyclic dependencies and compilation errors
|
|
- **Never** use map access syntax (`changeset[:field]`) on structs as they do not implement the Access behaviour by default. For regular structs, you **must** access the fields directly, such as `my_struct.field` or use higher level APIs that are available on the struct if they exist, `Ecto.Changeset.get_field/2` for changesets
|
|
- Elixir's standard library has everything necessary for date and time manipulation. Familiarize yourself with the common `Time`, `Date`, `DateTime`, and `Calendar` interfaces by accessing their documentation as necessary. **Never** install additional dependencies unless asked or for date/time parsing (which you can use the `date_time_parser` package)
|
|
- Don't use `String.to_atom/1` on user input (memory leak risk)
|
|
- Predicate function names should not start with `is_` and should end in a question mark. Names like `is_thing` should be reserved for guards
|
|
- Elixir's builtin OTP primitives like `DynamicSupervisor` and `Registry`, require names in the child spec, such as `{DynamicSupervisor, name: MyApp.MyDynamicSup}`, then you can use `DynamicSupervisor.start_child(MyApp.MyDynamicSup, child_spec)`
|
|
- Use `Task.async_stream(collection, callback, options)` for concurrent enumeration with back-pressure. The majority of times you will want to pass `timeout: :infinity` as option
|
|
- **Always run mix format after changing any Elixir files**
|
|
|
|
[... rest of the file remains unchanged ...] |