# EctoStateMachine: A Finite State Machine for Ecto Schemas, Frozen Since 2017

> EctoStateMachine adds a finite state machine pattern to Ecto schemas, letting you define states, events, and transitions in a few lines. It is a small, unmaintained library that works for simple cases but lacks modern Ecto features.

**asiniy/ecto_state_machine** — State machine pattern for Ecto

- Repository: https://github.com/asiniy/ecto_state_machine
- Stars: 100 · Forks: 20
- Language: Elixir
- License: not declared
- Published: 2026-08-17 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/asiniy-ecto-state-machine

## What EctoStateMachine Solves and Who It Is For

EctoStateMachine brings the finite state machine pattern to Ecto schemas. If you are building an Elixir application where a record must move through a fixed set of states, like a user going from unconfirmed to confirmed to blocked, this library saves you from writing the same guard clauses and changeset logic by hand. The target user is an Elixir developer already using Ecto who wants a declarative way to define transitions and validate them at the changeset level. The README shows a User model with states like :unconfirmed, :confirmed, :blocked, and :admin, and events like :confirm and :block. That is the core use case: a simple, single-column state field with explicit transition rules.

## How the State Machine Works in the Code

The mechanism is a macro that you invoke inside an Ecto schema module. You pass a list of states and a list of events. Each event has a name, a from list, a to state, and an optional callback function. The macro generates functions on the module. For example, given an event named :confirm, it creates User.confirm(user) which returns a changeset that transitions the state to :confirmed. It also creates User.can_confirm?(user) which returns a boolean. The callback is applied to the changeset, so you can add extra fields like confirmed_at. The state is stored in a column, :state by default, and you can change that with the column option. The generated functions rely on Ecto.Changeset.change, so the result is a changeset you pass to Repo.update. This design fits Ecto's philosophy of separating changeset construction from database operations.

## Getting It Running: Installation and Configuration

To install, add {:ecto_state_machine, "~> 0.1.0"} to your mix.exs dependencies, per the README. The latest release is 0.2.1, so you might use "~> 0.2.1" instead. After fetching deps, you call use EctoStateMachine inside your schema module with the states and events options. For a custom column, add column: :rules to the use call. The README also lists contribution steps: mix deps.get, configure config/test.exs and config/dev.exs, run mix ecto.migrate, and run mix test. There is no hex documentation link, so the README is the primary source. The package compiles with Elixir and Ecto versions from early 2017, which is a constraint you must check against your current environment.

## Limitations and Failure Modes

The library has several clear limitations. First, it uses Ecto.DateTime.utc in the README example, which was deprecated and removed in later Ecto versions. That means the example will not compile on modern Ecto. Second, the roadmap lists missing features: no bang! methods that raise on invalid transitions, no custom error messages for changesets, and no dedicated module for the state machine logic. The README itself admits the description is spaghetti. The library also does not appear to validate that your states and events are consistent at compile time; it trusts the developer to define a valid machine. If you need to store state history or enforce complex guards, this is the wrong tool. The code generation approach also means every event adds a function to your schema module, which can clutter large models.

## Alternatives and Their Different Approaches

A common alternative is to write your own state machine logic with plain Ecto changesets and custom validate functions. That approach gives you full control over error messages and allows you to use modern Ecto features like Ecto.Changeset.validate_inclusion. Another alternative is the finite_state_machine package, which is a general-purpose FSM library for Elixir, though it does not integrate with Ecto changesets directly. The key difference is that EctoStateMachine generates changeset-returning functions, while a generic FSM library would manage state transitions outside the database layer. If you want to stay within Ecto, you could also use Ecto.Enum for the state column and write a simple case statement for allowed transitions. That gives you type safety at the database level but no automatic can_*? methods.

## Maintenance and Upgrade Cost

The repository has not been pushed to since January 2017. The last release is 0.2.1, and the roadmap items like bang! methods and custom error messages remain unchecked. There is no license file mentioned in the repository metadata, which is a red flag for production use. You would need to contact the author or check the repository directly for licensing. The library depends on Ecto, and since Ecto has evolved significantly, you will likely face compilation issues. Upgrading to a newer Ecto may require forking the library and patching the Ecto.DateTime usage. The maintenance cost is high because you are essentially adopting a frozen project. If you need those missing features, you will have to implement them yourself.

## Final Verdict: Where It Fits and Where It Does Not

EctoStateMachine is a tiny library that demonstrates a useful pattern, but it is not production-ready for modern Elixir projects. It works if you have a legacy codebase on Ecto 2.x and need a quick way to define simple state transitions. You can verify that by checking the test/dummy directory in the repository, as the README suggests. For any new project, the lack of maintenance and missing features makes it a poor choice. The specific next step is to look at the repository's test suite to see which Ecto version it pins, and then decide if you can live with that. If you cannot, write your own state machine logic or use a maintained alternative.

## Conclusion

Adopt EctoStateMachine if you are on an older Ecto version and need a straightforward, macro-based state machine with custom callbacks and a custom column name. Do not use it for new projects on modern Ecto, as it relies on Ecto.DateTime and lacks bang functions and custom error messages. Before adopting, verify that the library still compiles with your Ecto and Elixir versions, and check the repository for any forks or patches. The last release is 0.2.1 from January 2017, so expect to maintain it yourself.

## FAQ

### What are the two types of state machines?

The repository does not discuss types of state machine. It implements the finite state machine pattern inside Ecto, where you declare states, events with source and target states, and an optional column, and the model gains generated transition and predicate functions.

### How does an FSM work in ecto_state_machine?

You declare states and events in the model, then call the generated transition functions such as User.confirm(user) or User.make_admin(user). Each returns a changeset, and you store it yourself with Repo.update. Predicates such as User.can_confirm? report whether a transition is available from the current state.

### What are the 5 states of a finite-state machine?

The example in the README uses four, not five: unconfirmed, confirmed, blocked, and admin. The events connect them, with confirm going from unconfirmed to confirmed, make_admin from confirmed to admin, and block from either confirmed or admin to blocked.

### What is ecto elixir, and what does this package add to it?

It is an Elixir package that brings the finite state machine pattern to Ecto schemas. The generated functions are added to your model module by using EctoStateMachine, and the repository points at a test/dummy directory showing how to organise a sample application around it.

## Sources

- [Official README](https://github.com/asiniy/ecto_state_machine#readme)
- [Project repository](https://github.com/asiniy/ecto_state_machine)
- [Release notes](https://github.com/asiniy/ecto_state_machine/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/asiniy-ecto-state-machine
