Language-Specific Patterns
Language-Specific Patterns
Section titled “Language-Specific Patterns”Circular dependencies can look different depending on the language: import style, barrel files, and runtime behavior all affect where cycles appear and how to fix them. Arxo detects cycles in TypeScript/JavaScript and Python. This page summarizes common patterns and fixes for each.
For an overview, see Circular Dependencies. For refactoring patterns that work in any language, see Refactoring Patterns.
TypeScript / JavaScript
Section titled “TypeScript / JavaScript”Common causes
Section titled “Common causes”- Barrel files —
index.tsre-exports from many files, and those files import fromindex.ts, forming A → index → B → index → A. - Types and values — File A imports a type and a value from B, B imports from A; the runtime import creates the cycle even if the type is only used in type position.
- Circular component trees — e.g. React components that import each other through a long chain back to the start.
What to do
Section titled “What to do”- Prefer direct imports to the concrete file instead of the barrel when that would break the cycle.
- Extract shared types into a
types.ts(or similar) that neither side imports from the other — see Refactoring Patterns: Move Types to Definitions File. - Split barrels so that the cycle is broken (e.g. one barrel for “public” API, no circular re-exports).
Note on tree-shaking
Section titled “Note on tree-shaking”Cycles prevent reliable tree-shaking. If bundle size matters, fixing import cycles is a prerequisite.
Python
Section titled “Python”Common causes
Section titled “Common causes”- Circular imports — Module A imports B, B imports A (or A → B → C → A). Python may load them at runtime but with fragile order and “partial module” issues.
- Type hints —
from __future__ import annotationsand forward references can hide circular imports at runtime but the static graph still has a cycle. - Shared models — Two modules that define or use each other’s types or models.
What to do
Section titled “What to do”- Dependency injection or late imports (import inside a function) can break runtime cycles; Arxo still sees the static import graph, so prefer removing the static cycle (e.g. move shared code to a third module).
- Extract shared code into a separate module both A and B import from — see Refactoring Patterns: Extract Shared Module.
- Use a types-only module or
TYPE_CHECKINGimports so that the main execution path doesn’t form a cycle.
General Advice
Section titled “General Advice”- Group by folder or package — Use
group_by: folder(or equivalent) in Arxo so that cycles are reported at the level you care about (e.g. packages or modules, not every single file). - Fix small cycles first — Two- or three-node cycles are usually easier to break and still improve metrics; see Fixing Cycles.
- Use the same grouping in CI — So that “no new cycles” is enforced at the same granularity you use locally.
Next Steps
Section titled “Next Steps”- Refactoring Patterns — Extract shared, DI, types file, abstraction layer
- Fixing Cycles — Step-by-step process
- Performance — TypeScript and Python benchmarks
- Circular Dependencies — Back to overview