Skip to content

docs(profile): steer newcomers to modern-di, and state the declaration-model edge accurately#48

Merged
lesnik512 merged 3 commits into
mainfrom
docs/di-positioning
Jul 13, 2026
Merged

docs(profile): steer newcomers to modern-di, and state the declaration-model edge accurately#48
lesnik512 merged 3 commits into
mainfrom
docs/di-positioning

Conversation

@lesnik512

@lesnik512 lesnik512 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Acts on two findings from the (still unmerged) research/adoption-strategy branch in modern-di. One of them did not survive verification — see below.

The org profile was inverting your own guidance

that-depends' README says, in its own words: "If you're starting a new project, consider modern-di", and it ships a migration guide pointing that way. But nothing on any org surface said so, and the two canonical blurbs pulled the opposite direction:

Repo Blurb
modern-di "Powerful dependency-injection framework with IoC container and scopes"
that-depends "Simple, typed dependency-injection framework for Python"

A newcomer scanning that table sees "simple" vs "powerful", has no other signal, and a good share will pick simple — the one you steer away from.

Adds a one-line steer above the DI table on profile/README.md and the docs site. The canonical one-liners are untouched — they're synced across the GitHub description and pyproject on both repos, so rewriting them is a bigger, separate call.

The research's "decorator-free" headline is wrong — corrected

The roadmap (B1) calls "decorator-free, type-annotation DI" modern-di's headline differentiator. It doesn't hold. I checked all 12 integration READMEs:

modern-di dishka
@provide on providers never — plain class attributes on a Group always required
@inject on handlers not needed in FastAPI, Litestar, FastStream, taskiq
required in Starlette, Flask, aiohttp, Celery, arq, aiogram, Typer
required (incl. FastAPI, Litestar)

So the universal, defensible difference is no @provide, not "decorator-free". The @inject advantage is real but holds in 4 of 11 integrations, not all of them.

The copy now states the claim and its limits — including that FromDI(Dependencies.user_service) at the FastAPI boundary is still a marker. Shipping the unqualified version would have put a false claim about your own library into a Show HN thread, where the first commenter opens modern-di-celery's README and finds @inject. The honest framing is the whole credibility play; it only works if it's actually honest.

Verification

just check-planning: OK. mkdocs build --strict: clean. All 12 integration READMEs inspected directly for decorator usage.

…edge

that-depends' own README says "if you're starting a new project, consider
modern-di", but nothing on the org surfaces said so -- and the two canonical
blurbs actively inverted it: modern-di reads "Powerful", that-depends reads
"Simple". A newcomer with no other signal picks "simple", i.e. the one we
steer away from.

Adds a one-line steer above the DI table on profile/README.md and the docs
site: modern-di for new projects, that-depends when you need async
resolution. Leaves the canonical one-liners untouched, since those are synced
across the GitHub description and pyproject on both repos.

Also plays modern-di's strongest card in the launch copy. Verified against
source, not taken on trust: modern_di defines and exports no DI decorator,
while dishka needs @provide on providers and @Inject on every handler. The
copy states the limit of the claim too -- FromDI(...) at the FastAPI boundary
is still a marker -- so the claim survives the scrutiny an HN thread applies.
@lesnik512 lesnik512 force-pushed the docs/di-positioning branch from bea48eb to c58f965 Compare July 13, 2026 20:39
…integrations

The previous copy said modern-di "ships no @inject/@provide decorator at all".
That was wrong: it generalized from FastAPI and Litestar. Checked all 12
integrations -- @Inject is required in Starlette, Flask, aiohttp, Celery, arq,
aiogram and Typer, and only FastAPI, Litestar, FastStream and taskiq are free
of it.

The universal, defensible difference is narrower: providers are plain class
attributes, so there is no @provide anywhere, where dishka requires one on
every provider. The @Inject advantage is real but holds in 4 of 11
integrations, not all of them.

Copy now states both the claim and its limits, so it survives the scrutiny an
HN thread applies -- which is the entire point of the honest framing.
@lesnik512 lesnik512 changed the title docs(profile): steer newcomers to modern-di, name the decorator-free differentiator docs(profile): steer newcomers to modern-di, and state the declaration-model edge accurately Jul 13, 2026
modern-di is not sync-only. The container is an async context manager, async
finalizers are supported (there is a dedicated AsyncFinalizerInSyncCloseError
pointing at close_async), and async resources -- an aiohttp session, an asyncpg
pool -- are constructed in the framework lifespan and injected by type via a
ContextProvider, which is a documented recipe. Most of the 13 integrations are
async frameworks.

What is actually synchronous is the resolve path: container.resolve(X) never
awaits. That is the claim the copy should make, and it now does, across all ten
places that said "sync-only" or "sync resolution by design".

Also drops the advice to use Dishka for async. The ContextProvider recipe is
the answer, so the copy points there instead. Dishka is still named honestly as
the more established alternative -- we just do not send people to it.

Fixes the same misframing in the org-profile and docs-site DI steers added
earlier on this branch.
@lesnik512 lesnik512 merged commit 2e9d42e into main Jul 13, 2026
1 check passed
@lesnik512 lesnik512 deleted the docs/di-positioning branch July 13, 2026 20:57
lesnik512 added a commit that referenced this pull request Jul 13, 2026
Two rendering bugs in the DI steer shipped in #48.

1. modern-python.org was serving the literal text '!!! tip "Which one should
   I use?"' with the body as a raw code block. The admonition extension was
   never enabled in mkdocs.yml -- the site had no other admonition, so nothing
   had caught it. mkdocs build --strict passes either way, since a fell-through
   block is valid markdown, just not the markdown you meant.

2. On the org profile, GitHub renders a single newline as <br>, so the
   hard-wrapped paragraph broke mid-sentence in 8 places. The rest of
   profile/README.md avoids this by leaving prose unwrapped.

Both surfaces now carry the steer as two blank-line-separated paragraphs, which
renders identically under GFM and Python-Markdown with no <br> tricks.

Verified: profile renders 4 tables, 0 <br>, 2 <p>; the site renders a real
<div class="admonition tip"> with title + 2 paragraphs and no literal '!!!'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant