惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Schneier on Security
Schneier on Security
N
Netflix TechBlog - Medium
IT之家
IT之家
MongoDB | Blog
MongoDB | Blog
博客园_首页
S
SegmentFault 最新的问题
H
Help Net Security
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
T
The Blog of Author Tim Ferriss
量子位
GbyAI
GbyAI
M
MIT News - Artificial intelligence
Recorded Future
Recorded Future
P
Privacy & Cybersecurity Law Blog
B
Blog
月光博客
月光博客
博客园 - 聂微东
Vercel News
Vercel News
罗磊的独立博客
腾讯CDC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
Arctic Wolf
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
L
Lohrmann on Cybersecurity
I
Intezer
小众软件
小众软件
T
The Exploit Database - CXSecurity.com
Jina AI
Jina AI
C
Check Point Blog
AWS News Blog
AWS News Blog
C
Cisco Blogs
Martin Fowler
Martin Fowler
The Last Watchdog
The Last Watchdog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
宝玉的分享
宝玉的分享
S
Security Affairs
大猫的无限游戏
大猫的无限游戏
N
News and Events Feed by Topic
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Hacker News: Front Page
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Full Disclosure
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog

jola.dev

Elixir Cluster 101 | jola.dev How to stop Claude from saying load-bearing | jola.dev CI workflows on Tangled for Elixir | jola.dev Automatically syncing your blog to atproto and standard.site | jola.dev Appreciation for the small web | jola.dev Treating LLMs as programming books Publishing your blog to standard.site in Elixir Generating OG images in Elixir The social contract of writing Highest Random Weight in Elixir bunnyx: a bunny.net Elixir client library Building for the joy of building Running local models on an M4 with 24GB memory How to hit your Claude weekly limit so you can go outside and touch grass Dropping Cloudflare for bunny.net Building a blog with Elixir and Phoenix Stay in the Loop: How I Actually Use Claude Code Ruthless Prioritization: The Path to Delivery Estimates Are More Valuable Than You Think When Software Engineers Think They Need More Focus Time If the Goal is Resiliency, Defensive Programming is Your Enemy The Magic of Daily Pull Requests: Why Smaller is Better Building a Distributed Rate Limiter in Elixir with HashRing Announcing Hex Diff Building Hex Diff Push-based GenStage The Erlang :queue module in Elixir Patterns for managing ETS tables Health checks for Plug and Phoenix The new `Registry.select/2` and what match specs are Elixir String Processing Optimization
Let libraries be libraries | jola.dev
https://jola.dev/about · 2026-07-07 · via jola.dev

My Elixir library pet peeve: application.ex. There are very few reasons for why you would ever need to have a library be its own application, and making it one often encourages anti-pattern library design that leads to less usable code. And yet, I still see a lot of Elixir libraries do this.

What does application.ex do

This is the file that includes the start callback for your application. For most of us, this is how we’re used to working with Elixir. We create our application with mix phx.new or mix new --sup and that’s what pops out. You get a nice little supervision tree and use it to add your GenServers and whatnot, maybe you put in a logger handler in the startup logic, some logs, maybe set up some telemetry. It’s a very intuitive place to put start up logic.

In fact, tons of your dependencies do this. Try it out for yourself. Here’s what I get when I list the running applications that have their own start callback, for this blog, locally.

iex(1)> :application.info()[:running] |> Enum.filter(fn {_app, master} -> is_pid(master) end) |> Enum.map(&elem(&1, 0))

[:jola_dev, :mimic, :image, :req, :sentry, :gettext, :telemetry_poller,

:nimble_pool, :tailwind, :esbuild, :phoenix_live_dashboard, :phoenix_live_view,

:phoenix_live_reload, :phoenix, :bandit, :phoenix_pubsub, :plug, :telemetry,

:plug_crypto, :runtime_tools, :logger, :hex, :inets, :ssl, :mix, :iex, :elixir,

:sasl, :kernel]

While running locally mix actually figures out which dependencies are applications and need to be started for you automatically. The way mix knows whether to treat something like an application is this bit in the mix.exs of the app:

def application do

[

mod: {JolaDev.Application, []},

extra_applications: [:logger, :runtime_tools]

]

end

Ok! Now we've established the difference between what I'm calling a “library” and an “application”, in the Elixir terminology (even though we tend to refer to both things as libraries when we depend on them). In the OTP world, applications that don't have a start callback are known as "library applications". I guess we're not doing ourselves any favors with the terminology.

Anti-pattern library design

The Elixir docs have an incredible section on Anti-patterns that I highly recommend reading. They cover all kinds of anti-patterns, whether they’re related to code, design, processes, or meta-programming. Even if you get nothing else out of this article, I’m happy to just be raising awareness of this great resource.

The section I’m focused on here is actually a little bit hidden away under Using application configuration for libraries. This section starts out covering the topic of using application config, like what you have in config.exs, to let users configure your library. The point it’s making is that if you make your library configurable through app config, since app config is global, each option can only have a single value set per runtime. You can’t have one function use one version and another function use another. This becomes especially bad when we’re talking about transitive dependencies. You want to use one config value for the library, but your dependency that also uses that library wants a different one. Fair enough.

But hidden below, in a sub-header, is the part that I want to rant about: “Additional remarks: Supervision trees”. It lays out the case cleanly, by treating it as a different aspect of the same problem that we just talked through. When a library defines an application.ex with its own supervision tree, then it’s no longer possible to configure that supervision tree. I can’t put it under a sub-tree of my app, I can’t control the supervision strategy. Not to mention that you can no longer run two instances of that tree, and you can’t conditionally start the library. That last one has bitten me a bunch of times.

I’m not going to list out any libraries that have fallen into the trap of running as an application, and become less configurable and more awkward to use because of it, but I’m sure you’ve run into it yourself. There’s no point in criticizing anyone for their work, especially when we’re talking about volunteered open source libraries. I’m only sharing this to help future library designers avoid this common design anti-pattern.

Examples of the better design

The docs mention Nx and DNS Cluster as examples of libraries that offer supervision trees, but let you configure and control them instead of starting them for you in the background. Another great example is Finch, the library that underpins the very popular HTTP client Req. Finch does come with a supervision tree, but in a configurable way where you can define the Finch supervision tree and how it should run. Here’s an example of what adding Finch to your app looks like:

children = [

{Finch, name: MyFinch}

]

You can add multiple Finch “instances”, each one with its own set of configuration options.

children = [

{Finch, name: MyFinch},

{Finch, name: SecondFinch},

{Finch,

name: DifferentOptionsFinch,

pools: %{

:default => [size: 10, count: 2],

"https://hex.pm" => [size: 32, count: 8]

}

}

]

Each one starts its own supervision tree and runs completely independent of the others. That gives you, the user, an incredible level of configurability.

An appeal to the community

I’m not saying a library can’t ever be an application, in fact one of my favorite libraries of all time, Req, runs as an application. It does that to be easier to use and quicker to set up, it starts up a Finch instance for you automatically, but it also supports running separate Finch trees, giving you the best of both worlds. It runs as an application without falling into the usual traps. There are reasons your library might need to start something, but it should be the exception and you can almost always solve it some other way.

So when you’re tinkering on your next library, please consider using Finch and the other examples as references and offering it as a fully configurable drop in supervision tree that the user can add to their own tree, with their own options, under their own supervisor. Let your library be a library.