NDepend Blog

Improve your .NET code quality with NDepend

L:ack of Cohesion of Methods: What Is This And Why Should You Care?

Lack of Cohesion of Methods: What Is This And Why Should You Care?

May 14, 2026 9 minutes read

Lack of cohesion of methods (usually shortened to LCOM) is one of those things that sits fairly high up on the software hierarchy of needs. What’s the “software hierarchy of needs?” It’s a thing that I just made up, shamelessly copying Maslow’s Hierarchy of Needs. (Though in a quick search to see how effectively I’ve planted this flag, I see that Scott Hanselman had the idea before me. But mine looks a little different than his because I’m talking about the software, rather than the humans writing it.)

Anyway, here’s the hierarchy.

  • The cost of change stays relatively flat because the software is highly maintainable.
  • You can change it over time in response to evolving requirements and market realities.
  • It performs and behaves acceptably in production.
  • Technically, it fulfills the functional requirements and user value proposition.
  • It simply exists in production.

The software hierarchy of needs.

When it comes to something like lack of cohesion of methods, you’re talking about the top two categories. Early in your career, you’ll tend to have worries like just wheezing past the finish line with a feature and like getting your code to do what you want it to. With practice and time, you then start worrying about non-functional concerns and maintainability. And when you start worrying about that, you should start paying attention to LCOM (among other things).

So for the rest of this post, I’ll focus on this one idea: lack of cohesion of methods. What is it, where did it come from, how do you actually measure it (in all its flavors), and why should you care?

First of All, What is Cohesion?

Before getting into what “lack of cohesion” means, it’s probably worth covering the idea of cohesion. So, to the dictionary!

The act or state of cohering, uniting, or sticking together.

In the simplest terms, things are cohesive when they stick together and stay together. That applies to the wide world, as well as to software. When you describe your group at work as cohesive, people will understand you to say that they work well together and get along.

In the world of source code, the same idea applies, albeit more narrowly. If we have something like an assembly or a class and we say that it’s cohesive, we mean that its innards are coherently interrelated. The fields, the methods, the bits of state — they all pull in the same direction and exist for the same reason.

Cohesion and Lack of Cohesion Briefly Demonstrated

Instead of further description, let’s illustrate with some code. First up, here’s a cohesive class, using C# 7 language features to make the class compact.

This class has a field, a property, and two methods. Notice that both methods and the property all reference the field. This is a cohesive class, with its functionality entirely wrapped up in manipulating the number field.

Now, consider another class.

This class has three fields and three methods. Each method refers to its own field, and there’s no interrelation of which to speak. You look at this class and justifiably ask yourself whether these methods and their referenced fields even belong in the same class. (Indeed, you probably wonder why not just have a single method and field and create multiple instances of it to achieve this goal — and you should wonder that.)

Lack of Cohesion of Methods and Design Principles

When it comes to your code, you want to shoot for having cohesion in your codebases. Think of it with the mantra, “Things that need to change together should exist together.” In this vein, cohesion and LCOM tie in with two design ideas you may have heard of:

The SRP, one of the so-called SOLID principles, states that a code element should have a single reason to change. Admittedly, this is a little subjective, but you can understand it by thinking of our example classes above. Why would someone change NumberManipulator? Well, you’d change it in order to allow clients of the class to do different things to _number. What about for the non-cohesive version? More or less the same basic reasoning but for three different versions of _number. Yikes.

It’s hard to look at a codebase and easily know whether you’re getting the SRP right entirely. But you can recognize when you’re getting it badly wrong. One way this occurs is that modifying the code to add features requires you to touch the codebase in many places (mimicking the spray of a shotgun). Shotgun surgery indicates a lack of cohesion — the things that need to change at the same time are found all over the place.

One Name, Several Formulas: LCOM, LCOM4, LCOM HS

One thing that trips people up: if you go searching for “LCOM” online, different tools give you wildly different values for the same class. That’s because there’s no single LCOM. There’s a small family of them, and each tool picks the one it likes. Quick map, using M for the number of methods, F for the number of instance fields, and MF for the count of methods that touch a given field.

  • LCOM (a.k.a. LCOM2): 1 – sum(MF) / (M * F). Ranges from 0 (every method touches every field) to 1 (none do). This is the formula NDepend reports as plain LCOM.
  • LCOM HS (Henderson-Sellers, sometimes called LCOM* or LCOM3): (M – sum(MF)/F) / (M – 1). Range [0, 2]. Zero is perfect cohesion; values past 1 usually signal dead state. This is NDepend’s LCOM HS.
  • LCOM4: count the connected components of the graph where methods are nodes and an edge exists when two methods share a field or one calls the other. LCOM4 = 1 means the class is cohesive; LCOM4 >= 2 means it’s really two (or more) classes wearing a trench coat. SonarQube uses this one by default.

So when someone says “this class has an LCOM of 0.6,” your first question should be “which LCOM?” A 0.6 in one formula can mean something very different in another.

Lack of Cohesion of Methods: Getting Specific

So you can see that the relatively granular metric of LCOM actually relates to important and broad software design principles. People have been recommending that you write cohesive code, even if they haven’t been using that term.

Let’s look now at LCOM the metric (as NDepend computes it), rather than cohesion the concept. To dig in, let’s look at how NDepend computes it.

LCOM for a class will range between 0 and 1, with 0 being totally cohesive and 1 being totally non-cohesive. This makes sense since a low “lack of cohesion” score would mean a lot of cohesion.

