From coding-agent
Centralized config and lightweight dependency injection — single config module, validated at startup, factory/constructor injection for wiring, composition root pattern. No heavy DI frameworks unless the app actually needs them.
npx claudepluginhub devjarus/coding-agentThis skill uses the workspace's default tool permissions.
Two related concerns: where config comes from, and how dependencies are wired. Both follow the same principle — **centralize and inject, don't scatter and import**.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Two related concerns: where config comes from, and how dependencies are wired. Both follow the same principle — centralize and inject, don't scatter and import.
process.env / os.environ / System.getenv is accessed in exactly ONE file — the config module. Everything else receives config via injection.
.env gitignored, .env.example committedconfig.database.url, config.auth.jwtSecretconfig.isProduction not process.env.NODE_ENV === 'production'For framework-specific config patterns, see rules/patterns-by-framework.md.
1. Define interfaces/protocols for dependencies
2. Accept them via constructor/factory parameters
3. Wire everything in ONE place (composition root)
4. For tests, pass fakes directly — no framework needed
5. Config is just another dependency
| Complexity | Pattern | Example |
|---|---|---|
| Simple (< 15 services) | Factory functions + constructor injection | Most apps start here |
| Medium (15-50 services) | Composition root with grouped factory functions | Growing apps |
| Complex (50+ services, multiple entry points) | Lightweight container (awilix, Wire, Dagger) | Large apps, monorepos |
| Framework provides it | Use the framework's DI (FastAPI Depends, Spring, SwiftUI Environment) | When it's idiomatic |
Default: start with factory functions. Add a container only when the composition root becomes painful.
One file/function that wires all dependencies. The ONLY place that knows about concrete implementations.
src/
├── composition-root.ts ← wires everything, knows all concrete classes
├── config.ts ← reads env vars, exports typed config
├── services/
│ ├── user-service.ts ← accepts interfaces, knows nothing about DB/config
│ └── order-service.ts
├── repositories/
│ ├── user-repo.ts ← implements interface, accepts db connection
│ └── order-repo.ts
└── index.ts ← calls composition root, starts server
For language-specific composition root patterns, see rules/dependency-injection.md.
WITHOUT DI:
Service imports DB directly → can't test without real DB
Service imports config directly → can't test with different config
Must mock imports → fragile, tied to file paths
WITH DI:
Service accepts interfaces → pass fakes in tests
No mocking library needed for most tests
Tests are fast, isolated, don't need infrastructure
shared / instance / module-level vars. Makes testing painful.Container.resolve(Thing) inside business logic hides dependencies.@Autowired lateinit var repo hides what a class needs.import { db } from '../db' hardwires the dependency.Wave 1 (foundation) should set up:
.env.example — every var with description and safe default