
Improve Architecture
- 12 installs
- 15 repo stars
- Updated August 1, 2026
- connorads/dotfiles
Explores a codebase for architectural friction and proposes module-deepening refactors as GitHub issue RFCs with multiple interface designs.
About
Explores a codebase for architectural friction and proposes module-deepening refactors as GitHub issue RFCs using a Design-It-Twice approach. A developer uses it to find refactoring opportunities, reduce complexity, or review codebase health.
- Friction-driven exploration surfaces coupling, shallow modules, and hard-to-test areas
- Uses parallel sub-agents to design multiple interface alternatives, proposed as GitHub issue RFCs
Improve Architecture by the numbers
- 12 all-time installs (skills.sh)
- Ranked #796 of 1,354 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/connorads/dotfiles --skill improve-architectureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 12 |
|---|---|
| repo stars | ★ 15 |
| Last updated | August 1, 2026 |
| Repository | connorads/dotfiles ↗ |
What it does
Explores a codebase for architectural friction and proposes module-deepening refactors as GitHub issue RFCs with multiple interface designs.
Files
Improve Architecture
Explore a codebase organically, surface architectural friction, and propose refactors via GitHub issue RFCs. Uses "Design It Twice" — multiple parallel interface designs compared in prose.
Process
1. Friction-driven exploration
Use subagents to explore the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
- Understanding one concept requires bouncing between many small files
- Modules so shallow the interface is nearly as complex as the implementation
- Pure functions extracted just for testability, but real bugs hide in how they're called
- Tightly-coupled modules creating integration risk in the seams between them
- Untested or hard-to-test areas
- Code that fights you when you try to change it
The friction you experience IS the signal. Be honest about what confused you or slowed you down — that's the most valuable information.
2. Present candidates
Show a numbered list of opportunities. For each:
- Cluster: which modules/concepts are involved
- Why coupled: shared types, call patterns, co-ownership of a concept
- Dependency category: see references/dependency-categories.md
- Test impact: existing coverage, what boundary tests would replace
Don't propose solutions yet. Ask the user which candidate to explore.
3. Frame the problem space
For the chosen candidate, write up:
- Constraints any new interface must satisfy
- Dependencies it relies on
- A rough illustrative sketch to ground the constraints (not a proposal, just framing)
Show this to the user, then immediately proceed to step 4. The user reads while sub-agents work.
4. Design It Twice
Spawn 3+ sub-agents in parallel, each with a different design constraint and a separate technical brief (file paths, coupling details, dependency category, what's being hidden).
- Agent 1: Minimise the interface — 1-3 entry points max
- Agent 2: Maximise flexibility — support many use cases and extension
- Agent 3: Optimise for the most common caller — default case trivial
- Agent 4 (if applicable): Ports & adapters 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 (see references/dependency-categories.md) 5. Trade-offs
Present designs sequentially, then compare in prose. Give an opinionated recommendation — which design is strongest and why. If elements combine well, propose a hybrid.
5. Create GitHub issue RFC
After the user picks a design (or accepts the recommendation), create a GitHub issue using gh issue create. Don't ask to review first — create and share the URL.
Use the RFC template:
## Problem
Describe the architectural friction:
- Which modules are shallow and tightly coupled
- What integration risk exists in the seams
- 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**: behaviours to verify at the interface
- **Old tests to delete**: shallow module tests made redundant
- **Test environment needs**: local stand-ins or adapters required
Replace, don't layer — old unit tests on shallow modules become waste once boundary tests exist. Delete them.
## Implementation Recommendations
Durable guidance 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 interfaceDependency Categories
When assessing a candidate for deepening, classify its dependencies. This determines the testing strategy and whether deepening is viable.
1. In-process
Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test directly.
Test strategy: Unit tests with real values, no doubles.
2. Local-substitutable
Dependencies with local test stand-ins (e.g. PGLite for Postgres, SQLite for a relational DB, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite.
Test strategy: Integration tests with local stand-in.
3. Remote-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; transport is injected.
Test strategy: In-memory adapter for tests, real HTTP/gRPC adapter for production. Consumer-driven contract tests (Pact-style) if the remote service has its own release cycle.
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 at boundary)
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.
Test strategy: Mock/stub at the boundary only. Record real responses for regression tests if the API is stable.
Relationship to Khorikov's taxonomy
This extends Vladimir Khorikov's categories (from Unit Testing: Principles, Practices, and Patterns) with a practical distinction: Khorikov separates managed vs unmanaged out-of-process dependencies, but doesn't distinguish between your own remote services (where contract tests are possible) and true third-party services (where you can only mock). That distinction matters for choosing a testing strategy.
| This taxonomy | Khorikov equivalent |
|---|---|
| In-process | In-process, private |
| Local-substitutable | Out-of-process, managed |
| Remote-owned | Out-of-process, unmanaged (your org) |
| True external | Out-of-process, unmanaged (third party) |