For AI agentsA Markdown index of these docs is at /llms.txt. Every page is also Markdown: add .md to its address.

Agent Rules / Names and imports

Do import aliases confuse coding agents?

Rule AR-04

Alias imports only to resolve a real clash, and never alias a module to a one-letter name in shared code.

An alias gives one thing two names. A search for format_amount misses the file that calls fa(total), and a search for fa( matches half the repo. Aliases that the whole ecosystem shares (np, pd) cost nothing because everyone, agents included, already knows them. Aliases you invent cost a lookup in every file that uses them.

Why it happens

Python. The import statement binds the name after as and only that name. For import foo.bar.baz as fbb the language reference says the name following as is bound directly to the imported module, and foo isn't bound at all. For the from form, "a reference to that value is stored in the current namespace, using the name in the as clause if it is present, otherwise using the attribute name". After the import line, the original name does not exist in that file. Any text search for it fails there, and any tool tracing a call must read the import line first and map fa back to format_amount.

TypeScript and JavaScript. import { formatAmount as fa } from "./money" binds only fa, and export { formatAmount as fmt } publishes the function under a different name than the one it was defined with. The second form is the worse one, because a single rename at the export changes the name every importer must use and nothing in the importers' text matches the definition.

An alias is also a signal. When a file aliases from a import format_date as format_a, there is usually a clash upstream (see AR-03), and fixing the name at the source removes the need for the alias.

Bad and good

python# bad: invented aliases, one-letter module names import billing.money as m from billing.dates import month_start as ms from billing.tax import apply_vat as av def monthly_total(rows): return m.format_amount(av(m.sum_rows(rows, ms(rows[0].when))))
python# good: names stay the same as their definitions from billing import money from billing.dates import month_start from billing.tax import apply_vat def monthly_total(rows): return money.format_amount(apply_vat(money.sum_rows(rows, month_start(rows[0].when))))
ts// bad import * as m from "./money"; import { applyVat as av } from "./tax"; export { formatAmount as fmt } from "./money";
ts// good import * as money from "./money"; import { applyVat } from "./tax"; export { formatAmount } from "./money";

When a clash is real, alias with a name that says where the thing came from:

pythonfrom billing.utils import format_date as format_invoice_date from reports.utils import format_date as format_report_date

Trade-offs and when an alias is acceptable

  • Community aliases (import numpy as np, import pandas as pd) are shared conventions and are fine. Check them against a list rather than relying on taste. Ruff's ICN001 enforces the conventional alias from a configured list (the docs example is pandas as pd).
  • A real clash: two imports with the same name in one file. The alias should name the origin, as above. The better fix is a rename at the source (AR-03), when you own both.
  • Long names: a module name that makes every line wrap is a case for importing the submodule (from concurrent import futures), not for a one-letter alias.
  • Compatibility shims: an alias that keeps an old public name alive after a rename is legitimate. Put it in one place, mark it deprecated, and delete it on a schedule.
  • Redundant aliases are different. from .x import a as a is a re-export marker that type checkers read (see AR-02). Don't remove it mechanically.

Enforce it

ToolSettingWhat it catchesChecked
ruffICN001 unconventional-import-alias; settings lint.flake8-import-conventions.aliases and extend-aliasesa module imported under an alias that isn't the configured conventional one2026-10-30
ruffICN002 banned-import-alias; setting lint.flake8-import-conventions.banned-aliasesaliases you list as banned for a module (the docs example: tensorflow.keras.backend as K)2026-10-30
ruffPLC0414 useless-import-aliasimport numpy as numpy. The rule doesn't apply in __init__.py files, where such aliases are re-exports2026-10-30
ruffPLR0402 manual-from-importimport concurrent.futures as futures, which reads better as from concurrent import futures2026-10-30
eslintno-restricted-syntax with a selector on ImportNamespaceSpecifier and a regular expression on local.namenamespace imports bound to a one-letter name. The selector syntax supports [attr=/regex/] comparisons, ESLint 9.39.5 accepted this exact selector and flagged only the one-letter namespace name2026-10-30

No tool checked here bans every alias. The rule is a review rule with ICN001 and ICN002 as the automatic part.

In pyproject.toml:

toml[tool.ruff.lint] extend-select = ["ICN001", "ICN002", "PLC0414", "PLR0402"] [tool.ruff.lint.flake8-import-conventions] extend-aliases = { "billing.money" = "money" } banned-aliases = { "billing.money" = ["m", "bm"] }

(The mapping shape for extend-aliases and banned-aliases is {module = alias} and {module = [aliases]}; ruff 0.17.3 accepted this shape with no config error, and reported ICN001 and ICN002 as expected.)

Rule block for AGENTS.md / CLAUDE.md

markdown- Do not alias imports (`as x`) unless two imported names clash in one file. Use a descriptive alias that names the origin (`format_invoice_date`), never a one- or two-letter name. - Allowed conventional aliases: `np`, `pd`, and the ones listed in pyproject.toml. - When you meet an alias you didn't write, search for both names before you edit either.

Edge cases

  • Search both names: when you rename a function that is imported with as, the alias line is a caller. Search for the original name in import lines.
  • **Aliases in __init__.py:** from .x import a as b publishes the thing under a different name than its definition. Treat it as a rename and list it where the API is documented.
  • **import x.y as z versus from x import y:** the second leaves the submodule under its own name, which is what PLR0402 asks for.
  • Type-only aliases: import type { Invoice as InvoiceRow } has the same cost, and the same rule applies.
  • Dynamic access through the alias (getattr(m, name)) hides names from every tool at once. See AR-05 and AR-06 for related patterns.

What LLMGRAPH shows

Calls through import ... as aliases print certain against the defining function, so the alias cost falls on grep and human readers. A one-line getattr(...)(...) prints plus 1 dynamic call nearby that could reach anything.

References

  • Python Language Reference, "The import statement" (as binding for both forms): https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
  • Ruff ICN001, ICN002: https://docs.astral.sh/ruff/rules/unconventional-import-alias/ · https://docs.astral.sh/ruff/rules/banned-import-alias/
  • Ruff PLC0414, PLR0402: https://docs.astral.sh/ruff/rules/useless-import-alias/ · https://docs.astral.sh/ruff/rules/manual-from-import/
  • Pyright, "Typed libraries" (redundant alias as a re-export marker): https://github.com/microsoft/pyright/blob/main/docs/typed-libraries.md
  • ESLint selectors: https://eslint.org/docs/latest/extend/selectors
  • ESTree node definitions: https://github.com/estree/estree/blob/master/es2015.md

Changelog

  • 2026-10-30: draft 1.
  • 2026-10-30: tool checks resolved (tsc 7.0.2, eslint 9.39.5, ruff 0.17.3, mypy 2.4.1, pyright 1.1.414)
  • AR-01 Why do star imports make my coding agent miss callers?
  • AR-03 What happens when two functions share a name?
  • AR-02 Do barrel files hide calls from coding agents?

Last verified

For agentsthis page as Markdown/agent-readable/ar-04.mdllms.txt