agentsclimarketplace

Software architecture

Skill keez97/claude-architecture-skills/skills/software-architecture

7 Claude Code skills for software architecture review (Python, web, cloud, microservices). Includes A/B benchmarks against unskilled baseline, assertion-graded eval suite, and interactive dashboards.

Install
npx -y skills add keez97/claude-architecture-skills --skill software-architecture

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Design scalable systems with Clean Architecture, SOLID principles, and Architecture Decision Records. Use this for decomposing systems, choosing architectural patterns, documenting decisions, and avoiding over-engineering. Language-agnostic: applies to any tech stack.

SKILL.md

18.6 KB, as published. Nobody here has run it

Software Architecture Skill

You've got a system to design or refactor. This skill cuts through the academic noise and gives you practical architecture decisions rooted in trade-offs, not dogma.

When to Use This Skill

  • "How should I structure this?" — system decomposition, boundaries, layering
  • "Should I use microservices/CQRS/event-driven?" — pattern selection with honest trade-offs
  • "This code is becoming a mess" — coupling, cohesion, and refactoring strategy
  • SOLID violations — identifying coupling, hard-to-test code, tight dependencies
  • Architectural decisions — documenting why you chose Pattern X over Pattern Y
  • Any language-agnostic design question — these principles transcend tech stacks

Not for Python-specific style/lint questions—use python-architecture-review for that.


Review Mode: Architecture Health Audit

When analyzing existing systems, the skill produces:

Architecture Health Score

Visual assessment using a structured template with these dimensions:

Architecture Health Score
=========================

Coupling              ████░░░░░░  [Good/Needs Work] - module interdependencies and circular refs
Cohesion             ██████░░░░  [Good/Needs Work] - components do one thing well
Abstraction Level    ███████░░░  [Good/Needs Work] - appropriate abstraction vs. over-engineering
Testability          ██░░░░░░░░  [Critical Issues] - ability to unit test in isolation
Pattern Consistency  █████░░░░░  [Good/Needs Work] - architectural patterns applied uniformly

