erlang-otp-behaviors
Use when oTP behaviors including gen_server for stateful processes, gen_statem for state machines, supervisors for fault tolerance, gen_event for event handling, and building robust, production-ready Erlang applications with proven patterns.
Install / Use
npx skills add benchflow-ai/skillsbench --skill erlang-otp-behaviorsInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Our assessment of erlang-otp-behaviors
erlang-otp-behaviors scores 92/100 on our quality scale, 810th of 4,653 Development & Engineering skills we index (top 18%).
Its SKILL.md is 17 KB long, well organised into 9 sections with 3 code examples: a thorough specification that gives an agent plenty to work with.
With 1,813 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated about 2 months ago, so erlang-otp-behaviors is actively maintained.
- It is released under the Apache-2.0 license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
Safety scan
No issues foundOur scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands.
Automated pattern scan on 2026-10-02. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.
erlang-otp-behaviors compared with similar skills
All 4 of these similar skills score higher than erlang-otp-behaviors; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| erlang-otp-behaviors (this skill)by benchflow-ai | 92 | 1.8k | 2mo ago | SKILL.md |
| ai-job-searchby MadsLorentzen | 100 | 44.7k | today | CLAUDE.md |
| claude-howtoby luongnv89 | 100 | 41.7k | 2d ago | CLAUDE.md |
| algorithmic-artby anthropics | 100 | 177.9k | 9d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 9d ago | SKILL.md |
Frequently asked questions
- How do I install erlang-otp-behaviors?
- Run
npx skills add benchflow-ai/skillsbench --skill erlang-otp-behaviors. The install tabs above show the steps for each supported agent. - Which AI agents does erlang-otp-behaviors work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is erlang-otp-behaviors safe to use?
- Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. It is Apache-2.0-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is erlang-otp-behaviors still maintained?
- The repository was last updated about 2 months ago, so erlang-otp-behaviors is actively maintained.
Skill content
View source on GitHubname: erlang-otp-behaviors description: Use when oTP behaviors including gen_server for stateful processes, gen_statem for state machines, supervisors for fault tolerance, gen_event for event handling, and building robust, production-ready Erlang applications with proven patterns.
Erlang OTP Behaviors
Introduction
OTP (Open Telecom Platform) behaviors provide reusable patterns for common process types in Erlang systems. These abstractions handle complex details like message passing, error handling, and state management, allowing developers to focus on business logic while maintaining system reliability.
Behaviors define interfaces that processes must implement, with OTP handling the infrastructure. Gen_server provides client-server processes, gen_statem implements state machines, supervisors manage process lifecycles, and gen_event coordinates event distribution. Understanding these patterns is essential for production Erlang.
This skill covers gen_server for stateful processes, gen_statem for complex state machines, supervisor trees for fault tolerance, gen_event for event handling, application behavior for packaging, and patterns for building robust OTP systems.
Gen_Server Basics
Gen_server implements client-server processes with synchronous and asynchronous communication.
-module(counter_server).
-behaviour(gen_server).
%% API
-export([start_link/0, increment/0, decrement/0, get_value/0, reset/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
%% State record
-record(state, {count = 0}).
%%%===================================================================
%%% API
%%%===================================================================
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
increment() ->
gen_server:cast(?SERVER, increment).
decrement() ->
gen_server:cast(?SERVER, decrement).
get_value() ->
gen_server:call(?SERVER, get_value).
reset() ->
gen_server:call(?SERVER, reset).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
{ok, #state{}}.
%% Synchronous calls (with response)
handle_call(get_value, _From, State) ->
{reply, State#state.count, State};
handle_call(reset, _From, State) ->
{reply, ok, State#state{count = 0}};
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
%% Asynchronous casts (no response)
handle_cast(increment, State) ->
NewCount = State#state.count + 1,
{noreply, State#state{count = NewCount}};
handle_cast(decrement, State) ->
NewCount = State#state.count - 1,
{noreply, State#state{count = NewCount}};
handle_cast(_Msg, State) ->
{noreply, State}.
%% Handle other messages
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Complex gen_server example: Cache
%%%===================================================================
-module(cache_server).
-behaviour(gen_server).
-export([start_link/1, put/2, get/1, delete/1, clear/0, size/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {
cache = #{},
max_size = 1000,
hits = 0,
misses = 0
}).
start_link(MaxSize) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [MaxSize], []).
put(Key, Value) ->
gen_server:call(?MODULE, {put, Key, Value}).
get(Key) ->
gen_server:call(?MODULE, {get, Key}).
delete(Key) ->
gen_server:cast(?MODULE, {delete, Key}).
clear() ->
gen_server:cast(?MODULE, clear).
size() ->
gen_server:call(?MODULE, size).
init([MaxSize]) ->
process_flag(trap_exit, true),
{ok, #state{max_size = MaxSize}}.
handle_call({put, Key, Value}, _From, State) ->
Cache = State#state.cache,
case maps:size(Cache) >= State#state.max_size of
true ->
{reply, {error, cache_full}, State};
false ->
NewCache = maps:put(Key, Value, Cache),
{reply, ok, State#state{cache = NewCache}}
end;
handle_call({get, Key}, _From, State) ->
Cache = State#state.cache,
case maps:find(Key, Cache) of
{ok, Value} ->
NewState = State#state{hits = State#state.hits + 1},
{reply, {ok, Value}, NewState};
error ->
NewState = State#state{misses = State#state.misses + 1},
{reply, not_found, NewState}
end;
handle_call(size, _From, State) ->
Size = maps:size(State#state.cache),
{reply, Size, State};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
handle_cast({delete, Key}, State) ->
NewCache = maps:remove(Key, State#state.cache),
{noreply, State#state{cache = NewCache}};
handle_cast(clear, State) ->
{noreply, State#state{cache = #{}}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(Reason, State) ->
io:format("Cache terminating: ~p~n", [Reason]),
io:format("Stats - Hits: ~p, Misses: ~p~n", [State#state.hits, State#state.misses]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% gen_server with timeouts
%%%===================================================================
-module(session_server).
-behaviour(gen_server).
-export([start_link/0, touch/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(TIMEOUT, 30000). % 30 seconds
-record(state, {
last_activity,
data = #{}
}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
touch() ->
gen_server:cast(?MODULE, touch).
init([]) ->
{ok, #state{last_activity = erlang:system_time(millisecond)}, ?TIMEOUT}.
handle_call(_Request, _From, State) ->
{reply, ok, State, ?TIMEOUT}.
handle_cast(touch, State) ->
NewState = State#state{last_activity = erlang:system_time(millisecond)},
{noreply, NewState, ?TIMEOUT};
handle_cast(_Msg, State) ->
{noreply, State, ?TIMEOUT}.
handle_info(timeout, State) ->
io:format("Session timed out~n"),
{stop, normal, State};
handle_info(_Info, State) ->
{noreply, State, ?TIMEOUT}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Gen_server provides structure for stateful processes with client-server patterns.
Gen_Statem for State Machines
Gen_statem implements finite state machines with explicit state transitions.
-module(door_fsm).
-behaviour(gen_statem).
-export([start_link/0, open/0, close/0, lock/0, unlock/1]).
-export([init/1, callback_mode/0, terminate/3, code_change/4]).
-export([locked/3, unlocked/3, open/3]).
-define(CODE, "1234").
start_link() ->
gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).
open() ->
gen_statem:call(?MODULE, open).
close() ->
gen_statem:call(?MODULE, close).
lock() ->
gen_statem:call(?MODULE, lock).
unlock(Code) ->
gen_statem:call(?MODULE, {unlock, Code}).
init([]) ->
{ok, locked, #{}}.
callback_mode() ->
state_functions.
%% Locked state
locked(call, {unlock, Code}, Data) when Code =:= ?CODE ->
{next_state, unlocked, Data, [{reply, ok}]};
locked(call, {unlock, _WrongCode}, Data) ->
{keep_state, Data, [{reply, {error, wrong_code}}]};
locked(call, _Event, Data) ->
{keep_state, Data, [{reply, {error, door_locked}}]}.
%% Unlocked state
unlocked(call, lock, Data) ->
{next_state, locked, Data, [{reply, ok}]};
unlocked(call, open, Data) ->
{next_state, open, Data, [{reply, ok}]};
unlocked(call, _Event, Data) ->
{keep_state, Data, [{reply, ok}]}.
%% Open state
open(call, close, Data) ->
{next_state, unlocked, Data, [{reply, ok}]};
open(call, _Event, Data) ->
{keep_state, Data, [{reply, {error, door_open}}]}.
terminate(_Reason, _State, _Data) ->
ok.
code_change(_OldVsn, State, Data, _Extra) ->
{ok, State, Data}.
%%%===================================================================
%%% Connection state machine
%%%===================================================================
-module(connection_fsm).
-behaviour(gen_statem).
-export([start_link/0, connect/0, disconnect/0, send/1]).
-export([init/1, callback_mode/0, terminate/3, code_change/4]).
-export([disconnected/3, connecting/3, connected/3]).
-record(data, {
socket = undefined,
buffer = <<>>,
retry_count = 0
}).
start_link() ->
gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).
connect() ->
gen_statem:call(?MODULE, connect).
disconnect() ->
gen_statem:call(?MODULE, disconnect).
send(Data) ->
gen_statem:call(?MODULE, {send, Data}).
init([]) ->
{ok, disconnected, #data{}}.
callback_mode() ->
[state_functions, state_enter].
%% Disconnected state
disconnected(enter, _OldState, _Data) ->
io:format("Entered disconnected state~n"),
keep_state_and_data;
disconnected(call, connect, Data) ->
case connect_to_server() of
{ok, Socket} ->
{next_state, connected, Data#data{socket = Socket, retry_count = 0},
[{reply, ok}]};
error ->
NewData = Data#data{retry_count = Data#data.retry_count + 1},
case NewData#data.retry_count < 3 of
true ->
{next_state, connecting, NewData, [{reply, {error, retrying}}]};
false ->
{keep_state, NewData, [{reply, {error, max_retries}}]}
end
end.
%% Connecting state
connecting(enter, _OldState, _Data) ->
erlang:send_after(1000, self(), retry_connect),
keep_state_and_data;
connecting(info, retry_connect, Data) ->
case connect_to_server() of
{ok, Socket} ->
{next_state, connected, Data#data{socket = Socket, retry_count = 0}};
error ->
NewData = Data#data{retry_count = Data#data.retry_count + 1},
case NewData#data.retry_count < 3 of
true ->
{keep_state, NewData};
false ->
{next_state, disconnected, NewData}
end
end.
%% Connected state
connected(enter, _OldState, _Data) ->
io:format("Connection established~n"),
keep_state_and_data;
connected(call, {send, Data}, StateData) ->
case send_data(StateData#data.socket, Data) of
ok ->
{keep_state_and_data, [{reply, ok}]};
error ->
{next_state, disconnected, StateData, [{reply, {error, send_failed}}]}
end;
connected(call, disconnect, StateData) ->
close_connection(StateData#data.socket),
{next_state, disconnected, StateData#data{socket = undefined}, [{reply, ok}]}.
terminate(_Reason, _State, Data) ->
case Data#data.socket of
undefined -> ok;
Socket -> close_connection(Socket)
end.
code_change(_OldVsn, State, Data, _Extra) ->
{ok, State, Data}.
%% Helper functions
connect_to_server() ->
{ok, socket}.
send_data(_Socket, _Data) ->
ok.
close_connection(_Socket) ->
ok.
Gen_statem provides structured state machine implementation with explicit transitions.
Supervisor Trees
Supervisors monitor child processes and restart them on failure for fault tolerance.
-module(my_supervisor).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
SupFlags = #{
strategy => one_for_one,
intensity => 5,
period => 60
},
ChildSpecs = [
#{
id => counter_server,
start => {counter_server, start_link, []},
Truncated for display — read the full file on GitHub.
Related Skills
ai-job-search
44.7kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
claude-howto
41.7kA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
