Automated Detection Plan
View SourceProblem Statement
We need to create automated detection for common mistakes in Elixir Phoenix projects. Each check should target a single potential issue and be executable as part of the standard Credo workflow.
Current State
We have documented 30+ common mistakes in docs/typical-elixir-phoenix-mistakes.md. The Oeditus codebase uses:
- Ecto for database operations
- Phoenix LiveView for real-time UIs
- PubSub for broadcasting
- Oban for background jobs
- Docker for isolated execution
- Standard Elixir patterns (contexts, changesets, etc.) Some patterns in the current codebase:
- Proper preloading in
get_audit_with_findings!/1(lib/oeditus/audits.ex:36-40) - PubSub broadcasting in
update_audit_progress/2(lib/oeditus/audits.ex:78-82) - PubSub subscription in LiveView mount (lib/oeditus_web/live/audit_live/show.ex:11-13)
- Filtering in Elixir after Repo.all in
list_audit_findings/1(potential optimization target)
Implementation Status
We have implemented 20 custom Credo checks as an Elixir library (oeditus_credo) that integrates with the standard Credo workflow.
Check Organization
Implemented as custom Credo checks in lib/credo/check/warning/:
Error Handling (3 checks)
missing_error_handling.ex- Detects{:ok, x} =without error handlingsilent_error_case.ex- Detects case statements missing error branchesswallowing_exception.ex- Detects try/rescue without logging or re-raising
Database & Performance (3 checks)
inefficient_filter.ex- Detects Repo.all followed by Enum filteringn_plus_one_query.ex- Detects potential N+1 queries (Enum.map with Repo calls)missing_preload.ex- Detects Ecto queries without proper preloading
LiveView & Concurrency (5 checks)
unmanaged_task.ex- Detects unsupervised Task.async callssync_over_async.ex- Detects blocking operations in LiveView/GenServermissing_handle_async.ex- Detects blocking in handle_event without async patternmissing_throttle.ex- Detects form inputs without phx-debounce/throttleinline_javascript.ex- Detects inline JS handlers instead of phx-* bindings
Code Quality (4 checks)
hardcoded_value.ex- Detects hardcoded URLs, IPs, and secretsdirect_struct_update.ex- Detects direct struct updates instead of changesetscallback_hell.ex- Detects deeply nested case statementsblocking_in_plug.ex- Detects blocking operations in Plug functions
Telemetry & Observability (5 checks)
missing_telemetry_in_oban_worker.ex- Detects Oban workers without telemetrymissing_telemetry_in_live_view_mount.ex- Detects LiveView mount/3 without telemetrytelemetry_in_recursive_function.ex- Detects telemetry in recursive functions (anti-pattern)missing_telemetry_in_auth_plug.ex- Detects auth plugs without telemetrymissing_telemetry_for_external_http.ex- Detects HTTP calls without telemetry wrapper
Implementation Approach
Implemented as Credo Checks (20 checks)
All checks use Elixir's AST (Abstract Syntax Tree) analysis via Credo's framework:
- N+1 queries - Analyzes
Enum.maporEnum.eachcontainingRepo.getor association access - Missing preload - Analyzes query pipelines for missing
preloadbeforeRepo.all/Repo.one - Sync-over-async - Detects blocking operations in
handle_event,handle_call,handle_info - Unmanaged tasks - Finds
Task.asyncnot under supervision - Missing error handling - Detects
{:ok, x} =pattern without error handling - Hardcoded values - Pattern matches string literals containing URLs, IPs, secrets
Direct struct updates - Detects
%Struct{x | field: value}patterns on schemas- Inefficient filter - Detects
Repo.all()followed byEnum.filter/reject/find - Missing throttle/debounce - Parses HEEx for
phx-change/phx-keyupwithout throttling - Inline JavaScript - Detects
onclick=,onchange=, etc. in templates - Silent error case - Detects case statements without error/failure branches
- Swallowing exceptions - Detects try/rescue without logging or re-raising
- Missing handle_async - Detects blocking in
handle_eventwithout async wrapper - Callback hell - Detects deeply nested case statements (configurable depth)
- Blocking in Plug - Detects expensive operations in
call/2andinit/116-20. Telemetry checks - Comprehensive telemetry instrumentation detection
Not Implemented Yet
- Missing indexes (requires schema + migration analysis)
- Cartesian product joins (requires query semantic analysis)
- Channel memory leaks (requires subscription tracking)
- GenServer state bloat (requires runtime analysis)
- Excessive re-renders (requires LiveView semantics understanding)
- Memory leaks in streams (requires stream usage pattern analysis)
- God contexts (requires architectural analysis)
- Timeout misconfigurations (requires config + actual timing data)
- Test database issues (requires test execution analysis)
Usage
Install the library and add checks to .credo.exs:
defp deps do
[
{:oeditus_credo, "~> 0.1.0", only: [:dev, :test], runtime: false}
]
endConfigure in .credo.exs:
%{
configs: [
%{
name: "default",
checks: %{
enabled: [
{OeditusCredo.Check.Warning.MissingErrorHandling, []},
{OeditusCredo.Check.Warning.NPlusOneQuery, []},
# ... all 20 checks ...
]
}
}
]
}Run with:
mix credo
Output Format
Standard Credo output with file path, line number, and issue description:
┃ [W] → Potential optimization: Repo.all followed by filtering in Elixir
┃ lib/oeditus/audits.ex:115:5 (OeditusCredo.Check.Warning.InefficientFilter)
┃ [W] → Consider using phx-debounce for this form input
┃ lib/oeditus_web/live/audit_live/show.ex:27:3 (OeditusCredo.Check.Warning.MissingThrottle)Test Coverage
58 comprehensive tests covering all 20 checks with positive and negative cases.