Overall: [One-sentence verdict on system's architectural health]

Assess across these five dimensions:

  • Coupling: module interdependencies and circular references. Loose coupling = easier to change.
  • Cohesion: whether components do one thing well. High cohesion = components with clear purpose.
  • Abstraction Level: choosing right level of abstraction. Too little = scattered logic; too much = over-engineering.
  • Testability: ability to unit test in isolation. Can you test business logic without HTTP/DB?
  • Pattern Consistency: architectural patterns applied uniformly. Inconsistent patterns = hard to navigate.

Findings Table

Present every finding in a structured table for easy prioritization:

IDSeverityFindingImpactFixEffortUnlocks
F1HighGod object in UserService (500+ lines)Hard to test, blocks parallelizationExtract Repository, Factory patternsMediumAsync refactor (F3)
F2HighCircular import: models → utils → modelsUnpredictable load order, brittleMove shared code to common/LowCan parallelize tests
F3MediumNo seams for database layerCan't test business logic in isolationInject data access interfaceMediumIntegration test suite

For each finding, include:

  • ID — Reference number (F1, F2, ...)
  • Severity — CRITICAL / HIGH / MEDIUM / LOW (impact on production, testing, or developer velocity)
  • Finding — What the problem is, with file/module references
  • Impact — What actually goes wrong. Quantify: "At 10k users, this class will be unchageable" beats generic "hard to maintain."
  • Fix — Concrete code showing the solution
  • Effort — Quick (< 1 hour), Moderate (half day), Significant (1-2 days), Large (week+)
  • Unlocks — What other fixes this enables

Anti-Patterns & Over-Engineering Flags

  • Repository pattern on a simple table? Usually overkill. Justify before adding it.
  • Event-driven everywhere? Each event adds latency, tracing complexity, and eventual consistency headaches.
  • Microservices at 3 engineers? You're paying for distributed systems complexity; get value or consolidate.
  • Abstract interface on every class — tight coupling to abstraction. Only abstract what varies.

Before/After Showcase

For the 2-3 most impactful findings, show complete before/after code transformations. Include enough context that the reader sees how it fits into the broader codebase.

Example 1: Tight Coupling → Dependency Injection

# BEFORE: UserService tightly coupled to PostgreSQL
class UserService:
    def __init__(self):
        self.db = PostgreSQL(host="localhost", db="users")

    def get_user(self, user_id: int):
        return self.db.query("SELECT * FROM users WHERE id = ?", user_id)
# AFTER: Depend on abstraction, inject concrete implementation
class UserRepository(ABC):
    @abstractmethod
    def get_by_id(self, user_id: int) -> User: ...

class PostgreSQLUserRepository(UserRepository):
    def __init__(self, engine):
        self.engine = engine

    def get_by_id(self, user_id: int) -> User:
        return self.engine.query(User).filter(User.id == user_id).first()

class UserService:
    def __init__(self, repo: UserRepository):
        self.repo = repo

    def get_user(self, user_id: int) -> User:
        return self.repo.get_by_id(user_id)

# At bootstrap:
repo = PostgreSQLUserRepository(engine)
service = UserService(repo)  # Now testable with mock repo

Example 2: Mixed Concerns (SRP Violation) → Extracted Services

# BEFORE: UserService handles auth AND email notifications AND audit logging
class UserService:
    def register(self, email: str, password: str):
        user = User(email=email, password_hash=hash(password))
        db.save(user)
        send_email(email, "Welcome!")  # Not user service's job
        audit_log("user_registered", user.id)  # Not user service's job
        return user
# AFTER: Single responsibility, inject dependencies
class UserService:
    def __init__(self, email_service: EmailService, audit: AuditService):
        self.email = email_service
        self.audit = audit

    def register(self, email: str, password: str) -> User:
        user = User(email=email, password_hash=hash(password))
        db.save(user)
        self.email.send_welcome(email)  # Delegated
        self.audit.log("user_registered", user.id)  # Delegated
        return user
    # Now testable: mock email_service and audit_service

Design Mode: New System Architecture

When designing from scratch or major refactors:

1. Requirements Restatement

  • Scale targets: RPS, data volume, read/write ratio, peak burst
  • Operational constraints: deployment frequency, SLA, latency budgets
  • Coupling points: what can be loosely coupled, what must be tightly coupled
  • Common change patterns: what do users/product ask for most frequently?

2. Architecture Decision Records (ADRs)

Create ADRs for all significant decisions. Use this template:

# ADR-001: [Short title]

## Status
Proposed | Accepted | Deprecated

## Context
Why this decision matters. What problem are we solving?
- Technical constraints
- Business constraints
- Trade-offs being evaluated

## Decision
What we chose and why.

## Alternatives Considered
- Alt A: pros/cons, decision rationale
- Alt B: pros/cons, decision rationale

## Consequences
What becomes easier? What becomes harder?
- Positive: ...
- Negative: ...
- Risks: ...

## Revisit When
- Scale hits X RPS
- Team grows beyond N engineers
- New tech emerges (e.g., "if FastAPI adds streaming native support")
- Requirements change (e.g., "if we need sub-second latency")

Key ADRs for most systems:

  • API transport (REST vs gRPC vs GraphQL)
  • Database strategy (monolithic SQL vs sharding vs event sourcing)
  • Async patterns (message queue vs function triggers)
  • Deployment model (monolith vs services)

3. Component Decomposition

  • Domain-driven boundaries: align technical structure to business domains
  • What data lives where: which service owns the source of truth?
  • Synchronous vs async calls: where is strong consistency required?
  • Failure mode design: if component X fails, what breaks and why?

4. Design Patterns Applied Judiciously

Use a pattern if it solves an actual pain point, not because it sounds professional.

Repository Pattern

  • When: multiple data sources (SQL + cache + API), want pluggable implementations
  • When NOT: single well-designed table with ORM, complexity tax isn't worth it
  • Fix Example: Instead of UserRepository.get_by_email(), inject data layer interface

Factory Pattern

  • When: complex object creation, parametric type selection
  • When NOT: simple constructors; you're not saving real complexity
  • Real use: Create different logger implementations based on environment

Strategy Pattern

  • When: multiple algorithms for the same task; client needs to swap them
  • Real example: Payment processors (Stripe vs Square vs PayPal), swap based on config
  • Anti-pattern: Strategy for "is user admin?" — that's an if statement, not a pattern

Observer/Pub-Sub

  • When: decoupling event producers from consumers (e.g., user signup → email, analytics, audit log)
  • Cost: eventual consistency, harder debugging (event fired but subscriber down?), monitoring complexity
  • Design: Keep synchronous work in the request, async work in subscribers

Mediator Pattern

  • When: many objects need coordinated interaction (orchestration)
  • Cost: single point of failure, hard to test (mediator knows everything)
  • Prefer: explicit interfaces between components over hidden mediator magic

SOLID Principles: Applied, Not Preached

Single Responsibility Principle (SRP)

Problem: Class does two things, changes for two reasons, hard to test.

# VIOLATION: UserService handles both auth AND email notifications
class UserService:
    def register(self, email, password):
        user = User(email, self.hash(password))
        db.save(user)
        self.send_welcome_email(user)  # Not your job

Fix: Inject an email service, decouple notification policy.

class UserService:
    def __init__(self, email_service: EmailService):
        self.email_service = email_service

    def register(self, email, password):
        user = User(email, self.hash(password))
        db.save(user)
        self.email_service.send_welcome(user)  # Now testable

Open/Closed Principle (OCP)

Problem: Adding a new payment processor requires changing existing code.

# VIOLATION: Modify the router for each provider
if processor == "stripe":
    charge_stripe(...)
elif processor == "square":
    charge_square(...)  # Edit the handler every time

Fix: Strategy pattern with registry.

class PaymentRouter:
    def __init__(self, providers: dict[str, PaymentProvider]):
        self.providers = providers

    def charge(self, provider_name, amount):
        return self.providers[provider_name].charge(amount)

# Register once at startup
router = PaymentRouter({
    "stripe": StripeProvider(...),
    "square": SquareProvider(...)
})

Liskov Substitution Principle (LSP)

Problem: Subclass breaks the contract of the parent.

# VIOLATION: CachedUserRepository is not truly a UserRepository
class UserRepository:
    def get(self, id): ...

class CachedUserRepository(UserRepository):
    def get(self, id):
        if id in cache: return cache[id]
        # Lies: doesn't fetch from DB; caller thinks it's fresh

Fix: Don't inherit; compose or make caching explicit in the interface.

Interface Segregation Principle (ISP)

Problem: Class depends on a fat interface with methods it doesn't use.

# VIOLATION: UserService imports PaymentProcessor but only uses one method
class UserService:
    def __init__(self, payment: PaymentProcessor):  # Has 10 methods
        self.payment = payment

    def refund_subscription(self, user_id):
        self.payment.refund(user_id)  # Uses only this one

Fix: Depend on a narrower interface.

class RefundService:
    def __init__(self, refunder: Refundable):  # Single method interface
        self.refunder = refunder

    def refund_subscription(self, user_id):
        self.refunder.refund(user_id)

Dependency Inversion Principle (DIP)

Problem: High-level modules depend on low-level concrete details.

# VIOLATION: UserService is tightly coupled to PostgreSQL
class UserService:
    def __init__(self):
        self.db = PostgreSQL(...)  # Hardcoded dependency

Fix: Depend on abstraction; inject concrete implementation.

class UserService:
    def __init__(self, repo: UserRepository):  # Interface dependency
        self.repo = repo

# At bootstrap:
app.use(UserService(PostgreSQLUserRepository(...)))

Architectural Styles: Trade-Offs Explained

Clean Architecture

  • Layers: Entities → Use Cases → Interface Adapters → Frameworks
  • Benefit: Business logic independent of frameworks, testable without database
  • Cost: Can feel heavy for CRUD apps; over-engineering at small scale
  • Use when: Long-lived product, changing requirements, large team

Hexagonal (Ports & Adapters)

  • Core idea: Business logic at center, all external I/O at edges
  • Benefit: Swap adapters (PostgreSQL → MongoDB, REST → gRPC)
  • Cost: More interfaces to maintain, indirection
  • Use when: Multiple I/O protocols (API + CLI + background job), multiple persistence options

Layered

  • Layers: Presentation → Business → Data
  • Benefit: Simple, familiar, works for most web apps
  • Cost: Business logic often leaks into presentation/data layers
  • Use when: Standard CRUD app, small team, fast iteration

Event-Driven

  • Pattern: Components communicate via events (produced → broker → consumed)
  • Benefit: Decouples producers from consumers, scales reads independently
  • Cost: Eventual consistency, harder debugging, requires idempotent consumers
  • Use when: High-scale systems, many independent subscribers, can tolerate eventual consistency

CQRS (Command Query Responsibility Segregation)

  • Split: Separate write model (commands) from read model (queries)
  • Benefit: Optimize reads independently (caching, denormalization), scale reads
  • Cost: Dual models, eventual consistency, complex migrations
  • Use when: Complex queries on simple writes, read-heavy workloads (analytics, dashboards)

System Design Checklist

Decomposition

  • Identify bounded contexts (business domains)
  • Assign one service/module per domain
  • Define contracts between modules (APIs, events)
  • Map data ownership (who is the source of truth?)

Boundaries & Coupling

  • Synchronous calls only where strong consistency required
  • Async (events, queues) for eventual consistency paths
  • Circular dependencies? Extract shared module or reverse dependency
  • Test each component in isolation without starting others

Evolution Planning

  • What changes most frequently? Keep that layer thinnest.
  • What is a business rule vs. infrastructure detail?
  • Can you add a feature without touching 5 files?
  • If load doubles, can you scale horizontally?

Operational Concerns

  • How do you deploy? (Monolith, blue-green, rolling, canary?)
  • How do you observe failures? (Logging, metrics, traces)
  • How do you rollback a bad deploy?
  • What's the SLA and latency budget per layer?

Anti-Patterns & Red Flags

Red FlagWhy It HurtsFix
Every class has an interfaceOver-abstraction; hard to readAbstract only what varies
10+ constructor parametersHidden coupling; hard to testInject fewer, larger objects
Circular importsLoad order fragile; circular logicExtract shared → higher level
God object (500+ lines)Does many things; hard to testExtract responsibilities
No clear data ownershipBugs from conflicting updatesDefine source of truth per domain
Async everywhereUnpredictable latency, tracing nightmareKeep happy path synchronous

Destructive Operations: Structural Refactoring Safety

Architecture reviews often recommend structural changes: splitting monolithic files, extracting modules, reorganizing directories, changing barrel exports. These are among the most dangerous operations in software development because they touch import chains across the entire codebase and can silently corrupt code.

Risk Classification

Every structural recommendation must include a risk classification:

Risk LevelOperation TypeRequired Protocol
LowSingle-file refactoring, function extraction within a fileStandard review
MediumExtracting code to a new file, adding a new moduleBuild verification after change
HighSplitting files into multiple modules, restructuring directoriesWrite → Verify → Delete order, git checkpoints per round
CriticalChanging build config, module boundaries, or project structureUser approval + full file-split protocol

The File Split Trap

The most common destructive mistake: recommending that a large file (1,000+ lines) be split into many smaller modules. This sounds like good architecture advice, and it often is — but the execution is where codebases get destroyed.

What goes wrong:

  • Text-based splitting cuts function bodies in half
  • Interfaces get truncated mid-definition
  • Orphaned closing braces become syntax errors in downstream files
  • Barrel files miss re-exports, breaking every consumer
  • export * doesn't forward default exports (TypeScript/JavaScript trap)

When recommending a file split:

  1. Classify it as HIGH or CRITICAL risk
  2. Estimate effort including verification overhead (not just the code move)
  3. Note that the original file must be preserved until all splits compile
  4. Recommend incremental rounds of 3-5 files maximum per round
  5. Require full compilation check between rounds

A working monolith is always better than a broken set of small files.

Next Steps

  1. Capture decisions as ADRs — even retrofit existing systems
  2. Map current state — identify coupling, circular deps, fat objects
  3. Plan migration path — phased refactoring toward target architecture
  4. Measure progress — coupling metrics, test coverage, time-to-feature
  5. Revisit annually — scale changes, team changes, tech changes invalidate old decisions
  6. Verify structural changes — any refactoring that moves code between files requires compilation gates and git checkpoints

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.