DEV Community

Cover image for Refactoring 024 - Replace Global Variables with Dependency Injection
Maxi Contieri
Maxi Contieri

Posted on • Originally published at maximilianocontieri.com

Refactoring 024 - Replace Global Variables with Dependency Injection

Break Hidden Dependencies for Cleaner Code

TL;DR: Replace global variables with dependency injection to improve testability and reduce coupling. πŸ’‰

Problems Addressed πŸ˜”

Related Code Smells πŸ’¨

Steps πŸ› οΈ

  1. Identify global variables used across your codebase.
  2. Create a real-world abstraction to encapsulate these variables.
  3. Pass dependencies explicitly via function parameters or constructors.
  4. Refactor existing code to use the new dependency-injected structure.
  5. Remove the original global variable declarations.

Sample Code πŸ’»

Before ❌

// This global variable holds the API configuration  
const globalConfig = { apiUrl: "https://api.severance.com" };  

function fetchOuties() {  
  return fetch(`${globalConfig.apiUrl}/outies`);  
  // globalConfig is NOT passed as parameter
}
Enter fullscreen mode Exit fullscreen mode

After πŸ‘‰

function fetchOuties(parameterConfig) {  
  return fetch(`${parameterConfig.apiUrl}/outies`);  
  // 1. Identify global variables
  // used across your codebase.
  // 4. Refactor the existing code 
  // to use the new dependency-injected structure. 
}  

const applicationConfig = { apiUrl: "https://api.severance.com" };  
// 2. Create a real-world abstraction
// to encapsulate these variables.

fetchOuties(applicationConfig); 
// 3. Pass dependencies explicitly 
// via function parameters or constructors.

//  const globalConfig = { apiUrl: "https://api.severance.com" };  
// 5. Remove the original 
// global variable declarations.

// Why Is 'config' a Dependency?
// Because:
// outies() depends on knowing the API URL to work
// Without this information, 
// The function can't perform its core task
// The dependency is 
// explicitly declared in the function signature
Enter fullscreen mode Exit fullscreen mode

A Step Beyond: API Reification

class ApiService {
  constructor(parameterConfig) {
    this.variableConfig = parameterConfig;
  }

  // parameterConfig, variableConfig
  // and applicationConfig
  // are very bad names. 
  // They are here to emphasize the change

  fetchOuties() {
    return fetch(`${this.variableConfig.apiUrl}/outies`);
  }
}

const apiService = 
  new ApiService({ apiUrl: "https://api.severance.com" });
apiService.fetchOuties();
Enter fullscreen mode Exit fullscreen mode

Type πŸ“

[X] Semi-Automatic

Safety πŸ›‘οΈ

This refactoring is safe if you audit all global variable references and thoroughly test the code after injection.

Why is the Code Better? 🌱

Testability: Dependencies can be replaced (not mocked) for unit tests.

Explicit Contracts: Functions declare what they need.

Scalability: Configuration changes don’t require code edits.

Coupling: Code is less coupled.

How Does it Improve the Bijection? πŸ—ΊοΈ

By making dependencies explicit, the code mirrors real-world interactions where components rely on declared inputs, not hidden state.

You also reduce Coupling which is usually the more important problem you must solve.

Limitations ⚠️

Over-injection can lead to parameter bloat.

Common Misconceptions

"But it's just a parameter!"

  • Exactly! Passing dependencies via parameters is Dependency Injection. Frameworks often obscure this basic principle.

"This is too simple to be DI!"

  • Dependency Injection doesn't require complex frameworks. This is a pure, framework-less injection.

"Dependency Injection vs Dependency Inversion"

  • Inversion is the principle (why). It tells you to depend on abstractions to reduce coupling.
  • Injection is the practice (how). It’s one way (there are many others) to apply the principle by passing dependencies from outside instead of creating them inside a class.

Refactor with AI πŸ€–

You can use AI tools to analyze your codebase and identify global variables.

The AI can suggest where to implement dependency injection and help generate the necessary interfaces or classes for your dependencies.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: 1. Identify global variables used across your codebase.2. Create a real-world abstraction to encapsulate these variables. 3. Pass dependencies explicitly via function parameters or constructors. 4. Refactor existing code to use the new dependency-injected structure. 5. Remove the original global variable declarations.

Tags 🏷️

  • Dependency Injection

Level πŸ”‹

[X] Intermediate

Related Refactorings πŸ‘©β€β€οΈβ€πŸ’‹β€οΏ½

Refactoring Guru

See also πŸ”

Wikipedia: Dependency Injection

Wikipedia: Dependency Inversion

Credits πŸ™

Image by Petra on Pixabay


This article is part of the Refactoring Series.

Top comments (0)