I noticed something recently about good codebases: they all look surprisingly similar. Regardless of programming language, business domain, and other characteristics, good code "feels" like it has the same flow to it. On the other hand, bad codebases can be bad in a huge variety of ways.
There are innumerable problems that can plague a codebase, and this post is all about one of them that is most commonly seen in object-oriented design contexts: the god class anti-pattern.
Table of Contents
- God Class: The Fundamentals
- How to Tackle a God Class in Your Codebase
- Defeat the God Class and Reap the Benefits
God Class: The Fundamentals
As promised, let's start by answering the fundamental questions about this anti-pattern.
What Is a God Class?
God class—or, alternatively, god object—is an anti-pattern that consists of having a class that does too much. A god class is usually a huge class that concentrates a lot of responsibilities, controls and oversees many different objects, and effectively does everything in the application.
I appreciate that the definition above doesn't make the god class sound like such a terrible problem. In fact, you might even think it sounds like a great idea. "Isn't a single, centralized place to concentrate all business logic of an application an amazing thing?"
That's certainly not the case, as you'll see next.
5 Reasons Why a God Class Is a Problem
God Classes Violate SOLID
The SOLID principles are a set of well-accepted guidelines you can apply in order to achieve good software design. SOLID is an acronym, and the S stands for the single-responsibility principle, which broadly means that each module/class/function should strive to do a single thing.
Codebases that follow the single-responsibility principle end up with many small pieces that engineers can then reuse and rearrange as they see fit. This reduces code duplication and other forms of technical debt while facilitating testing.
God classes are the literal opposite of the single-responsibility principle, taking on too many responsibilities and being highly unfocused. This causes engineers to reinvent the wheel, duplicate code, and incur technical debt.
God Classes Make Your Code Fragile
Because the god class is responsible for so much, it has to be frequently updated, increasing the changes that breaking changes are introduced. It can be hard, perhaps even impossible, to know how changes to a god class will impact the rest of the application.
LinearB can help you to get ahead of breaking changes. Our rework metric keeps track of when recently changed code is being changed again. We leverage the rework metric to identity high-risk deltas so that our WorkerB automation bot can send you an alert and you can take action before the delta gets merged in.
God Classes Harm Testability
We all know how important testability is, but in order to test your code, it needs to be testable—i.e. written in a certain style that makes unit testing easy and practical to do.
What are the properties of a class that make it testable? There are many, but, generally speaking, the most testable classes are the ones that are small, focused, and aren't coupled to other types.
This is the opposite of a god class. Since this big, all-powerful class controls and oversees many other objects, it is tightly coupled to those types. Just instantiating the god class so you can start testing it can be quite a challenge due to the complex setup and the need to stub/mock many dependencies.
God Classes Harm Performance
As long as objects are reachable, the garbage collector will consider them alive and not mark them for collection.
A god class is a marvelous way to keep many objects alive because it holds so many references. As a result, many objects that are no longer used will keep wasting space in memory, which can degrade performance.
God Classes Make the Code Harder to Understand
Finally, god classes usually increase the complexity of code. Since they have so many responsibilities and talk to so many different types, it shouldn't come as a surprise that the code in these classes is complex.
What do I mean by complexity? Several things, actually. Complexity can refer to a specific metric, such as cyclomatic complexity.
But it can also be more subtle. Even a code block whose cyclomatic complexity isn't high can be complex in different ways, such as relying on external dependencies that cause side effects. All of these different complexities add to the overall cognitive complexity of code, making it harder to understand and harming engineers' productivity and performance.
It's crucial to be mindful about the cognitive load of engineers on your teams. Author Manuel Pais came on to the Dev Interrupted podcast recently to discuss how to assess cognitive load on dev teams:
How Does the God Class Anti-Pattern Develop?
I'd say it's less of a technical and more of a cultural problem. If your team suffers from god classes, here are some of the likely causes:
- Lack of emphasis on engineering excellence. Teams need to put effort into improving their codebases, including prioritizing and encouraging practices such as refactoring.
- Lack of unit tests. Without the safety net of an automated test suite, engineers are less likely to aggressively refactor their codebases, which, in turn, results in them writing more and more untestable code, creating a vicious cycle.
- Not tracking the right metrics. It's crucial to track and improve the right set of engineering metrics so you can identify worrying trends in your team's output and address them.
- Poor code review process. Code reviews, when properly done, are great for code quality. But when not done right—or at all—code smells and anti-patterns abound, including the god class.
With our metrics, Team Goals, and automations like alerts using our WorkerB bot, LinearB can help you build a robust code review process that helps you ship higher quality code and ship code faster.
How to Tackle a God Class in Your Codebase
In object-oriented programming, creating a god class is easy; undoing it is another matter. How do you go about moving the functionality from a big, public class into more precisely scoped pieces? You might not like the answer, but here it goes: slowly and carefully.
If the god class isn't tested, start by covering it with automated tests. Chances are, the class isn't particularly testable, so it might make sense to start with integration tests rather than unit tests. Start with what Micheal Feathers calls characterization tests—i.e. tests you write to learn and describe what a system currently does, without changing anything. Then, start adding unit tests when it becomes possible.
When you reach some comfortable code coverage, start breaking the big class apart into smaller, separate classes. Don't do this in a single step; instead, use something like the facade pattern to ease the transition. At some point, you'll feel comfortable enough to remove the facade and expose the new, small, modular classes.
Defeat the God Class and Reap the Benefits
The novel Anna Karenina famously opens with the line, "All happy families are alike; each unhappy family is unhappy in its own way." In my experience, good codebases are eerily similar, while bad ones can be plagued by a plethora of different ailments, and in this post, we looked at one: the god class.
With the right processes in place, your team can eradicate god classes, empower your developers to grow, and ultimately ship better quality code faster. LinearB provides everything you need to refine the processes on your engineering teams, from metrics to a goal setting and tracking framework to helpful automations.