Why this matters
The Main Sequence is the ideal line on a two-dimensional graph where the axes are Abstraction (A) and Instability (I), both from 0 to 1. The Main Sequence is the line A + I = 1. Components on this line are perfectly balanced: either stable and abstract, or volatile and concrete.
Pain Zone (A≈0, I≈0): Stable and concrete. A component that many modules depend on, but that contains only concrete implementation. It cannot be extended without modification. Every new requirement requires editing it, which ripples to all dependents. This is the most dangerous zone — the component resists change but cannot evolve. Think of a god class or a catch-all utility module.
Uselessness Zone (A≈1, I≈1): Abstract and unstable. A component of pure interfaces that nobody depends on. It was perhaps created in anticipation of future use, or it was heavily depended-upon once but all those dependents were refactored away. It is abstract but vestigial — present without purpose.
Distance from Main Sequence: D = |A + I − 1|. Ideal D = 0. Any component with D > 0.3 is worth investigating. You can measure this automatically with static analysis tools and include D in CI quality gates.
The problem
A DatabaseManager class highly depended upon (I≈0) but entirely concrete (A≈0). D = 1.0 — maximum distance from the Main Sequence. Every new requirement forces editing this stable component.
✗ Bad — Pain Zone (A≈0, I≈0)
# database_manager.py ← imported by 15 modules (stable, I≈0)
import sqlite3
class DatabaseManager:
def __init__(self, db_path: str):
self._conn = sqlite3.connect(db_path) # concrete: SQLite
def get_user(self, user_id: str) -> dict:
cursor = self._conn.execute(
"SELECT * FROM users WHERE id = ?", (user_id,)
)
return dict(cursor.fetchone())
def save_order(self, order: dict) -> None:
self._conn.execute(
"INSERT INTO orders (id, total) VALUES (?, ?)",
(order["id"], order["total"])
)
# D = |0 + 0 - 1| = 1.0 ← maximum distance from Main Sequence
# Adding PostgreSQL support: rewrite everything. Pain Zone confirmed.
// DatabaseManager.ts ← imported by 15 modules (stable, I≈0)
import { Pool } from "pg";
export class DatabaseManager {
private pool = new Pool({ connectionString: process.env.DATABASE_URL });
async getUser(userId: string): Promise> {
const result = await this.pool.query(
"SELECT * FROM users WHERE id = $1", [userId]
);
return result.rows[0];
}
async saveOrder(order: { id: string; total: number }): Promise {
await this.pool.query(
"INSERT INTO orders (id, total) VALUES ($1, $2)", [order.id, order.total]
);
}
}
// D = |0 + 0 - 1| = 1.0 ← maximum distance from Main Sequence
// Adding MySQL support: rewrite everything. Pain Zone confirmed.
The solution
Split into DatabasePort (stable, abstract, D=0) and PostgresDatabaseAdapter (volatile, concrete, D=0). Each sits exactly on the Main Sequence.
✓ Good — On the Main Sequence
# database_port.py ← stable (I≈0), abstract (A≈1)
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class UserRecord:
id: str
email: str
@dataclass
class OrderRecord:
id: str
total: float
class DatabasePort(ABC):
@abstractmethod
def get_user(self, user_id: str) -> UserRecord: ...
@abstractmethod
def save_order(self, order: OrderRecord) -> None: ...
# D(DatabasePort) = |1 + 0 - 1| = 0 ← on the Main Sequence
# postgres_adapter.py ← volatile (I≈1), concrete (A≈0)
import psycopg2
class PostgresDatabaseAdapter(DatabasePort):
def __init__(self, dsn: str):
self._conn = psycopg2.connect(dsn)
def get_user(self, user_id: str) -> UserRecord:
cur = self._conn.cursor()
cur.execute("SELECT id, email FROM users WHERE id = %s", (user_id,))
row = cur.fetchone()
return UserRecord(id=row[0], email=row[1])
def save_order(self, order: OrderRecord) -> None:
cur = self._conn.cursor()
cur.execute("INSERT INTO orders VALUES (%s, %s)", (order.id, order.total))
self._conn.commit()
# D(PostgresDatabaseAdapter) = |0 + 1 - 1| = 0 ← on the Main Sequence
// DatabasePort.ts ← stable (I≈0), abstract (A≈1)
export interface UserRecord { id: string; email: string; }
export interface OrderRecord { id: string; total: number; }
export interface DatabasePort {
getUser(userId: string): Promise;
saveOrder(order: OrderRecord): Promise;
}
// D(DatabasePort) = |1 + 0 - 1| = 0 ← on the Main Sequence
// PostgresDatabaseAdapter.ts ← volatile (I≈1), concrete (A≈0)
import { Pool } from "pg";
import { DatabasePort, UserRecord, OrderRecord } from "./DatabasePort";
export class PostgresDatabaseAdapter implements DatabasePort {
private pool = new Pool({ connectionString: process.env.DATABASE_URL });
async getUser(userId: string): Promise {
const result = await this.pool.query(
"SELECT id, email FROM users WHERE id = $1", [userId]
);
return result.rows[0] as UserRecord;
}
async saveOrder(order: OrderRecord): Promise {
await this.pool.query(
"INSERT INTO orders (id, total) VALUES ($1, $2)", [order.id, order.total]
);
}
}
// D(PostgresDatabaseAdapter) = |0 + 1 - 1| = 0 ← on the Main Sequence
Key takeaway
Calculate D = |A + I − 1| for your key components. Anything above 0.3 is a design smell worth investigating. Components in the Pain Zone (stable+concrete) need interfaces extracted from them. Components in the Uselessness Zone (abstract+unstable) need either new dependents or deletion. The Main Sequence is the architectural optimum — balance abstraction with stability.