Why this matters
Robert C. Martin draws no line between "design" and "architecture" — they are the same activity at different zoom levels. The low-level details support the high-level structure; separate them and you lose coherence. The single measure of a good architecture is simple: how much effort does it take to meet the current requirements, and how does that effort grow over time? If every new feature costs more than the last, the architecture is failing — regardless of whether the system works today.
Many teams fall into the trap of writing working software first and worrying about structure later. But messy code creates a productivity trap: velocity slows to near zero, developers spend more time fighting the code than writing it, and the business pays in perpetuity. Good architecture keeps the cost of change flat, even as the system grows.
The problem
A god class that handles reporting, persistence, and formatting has no architectural separation. Every new requirement touches the same class, making every change risky and expensive.
Bad
class ReportManager:
def __init__(self, db_conn):
self.db_conn = db_conn
def run(self, user_id):
row = self.db_conn.execute(
"SELECT * FROM reports WHERE user_id = ?", (user_id,)
).fetchone()
html = f"<h1>{row['title']}</h1><p>{row['body']}</p>"
self.db_conn.execute(
"UPDATE reports SET html = ? WHERE user_id = ?", (html, user_id)
)
self.db_conn.commit()
return html
class ReportManager {
constructor(private db: Database) {}
run(userId: number): string {
const row = this.db.query(
"SELECT * FROM reports WHERE user_id = ?", [userId]
);
const html = `<h1>${row.title}</h1><p>${row.body}</p>`;
this.db.execute(
"UPDATE reports SET html = ? WHERE user_id = ?", [html, userId]
);
return html;
}
}
The solution
Three focused classes — each with a single purpose — means each future change touches only one class. The architecture keeps the cost of change low.
Good
class ReportRepository:
def get(self, user_id: int) -> dict: ...
def save_html(self, user_id: int, html: str) -> None: ...
class ReportFormatter:
def to_html(self, row: dict) -> str:
return f"<h1>{row['title']}</h1><p>{row['body']}</p>"
class ReportService:
def __init__(self, repo: ReportRepository, fmt: ReportFormatter):
self.repo = repo
self.fmt = fmt
def generate(self, user_id: int) -> str:
row = self.repo.get(user_id)
html = self.fmt.to_html(row)
self.repo.save_html(user_id, html)
return html
class ReportRepository {
get(userId: number): ReportRow { ... }
saveHtml(userId: number, html: string): void { ... }
}
class ReportFormatter {
toHtml(row: ReportRow): string {
return `<h1>${row.title}</h1><p>${row.body}</p>`;
}
}
class ReportService {
constructor(
private repo: ReportRepository,
private fmt: ReportFormatter,
) {}
generate(userId: number): string {
const row = this.repo.get(userId);
const html = this.fmt.toHtml(row);
this.repo.saveHtml(userId, html);
return html;
}
}
Key takeaway
Good architecture is not about patterns or buzzwords — it is about keeping the cost of change low for the entire lifetime of the system.