Static Methods: Time to Hit Rock Bottom

A Tragic Story

It starts out innocently enough. You’re out at a party with some friends, and some people you want to impress are there. Everyone is having a good time, but you notice that some of the ‘cool’ kids are periodically going into a room and coming out giggling. Not wanting to be left out, you ask what’s going on, and are told that it’s none of your business. You have some concept that they’re doing something wrong — something forbidden — and you see what a mystique it creates. Later on, you ask around subtly and figure out what’s going on, and the next time you’re at a party, you know the right things to say to get an invitation to that coveted side room. You make your way in nervously, and there, you are introduced to the exhilarating euphoria of casting aside all thoughts of dependency and responsibility and writing a static method. Now, you’re one of the cool kids.

It’s no problem at first. You just write static methods on the weekends sometimes because it feels good to allow all your classes to access some service without passing an instance around. It’s not as if all of your code is procedural, by any stretch. And, yeah, sometimes you get a little weird behavior or hard to track down defects, but you’ve got it under control. Some of your less cool friends tell you that maybe you should think about modeling those behaviors with instances, but you tell them to mind their own business — they wouldn’t understand.

Static methods are just a way of blowing off steam and dealing with stress. When you’ve got project managers riding you to get things done, deadlines to meet and lots of things on your plate, it’s really gratifying to write a static method or two. They’re so easy, so efficient. It helps you get through the day.

But then, something weird starts to happen. They’re not helping you feel good anymore — they’re not cleaning up your code or making it more efficient. You need them just to get by. You have 50 classes that all depend on some static class with static variables, and they need to retrieve and set those variables in just the right order. It’s a mess, and it’s impossible to figure out why you’ve got this stupid defect that the stupid user shouldn’t care about anyway, that only occurs on Windows 7 when two specific screens are showing, the database connection is closing and a window was opened previously. If only you didn’t have all of these stupid global variables in your static classes, this wouldn’t be a problem. If only you’d listened to your friends. The only way out of this static nightmare for even a few minutes is… another static global flag.

Okay, okay, you admit it. This has gotten a little out of hand. So, you go to the project managers and tell them that the code can’t be fixed. It’s time for version 2.0. You’re going to rewrite everything with a much better design, and this time, you swear, you’re only going to use static methods in moderation. You’ll be a lot more responsible with them this time around. You’ve learned your lesson. Yessiree, this time, everything’s going to be different.

But it’s not. You need to hit rock bottom. Only then can you find your way back.

Static Methods are Like an Addiction

It never seems to fail in a code base of decent size. Poke around the portions of the code that tend to cause the most issues and be hardest to reason about, and the ‘static’ keyword will tend to abound. The worse it is, the more of them you’ll see. People know that global variables are bad, and it is becoming widely accepted that singletons tend to be problematic and abused (if one doesn’t believe use counts as abuse). People generally concur that static state is problematic. And yet, it keeps on keeping on. More is introduced every day. Here are some observations that I’ve made about the use of statics in code:

  1. Most people rationalize their use.
  2. Time constraints and stress are often cited as justifications.
  3. Promises to do something about them at a nebulous future date abound.
  4. People continue using them in spite of being aware of demonstrable, negative effect.
  5. The ‘solution’ for the problems they’ve caused is often using more of them.
  6. People get defensive when asked about them.

One blogger that I follow, Julian Birch, compares them to crack in a post entitled “Going Cold Turkey on Static Methods”:

Static methods are like crack, they’re convenient, they solve your problems quickly and you quickly become dependent upon them. You’re so dependent you don’t even know you’ve got a problem.

I hadn’t seen this particular post when I started mine, but it was nice to google around a bit and see that I’m not the only one to whom this particular metaphor had occurred.

Emerging from the Metaphor

The metaphor here is just that – a literary device intended for illustrative effect in demonstrating a point. I’m not actually suggesting that static methods will ruin your life, and I’m not attempting to trivialize the struggles that people have with actual addiction. I’m posting this because of my observation of the tendency of static methods in a code base to become a real, huge problem, that people immersed in the code base don’t see, but outside observers are immediately alarmed by.

Static state/methods in a code base generally represent a tradeoff, but a subtly different one, in my opinion, than their advocates would have you believe. They claim a tradeoff between complex instantiation logic and complex runtime logic. I claim a tradeoff of a little extra reasoning up front for an avalanche of future problems. To me, introducing a bunch of static functionality into a code base is like saying “I’m making a conscious choice to avoid all the effort needed to plug in my sump pump, and if my basement floods with sewage, I’m fine with that.” What?!? Seriously?!?

Rationale

In a post with a different device theme, the pun, John Sonmez says:

I don’t like static methods. They make me cringe. In the universe of OO static methods are anti-matter.

He goes on to talk about specific issues with the concept of static methods, including:

  1. A tendency to violate the SRP since they are methods on a class that have nothing to do with the state of that class.
  2. They are global procedures (as in procedural code) that are categorically not object oriented programming.
  3. They kill testability of code bases. Misko Hevery agrees.
  4. They are not polymorphic in that they cannot inherit, be inherited, or implement interfaces.
  5. They force programmers to know more things up front.

I would add to this list:

  1. They hide dependencies and allow an illusion of simplicity in APIs to be created (they cause your API to ‘lie’.)
  2. They make runtime reasoning about the code very difficult when they encapsulate state.
  3. Simply by existing, they tend to promote global flags if there is a need to extend functionality and not break existing clients.
  4. Static classes tend naturally to balloon in size, since their methods can’t be extended.
  5. They increase potential dependencies in your code base by a factor of order combinatorial.
  6. They cast temporal couplings in stone within your code.

Recovery

If you’ve been reading this and not nodding along, you’ve probably experienced some indignation. Please don’t take this as an assault on the choices you’ve made as a programmer. I’m talking from experience here. Ben Franklin famously said “Experience is the best teacher, but a fool will learn from no other.” I am that fool. I’ve struggled with ugly messes that I (and others) have created via the static concept, and I’ve hit that rock bottom state.

The transition away isn’t as bad as you think. I’ve now regularly been creating entire programs and applications that have no static methods. Zip, zero. It isn’t impossible. Heck, it isn’t even hard. It just requires up front reasoning about your dependency graph, but that reasoning is a skill that sharpens with practice. Like unit testing or TDD, it’s a skill that seems a little unnatural, especially if you started in a procedural language, but it does come, and faster than you think. Before you know it, you’ll be loving your static-free existence and offering counseling to those who are walking your current path through a static nightmare.

  • Dan

    This is by far the best metaphor I’ve ever encountered for the use of static methods. Outstanding!

  • http://www.daedtech.com Erik

    I’m glad you liked it — thanks for the feedback and for reading. :)

  • josh

    I love using static methods in moderation. If I know a method does not depend on an object’s fields, I’d rather make it static. Then you know it only depends on its parameters. I’d say they actually force you to know less up front, they make dependencies more clear, and they help you avoid excess state encapsulation. You’ll need a little more evidence than, “I’m talking from experience here,” to convince me they are always bad. But what do I know, right?

  • http://www.daedtech.com Erik

    Hi Josh,

    Thanks for reading and your feedback.

    First, the line about me speaking from experience was not intended to convince anyone to do anything — that was more to say that my opinion on statics was based on my own experience rather than dogmatic adherence to a principle or an idea I’d read somewhere. The evidence was intended to be in the “rationale” section.

    As to your own use of static as you describe, I agree with the sentiment that if a method performs no operation on the instance fields of a class then distinguishing it as static helps clarify dependencies up front. But my question then is, assuming we’re talking about a non-private method, why does your class have a method that has nothing to do with its state? That seems to me like a class that has the responsibility of at least two classes – its statics are just in there for namespace scoping and have a purpose divorced from the rest of the class. (For private helper methods, if I have helpers that are basically functional methods, I’m not opposed to those being static to distinguish their nature, though usually that starts to beg for a new class).

    To clarify my position on the matter, my suggestion isn’t to take all of the methods that are static in your code base and simply make them instance methods. I agree with you that doing such a thing would only muddy the waters. My suggestion is to find a home for the static methods so that they aren’t free-floating, procedural utils methods or, worse, encapsulators of some static state (read: global variables).

  • Robert

    You are correct in what you say that when you start to use static methods you will have a hard time to stop using them, I am in that situation.

    Currently I use them quite frequently for the following:

    - Factory methods

    - Wrappers for stored procedures, one example is when fetching a array of User objects from the DB. I don’t think I would like to create some kind of custom “UserCollection” object for this just to get rid of the static method. But I am open for suggestions.

  • http://www.daedtech.com Erik

    Hi Robert,

    Thanks for the comment.

    If you’re interested in eliminating static implementations for static factory methods, you could always use an instance factory method or the Abstract Factory Pattern. (I’ve also posted about that here). Another approach is to use an IoC container, which will create a scenario where you actually push the object instantiation out of your code altogether.

    With regards to database access, I usually handle this with an instance-based data access layer. There are also proponents of the onion architecture who I imagine would also favor an instance based approach, since that’s all about isolation and testability (both of which are hurt by statics).

    If, however, you are using something like CLR stored procedures where static is forced on you by the technology itself, that can be trickier. My personal taste when in a situation like this is to create an instance wrapper for the statics that implement an interface and inject/pass that around for testability. If it’s going to be forced on me, I can at least push it out somewhere that it’s only used once to minimize the impact on the code base. Another option is just to sigh and live with it — probably not what I’d do, but understandable in this situation if you don’t want to incur a bunch of overhead for the sake of purity. (Of course, you can always change which tools/technologies you use, but I fully recognize that this may be neither possible nor desirable in a given situation to affect this outcome).