Architecture Analyzer
Analyze a software codebase and produce a single comprehensive Markdown document that gives an LLM everything it needs to build, extend, debug, and test the codebase.
The output is not documentation for humans — it's an LLM context document. Every section exists because it helps a model write correct code on the first try: knowing which patterns to follow, which conventions to respect, where new code goes, and what to avoid.
This skill is stack-agnostic. It works on .NET, Node.js/TypeScript, Python, Java/Kotlin, Go, Ruby, PHP, Rust, and polyglot repos. The exploration steps describe what to discover; the per-stack hints describe where to look for it. When analyzing a .NET solution, the depth and precision must match a .NET-specialized analyzer — the generality of this skill is a layer on top, not a reduction in rigor.
Before You Start
Read the output template at references/output-template.md. It defines the exact sections, ordering, and format of the final document. Keep it open as your guide throughout the analysis.
Workflow Overview
The analysis has three phases:
- Detect — identify the primary stack(s) so subsequent steps use the right vocabulary and file patterns
- Explore — systematically scan the codebase to gather raw facts
- Synthesize — assemble those facts into the output template with real code snippets
Do NOT start writing the document until you've completed exploration. Premature writing leads to incomplete sections and backtracking.
Phase 0: Detect the Stack
Before deep exploration, identify the primary language(s), runtime(s), and build system(s). This determines which file patterns and conventions to look for in every later step.
Run these checks in parallel:
| Stack |
Manifest / Marker Files |
| .NET |
*.sln, *.csproj, *.fsproj, *.vbproj, global.json, Directory.Build.props, Directory.Packages.props |
| Node.js / TypeScript |
package.json, tsconfig.json, pnpm-workspace.yaml, nx.json, turbo.json, lerna.json |
| Python |
pyproject.toml, setup.py, setup.cfg, requirements*.txt, Pipfile, poetry.lock, uv.lock |
| Java / Kotlin |
pom.xml, build.gradle, build.gradle.kts, settings.gradle, gradlew |
| Go |
go.mod, go.sum, go.work |
| Ruby |
Gemfile, *.gemspec, Rakefile |
| PHP |
composer.json, composer.lock |
| Rust |
Cargo.toml, Cargo.lock |
| Other |
Dockerfile, Makefile, BUILD.bazel, flake.nix |
If multiple stacks coexist, classify each top-level module/service by its manifest and analyze each one with the appropriate conventions. Note the polyglot structure explicitly in the output document.
Record the detected stack(s); every later step has stack-specific guidance you'll consult.
Phase 1: Explore
Work through these steps in order. Each step builds context for the next. Use search tools aggressively — file_search, grep_search, read_file, and semantic_search are your primary instruments.
Step 1: Repository Structure & Project Map
Find the build/solution manifest(s) and enumerate all projects/modules/packages.
Stack hints:
- .NET: Read
*.sln to get all *.csproj paths; also file_search **/*.csproj to catch any not in the .sln. For each project extract <TargetFramework>, output type (Exe/Library/Web SDK), and <ProjectReference> edges. Also check Directory.Build.props, Directory.Packages.props, global.json, .editorconfig.
- Node.js / TS: Read root
package.json. For monorepos, expand workspaces, pnpm-workspace.yaml, nx.json, turbo.json to find every package. For each package, extract name, main/exports, dependencies, devDependencies, and tsconfig.json references.
- Python: Read
pyproject.toml (PEP 621 / Poetry / uv / Hatch sections) or setup.cfg/setup.py. For monorepos, look for packages/, src/, multiple pyproject.toml files. Capture dependencies and entry points ([project.scripts]).
- Java / Kotlin: Parse
pom.xml (<modules>) or settings.gradle{.kts} (include(...)). For each module read its pom.xml / build.gradle for dependencies and packaging type (jar/war).
- Go: Read
go.mod (module path, Go version, requires). For multi-module repos, use go.work. Treat each cmd/<name>/main.go as an executable entry point.
- Ruby: Read
Gemfile and any *.gemspec. For Rails, note config/application.rb and engines.
- PHP: Read
composer.json (require, autoload PSR-4 roots).
- Rust: Read root
Cargo.toml [workspace] members; each member has its own Cargo.toml.
For each project/module/package, extract:
- Name, type (library, executable, web service, worker, test suite)
- Target runtime / language version
- Dependency edges (project-to-project)
- Whether it's a build artifact, a test, or a tool
Also check for repo-wide config:
- Lockfiles (pin language/runtime versions)
- Linter/formatter configs (
.editorconfig, .eslintrc, ruff.toml, .rubocop.yml, etc.)
- Pre-commit / git hooks
- Container/orchestration manifests (
Dockerfile, docker-compose.yml, k8s/, helm/)
Why this matters: The project map is the skeleton of the entire document. Every later section maps back to a project.
Step 2: Tech Stack
Build the technology inventory from manifests and lockfiles.
Capture, where applicable:
- Runtime / language version (pin from lockfile or version manager file:
global.json, .nvmrc, .python-version, .ruby-version, go.mod, rust-toolchain.toml)
- Web / API framework (ASP.NET Core, Express, Fastify, NestJS, Next.js, FastAPI, Django, Flask, Spring Boot, Rails, Laravel, Gin, Echo, Axum, etc.)
- ORM / data access (EF Core, Dapper, Prisma, TypeORM, Drizzle, SQLAlchemy, Django ORM, Hibernate/JPA, GORM, ActiveRecord, Eloquent, sqlx, Diesel)
- Primary database (PostgreSQL, MySQL, SQL Server, Oracle, MongoDB, DynamoDB, Redis, etc.) — read connection strings, driver packages, infra config
- Auth (JWT, OIDC/OAuth provider, session cookies, SAML; library + identity provider)
- Cloud / infra SDKs (AWS SDK, Azure SDK, GCP client libs)
- Messaging / queues (Kafka, RabbitMQ, SQS, SNS, EventBridge, Service Bus, MediatR/MassTransit/NServiceBus, BullMQ, Celery, Sidekiq)
- Testing (framework + assertion lib + mocking lib + coverage tool)
- Serialization (System.Text.Json, Newtonsoft.Json, Jackson, Gson, Pydantic, Marshmallow, encoding/json, serde)
- UI (Blazor, React, Vue, Angular, Svelte, Razor, ERB, Thymeleaf, etc.)
- HTTP client strategy (
IHttpClientFactory, axios, fetch, node-fetch, requests, httpx, OkHttp, reqwest, etc.)
- API docs tooling (Swashbuckle, NSwag, swagger-jsdoc, drf-spectacular, springdoc)
- API versioning approach
Extract actual version numbers from lockfiles or pinned manifests.
Step 3: Project Classification & Dependency Map
Classify each project/module/package into a layer. Common layers across stacks:
| Layer |
Signals |
| Presentation / API |
HTTP routing, controllers/handlers, request DTOs, web framework entry points |
| Application |
Use cases, commands/queries, handlers, orchestration logic, no direct infra refs |
| Domain / Core |
Entities, value objects, interfaces, result types, domain services; no infra refs |
| Data / Persistence |
ORM context, entity/schema definitions, migrations, repositories |
| Infrastructure |
Implements Core interfaces, API clients, cloud SDK usage, file/email/queue adapters |
| Tests |
Test framework references, fixtures, test base classes |
| Workers / Jobs |
Background services, queue consumers, schedulers, Lambda/Function handlers |
| Shared / Contracts |
DTOs/types/protobuf shared across projects, no business logic |
| Frontend |
UI app(s), build pipeline, client-side state |
Build the dependency graph from project references / package imports. Identify:
- Dependency direction (should flow downward: API → Application → Core → Data)
- Any violations or circular references
- Legacy projects (naming, comments, thin wrappers, deprecation markers)
Hosting model per executable: For each runnable project, determine how it runs:
- .NET: Look for
Amazon.Lambda.AspNetCoreServer, LambdaEntryPoint, FunctionHandler (Lambda); Dockerfile (container); WebApplication.Run() vs Lambda bootstrap in Program.cs/Startup.cs; Coravel (AddScheduler, UseScheduler).
- Node.js:
serverless.yml, sst.config.*, AWS CDK Function constructs, @vercel/*, next.config.*; Dockerfile; pm2 ecosystem files.
- Python:
mangum/aws-lambda-powertools (Lambda); gunicorn/uvicorn/hypercorn config; Dockerfile; Celery beat (scheduler).
- Java/Kotlin:
spring-boot-starter (jar), war packaging, aws-lambda-java-*, Dockerfile.
- Go:
cmd/<name>/main.go entry points; lambda.Start for Lambda.
Do NOT assume all projects share the same hosting model — API projects may run on Lambda while background jobs run as standalone hosts.
Step 4: Design Patterns
This is the most important exploration step. Scan for these patterns — they dictate how a model should write new code. Each pattern below is described in stack-neutral terms with stack hints for discovery.
CQRS / Mediator / Use-Case dispatch:
- .NET:
ICommand, IQuery, IRequest, ICommandHandler, IQueryHandler, IRequestHandler, MediatR, custom IMediator.
- TS/JS: NestJS
@CommandHandler/@QueryHandler, custom command bus classes, tRPC procedures.
- Python: hand-rolled command bus,
dependency-injector providers, FastAPI dependencies acting as use cases.
- Java/Kotlin: Axon Framework, Spring
@Service use-case classes, custom CommandBus.
- Go: handler functions per use case,
usecase package convention.
For all stacks: look at how handlers are discovered/registered (assembly scanning, decorators, DI container modules, manual wiring).
Result / Error Handling:
- .NET:
Result<T>, ErrorOr<T>, OneOf, exception-based.
- TS:
Result/Either (neverthrow, fp-ts), discriminated unions, thrown errors.
- Python:
returns library Result, raised exceptions, tuple (value, error).
- Java/Kotlin:
Either (Arrow), Result, checked exceptions.
- Go: idiomatic
(value, error) returns; custom error types.
- Rust:
Result<T, E>, ? operator, thiserror/anyhow.
Document how handlers return success/failure and how the API layer translates that into HTTP responses.
Validation:
- .NET:
AbstractValidator (FluentValidation), IValidator, custom IBusinessValidator, data annotations.
- TS: Zod, Yup, class-validator, Joi, Valibot.
- Python: Pydantic models, marshmallow, manual validators.
- Java/Kotlin: Bean Validation (
jakarta.validation), Hibernate Validator.
- Go:
go-playground/validator, manual.
- Rails: ActiveModel validations.
Capture how validation is wired (decorators, pipeline middleware, framework integration, manual calls).
Pipeline / Middleware / Interceptor chains:
- .NET:
IPipelineBehavior, custom decorators, ASP.NET middleware.
- TS: Express/Fastify middleware, NestJS interceptors/guards/pipes, tRPC middleware.
- Python: FastAPI dependencies/middleware, Django middleware, DRF permissions/throttles.
- Java/Kotlin: Spring
HandlerInterceptor, Filter, AOP aspects.
- Go:
http.Handler middleware chains.
For each pipeline/decorator, read the ACTUAL implementation and capture:
- Execution order
- Concurrency model (sequential vs parallel; e.g.,
Task.WhenAll, Promise.all, asyncio.gather, goroutines)
- Error aggregation strategy (first-error-wins? collect all?)
- What it logs
These details affect how a model writes new validators/handlers; getting them wrong causes subtle runtime bugs.
Domain Events / Outbox:
- .NET:
IDomainEvent, INotification, OutboxMessage table.
- TS: NestJS event emitter, custom event bus.
- Python:
blinker, custom dispatchers.
- Java/Kotlin: Spring
ApplicationEvent, Axon events.
- Cross-stack: outbox/inbox tables in DB, transactional event publishing.
Repository Pattern:
- Look for explicit repository interfaces vs. direct ORM context/session injection.
- .NET:
IRepository, repository base class, or direct DbContext — and whether reads go through a IDbContext read-only interface (no SaveChangesAsync) while writes use concrete DbContext.
- TS: TypeORM repositories, Prisma client directly, custom repository classes; check for read-only wrappers.
- Python: SQLAlchemy sessions directly, repository classes, Django ORM managers; check for read-only session/protocol vs write session.
- Java: Spring Data
Repository, custom JPA repositories; check for @Transactional(readOnly = true) query repositories vs write repositories.
DI / Composition Root Registration:
- .NET:
DependencyInjection.cs, Program.cs/Startup.cs, Scrutor scanning, lifetime (singleton/scoped/transient).
- TS: NestJS modules, InversifyJS, tsyringe, manual factory wiring.
- Python:
dependency-injector, FastAPI Depends, manual wiring.
- Java/Kotlin: Spring
@Configuration/@Bean, scope annotations (@Singleton/@RequestScope).
- Go: wire, manual constructor injection.
For every stack, document the lifetime/scope of each registration category (handlers, validators, decorators, infrastructure services). Lifetime mistakes are a top source of runtime bugs.
For each pattern found, locate one canonical example — a real file that best demonstrates the pattern. You'll extract code from it later.
Step 5: Data Layer
For each data store in use, capture schema management, access patterns, and conventions.
Relational + ORM:
- .NET / EF Core: Find classes inheriting
DbContext; read it for DbSet count, SaveChanges overrides, auditing interceptors, conventions. Find IEntityTypeConfiguration<T> for each entity. Check migration folder. Read 2–3 entity configs fully for HasData(...) seed patterns, HasCheckConstraint(...) (capture naming convention), and recurring fluent API (HasMaxLength, IsFixedLength, HasConversion, collation, computed columns).
- TS / Prisma: Read
schema.prisma — models, relations, @@map, @@index, enums, previewFeatures. Check migrations/ history.
- TS / TypeORM / Drizzle: Entity classes with decorators; migration directory; data source configuration.
- Python / SQLAlchemy:
Base = declarative_base(), mapped classes, __tablename__, Alembic versions/ directory, env.py.
- Python / Django:
models.py per app, migrations/ per app, Meta classes, custom managers.
- Java / JPA:
@Entity classes, @Table, @Column, Liquibase/Flyway in db/migration/.
- Ruby / ActiveRecord:
app/models/*.rb, db/schema.rb, db/migrate/*.
- Go: GORM tags, sqlx queries, raw SQL with
migrate/goose.
Document for all:
- Base entity / mixin patterns (audit fields:
CreatedAt, UpdatedAt, CreatedBy)
- Schema / namespace partitioning
- Read-interface vs write-concrete split: many codebases expose a read-only interface that hides save/commit so query handlers can't accidentally mutate, while command handlers depend on the concrete context that has save/commit. Check whether the codebase follows this split and what the interface is named. Stack hints:
- .NET / EF Core:
IDbContext (or IReadDbContext, IApplicationDbContextReadOnly) — typically exposes DbSet<T> / IQueryable<T> but NO SaveChangesAsync. The concrete DbContext is used for writes.
- TS / Prisma: a typed wrapper exposing only
findMany/findUnique/findFirst (no create/update/delete/$transaction), or the full PrismaClient for writes.
- TS / TypeORM:
EntityManager vs read-only repository wrappers; or separate read/write DataSource.
- Python / SQLAlchemy: a read-only session factory (autoflush off, no
commit) vs the write session; or a ReadOnlySession protocol.
- Java / JPA:
@Transactional(readOnly = true) query repositories vs write repositories; or separate read/write EntityManager qualifiers.
- Go / GORM: separate
*gorm.DB instances (read replica vs primary) injected by role.
- Rule to capture and document: never inject both the read interface and the write concrete into the same class. If a class needs to write, it depends on the write concrete and uses it for reads too. Query handlers depend on the read interface only.
- Naming conventions (snake_case columns? PascalCase entities? configured how?)
- Transaction patterns used in handlers (
using var transaction = ..., @Transactional, db.session.begin(), unitOfWork)
- Cancellation/timeout conventions (e.g.,
CancellationToken rules in .NET, AbortSignal in JS, context.Context in Go)
- Enum ↔ lookup-table patterns
- Soft-delete / row-level security conventions
Non-relational:
- MongoDB: collections, indexes, validation schemas, change streams.
- DynamoDB: table definitions in IaC, single-table vs multi-table, GSIs, access patterns.
- Redis: key naming conventions, pub/sub channels, expiry policies.
Step 6: Application Layer / Modules
List the top-level business modules / domains / vertical slices.
Stack hints for "where modules live":
- .NET: directories under the Application project.
- TS (NestJS): feature modules (
*.module.ts).
- TS (other): directories under
src/features, src/domains, or apps/<name>.
- Python: Django apps, FastAPI routers, or domain packages.
- Java/Kotlin: packages under
com.<org>.<domain> or Gradle subprojects.
- Go: top-level packages or
internal/<domain>/.
- Rails: engines, namespaced controllers/models, or
app/services/<domain>.
Build a table of all modules with domain description and complexity estimate (Low/Medium/High based on file count and sub-feature depth).
Document the per-module folder convention (e.g., commands/queries/handlers/DTO subdirs, MVC controller/service/model, NestJS controller/service/dto, Django views/serializers/models). Show one tree.
Document how modules are registered / discovered — assembly scanning, decorator-based discovery, explicit module imports, framework conventions.
Step 7: Infrastructure & External Integrations
Map external integrations and their abstraction boundary.
For each integration capture: interface (in Core/Domain) → implementation (in Infrastructure) → HTTP client / SDK used.
Stack hints:
- .NET:
IHttpClientFactory, AddHttpClient, Refit, RestSharp; DelegatingHandler subclasses for cross-cutting HTTP concerns (token exchange, header propagation, retry-with-reauth); resilience via Polly / AddStandardResilienceHandler. Document each DelegatingHandler and the client it's attached to.
- TS:
axios interceptors, fetch wrapper classes, NestJS HttpModule; resilience via cockatiel, axios-retry.
- Python:
httpx clients with custom transports, requests Session wrappers, tenacity for retries.
- Java/Kotlin: Feign clients, RestTemplate/WebClient, OkHttp interceptors; Resilience4j.
- Go:
http.Client with custom Transport; libraries like go-resiliency.
Document DI lifetime / scope conventions: what's singleton vs scoped/request vs transient.
Step 8: API Layer
This step requires thoroughness — middleware, filters, and per-project auth/CORS differences are high-value for LLM code generation and easy to miss with surface-level scanning.
Controllers / Route Handlers:
- .NET: read 3–4 representative controllers ACROSS DIFFERENT API projects; identify base controller classes and what they provide (claims helpers, shared properties). Check if all controllers use the same base or newer ones differ. Look for API versioning configuration and
CacheProfile attributes.
- TS (NestJS):
@Controller classes, @UseGuards, @UseInterceptors, route versioning.
- TS (Express/Fastify): route registration files, route handler factories.
- Python (FastAPI):
APIRouter instances, dependency injection patterns, tags/response_model.
- Python (Django/DRF):
ViewSet, APIView, URL conf, serializers.
- Java (Spring):
@RestController, @RequestMapping, base controller classes.
- Go: route registration with
chi/gin/echo, handler patterns.
Middleware / Interceptors / Filters — read ALL of them, not a sample:
- Identify every middleware/interceptor/filter class (or function).
- For each, document: what request data it reads (JWT claims, headers, cookies), what context/scoped service it populates (e.g.,
CurrentUser, TenantContext, request.state.user), and the exact mapping (e.g., "country claim → CurrentUser.Country").
- Document delegation/impersonation patterns — custom headers parsed in middleware that override identity. Document header format and how delegate claims/permissions are modeled.
If more than 10 such components exist, focus on the 5 most referenced.
Action filters / decorators applied to endpoints — search exhaustively for custom attributes/decorators applied per-endpoint. For each:
- What it does (input sanitization? service-availability check? rate-limiting? logging?)
- Usage count (grep across all controllers)
- Whether it's applied globally or per-action
Auth — check EACH API project separately:
- Read the bootstrap file (
Program.cs/Startup.cs, main.ts, main.py, Application.java, etc.) of EVERY API project.
- Different projects often use different auth methods (JWT Bearer, OIDC cookie, ALB/reverse-proxy headers, Lambda Authorizer, session auth).
- Identify the ACTUAL identity provider in use (Okta, Entra ID, Auth0, Cognito, custom) — not what legacy config files suggest. The configuration class wired into the bootstrap reveals the real provider.
CORS — check EACH API project separately:
- Note restrictive (specific origins) vs permissive (allow-any) policies per project.
Exception handling:
- How unhandled exceptions become HTTP responses (exception filters, error middleware, framework defaults, problem-details responses).
- Map result/error states to HTTP status codes.
Step 9: Testing Conventions
Read: test project manifest(s) for framework, mocking library, assertion library, coverage tool
Stack hints:
- .NET: NUnit/xUnit/MSTest + Moq/NSubstitute + FluentAssertions; coverage via coverlet.
- TS: Jest/Vitest/Mocha + ts-mockito/jest mocks + testing-library; coverage via built-in.
- Python: pytest/unittest + unittest.mock/pytest-mock + hypothesis; coverage via coverage.py.
- Java/Kotlin: JUnit5 + Mockito/MockK + AssertJ; coverage via JaCoCo.
- Go: built-in
testing, testify, gomock; coverage via go test -cover.
- Ruby: RSpec / Minitest + WebMock + VCR.
Test base class / fixture discovery — be exhaustive:
- Search for ALL classes/files providing shared test setup:
*TestBase, *Base, *Fixture, pytest conftest.py fixtures, RSpec shared contexts, JUnit @ExtendWith extensions, Go test helpers in testutil/.
- For each, document what setup it provides (DB transaction wrapping, mock initialization, HTTP context setup, seed data), its inheritance chain, and any special attributes (
[NonParallelizable], @Execution(SAME_THREAD), pytest serial markers).
- Categorize: database test base, controller/HTTP test base, service test base, integration test base.
Read: 2–3 representative test files for naming patterns, assertion style, arrange/act/assert structure
Check: are tests unit, integration, or both? How is the DB handled (in-memory? testcontainers? transaction rollback? truncate?)
Look for: HTTP mocking libraries (WireMock, MockHttp, nock, responses, httpx MockTransport)
Step 10: Coding Conventions & Build Commands
Read: linter/formatter configs (.editorconfig, eslint, prettier, ruff, rubocop, gofmt, ktlint)
Read: shared build config (Directory.Build.props, root tsconfig, root pyproject)
Observe: naming patterns across read files (visibility defaults, immutability, record/dataclass usage, collection types, async conventions)
Check: README.md, Makefile, package.json scripts, justfile, taskfile.yml, CI config (.github/workflows, .gitlab-ci.yml, Jenkinsfile) for build/run/test commands
Check: launch/run config (launchSettings.json, .vscode/launch.json, docker-compose for local dev)
Step 11: Business Flows & Legacy Code
Trace: 3–5 key features end-to-end (route handler → use case → DB/external service)
Prioritize features that touch external systems, async processing, or complex validation
Look for: comments, docstrings, XML docs, README files that reveal business domain
Identify: any projects/modules marked as legacy, deprecated, or being phased out
Check: parallel old/new implementations of the same feature
Phase 2: Synthesize
Now open references/output-template.md and write the document section by section.
Key Principles
Use real code, not pseudocode. For each pattern section, extract an actual code snippet from the codebase that best demonstrates the pattern. Trim it to the essential lines — remove imports/usings, collapse irrelevant properties with // ... (or the language's comment syntax), but keep the structure and naming authentic. If a snippet would exceed ~30 lines, trim it. The goal is pattern recognition, not full file dumps.
Be specific, not generic. Don't write "the project uses CQRS." Write "the project uses an in-house Mediator (NOT MediatR), located in ProjectName.Core.Orchestration.Mediator" or "the project uses NestJS @CommandHandler with auto-discovery via CqrsModule." Specificity is what makes this document useful for code generation.
Match the depth of a stack-specialized analyzer. When analyzing a .NET solution, the output must include every .NET-specific detail a .NET-focused analyzer would capture: DbContext patterns, IPipelineBehavior order, DI lifetimes, DelegatingHandler chains, per-project auth provider, entity configuration seed/check-constraint conventions, test base inheritance trees. The stack-agnostic framing of this skill is a layer above — it must never reduce per-stack precision. Apply the same rigor to other stacks: for TS, capture decorator metadata, module hierarchies, Prisma schema details; for Python, capture dependency overrides, SQLAlchemy session scoping, Pydantic model conventions; etc.
Include the "don't do this" rules. Conventions about what to avoid (don't pass CancellationToken to SaveChangesAsync, don't await inside loops in this codebase, don't add code to legacy projects, don't cross-reference slices) are as valuable as the positive patterns.
Tables over prose. Use tables for tech stack, project classification, entity catalogs, integration mappings, error code mappings, and quick-reference routing.
ASCII diagrams for architecture. Use box-drawing characters to show the project dependency diagram. Group projects by layer with clear arrows showing dependency direction.
Writing the Sections
Follow the template in references/output-template.md for exact structure. For each section:
- Check your exploration notes for the relevant facts
- Extract the best code snippet(s) that demonstrate the pattern — in the codebase's actual language
- Write the section following the template's format guidance
- Include any "rules" or "conventions" you observed as explicit callouts
Output
Save the final document as architecture.md in the workspace root (or the location the user specifies).
The document title should follow the pattern:
# {ProjectName} — Architecture Prompt
The opening block should state its purpose:
> **Purpose**: Feed this prompt to an LLM so it completely understands how to build, extend, debug, and test the {ProjectName} codebase.
Handling Edge Cases
Monorepo with multiple solutions/workspaces: Ask the user which to analyze, or do them all as separate documents.
Polyglot repos: Identify each service's primary stack in Phase 0 and apply the appropriate per-stack guidance per service. Produce one document with clearly delimited per-service sections, or one document per service if they're independent.
Very large codebases (50+ projects/packages): Focus on the primary dependency chain (API → Application → Core → Data → Infrastructure). Group utility/tool projects into a summary table rather than individual descriptions.
Missing patterns: If a section from the template doesn't apply (e.g., no domain events, no legacy code, no ORM because it's raw SQL), skip it. Don't pad the document with "N/A" sections.
Ambiguous conventions: When you see two different patterns for the same thing (e.g., some handlers use constructor injection, others use method injection; some routes use class-based controllers, others use functional handlers), document both and note which is the current/preferred approach.
No README or CI config: Infer build/run/test commands from the project structure and ecosystem defaults (dotnet build, npm test, pytest, go test ./..., ./gradlew test, etc.) and note that these are inferred, not documented.
1---2name: architecture-analyzer3description: Deeply analyze a software codebase (any language or framework) and produce a comprehensive architecture document optimized for LLM consumption - so any AI can build, extend, debug, and test the codebase. Outputs a single Markdown file covering tech stack, project map, design patterns, data layer, modules, infrastructure, API layer, testing, coding conventions, build commands, feature walkthrough, business flows, and legacy guidance - all with real extracted code snippets. USE FOR - generate architecture doc, analyze codebase, document codebase for AI, create codebase context, onboard AI to repo, solution overview, reverse-engineer architecture, map the codebase, understand this project, explain this codebase structure, create LLM prompt for codebase, extract project patterns, codebase knowledge base, architecture.md. Even if the user just says "document this repo" or "help me understand this codebase" or "create a context file for AI", this skill applies. Works with any stack - .NET, Node.js/TypeScript, Pytho4---5
6# Architecture Analyzer
7
8Analyze a software codebase and produce a single comprehensive Markdown document that gives an LLM everything it needs to build, extend, debug, and test the codebase.
9
10The output is not documentation for humans — it's an **LLM context document**. Every section exists because it helps a model write correct code on the first try: knowing which patterns to follow, which conventions to respect, where new code goes, and what to avoid.
11
12This skill is **stack-agnostic**. It works on .NET, Node.js/TypeScript, Python, Java/Kotlin, Go, Ruby, PHP, Rust, and polyglot repos. The exploration steps describe *what* to discover; the per-stack hints describe *where* to look for it. When analyzing a .NET solution, the depth and precision must match a .NET-specialized analyzer — the generality of this skill is a layer on top, not a reduction in rigor.
13
14## Before You Start
15
16Read the output template at [references/output-template.md](references/output-template.md). It defines the exact sections, ordering, and format of the final document. Keep it open as your guide throughout the analysis.
17
18## Workflow Overview
19
20The analysis has three phases:
21
221. **Detect** — identify the primary stack(s) so subsequent steps use the right vocabulary and file patterns
232. **Explore** — systematically scan the codebase to gather raw facts
243. **Synthesize** — assemble those facts into the output template with real code snippets
25
26Do NOT start writing the document until you've completed exploration. Premature writing leads to incomplete sections and backtracking.
27
28---
29
30## Phase 0: Detect the Stack
31
32Before deep exploration, identify the primary language(s), runtime(s), and build system(s). This determines which file patterns and conventions to look for in every later step.
33
34Run these checks in parallel:
35
36| Stack | Manifest / Marker Files |
37|-------|------------------------|
38| .NET | `*.sln`, `*.csproj`, `*.fsproj`, `*.vbproj`, `global.json`, `Directory.Build.props`, `Directory.Packages.props` |
39| Node.js / TypeScript | `package.json`, `tsconfig.json`, `pnpm-workspace.yaml`, `nx.json`, `turbo.json`, `lerna.json` |
40| Python | `pyproject.toml`, `setup.py`, `setup.cfg`, `requirements*.txt`, `Pipfile`, `poetry.lock`, `uv.lock` |
41| Java / Kotlin | `pom.xml`, `build.gradle`, `build.gradle.kts`, `settings.gradle`, `gradlew` |
42| Go | `go.mod`, `go.sum`, `go.work` |
43| Ruby | `Gemfile`, `*.gemspec`, `Rakefile` |
44| PHP | `composer.json`, `composer.lock` |
45| Rust | `Cargo.toml`, `Cargo.lock` |
46| Other | `Dockerfile`, `Makefile`, `BUILD.bazel`, `flake.nix` |
47
48If multiple stacks coexist, classify each top-level module/service by its manifest and analyze each one with the appropriate conventions. Note the polyglot structure explicitly in the output document.
49
50Record the detected stack(s); every later step has stack-specific guidance you'll consult.
51
52---
53
54## Phase 1: Explore
55
56Work through these steps in order. Each step builds context for the next. Use search tools aggressively — `file_search`, `grep_search`, `read_file`, and `semantic_search` are your primary instruments.
57
58### Step 1: Repository Structure & Project Map
59
60Find the build/solution manifest(s) and enumerate all projects/modules/packages.
61
62**Stack hints**:
63- **.NET**: Read `*.sln` to get all `*.csproj` paths; also `file_search **/*.csproj` to catch any not in the .sln. For each project extract `<TargetFramework>`, output type (Exe/Library/Web SDK), and `<ProjectReference>` edges. Also check `Directory.Build.props`, `Directory.Packages.props`, `global.json`, `.editorconfig`.
64- **Node.js / TS**: Read root `package.json`. For monorepos, expand `workspaces`, `pnpm-workspace.yaml`, `nx.json`, `turbo.json` to find every package. For each package, extract `name`, `main`/`exports`, `dependencies`, `devDependencies`, and `tsconfig.json` references.
65- **Python**: Read `pyproject.toml` (PEP 621 / Poetry / uv / Hatch sections) or `setup.cfg`/`setup.py`. For monorepos, look for `packages/`, `src/`, multiple `pyproject.toml` files. Capture dependencies and entry points (`[project.scripts]`).
66- **Java / Kotlin**: Parse `pom.xml` (`<modules>`) or `settings.gradle{.kts}` (`include(...)`). For each module read its `pom.xml` / `build.gradle` for dependencies and packaging type (jar/war).
67- **Go**: Read `go.mod` (module path, Go version, requires). For multi-module repos, use `go.work`. Treat each `cmd/<name>/main.go` as an executable entry point.
68- **Ruby**: Read `Gemfile` and any `*.gemspec`. For Rails, note `config/application.rb` and engines.
69- **PHP**: Read `composer.json` (`require`, `autoload` PSR-4 roots).
70- **Rust**: Read root `Cargo.toml` `[workspace]` members; each member has its own `Cargo.toml`.
71
72For each project/module/package, extract:
73- Name, type (library, executable, web service, worker, test suite)
74- Target runtime / language version
75- Dependency edges (project-to-project)
76- Whether it's a build artifact, a test, or a tool
77
78Also check for repo-wide config:
79- Lockfiles (pin language/runtime versions)
80- Linter/formatter configs (`.editorconfig`, `.eslintrc`, `ruff.toml`, `.rubocop.yml`, etc.)
81- Pre-commit / git hooks
82- Container/orchestration manifests (`Dockerfile`, `docker-compose.yml`, `k8s/`, `helm/`)
83
84**Why this matters**: The project map is the skeleton of the entire document. Every later section maps back to a project.
85
86### Step 2: Tech Stack
87
88Build the technology inventory from manifests and lockfiles.
89
90Capture, where applicable:
91- **Runtime / language version** (pin from lockfile or version manager file: `global.json`, `.nvmrc`, `.python-version`, `.ruby-version`, `go.mod`, `rust-toolchain.toml`)
92- **Web / API framework** (ASP.NET Core, Express, Fastify, NestJS, Next.js, FastAPI, Django, Flask, Spring Boot, Rails, Laravel, Gin, Echo, Axum, etc.)
93- **ORM / data access** (EF Core, Dapper, Prisma, TypeORM, Drizzle, SQLAlchemy, Django ORM, Hibernate/JPA, GORM, ActiveRecord, Eloquent, sqlx, Diesel)
94- **Primary database** (PostgreSQL, MySQL, SQL Server, Oracle, MongoDB, DynamoDB, Redis, etc.) — read connection strings, driver packages, infra config
95- **Auth** (JWT, OIDC/OAuth provider, session cookies, SAML; library + identity provider)
96- **Cloud / infra SDKs** (AWS SDK, Azure SDK, GCP client libs)
97- **Messaging / queues** (Kafka, RabbitMQ, SQS, SNS, EventBridge, Service Bus, MediatR/MassTransit/NServiceBus, BullMQ, Celery, Sidekiq)
98- **Testing** (framework + assertion lib + mocking lib + coverage tool)
99- **Serialization** (System.Text.Json, Newtonsoft.Json, Jackson, Gson, Pydantic, Marshmallow, encoding/json, serde)
100- **UI** (Blazor, React, Vue, Angular, Svelte, Razor, ERB, Thymeleaf, etc.)
101- **HTTP client strategy** (`IHttpClientFactory`, `axios`, `fetch`, `node-fetch`, `requests`, `httpx`, `OkHttp`, `reqwest`, etc.)
102- **API docs tooling** (Swashbuckle, NSwag, swagger-jsdoc, drf-spectacular, springdoc)
103- **API versioning approach**
104
105Extract actual version numbers from lockfiles or pinned manifests.
106
107### Step 3: Project Classification & Dependency Map
108
109Classify each project/module/package into a layer. Common layers across stacks:
110
111| Layer | Signals |
112|-------|---------|
113| **Presentation / API** | HTTP routing, controllers/handlers, request DTOs, web framework entry points |
114| **Application** | Use cases, commands/queries, handlers, orchestration logic, no direct infra refs |
115| **Domain / Core** | Entities, value objects, interfaces, result types, domain services; no infra refs |
116| **Data / Persistence** | ORM context, entity/schema definitions, migrations, repositories |
117| **Infrastructure** | Implements Core interfaces, API clients, cloud SDK usage, file/email/queue adapters |
118| **Tests** | Test framework references, fixtures, test base classes |
119| **Workers / Jobs** | Background services, queue consumers, schedulers, Lambda/Function handlers |
120| **Shared / Contracts** | DTOs/types/protobuf shared across projects, no business logic |
121| **Frontend** | UI app(s), build pipeline, client-side state |
122
123Build the dependency graph from project references / package imports. Identify:
124- Dependency direction (should flow downward: API → Application → Core → Data)
125- Any violations or circular references
126- Legacy projects (naming, comments, thin wrappers, deprecation markers)
127
128**Hosting model per executable**: For each runnable project, determine how it runs:
129- **.NET**: Look for `Amazon.Lambda.AspNetCoreServer`, `LambdaEntryPoint`, `FunctionHandler` (Lambda); `Dockerfile` (container); `WebApplication.Run()` vs Lambda bootstrap in `Program.cs`/`Startup.cs`; Coravel (`AddScheduler`, `UseScheduler`).
130- **Node.js**: `serverless.yml`, `sst.config.*`, AWS CDK `Function` constructs, `@vercel/*`, `next.config.*`; `Dockerfile`; `pm2` ecosystem files.
131- **Python**: `mangum`/`aws-lambda-powertools` (Lambda); `gunicorn`/`uvicorn`/`hypercorn` config; `Dockerfile`; Celery beat (scheduler).
132- **Java/Kotlin**: `spring-boot-starter` (jar), `war` packaging, `aws-lambda-java-*`, `Dockerfile`.
133- **Go**: `cmd/<name>/main.go` entry points; `lambda.Start` for Lambda.
134
135Do NOT assume all projects share the same hosting model — API projects may run on Lambda while background jobs run as standalone hosts.
136
137### Step 4: Design Patterns
138
139This is the most important exploration step. Scan for these patterns — they dictate how a model should write new code. Each pattern below is described in stack-neutral terms with stack hints for discovery.
140
141**CQRS / Mediator / Use-Case dispatch**:
142- **.NET**: `ICommand`, `IQuery`, `IRequest`, `ICommandHandler`, `IQueryHandler`, `IRequestHandler`, `MediatR`, custom `IMediator`.
143- **TS/JS**: NestJS `@CommandHandler`/`@QueryHandler`, custom command bus classes, tRPC procedures.
144- **Python**: hand-rolled command bus, `dependency-injector` providers, FastAPI dependencies acting as use cases.
145- **Java/Kotlin**: Axon Framework, Spring `@Service` use-case classes, custom `CommandBus`.
146- **Go**: handler functions per use case, `usecase` package convention.
147
148For all stacks: look at how handlers are discovered/registered (assembly scanning, decorators, DI container modules, manual wiring).
149
150**Result / Error Handling**:
151- **.NET**: `Result<T>`, `ErrorOr<T>`, `OneOf`, exception-based.
152- **TS**: `Result`/`Either` (neverthrow, fp-ts), discriminated unions, thrown errors.
153- **Python**: `returns` library `Result`, raised exceptions, tuple `(value, error)`.
154- **Java/Kotlin**: `Either` (Arrow), `Result`, checked exceptions.
155- **Go**: idiomatic `(value, error)` returns; custom error types.
156- **Rust**: `Result<T, E>`, `?` operator, `thiserror`/`anyhow`.
157
158Document how handlers return success/failure and how the API layer translates that into HTTP responses.
159
160**Validation**:
161- **.NET**: `AbstractValidator` (FluentValidation), `IValidator`, custom `IBusinessValidator`, data annotations.
162- **TS**: Zod, Yup, class-validator, Joi, Valibot.
163- **Python**: Pydantic models, marshmallow, manual validators.
164- **Java/Kotlin**: Bean Validation (`jakarta.validation`), Hibernate Validator.
165- **Go**: `go-playground/validator`, manual.
166- **Rails**: ActiveModel validations.
167
168Capture how validation is wired (decorators, pipeline middleware, framework integration, manual calls).
169
170**Pipeline / Middleware / Interceptor chains**:
171- **.NET**: `IPipelineBehavior`, custom decorators, ASP.NET middleware.
172- **TS**: Express/Fastify middleware, NestJS interceptors/guards/pipes, tRPC middleware.
173- **Python**: FastAPI dependencies/middleware, Django middleware, DRF permissions/throttles.
174- **Java/Kotlin**: Spring `HandlerInterceptor`, `Filter`, AOP aspects.
175- **Go**: `http.Handler` middleware chains.
176
177For each pipeline/decorator, read the ACTUAL implementation and capture:
178- Execution order
179- Concurrency model (sequential vs parallel; e.g., `Task.WhenAll`, `Promise.all`, `asyncio.gather`, goroutines)
180- Error aggregation strategy (first-error-wins? collect all?)
181- What it logs
182
183These details affect how a model writes new validators/handlers; getting them wrong causes subtle runtime bugs.
184
185**Domain Events / Outbox**:
186- **.NET**: `IDomainEvent`, `INotification`, `OutboxMessage` table.
187- **TS**: NestJS event emitter, custom event bus.
188- **Python**: `blinker`, custom dispatchers.
189- **Java/Kotlin**: Spring `ApplicationEvent`, Axon events.
190- **Cross-stack**: outbox/inbox tables in DB, transactional event publishing.
191
192**Repository Pattern**:
193- Look for explicit repository interfaces vs. direct ORM context/session injection.
194- **.NET**: `IRepository`, repository base class, or direct `DbContext` — and whether reads go through a `IDbContext` read-only interface (no `SaveChangesAsync`) while writes use concrete `DbContext`.
195- **TS**: TypeORM repositories, Prisma client directly, custom repository classes; check for read-only wrappers.
196- **Python**: SQLAlchemy sessions directly, repository classes, Django ORM managers; check for read-only session/protocol vs write session.
197- **Java**: Spring Data `Repository`, custom JPA repositories; check for `@Transactional(readOnly = true)` query repositories vs write repositories.
198
199**DI / Composition Root Registration**:
200- **.NET**: `DependencyInjection.cs`, `Program.cs`/`Startup.cs`, Scrutor scanning, lifetime (singleton/scoped/transient).
201- **TS**: NestJS modules, InversifyJS, tsyringe, manual factory wiring.
202- **Python**: `dependency-injector`, FastAPI `Depends`, manual wiring.
203- **Java/Kotlin**: Spring `@Configuration`/`@Bean`, scope annotations (`@Singleton`/`@RequestScope`).
204- **Go**: wire, manual constructor injection.
205
206For every stack, **document the lifetime/scope** of each registration category (handlers, validators, decorators, infrastructure services). Lifetime mistakes are a top source of runtime bugs.
207
208For each pattern found, locate **one canonical example** — a real file that best demonstrates the pattern. You'll extract code from it later.
209
210### Step 5: Data Layer
211
212For each data store in use, capture schema management, access patterns, and conventions.
213
214**Relational + ORM**:
215- **.NET / EF Core**: Find classes inheriting `DbContext`; read it for `DbSet` count, `SaveChanges` overrides, auditing interceptors, conventions. Find `IEntityTypeConfiguration<T>` for each entity. Check migration folder. **Read 2–3 entity configs fully** for `HasData(...)` seed patterns, `HasCheckConstraint(...)` (capture naming convention), and recurring fluent API (`HasMaxLength`, `IsFixedLength`, `HasConversion`, collation, computed columns).
216- **TS / Prisma**: Read `schema.prisma` — models, relations, `@@map`, `@@index`, enums, `previewFeatures`. Check `migrations/` history.
217- **TS / TypeORM / Drizzle**: Entity classes with decorators; migration directory; data source configuration.
218- **Python / SQLAlchemy**: `Base = declarative_base()`, mapped classes, `__tablename__`, Alembic `versions/` directory, `env.py`.
219- **Python / Django**: `models.py` per app, `migrations/` per app, `Meta` classes, custom managers.
220- **Java / JPA**: `@Entity` classes, `@Table`, `@Column`, Liquibase/Flyway in `db/migration/`.
221- **Ruby / ActiveRecord**: `app/models/*.rb`, `db/schema.rb`, `db/migrate/*`.
222- **Go**: GORM tags, sqlx queries, raw SQL with `migrate`/`goose`.
223
224**Document for all**:
225- Base entity / mixin patterns (audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`)
226- Schema / namespace partitioning
227- **Read-interface vs write-concrete split**: many codebases expose a read-only interface that hides save/commit so query handlers can't accidentally mutate, while command handlers depend on the concrete context that has save/commit. Check whether the codebase follows this split and what the interface is named. Stack hints:
228 - **.NET / EF Core**: `IDbContext` (or `IReadDbContext`, `IApplicationDbContextReadOnly`) — typically exposes `DbSet<T>` / `IQueryable<T>` but NO `SaveChangesAsync`. The concrete `DbContext` is used for writes.
229 - **TS / Prisma**: a typed wrapper exposing only `findMany`/`findUnique`/`findFirst` (no `create`/`update`/`delete`/`$transaction`), or the full `PrismaClient` for writes.
230 - **TS / TypeORM**: `EntityManager` vs read-only repository wrappers; or separate read/write `DataSource`.
231 - **Python / SQLAlchemy**: a read-only session factory (autoflush off, no `commit`) vs the write session; or a `ReadOnlySession` protocol.
232 - **Java / JPA**: `@Transactional(readOnly = true)` query repositories vs write repositories; or separate read/write `EntityManager` qualifiers.
233 - **Go / GORM**: separate `*gorm.DB` instances (read replica vs primary) injected by role.
234 - **Rule to capture and document**: never inject both the read interface and the write concrete into the same class. If a class needs to write, it depends on the write concrete and uses it for reads too. Query handlers depend on the read interface only.
235- Naming conventions (snake_case columns? PascalCase entities? configured how?)
236- Transaction patterns used in handlers (`using var transaction = ...`, `@Transactional`, `db.session.begin()`, `unitOfWork`)
237- Cancellation/timeout conventions (e.g., `CancellationToken` rules in .NET, `AbortSignal` in JS, `context.Context` in Go)
238- Enum ↔ lookup-table patterns
239- Soft-delete / row-level security conventions
240
241**Non-relational**:
242- **MongoDB**: collections, indexes, validation schemas, change streams.
243- **DynamoDB**: table definitions in IaC, single-table vs multi-table, GSIs, access patterns.
244- **Redis**: key naming conventions, pub/sub channels, expiry policies.
245
246### Step 6: Application Layer / Modules
247
248List the top-level business modules / domains / vertical slices.
249
250**Stack hints for "where modules live"**:
251- **.NET**: directories under the Application project.
252- **TS (NestJS)**: feature modules (`*.module.ts`).
253- **TS (other)**: directories under `src/features`, `src/domains`, or `apps/<name>`.
254- **Python**: Django apps, FastAPI routers, or domain packages.
255- **Java/Kotlin**: packages under `com.<org>.<domain>` or Gradle subprojects.
256- **Go**: top-level packages or `internal/<domain>/`.
257- **Rails**: engines, namespaced controllers/models, or `app/services/<domain>`.
258
259Build a table of all modules with domain description and complexity estimate (Low/Medium/High based on file count and sub-feature depth).
260
261Document the **per-module folder convention** (e.g., commands/queries/handlers/DTO subdirs, MVC controller/service/model, NestJS controller/service/dto, Django views/serializers/models). Show one tree.
262
263Document **how modules are registered / discovered** — assembly scanning, decorator-based discovery, explicit module imports, framework conventions.
264
265### Step 7: Infrastructure & External Integrations
266
267Map external integrations and their abstraction boundary.
268
269For each integration capture: interface (in Core/Domain) → implementation (in Infrastructure) → HTTP client / SDK used.
270
271**Stack hints**:
272- **.NET**: `IHttpClientFactory`, `AddHttpClient`, `Refit`, `RestSharp`; `DelegatingHandler` subclasses for cross-cutting HTTP concerns (token exchange, header propagation, retry-with-reauth); resilience via Polly / `AddStandardResilienceHandler`. Document each `DelegatingHandler` and the client it's attached to.
273- **TS**: `axios` interceptors, `fetch` wrapper classes, NestJS HttpModule; resilience via `cockatiel`, `axios-retry`.
274- **Python**: `httpx` clients with custom transports, `requests` Session wrappers, `tenacity` for retries.
275- **Java/Kotlin**: Feign clients, RestTemplate/WebClient, OkHttp interceptors; Resilience4j.
276- **Go**: `http.Client` with custom `Transport`; libraries like `go-resiliency`.
277
278Document DI lifetime / scope conventions: what's singleton vs scoped/request vs transient.
279
280### Step 8: API Layer
281
282This step requires thoroughness — middleware, filters, and per-project auth/CORS differences are high-value for LLM code generation and easy to miss with surface-level scanning.
283
284**Controllers / Route Handlers**:
285- **.NET**: read 3–4 representative controllers ACROSS DIFFERENT API projects; identify base controller classes and what they provide (claims helpers, shared properties). Check if all controllers use the same base or newer ones differ. Look for API versioning configuration and `CacheProfile` attributes.
286- **TS (NestJS)**: `@Controller` classes, `@UseGuards`, `@UseInterceptors`, route versioning.
287- **TS (Express/Fastify)**: route registration files, route handler factories.
288- **Python (FastAPI)**: `APIRouter` instances, dependency injection patterns, `tags`/`response_model`.
289- **Python (Django/DRF)**: `ViewSet`, `APIView`, URL conf, serializers.
290- **Java (Spring)**: `@RestController`, `@RequestMapping`, base controller classes.
291- **Go**: route registration with `chi`/`gin`/`echo`, handler patterns.
292
293**Middleware / Interceptors / Filters** — read ALL of them, not a sample:
294- Identify every middleware/interceptor/filter class (or function).
295- For each, document: what request data it reads (JWT claims, headers, cookies), what context/scoped service it populates (e.g., `CurrentUser`, `TenantContext`, `request.state.user`), and the exact mapping (e.g., "`country` claim → `CurrentUser.Country`").
296- Document delegation/impersonation patterns — custom headers parsed in middleware that override identity. Document header format and how delegate claims/permissions are modeled.
297
298If more than 10 such components exist, focus on the 5 most referenced.
299
300**Action filters / decorators applied to endpoints** — search exhaustively for custom attributes/decorators applied per-endpoint. For each:
301- What it does (input sanitization? service-availability check? rate-limiting? logging?)
302- Usage count (grep across all controllers)
303- Whether it's applied globally or per-action
304
305**Auth — check EACH API project separately**:
306- Read the bootstrap file (`Program.cs`/`Startup.cs`, `main.ts`, `main.py`, `Application.java`, etc.) of EVERY API project.
307- Different projects often use different auth methods (JWT Bearer, OIDC cookie, ALB/reverse-proxy headers, Lambda Authorizer, session auth).
308- Identify the ACTUAL identity provider in use (Okta, Entra ID, Auth0, Cognito, custom) — not what legacy config files suggest. The configuration class wired into the bootstrap reveals the real provider.
309
310**CORS — check EACH API project separately**:
311- Note restrictive (specific origins) vs permissive (allow-any) policies per project.
312
313**Exception handling**:
314- How unhandled exceptions become HTTP responses (exception filters, error middleware, framework defaults, problem-details responses).
315- Map result/error states to HTTP status codes.
316
317### Step 9: Testing Conventions
318
319```
320Read: test project manifest(s) for framework, mocking library, assertion library, coverage tool
321```
322
323**Stack hints**:
324- **.NET**: NUnit/xUnit/MSTest + Moq/NSubstitute + FluentAssertions; coverage via coverlet.
325- **TS**: Jest/Vitest/Mocha + ts-mockito/jest mocks + testing-library; coverage via built-in.
326- **Python**: pytest/unittest + unittest.mock/pytest-mock + hypothesis; coverage via coverage.py.
327- **Java/Kotlin**: JUnit5 + Mockito/MockK + AssertJ; coverage via JaCoCo.
328- **Go**: built-in `testing`, `testify`, `gomock`; coverage via `go test -cover`.
329- **Ruby**: RSpec / Minitest + WebMock + VCR.
330
331**Test base class / fixture discovery — be exhaustive**:
332- Search for ALL classes/files providing shared test setup: `*TestBase`, `*Base`, `*Fixture`, pytest `conftest.py` fixtures, RSpec shared contexts, JUnit `@ExtendWith` extensions, Go test helpers in `testutil/`.
333- For each, document what setup it provides (DB transaction wrapping, mock initialization, HTTP context setup, seed data), its inheritance chain, and any special attributes (`[NonParallelizable]`, `@Execution(SAME_THREAD)`, pytest `serial` markers).
334- Categorize: database test base, controller/HTTP test base, service test base, integration test base.
335
336```
337Read: 2–3 representative test files for naming patterns, assertion style, arrange/act/assert structure
338Check: are tests unit, integration, or both? How is the DB handled (in-memory? testcontainers? transaction rollback? truncate?)
339Look for: HTTP mocking libraries (WireMock, MockHttp, nock, responses, httpx MockTransport)
340```
341
342### Step 10: Coding Conventions & Build Commands
343
344```
345Read: linter/formatter configs (.editorconfig, eslint, prettier, ruff, rubocop, gofmt, ktlint)
346Read: shared build config (Directory.Build.props, root tsconfig, root pyproject)
347Observe: naming patterns across read files (visibility defaults, immutability, record/dataclass usage, collection types, async conventions)
348Check: README.md, Makefile, package.json scripts, justfile, taskfile.yml, CI config (.github/workflows, .gitlab-ci.yml, Jenkinsfile) for build/run/test commands
349Check: launch/run config (launchSettings.json, .vscode/launch.json, docker-compose for local dev)
350```
351
352### Step 11: Business Flows & Legacy Code
353
354```
355Trace: 3–5 key features end-to-end (route handler → use case → DB/external service)
356 Prioritize features that touch external systems, async processing, or complex validation
357Look for: comments, docstrings, XML docs, README files that reveal business domain
358Identify: any projects/modules marked as legacy, deprecated, or being phased out
359Check: parallel old/new implementations of the same feature
360```
361
362---
363
364## Phase 2: Synthesize
365
366Now open [references/output-template.md](references/output-template.md) and write the document section by section.
367
368### Key Principles
369
370**Use real code, not pseudocode.** For each pattern section, extract an actual code snippet from the codebase that best demonstrates the pattern. Trim it to the essential lines — remove imports/usings, collapse irrelevant properties with `// ...` (or the language's comment syntax), but keep the structure and naming authentic. If a snippet would exceed ~30 lines, trim it. The goal is pattern recognition, not full file dumps.
371
372**Be specific, not generic.** Don't write "the project uses CQRS." Write "the project uses an in-house Mediator (NOT MediatR), located in `ProjectName.Core.Orchestration.Mediator`" or "the project uses NestJS `@CommandHandler` with auto-discovery via `CqrsModule`." Specificity is what makes this document useful for code generation.
373
374**Match the depth of a stack-specialized analyzer.** When analyzing a .NET solution, the output must include every .NET-specific detail a .NET-focused analyzer would capture: DbContext patterns, `IPipelineBehavior` order, DI lifetimes, `DelegatingHandler` chains, per-project auth provider, entity configuration seed/check-constraint conventions, test base inheritance trees. The stack-agnostic framing of this skill is a layer above — it must never reduce per-stack precision. Apply the same rigor to other stacks: for TS, capture decorator metadata, module hierarchies, Prisma schema details; for Python, capture dependency overrides, SQLAlchemy session scoping, Pydantic model conventions; etc.
375
376**Include the "don't do this" rules.** Conventions about what to avoid (don't pass CancellationToken to `SaveChangesAsync`, don't await inside loops in this codebase, don't add code to legacy projects, don't cross-reference slices) are as valuable as the positive patterns.
377
378**Tables over prose.** Use tables for tech stack, project classification, entity catalogs, integration mappings, error code mappings, and quick-reference routing.
379
380**ASCII diagrams for architecture.** Use box-drawing characters to show the project dependency diagram. Group projects by layer with clear arrows showing dependency direction.
381
382### Writing the Sections
383
384Follow the template in [references/output-template.md](references/output-template.md) for exact structure. For each section:
385
3861. Check your exploration notes for the relevant facts
3872. Extract the best code snippet(s) that demonstrate the pattern — in the codebase's actual language
3883. Write the section following the template's format guidance
3894. Include any "rules" or "conventions" you observed as explicit callouts
390
391### Output
392
393Save the final document as `architecture.md` in the workspace root (or the location the user specifies).
394
395The document title should follow the pattern:
396```
397# {ProjectName} — Architecture Prompt
398```
399
400The opening block should state its purpose:
401```
402> **Purpose**: Feed this prompt to an LLM so it completely understands how to build, extend, debug, and test the {ProjectName} codebase.
403```
404
405---
406
407## Handling Edge Cases
408
409**Monorepo with multiple solutions/workspaces**: Ask the user which to analyze, or do them all as separate documents.
410
411**Polyglot repos**: Identify each service's primary stack in Phase 0 and apply the appropriate per-stack guidance per service. Produce one document with clearly delimited per-service sections, or one document per service if they're independent.
412
413**Very large codebases (50+ projects/packages)**: Focus on the primary dependency chain (API → Application → Core → Data → Infrastructure). Group utility/tool projects into a summary table rather than individual descriptions.
414
415**Missing patterns**: If a section from the template doesn't apply (e.g., no domain events, no legacy code, no ORM because it's raw SQL), skip it. Don't pad the document with "N/A" sections.
416
417**Ambiguous conventions**: When you see two different patterns for the same thing (e.g., some handlers use constructor injection, others use method injection; some routes use class-based controllers, others use functional handlers), document both and note which is the current/preferred approach.
418
419**No README or CI config**: Infer build/run/test commands from the project structure and ecosystem defaults (`dotnet build`, `npm test`, `pytest`, `go test ./...`, `./gradlew test`, etc.) and note that these are inferred, not documented.