This skill should be used when the user is organizing imports, writing require/use/include statements, fixing import ordering, dealing with circular dependencies, or when a file has disordered, ungrouped, or inconsistent imports. Covers import grouping, sorting, separation, and the rule that imports are the table of contents for a file.
The import block is the first thing a reader sees after the module docstring. It answers the most fundamental question about any file: what does this depend on? A disordered import block is like a book with a scrambled table of contents — it forces the reader to scan every line to understand the file's dependencies. This is not a cosmetic concern. It is a readability failure.
The rules are simple, universal, and non-negotiable: group by origin, separate groups with blank lines, alphabetize within groups, import only what you use, never import everything.
Every language has the same conceptual hierarchy. Adapt the specifics; preserve the structure.
Group 1: Standard library / built-in modules
(blank line)
Group 2: Third-party / external packages
(blank line)
Group 3: Internal / project-level modules
(blank line)
Group 4: Relative / sibling imports
Rules that apply everywhere:
Python's import conventions are well-established. Tools like isort and ruff enforce them automatically — but you must understand the rules to configure the tools correctly and to catch what they miss.
Grouping order:
# 1. __future__ imports (always first, always alone)
from __future__ import annotations
# 2. Standard library
import os
import sys
from collections import defaultdict
from pathlib import Path
# 3. Third-party packages
import httpx
import pydantic
from fastapi import FastAPI, HTTPException
from sqlalchemy import Column, Integer, String
# 4. Internal / project modules
from myapp.config import Settings
from myapp.database import get_session
from myapp.models import User
# 5. Relative / sibling imports
from .exceptions import NotFoundError
from .schemas import UserCreate, UserResponse
Critical Python rules:
from __future__ import annotations is always the very first import. No exceptions.import os (module import) vs from os import path (name import): prefer module imports for standard library to keep the namespace explicit. os.path.join() is clearer about origin than path.join().from module import * is banned. It pollutes the namespace, makes it impossible to trace where a name came from, and breaks static analysis.__all__ in __init__.py to declare the public API explicitly. Every name in __all__ must be deliberately chosen.ruff rule I (isort-compatible) or standalone isort with profile = "black".TypeScript imports have more variety (node builtins, npm packages, workspace packages, path aliases, relative paths) but the same grouping principle applies.
Grouping order:
// 1. Node built-in modules
import fs from "node:fs";
import path from "node:path";
// 2. External packages (npm)
import express from "express";
import { z } from "zod";
// 3. Organization-scoped packages
import { logger } from "@myorg/logger";
import { validateRequest } from "@myorg/middleware";
// 4. Internal path aliases (@/ or ~/)
import { db } from "@/database";
import { UserModel } from "@/models/user";
import { config } from "@/config";
// 5. Relative imports
import { NotFoundError } from "./errors";
import { userSchema } from "./schemas";
import type { UserCreateInput } from "./types";
Critical TypeScript rules:
node: prefix: import fs from "node:fs", not import fs from "fs". The prefix eliminates ambiguity with npm packages that shadow built-in names.type imports use import type { ... } — this is not optional when verbatimModuleSyntax is enabled, and it signals to the reader (and bundler) that the import is erased at runtime.import { everything } from "./index") are acceptable for re-export modules but dangerous when they pull in large dependency trees. If your barrel import causes circular dependencies or bloated bundles, import from the specific file.import/order rule or eslint-plugin-simple-import-sort.Go's import conventions are the strictest and the best-tooled. goimports handles formatting automatically, but you must still structure imports correctly for readability.
Grouping order:
import (
// 1. Standard library
"context"
"fmt"
"net/http"
"time"
// 2. External packages
"github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5"
"go.uber.org/zap"
// 3. Internal packages
"myapp/internal/config"
"myapp/internal/database"
"myapp/internal/models"
)
Critical Go rules:
goimports (or gofumpt which includes it) to auto-format. But verify it groups correctly — some versions do not separate internal from external.import . "testing") are banned outside of test files using frameworks that require them (and even then, use sparingly).import _ "database/sql") are only acceptable for side-effect registration (e.g., database drivers). Always add a comment explaining why: import _ "github.com/lib/pq" // register PostgreSQL driver.import pg "github.com/jackc/pgx/v5" when two packages have the same name.Inline imports (importing inside a function instead of at the top of the file) are a code smell by default. They break the "table of contents" contract — the reader cannot see all dependencies at a glance.
Acceptable ONLY in these cases:
Documented circular dependency. Two modules that must reference each other, where a top-level import creates a cycle. The inline import breaks the cycle. Always add a comment:
def get_user_orders(user_id: str) -> list:
# Inline import to break circular dependency: models -> services -> models
from myapp.services.orders import OrderService
return OrderService.find_by_user(user_id)
Conditional heavy import. A module that is expensive to load (e.g., ML model, large data file) and only needed in a rarely-executed code path:
def generate_report():
# Lazy import: pandas is only needed for reporting, not on every request
import pandas as pd
...
Optional dependency. A feature that works with or without a package:
try:
import orjson as json # Faster JSON if available
except ImportError:
import json
In all three cases, the comment explaining WHY is mandatory. An inline import without an explanation is a violation.
from module import * is banned. In every language. Without exception.
Why:
# BAD — where does Request come from? Flask? Django? A local module?
from flask import *
from myapp.models import *
# GOOD — every dependency is traceable
from flask import Flask, Request, jsonify
from myapp.models import User, Order
// BAD
export * from "./models";
export * from "./utils";
// If models and utils both export "validate", which one wins?
// GOOD — explicit re-exports
export { User, Order } from "./models";
export { formatDate, parseId } from "./utils";
Centralize public APIs through index files, but make them explicit.
# myapp/models/__init__.py
from .user import User
from .order import Order
from .product import Product
__all__ = ["Order", "Product", "User"] # Alphabetized. Always.
// models/index.ts
export { Order } from "./order";
export { Product } from "./product";
export { User } from "./user";
// Alphabetized. Explicit. No star re-exports.
Delete them. Immediately. Not after the feature is done. Not when the linter runs. Now.
An unused import is:
"But I might need it later" is not a reason. Version control exists. Import it again when you need it. The 3 seconds of re-typing are cheaper than the permanent tax of a false dependency.
Working implementations in examples/:
examples/import-ordering.md — Multi-language examples (Python, TypeScript, Go) showing properly organized imports with correct grouping, separation, alphabetization, and common violations fixed.When writing or reviewing import blocks:
from x import *, export * from)from __future__ is the very first import if presentnode: prefix; type-only imports use import typeimport _) have a comment explaining the side effect__init__.py / index.ts with explicit names, not star exports__all__ (Python) and export statements are alphabetized