Big, messy classes slow projects down and scare developers away.
In this post I’ll show you how to use LCOM to spot those "god classes" in your PHP code - and how to break them into something leaner and easier to test.
LCOM in PHP – spotting classes that do too much
Every project has them. The UserManager, the DataHelper, the dreaded Utils.
Classes so big and messy that nobody wants to touch them. They grow over time, swallow more responsibilities, and eventually slow the whole team down.
But how do you know when a class is doing too much?
Instead of guessing, you can measure it - with a metric called LCOM.
What is LCOM?
LCOM stands for Lack of Cohesion of Methods.
In simple terms, it measures how much the methods in a class actually belong together.
If most methods use the same set of properties, the class is cohesive - that’s good.
If methods work on completely different properties, the class is basically doing multiple jobs at once - that’s bad.
High LCOM = low cohesion = a "god class" that tries to handle too many responsibilities.
LCOM flavours (LCOM1 vs LCOM2)
There isn’t just one formula for LCOM. Over the years, different versions have been proposed.
The two most common are:
- LCOM1 – looks at pairs of methods: the more pairs that share nothing, the higher the score.
- LCOM2 – groups methods into clusters that share at least one property; the number of clusters is the score.
Quick examples
-
Messy class
-
foo()uses$a,$d -
bar()uses nothing -
baz()uses$b,$d -
qux()uses$c
-
-> LCOM2 = 3 clusters ({foo,baz}, {bar}, {qux}) -> 3
-> LCOM1 = 5 pairs don't share anything, 1 shares $d (5 - 1 = 4) -> 4
-
Cohesive class
- All methods use
$a(one uses$a,$b,$c,$d)
- All methods use
-> LCOM2: one cluster -> 1.
-> LCOM1: every pair shares something -> 0.
Both metrics point to the same thing (messy vs cohesive), but the values look different.
And beyond…
There are also LCOM3 and LCOM4, which tweak the maths further. I won’t dive into them here, but the key point is:
👉 different definitions can give different numbers, even for the same class. That’s why it’s best to treat LCOM as a relative signal — spot the outliers, watch trends over time, don’t obsess over exact figures.
How to measure LCOM in PHP
The easiest way is to use static analysis tools such as phpmetrics.
They scan your codebase and calculate LCOM values for each class.
Example with phpmetrics:
php vendor/bin/phpmetrics --report-json=build/report.json src
Phpmetrics also generates handy HTML reports with charts and diagrams.
These make it easy to spot classes with high LCOM values at a glance.
If you just want to test it quickly without installing anything, you can use the phpqa Docker image.
Here’s the one-liner I used to generate an HTML report:
docker run --init -it --rm \
-v "$(pwd):/project" \
-v "$(pwd)/tmp-phpqa:/tmp" \
-w /project jakzal/phpqa \
phpmetrics --report-html=build/report.html src
In the HTML report, open report.html/oop.html.
There you can sort classes by their LCOM value (highest or lowest) to immediately find the worst offenders.
Example in PHP code
Let’s look at a simple class that shows poor cohesion:
class UserManager
{
private array $users = [];
private $mailer;
public function addUser(string $name): void { /* ... */ }
public function findUser(string $name): ?User { /* ... */ }
public function sendNewsletter(): void { /* ... */ } // unrelated to user storage
}
-
addUserandfindUserboth use the$usersproperty. -
sendNewsletteruses$mailerbut has nothing to do with$users.
This means the class mixes two very different responsibilities. Tools will report a high LCOM here, because the methods don’t share common data. In practice, this is a sign that you need to split the class.
How to fix high LCOM
When a class shows a high LCOM score, the remedy is almost always the same: split it into smaller, focused classes.
Here’s the earlier UserManager refactored:
class UserRepository
{
private array $users = [];
public function addUser(string $name): void { /* ... */ }
public function findUser(string $name): ?User { /* ... */ }
}
class NewsletterService
{
private $mailer;
public function sendNewsletter(): void { /* ... */ }
}
Now each class has a single responsibility.
-
UserRepositoryhandles storing and finding users. -
NewsletterServicedeals with sending newsletters.
This makes your design cleaner and your tests simpler: each class can be tested in isolation, with fewer dependencies and fewer edge cases to worry about.
Wrapping up
LCOM might sound academic, but in practice it’s a simple early warning system:
- Low LCOM (close to 0) -> cohesive class, one clear job.
- High LCOM -> class is mixing responsibilities, harder to maintain and test.
You don’t need to obsess over the exact numbers. Use them to spot the outliers in your codebase.







I think it a metric you shouldn't use. It can be skewed too easily to create an outlier.
A few examples of the top of my head;
For the example I think the
sendNewslettermethod is ambiguous.If it is implemented the way you describe, it shouldn't be in the class. I think this will happen more in the case of a
Utilsclass than a more defined class likeUserManagerI think it is more likely scenario is the use of a current user property inside of the function and something is done with that property to send the newsletter.
But again it shows that LCOM formulas are skewed too easily, just by using protected or private properties in this case.
As a side note, I think we should stop using the god class term. The term itself is a god object, because it refers to too many dependencies or too many unrelated methods or both. It should be separated into heavy dependency object and inconsistent methods object. The LCOM metric only relates to the inconsistent methods object. The heavy dependency object can be checked by other metrics.