Discouraged by Object-Oriented Programming, Many mixed languages support it. And developers abuse them.
TL;DR: Global functions bring a lot of coupling. Don't use them.
Problems
Tight Coupling
Poor Readability
Maintainability
Testability
Hidden side-effects
Solutions
- Wrap the function in a context object.
Refactorings ⚙️
https://maximilianocontieri.com/refactoring-007-extract-class
https://maximilianocontieri.com/refactoring-010-extract-method-object
https://maximilianocontieri.com/refactoring-020-transform-static-functions
https://maximilianocontieri.com/refactoring-024-replace-global-variables-with-dependency-injection
Examples
- External Resources Access, Database access, Time and Operating System resources.
Context
When you place a function in the global scope, you allow any part of your system to access it at any time.
This sounds convenient, but it creates a web of invisible connections.
If you want to change how that function works, you must check every corner of your application.
This practice also makes testing nearly impossible.
When a method calls a global function like getSystemTime() or saveToDatabase(), you cannot easily replace that function with a mock or a fake version during a test.
You end up testing the entire environment instead of just your logic.
Sample Code
Wrong
<?
class Employee {
function taxesPayedUntilToday() {
return database()->select(
"SELECT TAXES FROM EMPLOYEE".
" WHERE ID = " . $this->id() .
" AND DATE < " . currentDate());
}
}
Right
<?
final class EmployeeTaxesCalculator {
function taxesPayedUntilToday($context) {
return $context->SelectTaxesForEmployeeUntil(
$this->ssn,
$context->currentDate());
}
}
Detection
Many modern languages avoid them. For the permissive ones, scope rules can be applied and automatically checked.
Exceptions
Some languages use global functions as their core library (like str_len in C or some PHP functions).
In these cases, you might wrap them in your own objects to improve testability, but the functions themselves are part of the platform.
Level
[x] Beginner
Why the Bijection Is Important ️
In the MAPPER, objects have responsibilities and specific contexts.
A Global Function doesn't exist in a real-world simulation because every action belongs to an actor or a component.
When you use global functions, you break the bijection between your code and the entities you model.
You create a God-like action that exists nowhere and everywhere at the same time.
AI Generation
AI tools often suggest global functions because they look for the shortest code path.
They frequently provide utility snippets that live in the global scope because they don't know the specific architecture of your project.
AI Detection
You can ask an AI to "Refactor these global functions into domain objects" or "Apply dependency injection to remove global scope dependencies."
Many AI models are very good at identifying which class should own a specific logic.
Try Them!
Remember: AI Assistants make lots of mistakes
Suggested Prompt: Take these global functions and group them into logical context objects. Use dependency injection so I can test the classes that use them.
Conclusion
Structured programming considers global functions harmful. Yet, we can observe some bad practices cross paradigm boundaries.
- Singleton and Classes are global points of access.
Relations ❤️
https://maximilianocontieri.com/code-smell-60-global-classes
https://coderlegion.com/6959/singleton-the-root-of-all-evil
https://maximilianocontieri.com/code-smell-22-helpers
https://maximilianocontieri.com/code-smell-209-side-effects
https://en.wikipedia.org/wiki/Global_variable
Credits
Photo by Mae Mu on Unsplash
The road to programming hell is paved with global variables.
Steve McConnell
https://maximilianocontieri.com/software-engineering-great-quotes
This article is part of the CodeSmell Series.
https://coderlegion.com/10942/how-to-find-the-stinky-parts-of-your-code