aprs.me/CLAUDE.md
Graham McIntire 571814c080
Remove Redis dependency from RateLimiterWrapper
- Always use ETS-based RateLimiter instead of checking for REDIS_URL
- Remove RedisRateLimiter calls that were causing crashes
- Simplify wrapper to only delegate to Aprsme.RateLimiter
2025-10-25 09:44:44 -05:00

3.3 KiB

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

Git Commit Guidelines

  • Never add "Co-Authored-By: Claude" or similar AI co-authorship tags to commits
  • Commits should appear as authored solely by the user (Graham)
  • Keep commit messages clear and concise, describing what changed and why