
Improve Codebase Architecture
- 13 installs
- 6.3k repo stars
- Updated August 3, 2026
- sanity-io/sanity
Helps with ai & agent building tasks during AI-assisted development.
About
improve-codebase-architecture is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- improve-codebase-architecture
- AI & Agent Building
- AI-coding skill
Improve Codebase Architecture by the numbers
- 13 all-time installs (skills.sh)
- Ranked #11,355 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sanity-io/sanity --skill improve-codebase-architectureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 6.3k |
| Last updated | August 3, 2026 |
| Repository | sanity-io/sanity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Improve Codebase Architecture
Explore a codebase like an AI would, surface architectural friction, discover opportunities for improving testability, and propose module-deepening refactors as GitHub issue RFCs.
A deep module (John Ousterhout, "A Philosophy of Software Design") has a small interface hiding a large implementation. Deep modules are more testable, more AI-navigable, and let you test at the boundary instead of inside.
Process
1. Explore the codebase
Use the Agent tool with subagent_type=Explore to navigate the codebase naturally. Do NOT follow rigid heuristics — explore organically and note where you experience friction:
- Where does understanding one concept require bouncing between many small files?
- Where are modules so shallow that the interface is nearly as complex as the implementation?
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called?
- Where do tightly-coupled modules create integration risk in the seams between them?
- Which parts of the codebase are untested, or hard to test?
The friction you encounter IS the signal.
2. Present candidates
Present a numbered list of deepening opportunities. For each candidate, show:
- Cluster: Which modules/concepts are involved
- Why they're coupled: Shared types, call patterns, co-ownership of a concept
- Dependency category: See REFERENCE.md for the four categories
- Test impact: What existing tests would be replaced by boundary tests
Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?"
3. User picks a candidate
4. Frame the problem space
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
- The constraints any new interface would need to satisfy
- The dependencies it would need to rely on
- A rough illustrative code sketch to make the constraints concrete — this is not a proposal, just a way to ground the constraints
Show this to the user, then immediately proceed to Step 5. The user reads and thinks about the problem while the sub-agents work in parallel.
5. Design multiple interfaces
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a radically different interface for the deepened module.
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category, what's being hidden). This brief is independent of the user-facing explanation in Step 4. Give each agent a different design constraint:
- Agent 1: "Minimize the interface — aim for 1-3 entry points max"
- Agent 2: "Maximize flexibility — support many use cases and extension"
- Agent 3: "Optimize for the most common caller — make the default case trivial"
- Agent 4 (if applicable): "Design around the ports & adapters pattern for cross-boundary dependencies"
Each sub-agent outputs:
1. Interface signature (types, methods, params) 2. Usage example showing how callers use it 3. What complexity it hides internally 4. Dependency strategy (how deps are handled — see REFERENCE.md) 5. Trade-offs
Present designs sequentially, then compare them in prose.
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not just a menu.
6. User picks an interface (or accepts recommendation)
7. Create GitHub issue
Create a refactor RFC as a GitHub issue using gh issue create. Use the template in REFERENCE.md. Do NOT ask the user to review before creating — just create it and share the URL.
Reference
Dependency Categories
When assessing a candidate for deepening, classify its dependencies:
1. In-process
Pure computation, in-memory state, no I/O. Always deepenable — just merge the modules and test directly.
2. Local-substitutable
Dependencies that have local test stand-ins (e.g., PGLite for Postgres, in-memory filesystem). Deepenable if the test substitute exists. The deepened module is tested with the local stand-in running in the test suite.
3. Remote but owned (Ports & Adapters)
Your own services across a network boundary (microservices, internal APIs). Define a port (interface) at the module boundary. The deep module owns the logic; the transport is injected. Tests use an in-memory adapter. Production uses the real HTTP/gRPC/queue adapter.
Recommendation shape: "Define a shared interface (port), implement an HTTP adapter for production and an in-memory adapter for testing, so the logic can be tested as one deep module even though it's deployed across a network boundary."
4. True external (Mock)
Third-party services (Stripe, Twilio, etc.) you don't control. Mock at the boundary. The deepened module takes the external dependency as an injected port, and tests provide a mock implementation.
Testing Strategy
The core principle: replace, don't layer.
- Old unit tests on shallow modules are waste once boundary tests exist — delete them
- Write new tests at the deepened module's interface boundary
- Tests assert on observable outcomes through the public interface, not internal state
- Tests should survive internal refactors — they describe behavior, not implementation
Issue Template
<issue-template>
Problem
Describe the architectural friction:
- Which modules are shallow and tightly coupled
- What integration risk exists in the seams between them
- Why this makes the codebase harder to navigate and maintain
Proposed Interface
The chosen interface design:
- Interface signature (types, methods, params)
- Usage example showing how callers use it
- What complexity it hides internally
Dependency Strategy
Which category applies and how dependencies are handled:
- In-process: merged directly
- Local-substitutable: tested with [specific stand-in]
- Ports & adapters: port definition, production adapter, test adapter
- Mock: mock boundary for external services
Testing Strategy
- New boundary tests to write: describe the behaviors to verify at the interface
- Old tests to delete: list the shallow module tests that become redundant
- Test environment needs: any local stand-ins or adapters required
Implementation Recommendations
Durable architectural guidance that is NOT coupled to current file paths:
- What the module should own (responsibilities)
- What it should hide (implementation details)
- What it should expose (the interface contract)
- How callers should migrate to the new interface
</issue-template>