dependency-injection
Skill870 starsChanged 21 days agoMITView on GitHub
Use when managing dependencies between systems — autoloads, service locators, @export injection, and scene injection patterns
14 other projects use a version of this.
Adopted by
Most recently taken up by
- codewithmukesh/dotnet-claude-kittheir own version, first seen Nov 8, 2026
- AnyOrgName/php-skillstheir own version, first seen Nov 8, 2026
- jars-demo/jars-skillstheir own version, first seen Nov 1, 2026
- wrinfotel/verified-android-skillstheir own version, first seen Oct 20, 2026
- andr-ca/agentharnesstheir own version, first seen Oct 15, 2026
- Resgrid/Coretheir own version, first seen Oct 8, 2026
Used with
Projects with it also use api-versioning, caching and 7 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
wpankSkillapi-versioning
API versioning strategies — URL path, header, query param, content negotiation — with breaking change classification, deprecation timelines, migration patterns, and multi-version support. Use when evolving APIs, planning breaking changes, or managing version lifecycles.
Also in 3 projects that use this
wpankSkillcaching
Caching strategies, invalidation, eviction policies, HTTP caching, distributed caching, and anti-patterns. Use when designing cache layers, choosing eviction policies, debugging stale data, or optimizing read-heavy workloads.
Also in 3 projects that use this
diego-tobalinaSkilllogging
Logging best practices for Java (SLF4J/Logback) and Node.js (Winston/Pino). Use for structured logging, log levels, and avoiding sensitive data exposure.
Also in 4 projects that use this
ffsshhttiikkSkillopenapi
OpenAPI/Swagger specification and documentation
Also in 3 projects that use this
Model Context ProtocolSkillproject-structure
Where things live in the Inspector repo — which client owns which surface, what is in core/ versus clients/web, and what each top-level directory is for. Use when locating code, deciding where a new module belongs, or answering which package owns or contains a given area of the codebase.
Also in 3 projects that use this
harrissohail6Skillclean-architecture
Clean architecture and SOLID principles. Auto-activates when designing systems or refactoring code structure.
Also in 3 projects that use this
Other versions14 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 14
- trezor/trezor-suite★ 1.1ksame name
- codewithmukesh/dotnet-claude-kit★ 808same name
- GoBetterAuth/go-better-auth★ 277same name
- Resgrid/Core★ 238same name
- garrettw/php-arch-skills★ 13same name
- AnyOrgName/php-skills★ 4same name
- HamzaSheikh768/Agents-Skills-Lap★ 3same name
- ffsshhttiikk/opencode-agents-skills★ 2same name
- kimtth/agent-skill-100-lines-or-less★ 2same name
- andr-ca/agentharness★ 1same name
4 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository62 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- authoring-godot-prompter-skillsSkill
- releasing-godot-prompterSkill
- 2d-essentialsSkill
- 3d-essentialsSkill
- ability-systemSkill
- addon-developmentSkill
- ai-navigationSkill
- animation-systemSkill
- assets-pipelineSkill
- audio-systemSkill
- beehaveSkill
- camera-systemSkill
- component-systemSkill
- csharp-godotSkill
- csharp-signalsSkill
- dedicated-serverSkill
- dialogue-managerSkill
- dialogue-systemSkill
- event-busSkill
- export-pipelineSkill
- gdextensionSkill
- gdscript-advancedSkill
- gdscript-patternsSkill
- godot-brainstormingSkill
- godot-code-reviewSkill
- godot-debuggingSkill
- godot-grillSkill
- godot-mentorSkill
- godot-optimizationSkill
- godot-project-setupSkill
- godot-testingSkill
- godot-uiSkill
- hud-systemSkill
- input-handlingSkill
- inventory-systemSkill
- limboaiSkill
- localizationSkill
- math-essentialsSkill
- mobile-developmentSkill
- multiplayer-basicsSkill
- multiplayer-syncSkill
- multithreadingSkill
- particles-vfxSkill
- phantom-cameraSkill
- physics-systemSkill
- player-controllerSkill
- popochiuSkill
- procedural-generationSkill
- rendering-pipelineSkill
- resource-patternSkill
- responsive-uiSkill
- save-loadSkill
- scene-organizationSkill
- shader-basicsSkill
- state-machineSkill
- tween-animationSkill
- using-godot-prompterSkill
- web-exportSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
1 rule. 0 are common, 0 are distinctive, and 1 is very specific to this project.
0
Common
Most of the 13 edited versions we read keep these.
0
Distinctive
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 64
Every node that calls AudioManager directly is coupled to its concrete implementation.
“Common” is measured: kept by at least 6 in 10 of the 13 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dependency Injection in Godot 4.3+
Patterns for wiring dependencies between systems so nodes stay loosely coupled, swappable, and testable. All examples target Godot 4.3+ with no deprecated APIs.
Related skills: godot-testing for test-friendly architecture, event-bus for signal-based decoupling, godot-project-setup for autoload registration.
1. The Problem
Tight coupling makes code hard to test, extend, and swap. The most common form in Godot is reaching directly into a global autoload from everywhere in the codebase.
# BAD — tight coupling via direct autoload access scattered everywhere
# player.gd
func take_damage(amount: int) -> void:
health -= amount
AudioManager.play_sfx("hurt") # hard dependency on AudioManager
UIManager.update_health_bar(health) # hard dependency on UIManager
if health <= 0:
GameState.record_death() # hard dependency on GameState
# enemy.gd
func attack() -> void:
AudioManager.play_sfx("attack") # same AudioManager dependency again// BAD — tight coupling via direct autoload / global access scattered everywhere
// Player.cs
public partial class Player : CharacterBody3D
{
private int _health = 100;
public void TakeDamage(int amount)
{
_health -= amount;
GetNode<AudioManager>("/root/AudioManager").PlaySfx("hurt"); // hard dependency
GetNode<UIManager>("/root/UIManager").UpdateHealthBar(_health); // hard dependency
if (_health <= 0)
GetNode<GameState>("/root/GameState").RecordDeath(); // hard dependency
}
}
// Enemy.cs
public partial class Enemy : CharacterBody3D
{
public void Attack()
{
GetNode<AudioManager>("/root/AudioManager").PlaySfx("attack"); // same dependency again
}
}Problems with this approach:
- Every node that calls
AudioManagerdirectly is coupled to its concrete implementation. - Swapping
AudioManagerfor a different implementation requires changing every caller. - Unit-testing
Playerin isolation is impossible —AudioManager,UIManager, andGameStatemust all exist and be valid. - Autoload initialization order bugs silently break behaviour when scenes load.
- Hidden dependencies make it hard to see what a class actually needs to function.
2. Approach Comparison
| Pattern | Complexity | Testability | Best For |
| Autoloads | Low | Low | Truly global singletons: audio, settings, platform services |
| @export Injection | Low | High | Most nodes — wire deps in the editor, no runtime lookup needed |
| Service Locator | Medium | Medium | Plugins, optional systems, swappable implementations at runtime |
| Scene Injection | Low | High | Parent-to-child wiring: Level sets up Enemy, HUD sets up sub-panels |
3. Autoloads as Singletons
Register a script in Project Settings → Globals → Autoload for global access (AudioManager.play_sfx(...), GameState.score = 100). Best for cross-cutting concerns: audio, save state, event bus, settings. Resist autoloading domain-specific systems (those should be scene-injected).
See references/autoloads.md for the full AudioManager example (SFX + crossfade music) in GDScript + C#.
4. @export Node Injection
Expose collaborator nodes as @export var health_component: HealthComponent, then wire in the Inspector or via parent scene. Lifecycle: @export properties are assigned BEFORE _ready().
See references/export-injection.md for full
@exportpatterns (GDScript + C#) and lifecycle notes.
5. Service Locator Pattern
A central registry autoload mapping String keys to service instances. Services register themselves at _ready(), deregister at _exit_tree(), consumers call ServiceLocator.get_service(name). Useful when you want flexible runtime swap of implementations (testing, mods, A/B variants).
See references/service-locator.md for the full Service Locator (GDScript + C#) with typed helper methods.
6. Scene Injection
Parent scene loads its children, then in _ready() walks the tree assigning dependencies (enemy.player = $Player). Children declare @export properties but the parent — not the Inspector — sets them. Best for game-specific dependencies that change per level.
See references/scene-injection.md for the parent-injects-children pattern (GDScript + C#).
7. Testing with Dependency Injection
Injecting fakes / test doubles is what makes nodes testable. For autoloads: mock-replace before the test scene loads. For @export injection: swap the export to a test double. For Service Locator: register a fake under the same key.
See references/testing-with-di.md for GUT-based test patterns showing each injection technique.
8. When to Use What
| Situation | Recommended Pattern |
| Service used by nearly every node in every scene | Autoload singleton |
| Node needs 1–3 deps, scene is editor-authored | @export injection |
| System is optional or swappable at runtime | Service Locator |
| Parent scene constructs children and knows their needs | Scene injection |
| Writing tests for a node with external dependencies | @export or property injection + stubs |
| Plugin that must work in any project | Service Locator (self-registers, no assumptions) |
| Two sibling nodes need the same dep | Let their parent hold it and inject downward |
Quick decision guide:
Does every scene in the project need it?
YES → Autoload singleton
NO ↓
Is the dependency known at edit-time and wired in the Inspector?
YES → @export injection
NO ↓
Does the dependency need to be swapped at runtime (plugins, A/B testing)?
YES → Service Locator
NO ↓
Does a parent scene own both the consumer and the dependency?
YES → Scene injection
NO → Reconsider — either promote to autoload or restructure ownership9. Anti-patterns
Autoload for everything
# BAD — GameManager, EnemySpawner, InventorySystem, DialogueSystem all as autoloads.
# Every node in the game is coupled to every other system at module level.
# Test one component → must initialise all autoloads.
# GOOD — Only AudioManager, Settings, and SceneTransition are autoloads.
# EnemySpawner is a node in the Level scene, injected into enemies that need it.// BAD — GameManager, EnemySpawner, InventorySystem, DialogueSystem all as autoloads.
// Every node in the game is coupled to every other system at module level.
// Test one component → must initialise all autoloads.
// GOOD — Only AudioManager, Settings, and SceneTransition are autoloads.
// EnemySpawner is a node in the Level scene, injected into enemies that need it.Deep dependency chains
# BAD — Player needs HealthComponent, which needs AudioManager,
# which needs SoundBank, which needs FileSystem...
# A change deep in the chain breaks everything above it.
# GOOD — flatten: HealthComponent takes only AudioManager (or a narrow interface).
# Each node declares only immediate dependencies.// BAD — Player needs HealthComponent, which needs AudioManager,
// which needs SoundBank, which needs FileSystem...
// A change deep in the chain breaks everything above it.
// GOOD — flatten: HealthComponent takes only AudioManager (or a narrow interface).
// Each node declares only immediate dependencies.Circular dependencies
# BAD
# PlayerController._ready() calls ServiceLocator.get_service("inventory")
# InventorySystem._ready() calls ServiceLocator.get_service("player")
# Neither can fully initialise because the other isn't ready yet.
# GOOD — break the cycle with a signal.
# InventorySystem emits item_used; PlayerController connects to it.
# PlayerController never holds a reference to InventorySystem at all.// BAD
// PlayerController._Ready() calls ServiceLocator.GetService("inventory")
// InventorySystem._Ready() calls ServiceLocator.GetService("player")
// Neither can fully initialise because the other isn't ready yet.
// GOOD — break the cycle with a signal.
// InventorySystem emits ItemUsed; PlayerController connects to it.
// PlayerController never holds a reference to InventorySystem at all.Service Locator as a god object
# BAD — everything is registered: enemies, UI panels, individual nodes.
# ServiceLocator becomes a second, untyped scene tree.
# GOOD — only register stable, long-lived services (audio, analytics, save system).
# Short-lived nodes are wired by their parent via scene injection.// BAD — everything is registered: enemies, UI panels, individual nodes.
// ServiceLocator becomes a second, untyped scene tree.
// GOOD — only register stable, long-lived services (audio, analytics, save system).
// Short-lived nodes are wired by their parent via scene injection.Forgetting null checks after injection
# BAD — crashes if the @export was never set in the editor
func take_damage(amount: int) -> void:
audio.play_sfx("hurt") # "Nonexistent function 'play_sfx' in base 'Nil'" if audio was not wired
# GOOD — guard or assert clearly
func take_damage(amount: int) -> void:
assert(audio != null, "HealthComponent: audio dependency was not injected")
audio.play_sfx("hurt")
# OR — treat it as optional
func take_damage(amount: int) -> void:
if audio != null:
audio.play_sfx("hurt")// BAD — crashes if the [Export] was never set in the editor
public void TakeDamage(int amount)
{
_audio.PlaySfx("hurt"); // NullReferenceException if _audio was not wired
}
// GOOD — guard or assert clearly
public void TakeDamage(int amount)
{
if (_audio == null)
{
GD.PushError("HealthComponent: audio dependency was not injected");
return;
}
_audio.PlaySfx("hurt");
}
// OR — treat it as optional
public void TakeDamage(int amount)
{
_audio?.PlaySfx("hurt");
}10. Checklist
- Autoloads are used only for genuinely global services (audio, settings, platform)
- Nodes declare their dependencies explicitly (
@exportor a public property) rather than callingget_nodeon distant relatives @exportfields are validated (assertor null check) before use- Service Locator services call
unregisterin_exit_tree()/_ExitTree() - Scene injection is done in the parent's
_ready(), after children are fully initialised - No circular dependencies between services or autoloads
- Each node depends only on its immediate collaborators — no deep chains
- Test stubs/mocks are plain nodes that implement the same interface as the real service
- C#
@export([Export]) dependencies are disconnected / cleared in_ExitTree()if they hold event subscriptions - Service Locator is not used to store scene-specific or short-lived nodes
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

