Skip to content
Back to skills

Import Discipline

ASecurity

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.

  • 34 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 26, 2026
developmentjavascripttypescriptpythongojavasqlnodeexpressfastapidjango

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 26, 2026

npx -y skills add oborchers/fractional-cto --skill import-discipline --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Import Discipline?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Import Discipline
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/oborchers-import-discipline/badge)](https://www.skillsdirectory.com/skills/oborchers-import-discipline)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: import-discipline
description: "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."
version: 1.0.0
---

# Imports Are the Table of Contents

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.

## Universal Grouping Order

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:**

1. **One blank line between each group.** Not two. Not zero. One. This is the visual separator that lets the eye jump between dependency categories instantly.
2. **Alphabetized within each group.** Not by "importance," not by "order I wrote them," not by "how often I use them." Alphabetical. This makes merge conflicts smaller, makes it obvious when something is missing, and eliminates all subjective ordering debates forever.
3. **One import per line.** Multi-import lines hide dependencies and make diffs noisy. The exception is destructured imports in JavaScript/TypeScript where a single module provides multiple named exports — but even then, if it exceeds ~4 names, split across lines.
4. **No unused imports.** Not "I might need this later." Not "the linter will catch it." Delete it now. An unused import is a lie in the table of contents — it claims the file depends on something it does not.

## Python

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:**
```python
# 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.
- Use `__all__` in `__init__.py` to declare the public API explicitly. Every name in `__all__` must be deliberately chosen.
- Enforce with `ruff` rule `I` (isort-compatible) or standalone `isort` with `profile = "black"`.

## TypeScript

TypeScript imports have more variety (node builtins, npm packages, workspace packages, path aliases, relative paths) but the same grouping principle applies.

**Grouping order:**
```typescript
// 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 builtins use the `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.
- Barrel imports (`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.
- Enforce with ESLint `import/order` rule or `eslint-plugin-simple-import-sort`.

## Go

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:**
```go
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:**
- Go enforces unused imports at the compiler level. The code does not compile. This is the correct behavior.
- Use `goimports` (or `gofumpt` which includes it) to auto-format. But verify it groups correctly — some versions do not separate internal from external.
- Dot imports (`import . "testing"`) are banned outside of test files using frameworks that require them (and even then, use sparingly).
- Blank imports (`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`.
- Named imports are for conflict resolution only: `import pg "github.com/jackc/pgx/v5"` when two packages have the same name.

## Inline and Lazy Imports

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:**

1. **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:
   ```python
   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)
   ```

2. **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:
   ```python
   def generate_report():
       # Lazy import: pandas is only needed for reporting, not on every request
       import pandas as pd
       ...
   ```

3. **Optional dependency.** A feature that works with or without a package:
   ```python
   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.

## Star Imports and Wildcards

`from module import *` is banned. In every language. Without exception.

**Why:**
- The reader cannot determine where any name came from without running the code or using an IDE
- Two star imports with overlapping names silently shadow each other
- Static analysis tools cannot verify usage
- Adding a new export to the source module can silently break the importing module

```python
# 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
```

```typescript
// 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";
```

## Re-Exports

Centralize public APIs through index files, but make them explicit.

```python
# myapp/models/__init__.py
from .user import User
from .order import Order
from .product import Product

__all__ = ["Order", "Product", "User"]  # Alphabetized. Always.
```

```typescript
// models/index.ts
export { Order } from "./order";
export { Product } from "./product";
export { User } from "./user";
// Alphabetized. Explicit. No star re-exports.
```

## Unused Imports

Delete them. Immediately. Not after the feature is done. Not when the linter runs. Now.

An unused import is:
- A lie in the table of contents
- A false dependency that confuses refactoring tools
- A potential source of circular dependency issues
- Clutter that trains readers to ignore the import block

"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.

## Examples

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.

## Review Checklist

When writing or reviewing import blocks:

- [ ] Imports are grouped by origin: stdlib, third-party, internal, relative
- [ ] Exactly one blank line separates each group
- [ ] Imports are alphabetized within each group
- [ ] No unused imports exist in the file
- [ ] No star/wildcard imports (`from x import *`, `export * from`)
- [ ] No inline imports without a comment explaining the reason (circular dep, conditional heavy import, optional dep)
- [ ] Python: `from __future__` is the very first import if present
- [ ] TypeScript: Node builtins use the `node:` prefix; type-only imports use `import type`
- [ ] Go: blank imports (`import _`) have a comment explaining the side effect
- [ ] Re-exports are centralized in `__init__.py` / `index.ts` with explicit names, not star exports
- [ ] `__all__` (Python) and `export` statements are alphabetized
- [ ] Import style is consistent across every file in the project — if one file groups and sorts, all files group and sort

Files in this skill

  • SKILL.md10.9 KB
  • examples/import-ordering.md8.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.