Install
$ agentstack add skill-muvon-octomind-tap-programming-elixir ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Mental model
Elixir is a functional language built on the BEAM, a runtime designed for concurrency, isolation, and fault tolerance. The big architectural lever is OTP — supervision trees and lightweight processes — not language features. "Let it crash" is not a slogan; it's the design: code the happy path, let supervisors restart on failure, and avoid defensive programming. Most maintenance pain comes from treating processes like threads, from rescuing too eagerly, and from spreading business logic across GenServers when plain modules would do.
Functional core
- Pure functions in modules form the bulk of a codebase; processes are for state, isolation, and concurrency — not for organization
- Multi-clause functions with pattern matching replace
if/else/casechains - Pipe (
|>) when data flows linearly;then/2when the value isn't the first argument withfor happy-path chains of{:ok, _}/{:error, _}— the canonical control-flow construct for fallible pipelines- Tagged tuples for return values:
{:ok, value}/{:error, reason}— never raise for expected outcomes - Structs (
defstruct) for typed data;@typeand@specon the public API
OTP architecture
- A supervision tree is the application skeleton — children listed in the
Applicationmodule'sstart/2 - Each process has one job; if a process needs to do two things, it's two processes
GenServerfor stateful services with a clear public API (MyServer.call(...)wrappingGenServer.call)DynamicSupervisorfor children started at runtime;PartitionSupervisorfor sharded workloadsTaskandTask.Supervisorfor one-shot async work- Choose restart strategies deliberately:
:one_for_onefor independent children,:rest_for_onewhen later children depend on earlier ones - Let processes crash on unexpected input — the supervisor restarts cleaner state than rescue blocks paper over
Concurrency patterns
- Processes are cheap (millions on a node) — spawn freely when isolation helps
- Send messages, don't share state; the BEAM gives you message-passing for free
- Long-running work in a
Taskunder a supervisor, not in a controller or LiveView callback - Backpressure via GenStage / Flow / Broadway when producers can outpace consumers
- Time-outs on every
GenServer.call; the default 5 seconds is rarely what you want for slow operations
Error handling
withfor chained fallible operations — one path for success, one clause per failure shape- Tagged tuples for expected failures;
raiseonly for genuinely exceptional conditions - Provide context in errors:
{:error, {:not_found, %{type: :user, id: id}}}beats{:error, :not_found} - Rescue at system boundaries (HTTP handlers, message consumers) where you must respond to the outside world
Ecto and persistence
- Schemas describe database shape; changesets validate and cast at the boundary
- Keep query composition explicit: small named functions returning queryables, composed at the call site
- Multi-step writes use
Ecto.Multiso the whole transaction rolls back on failure - Avoid putting business logic in changesets — they validate data, they don't make decisions
- Migrations are forward-only in production; design rollback as a new migration
Phoenix and LiveView
- Contexts (
Accounts,Billing,Inventory) are the public API for a domain — controllers and LiveViews call contexts, not Ecto directly - LiveView holds UI state in
socket.assigns; long work goes to aTaskand streams results back viahandle_info - Streams (
stream/3) for large lists — they avoid sending the full collection on every diff - Channels for low-level WebSocket needs that LiveView doesn't fit
- Function components and slots for composable UI; keep template logic minimal
Testing
- ExUnit with
async: truefor tests that don't touch shared state — the BEAM's isolation makes most tests parallelizable - Pattern-match in
assert:assert {:ok, %User{name: "Ada"}} = create_user(params) describeblocks group tests around a function;setupandsetup_allfor fixtures- Mox for behaviour-based mocks; define a behaviour, swap implementations in tests
- Test contexts and pure modules directly; LiveView/controller tests are integration tests
Common pitfalls
%{map | key: val}only updates existing keys —Map.put/3for newString.to_atom/1on user input leaks memory (atoms aren't garbage-collected) — useString.to_existing_atom/1Enummaterializes;Streamis lazy — useStreamfor large or infinite sequences- A
GenServerthat calls itself synchronously deadlocks — usecast, or restructure
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Muvon
- Source: Muvon/octomind-tap
- License: Apache-2.0
- Homepage: https://octomind.run/tap/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.