Credo Configuration
Canonical FP bar: docs/fcis-engineering-rules.md — Functional Core, Imperative Shell: pure domain modules; side effects at edges. Enforce FCIS in reviews/refactors: fat LiveViews and mixed Repo+math are defects.
RULES — Follow these with no exceptions
1. Always run mix credo gen.config first — never hand-craft .credo.exs from scratch
2. Use --strict in CI — enables additional checks that are disabled by default
3. Add inline disables sparingly — document why each exception is necessary
4. Custom checks belong in lib/credo/checks/ — never inline them in application code
Setup Workflow
Follow this sequence when setting up or customizing Credo:
1. Add Dependency
# mix.exs
defp deps do
[
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
]
end
2. Fetch Dependencies
mix deps.get
3. Generate Default Config
mix credo gen.config
4. Customize .credo.exs
See Basic Configuration below.
5. Verify Setup
mix credo
Success: Credo exits 0 and prints a summary with issue counts per category. If issues are found, review them — fix violations, adjust check configuration, or add inline disable comments for intentional exceptions.
6. Run in Strict Mode
mix credo --strict
CI must use --strict.
7. Add to CI
See CI Integration below.
8. Create Custom Checks
Add custom check modules to lib/credo/checks/. See Custom Checks below.
Basic Configuration
Use
mix credo gen.configfor the complete check list rather than writing it by hand.
# .credo.exs
%{
configs: [
%{
name: "default",
strict: false,
color: true,
files: %{
included: ["lib/", "src/", "test/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
},
checks: %{
enabled: [
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
{Credo.Check.Design.AliasUsage, [if_nested_deeper_than: 2, if_called_more_often_than: 0]},
# ... add or override checks as needed
],
disabled: [
{Credo.Check.Consistency.MultiAliasImportRequireUse, []},
{Credo.Check.Design.DuplicatedCode, []},
]
}
},
%{
name: "strict",
strict: true,
files: %{
included: ["lib/", "src/", "test/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
},
checks: %{
enabled: [
{Credo.Check.Design.TagTODO, [priority: :high]},
{Credo.Check.Readability.Specs, []},
]
}
}
]
}
Disabling Checks
For a Single Line
# credo:disable-for-next-line
def my_function_with_long_name, do: :ok
def my_function, do: :ok # credo:disable-for-this-line
For a File
# At the top of the file
# credo:disable-for-this-file Credo.Check.Readability.ModuleDoc
defmodule MyApp.LegacyModule do
# No module doc needed
end
In Configuration
# .credo.exs
checks: %{
disabled: [
{Credo.Check.Readability.ModuleDoc, []}
]
}
CI Integration
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
credo:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: '1.15'
otp-version: '26.0'
- run: mix deps.get
- run: mix credo --strict
Handling Failures in CI
When Credo fails in CI, run mix credo locally to reproduce the failure. Only disable a check if it's a confirmed false positive — document the reason inline.
Mix Alias
# mix.exs
defp aliases do
[
"lint": ["credo --strict"],
"quality": ["format --check-formatted", "credo --strict", "sobelow --config"]
]
end
Custom Checks
Create custom checks for project-specific patterns in three steps:
- Define a module using
use Credo.Checkinlib/credo/checks/. - Implement
run/2withCredo.Code.prewalk/2to traverse the AST and collect issues. - Register the module under
checks.enabledin.credo.exs.
The example below detects direct Repo. calls inside LiveView modules — adapt find_issues/3 to match your own patterns:
# lib/credo/checks/no_direct_repo_in_live_view.ex
defmodule Credo.Check.NoDirectRepoInLiveView do
use Credo.Check, category: :design, base_priority: :high
@explanation """
LiveViews should not call Repo directly. Use context functions instead.
"""
def run(%Credo.SourceFile{} = source_file, params) do
issue_meta = IssueMeta.for(source_file, params)
source_file
|> Credo.Code.prewalk(&find_issues(&1, &2, issue_meta))
end
# Detect calls of the form MyApp.Repo.<any function>
defp find_issues(
{{:., _, [{:__aliases__, meta, [_, "Repo"]}, _fn]}, _, _} = ast,
issues,
issue_meta
) do
issue = format_issue(issue_meta, message: "Avoid direct Repo calls in LiveViews.", line_no: meta[:line])
{ast, [issue | issues]}
end
defp find_issues(ast, issues, _issue_meta), do: {ast, issues}
end
Register the custom check in .credo.exs:
checks: %{
enabled: [
{Credo.Check.NoDirectRepoInLiveView, []}
]
}
Common Pitfalls
| ❌ Don't | ✅ Do |
|---|---|
Hand-write .credo.exs from scratch |
Generate the baseline with mix credo gen.config |
Run plain mix credo in CI |
Run mix credo --strict in CI |
| Add Credo as a runtime dependency | Scope it: only: [:dev, :test], runtime: false |
| Scatter inline disables without explanation | Document why each credo:disable-* exception exists |
| Inline custom checks in application code | Put custom checks in lib/credo/checks/ |
Pin CI actions to @latest or @main |
Pin to a specific version tag (e.g. @v4) |
Integration
| Predecessor | This Skill | Successor |
|---|---|---|
| None (standalone) | credo-config | code-quality |
| code-quality | credo-config | code-review |
Companion skills:
code-quality— runsmix credo --strictagainst this configurationcode-review— enforces the same conventions during PR review