Here’s how the calculation works. For each field in the class, you count the methods that reference it, and then you add all of those up across all fields. You then divide that by the count of methods times the count of fields, and you subtract the result from one. So, for instance, consider the classes above.

  • NumberManipulator = 1 – (3/4) = 0.25 LCOM.
  • NonCohesiveNumberManipulator = 1 – (3/12) = .75 LCOM.

If you’re wondering about the phantom 4th method, consider that each of these classes technically has the default contructor, which counts. To get perfect cohesion, you would need a constructor to refer to the lone field as well.

NDepend for LCOM

Of course, you don’t need to actually compute this stuff for yourself. Here’s a screenshot of NDepend’s query and rule editor from the playpen codebase where I created this example.

You should absolutely use static analysis tooling at your disposal to compute lack of cohesion of methods. Out of the box, NDepend will both compute it for you and warn you when it gets excessive for types in your codebase. You can then adjust these thresholds and warnings as you see fit.

By default, NDepend flags types where LCOM > 0.8 (or LCOM HS > 1.0) and NbFields > 10 and NbMethods > 10. The size guards matter — cohesion metrics get noisy on tiny classes, so it pays to ignore the small fry and chase the big offenders. Those are usually the ones quietly costing you the most maintenance time anyway.

A couple of gotchas to keep in mind when you read these numbers. In C#, auto-properties like public int Count { get; set; } don’t behave the same as an old-school _count field; NDepend deliberately excludes property getters and setters from the method count to keep LCOM meaningful, but not every tool does that, so the same class can score very differently across tools. Dependency-injected service classes can also be misleading in the other direction: if every method uses the injected collaborator field, LCOM looks great even when the class is secretly doing too much. And static utility classes, with no instance state, fall outside what the metric can say anything about.

How to Fix a Class with High LCOM

Suppose NDepend is yelling at you about a class with an LCOM of 0.92. What do you actually do?

Open the class and list the fields on one side, the methods on the other, and for each method note which fields it touches. Patterns usually jump out fast: these four methods all touch _customer, those three only touch _invoice, and the last two don’t touch any state at all. Then apply the bread-and-butter refactorings: Extract Class on each cluster, Move Method for behavior that belongs elsewhere, Move Field when state goes with it, and just mark stateless methods static (or relocate them). Taking the NonCohesiveNumberManipulator example from above, the obvious refactor is to delete the class and use three instances of a single-field counter type.

The thing to internalize is that high LCOM is rarely the disease. It’s the symptom. The disease is usually “this class is two classes.” Fix that, and the metric follows.

Why Does LCOM Matter to You?

I’ll round out the explanation now by discussing the significance. So far, I’ve talked about this obliquely, pointing out that cohesive types and cohesion in your codebase in general helps you conform to the SRP and avoid shotgun surgery. But from a broader perspective…so what?

Well, it all boils down to maintenance. Every time you touch a codebase to implement new functionality or to fix a defect, you introduce risk. So if each change requires you to wander all over the codebase, you increase that risk. Likewise, if you have classes that mash incoherent and unrelated functionality together, you have much greater chance of breaking thing A when you’re trying to fix thing B.

There’s a sizable body of empirical research linking high LCOM to higher defect rates, higher change-proneness, and longer time-to-fix. This isn’t surprising — a class that’s secretly three classes is a class with three times the surface area for bugs and three times the chance that an innocent edit ripples somewhere unexpected. Test coverage gets harder too: unit tests for a low-cohesion class tend to bring along an embarrassing amount of setup, because each test has to construct state that’s only relevant to one of the class’s hidden personalities.

As a metric, LCOM alerts you to pockets of your code that are becoming risky. And, more than that, it can jolt you into recognizing a suboptimal design. If a warning from NDepend brings you to the class NonCohesiveNumberManipulator, you might look at that and say, “Huh. Why don’t we just use multiple instances of a class with a single field?” And the answer could be simply that nobody ever thought to do that before.

As I said at the beginning of the post, these concerns sit fairly high on the software hierarchy of needs. But you should never stop seeking to improve, so it’s important that you make yourself aware of things like LCOM.

LCOM FAQ

What does LCOM stand for?
LCOM stands for Lack of Cohesion of Methods (sometimes “Lack of Cohesion in Methods” — same thing). It’s a software metric, originally proposed by Chidamber and Kemerer in 1991, that measures how poorly the methods of a class hang together around shared state.

What is a good LCOM value?
It depends on which LCOM. For NDepend’s LCOM, lower is better and you want to stay under about 0.8 on non-trivial classes. For LCOM HS (Henderson-Sellers), stay under 1.0. For LCOM4, the goal is exactly 1 — anything 2 or more is the metric flat-out telling you to split the class.

What’s the difference between LCOM and LCOM4?
LCOM (the original) and its close cousins (LCOM2, LCOM3/LCOM*) compute a numerical score based on the ratio of methods touching shared fields. LCOM4 takes a different approach: it models the class as a graph and counts disconnected components. LCOM4 is more intuitive to interpret but ignores some kinds of cohesion the older formulas catch.

Why is high LCOM bad?
Because high LCOM usually means the class is doing more than one thing. That makes it harder to read, harder to test in isolation, harder to change without breaking unrelated behavior, and more likely to harbor bugs.

How do I lower the LCOM of a class?
Find the clusters of methods that share fields, then use Extract Class to peel each cluster into its own type. Move methods that belong elsewhere, mark stateless ones static, and delete dead fields. LCOM almost always falls on its own once the class genuinely does one thing.

This article is brought to you by the team behind NDepend — a proven .NET static analysis tool for improving code maintainability, security, and overall quality. Whether you’re modernizing a legacy .NET application or starting fresh in C#, get started with your free full-featured trial today!

Leave a Reply

Your email address will not be published. Required fields are marked *