ChaseGilliam

ChaseGilliam

Assay - incremental Dialyzer with watch mode, and human readable formatters

Hello everyone,

I’m finally happy enough with my library Assay to share it here. Assay is an incremental Dialyzer wrapper built for CI and local workflows. It reads your existing Dialyzer settings (with caveats) from mix.exs, runs the incremental engine, filters warnings via dialyzer_ignore.exs, and outputs results in formats suited for humans, CI, editors, and LLM-driven tools.

Why Assay?

Running Dialyzer in Elixir projects has long meant slow full rebuilds, cryptic Erlang-style output, and poor integration with CI and editor tooling. Assay tackles these pain points:

  • Incremental by default — leverages OPT26 Dialyzer’s incremental mode so re-analysis is fast after the initial run
  • Watch mode — mix assay.watch debounces file changes and re-runs analysis automatically, giving you continuous type-checking feedback as you code
  • Multiple output formats — text, github (annotations) lsp, llm, and elixir (pretty-prints Erlang terms as Elixir notation via erlex with some really nicely formatted errors)
  • Igniter-powered installer — mix assay.install auto-detects your project/umbrella apps, configures apps and warning_apps, creates ignore files, and optionally generates CI workflows (GitHub Actions or GitLab CI)
  • Good umbrella app support- Assay was built to run on a large umbrella app
  • It’s fast: We’ve seen a 7m Dialyzer run fall to as little as 9s for small changes.

How does this relate to Elixir’s new type system?

Elixir 1.18+ introduced gradual set-theoretic types in the compiler, with significant inference improvements landing in 1.19 and 1.20. This is exciting work but Dialyzer and the new type system are complementary, not competing. If you like Dialyzer you should like Assay.

Dialyzer, by contrast, performs whole-program analysis across module
boundaries. It catches things the compiler currently cannot:

  • Contract violations between callers and callees across modules
  • Functions that will never return due to type mismatches
  • Unreachable code and impossible pattern matches that span module boundaries
  • Violations of your existing @spec annotations (which the compiler doesn’t check yet)

Even as the compiler’s type system matures, there’s a good chance Dialyzer will continue to catch a different class of bugs. Running increment Dialyzer via Assay is cheap insurance.

Quick Start

Using the Igniter installer (recommended):

mix.exs

  [`
    {:assay, “~> 0.5”, runtime: false, only: [:dev, :test]},
    {:igniter, “~> 0.6”}
  ]
end

then run:

mix assay.install --yes
mix assay

Or configure manually — just add the dep and set your config in mix.exs:

def project do
[
  app: :my_app,
  assay: [
    dialyzer: [
      apps: :project_plus_deps,
      warning_apps: :project]
  ]
]
end

Assay supports symbolic selectors like :project, :project_plus_deps, :current, and :current_plus_deps — particularly handy for umbrella projects.

Output Formats

Run with multiple formatters at once for CI pipelines:

mix assay --format github --format sarif

The github formatter produces rich annotations with formatted bodies instead of raw Dialyzer text. The sarif formatter emits SARIF 2.1.0 JSON for standardized CI tool consumption. The llm format outputs
structured JSON optimized for LLM agents.

Links

https://github.com/Ch4s3/assay

First Post!

ChaseGilliam

ChaseGilliam

I just released 0.6.1there are various fixes and the following new features:

  • --format markdown output format renders Dialyzer warnings as a Markdown document, suitable for pasting into issues, PRs, or documentation.
  • --format marcli output format renders warnings with rich terminal styling via the optional marcli dependency (add {:marcli, "~> 0.3"} to your project deps to enable).

Where Next?

Popular in Announcing Top

wmnnd
Hi there, for my project DBLSQD, I needed a file storage solution that is a bit more flexible than Arc. Because I thought others might f...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story <- Meeseeks.all(html, css("tr.athing")) do...
New
tfwright
After working on it for a couple of months and using it in production for most of that time, today I’ve released LiveAdmin, a LiveView ba...
New
tmbb
I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps: bureaucrat - which contains a bunch ...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
Qqwy
Hello everyone, I wrote a small library today called MapDiff. It returns a map listing the (smallest amount of) changes to get from map...
New
sabiwara
Dune is a sandbox for Elixir and aims to safely evaluate user-provided code. You can try it out using this basic Elixir playground made ...
New
josevalim
Hello everyone, We have just released NimbleCSV which is a small and fast CSV parsing library for Elixir. It allows developers to define...
New
OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13966 106
New
anshuman23
Hello all, I have been working on my proposed project called Tensorflex as part of Google Summer of Code 2018.. Tensorflex can be used f...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement