Custom Credo checks for Elixir projects to enforce code quality and consistency patterns that reduce manual code review cycles.
This package provides custom Credo checks to automatically catch common code quality issues:
- Readability checks for better code organization and patterns
- Type safety improvements for better documentation and maintainability
- Assertion pattern simplifications for more idiomatic test code
Add the :optimum_credo package to your mix.exs dependencies:
def deps do
[
{:optimum_credo, "~> 0.1", only: [:dev, :test], runtime: false},
]
endAdd the checks you want in your .credo.exs configuration file:
%{
configs: [
%{
name: "default",
requires: ["./deps/optimum_credo/lib/**/*.ex"],
checks: [
# Existing Credo checks...
# OptimumCredo checks
{OptimumCredo.Check.Readability.ModuleOrganization, []},
{OptimumCredo.Check.Readability.DocumentationFormatting, []},
{OptimumCredo.Check.Readability.UnusedTypes, []},
{OptimumCredo.Check.Readability.VerboseAssertions, []},
{OptimumCredo.Check.Readability.ExtractableSpecTypes, []},
{OptimumCredo.Check.Readability.PrivateFunctionSpecs, []},
# Original checks
{OptimumCredo.Check.Readability.DepsOrder, []},
{OptimumCredo.Check.Readability.ImportOrder, []},
{OptimumCredo.Check.Readability.TypespecOrder, []},
]
}
]
}Then you can run mix credo as usual.
Enforces blank line separation between different statement types (use, import, alias, require).
Bad:
defmodule MyModule do
use Supervisor
import Telemetry.Metrics
alias MyApp.SomeModule
endGood:
defmodule MyModule do
use Supervisor
import Telemetry.Metrics
alias MyApp.SomeModule
endEnsures proper spacing in function documentation, specifically blank lines after section headers.
Bad:
@doc """
Returns the index URL.
## Examples
iex> index_url()
"http://localhost:4000/index.md"
"""Good:
@doc """
Returns the index URL.
## Examples
iex> index_url()
"http://localhost:4000/index.md"
"""Detects @type definitions that are never used in the module.
Bad:
@type short_id :: String.t() # Never used
@type conn :: Plug.Conn.t()
@spec index(conn()) :: conn()
def index(conn), do: connGood:
@type conn :: Plug.Conn.t()
@spec index(conn()) :: conn()
def index(conn), do: connDetects verbose assertion patterns that can be simplified to idiomatic Elixir.
Bad:
assert drop.id != nil
assert user.name == nil
refute item.id != nilGood:
assert drop.id
refute user.name
refute item.idEnforces extraction of common types in @spec declarations to semantic module-level type aliases.
Bad:
@spec show(Plug.Conn.t(), map()) :: Plug.Conn.t()
@spec index(Plug.Conn.t()) :: Plug.Conn.t()Good:
@type conn :: Plug.Conn.t()
@type params :: map()
@spec show(conn(), params()) :: conn()
@spec index(conn()) :: conn()Detects @spec declarations for private functions, which should not have specs.
Bad:
@spec lookup_cached_entries(list()) :: {:hit, content()} | :miss
defp lookup_cached_entries(ets_match_spec) do
# ...
endGood:
defp lookup_cached_entries(ets_match_spec) do
# ...
endEnforces alphabetical ordering of dependencies in app_deps and optimum_deps functions.
Enforces alphabetical ordering of import statements.
Enforces alphabetical ordering of typespec definitions.
- Reduces Manual Review Cycles: Catches trivial formatting issues automatically
- Enforces Team Standards: Consistent code organization across projects
- Speeds Up Development: Less back-and-forth on style issues
- Improves Type Safety: Better documentation and semantic meaning through type aliases
Run the test suite:
mix test- Fork the repository
- Create a feature branch
- Add tests for your changes
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License.