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)