DaedTech

Stories about Software

By

Introduction to Unit Testing Part 2: Let’s Write a Test

In the last post in this series, I covered the essentials of unit testing without diving into the topic in any real detail. The idea was to offer sort of a guerrilla guide to what unit testing is for those who don’t know but feel they should. Continuing on that path and generating material for my upcoming presentation, I’m going to continue with the introduction.

In the previous post, I talked about what unit testing is and isn’t, what its actual purpose is, and what some best practices are. This is like explaining the Grand Canyon to someone that isn’t familiar with it. You now know enough to say that it’s a big hole in the earth that provides some spectacular views and that you can hike down into it, seeing incredible shifts in flora and fauna as you go. You can probably convince someone you’ve been there in a casual conversation, even without having seen it. But, you won’t really get it until you’re standing there, speechless. With unit testing, you won’t really get it until you do it and get some benefit out of it.

So, Let’s Write that Test

Let’s say that we have a class called PrimeFinder. (Anyone who has watched my Pluralsight course will probably recognize that I’m recycling the example class from there.) This class’s job is to determine whether or not numbers are prime, and it looks like this:

public class PrimeFinder
{
    public bool IsPrime(int possiblePrime)
    {
        return possiblePrime != 1 && !Enumerable.Range(2, (int)Math.Sqrt(possiblePrime) - 1).Any(i => possiblePrime % i == 0);
    }       
}

Wow, that’s pretty dense looking code. If we take the method at face value, it should tell us whether a number is prime or not. Do we believe the method? Do we have any way of knowing that it works reliably, apart from running an entire application, finding the part that uses it, and poking at it to see if anything blows up? Probably not, if this is your code and you needed my last post to understand what a unit test was. But this is a pretty simple method in a pretty simple class. Doesn’t it seem like there ought to be a way to make sure it works?

I know what you’re thinking. You have a scratchpad and you copy and paste code into it when you want to experiment and see how things work. Fine and good, but that’s a throw-away effort that means nothing. It might even mislead when your production code starts changing. And checking might not be possible if you have a lot of dependencies that come along for the ride.

But never fear. We can write a unit test. Now, you aren’t going to write a unit test just anywhere. In Visual Studio, what you want to do is create a unit test project and have it refer to your code. So if the PrimeFinder class is in a project called Daedtech.Production, you would create a new unit test project called DaedTech.Production.Test and add a project reference to Daedtech.Production. (In Java, the convention isn’t quite as cut and dry, but I’m going to stick with .NET since that’s my audience for my talk). You want to keep your tests out of your production code so that you can deploy without also deploying a bunch of unit test code.

Once the test class is in place, you write something like this, keeping in mind the “Arrange, Act, Assert” paradigm described in my previous post:

[TestMethod]
public void Returns_False_For_One()
{
    var primeFinder = new PrimeFinder(); //Arrange

    bool result = primeFinder.IsPrime(1); //Act

    Assert.IsFalse(result); //Assert
}

The TestMethod attribute is something that I described in my last post. This tells the test runner that the method should be executed as a unit test. The rest of the method is pretty straightforward. The arranging is just declaring an instance of the class under test (commonly abbreviated CUT). Sometimes this will be multiple statements if your CUTs are more complex and require state manipulation prior to what you’re testing. The acting is where we test to see what the method returns when we pass it a value of 1. The asserting is the Assert.IsFalse() line where we instruct the unit test runner that a value of false for result means the test should pass, but true means that it should fail since 1 is not prime.

Now, we can run this unit test and see what happens. If it passes, that means that it’s working correctly, at least for the case of 1. Maybe once we’re convinced of that, we can write a series of unit tests for a smattering of other cases in order to convince ourselves that this code works. And here’s the best part: when you’re done exploring the code with your unit tests to see what it does and convince yourself that it works (or perhaps you find a bug during your testing and fix the code), you can check the unit tests into source control and run them whenever you want to make sure the class is still working.

Why would you do that? Well, might be that you or someone else later starts playing around with the implementation of IsPrime(). Maybe you want to make it faster. Maybe you realize it doesn’t handle negative numbers properly and aim to correct it. Maybe you realize that method is written in a way that’s clear as mud and you want to refactor toward readability. Whatever the case may be, you now have a safety net. No matter what happens, 1 will never be prime, so the unit test above will be good for as long as your production code is around–and longer. With this test, you’ve not only verified that your production code works now; you’ve also set the stage for making sure it works later.

Resist the Urge to Write Kitchen Sink Tests

kitchensink

When I talked about a smattering of tests, I bet you had an idea. I bet your idea was this:

[TestMethod]
public void Test_A_Bunch_Of_Primes()
{
    var primes = new List() { 2, 3, 5, 7, 11, 13, 17 };
    var primeFinder = new PrimeFinder();


    foreach(var prime in primes)
        Assert.IsTrue(primeFinder.IsPrime(prime));
}

After all, it’s wasteful and inefficient to write a method for each case that you want to test when you could write a loop and iterate more succinctly. It’ll run faster, and it’s more concise from a coding perspective. It has every advantage that you’ve learned about in your programming career. This must be good. Right?

Well, not so much, counterintuitive as that may seem. In the first place, when you’re running a bunch of unit tests, you’re generally going to see their result in a unit test runner grid that looks something like a spreadsheet. Or perhaps you’ll see it in a report. If when you’re looking at that, you see a failure next to “IsPrime_Returns_False_For_12” then you immediately know, at a glance, that something went wrong for the case of 12. If, instead, you see a failure for “Test_A_Bunch_Of_Primes”, you have no idea what happened without further investigation. Another problem with the looping approach is that you’re masking potential failures. In the method above, what information do you get if the method is wrong for both 2 and 17? Well, you just know that it failed for something. So you step through in the debugger, see that it failed for 2, fix that, and move on. But then you wind up right back there because there were actually two failures, though only one was being reported.

Unit test code is different from regular code in that you’re valuing clarity and the capture of intent and requirements over brevity and compactness. As you get comfortable with unit tests, you’ll start to give them titles that describe correct functionality of the system and you’ll use them as kind of a checklist for getting code right. It’s like your to-do list. If every box is checked, you feel pretty good. And you can put checkboxes next to statements like “Throws_Exception_When_Passed_A_Null_Value” but not next to “Test_Null”.

There are some very common things that new unit testers tend to do for a while before things click. Naming test methods things like “Test_01” and having dozens of asserts in them is very high on the list. This comes from heavily procedural thinking. You’ll need to break out of that to realize the benefit of unit testing, which is inherently modular because it requires a complete breakdown into components to work. If nothing else, remember that it’s, “Arrange, Act, Assert,” not, “Arrange, Act, Assert, Act, Assert, Assert, Assert, Act, Act, Assert, Assert, Act, Assert, etc.”

Wrap-Up

The gist of this installment is that unit tests can be used to explore a system, understand what it does, and then guard to make sure the system continues to work as expected even when you are others are in it making changes later. This helps prevent unexpected side effects during later modification (i.e. regressions). We’ve also covered that unit tests are generally small, simple and focused, following the “Arrange, Act, Assert” pattern. No one unit test is expected to cover much ground–that’s why you build a large suite of them.

By

It’s a Work in Progress

I’ll have that for you next week. Oh, you want it to do that other thing that we talked about earlier where it hides those controls depending on what the user selects? I’ll have it for you next month. Oh, and you also want the new skinning and the performance improvements too? I’ll get started and we’ll set about six months out as the target date. Unless you want to migrate over to using Postgres. Then make it a year.

Have you ever had any conversations that went like this? Ever been the person making this promise or at least the one that would ultimately be responsible for delivering it? If so, I bet you felt like, “man, I’m totally just making things up, but I guess we’ll worry about that later.” Have you ever been on the hearing end of this kind of thing? I bet you felt like, “man, he’s totally just making things up, and none of this will ever actually happen.”

As you’ll know if you’ve been checking in at this blog for a while, my opinions about the “waterfall methodology” are no secret. It isn’t my intention to harp on that here but rather to point out that the so-called waterfall approach to software development is simply part of what I consider to be a larger fail: namely, putting a lot of distance between promises and deliveries.

I get the desire to do this–I really do. It’s the same reason that we root for underdogs in sports or we want the nice guy not to finish last. Having success is good, but sudden, unexpected success is awesome. It creates a narrative that is far more compelling than “guy who always delivers satisfactory results does it yet again.” Instead, it says, “I just pulled this thing called an iPod out of nowhere and now I’m going to be the subject of posthumous books and cheesy project manager motivational calendars in a decade.”

Companies have done things like this for a long, long time–particularly companies that sell tangible goods. It’s been the story of a consumer-based world where you innovate, patent, and then sell temporarily monopolized widgets, making a fortune by giving the world something new and bold. You probably wind up on the cover of various magazines with feel-good stories about the guy behind the revolutionary thing no one saw coming, except for one visionary (or a team of them) toiling away in anonymity and secrecy, ready to pull back the curtain and dazzle us with The Prestige.

Magician

But here’s the thing. The world is getting increasingly service oriented. You don’t really buy software anymore because now you subscribe and download it from “the cloud” or something. Increasingly, you rent tools and other things that perhaps you would previously have purchased. Even hardware goods and electronics have become relatively disposable, and there is the expectation of constant re-selling. Even the much-ballyhooed innovator, Apple, hasn’t really done anything interesting for a while. We’re in a world where value is delivered constantly and on a slight incline, rather than in sudden, massive eruptions and subsequent periods of resting on laurels.

What does the shifting global economic model have to do with the “waterfall” method of getting things done? Well, it pokes a hole in the pipe-dream of “we’ll go off and bury ourselves in a bunker for six months and then emerge with the software that does everything you want and also makes cold fusion a reality.” You won’t actually do that because no one really does anymore, and even back when people did, they failed a lot more often than they succeeded in this approach.

I love to hear people say, “it’s a work in progress.” That means that work is getting done and those doing it aren’t satisfied, and those two components are essential to the service model. These words mean that the speaker is constantly adding value. But more than that, it means that the speaker is setting small, attainable goals and that those listening can expect improvements soon and with a high degree of confidence. The person doing the work and the target audience are not going to get it perfect this week, next week, next month, or maybe ever. But they will get it better and better at each of those intervals, making everyone involved more and more satisfied. And having satisfaction steadily improve sure beats having no satisfaction at all for a long time and then a very large chance of anger and lawsuits. Make your work a work in progress, and make people’s stock in your labor a blue chipper rather than a junk bond.

By

Introduction to Unit Testing (Don’t Worry, Your Secret is Safe with Me)

Have you ever been introduced to someone and promptly forgotten their name? Or have you ever worked with or seen someone enough socially that you ought to know their name? They know yours, and you’re no longer in a position that it’s socially acceptable for you to ask them theirs. You’re stuck, so now you’re sentenced to an indefinite period of either avoiding calling them by name, avoiding them altogether, or awkwardly trying to get someone else to say their name. Through little to no fault of your own, you’re going to be punished socially for an indefinite period of time.

I think the feeling described strongly parallels the feeling that a developer has when the subject of unit testing comes up. Maybe it’s broached in an interview by the interviewer or interviewee. Perhaps it comes up when a new team member arrives or when you arrive as the new team member. Maybe it’s at a conference over drinks or at dinner. Someone asks what you do for unit testing, and you scramble to hide your dirty little secret–you don’t have the faintest idea how it works. I say that it’s a dirty little secret because, these days, it’s generally industry-settled case law that unit testing is what the big boys do, so you need either to be doing it or have a reason that you’re not. Unless you come up with an awesome lie.

The reasons for not doing it vary. “We’re trying to get into it.” “I used to do it, but it’s been a while.” “Oh, well, sometimes I do, but sometimes I don’t. It’s hard when you’re in the {insert industry here} industry.” “We have our own way of testing.” “We’re going to do that starting next month.” It’s kind of like when dentists ask if you’re flossing. But here’s the thing–like flossing, (almost) nobody really debates the merits of the practice. They just make excuses for not doing it.

I’m not here to be your dentist and scold you for not flossing. Instead, I’m here to be your buddy: the buddy with you at the party that knows you don’t know that guy’s name and is going to help you figure it out. I’m going to provide you with a survival guide to getting started with unit testing–the bare essentials. It is my hope that you’ll take this and get started following an excellent practice. But if you don’t, this will at least help you fake it a lot better at interviews, conferences, and other discussions.

(If you’re a grizzled unit-testing veteran this will be review for you, though you can feel free to read through and critique the finer points)

Let’s Get our Terminology Straight

One common pitfall for the unit-test-savvy faker is misuse of the term. Nothing is a faster giveaway that you’re faking it than saying that you unit test and proceeding to describe something you do that isn’t unit testing. This is akin to claiming to know the mystery person’s name and then getting it wrong. Those present may correct you or they may simply let you embarrass yourself thoroughly.

Unit testing is testing of units in your code base–specifically, the most granular units or the building blocks of your application. In theory, if you write massive, procedural constructs, the minimum unit of code could be a module. But in reality, the unit in question is a class or method. You can argue semantics about the nature of a unit, but this is what people who know what they’re talking about in the industry mean by unit test. It’s a test for a class and most likely a specific method on that class.

Here are some examples of things that are not unit tests and some good ways to spot fakers very quickly:

  1. Developer Testing. Compiling and running the application to see if it does what you want it to do is not unit testing. A lot of people, for some reason, refer to this as unit testing. You can now join the crowd of people laughing inwardly as they stridently get this wrong.
  2. Smoke/Quality Testing. Running an automated test that chugs through a laundry list of procedures involving large sections of your code base is not a unit test. This is the way the QA department does testing. Let them do their job and you stick to yours.
  3. Things that involve databases, files, web services, device drivers, or other things that are external to your code. These are called integration tests and they test a lot more than just your code. The fact that something is automated does not make it a unit test.

So what is a unit test? What actually happens? It’s so simple that you’ll think I’m kidding. And then you’ll think I’m an idiot. Really. It’s a mental leap to see the value of such an activity. Here it is:

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_False_For_1()
{
    var finder = new PrimeFinder();
    Assert.IsFalse(finder.IsPrime(1)); 
}

I instantiate a class that finds primes, and then I check to make sure that IsPrime(1) returns false, since 1 is not a prime number. That’s it. I don’t connect to any databases or write any files. There are no debug logs or anything like that. I don’t even iterate through all of the numbers, 1 through 100, asserting the correct thing for each of them. Two lines of code and one simple check. I am testing the smallest unit of code that I can find–a method on the class yields X output for Y input. This is a unit test.

I told you that you might think it’s obtuse. I’ll get to why it’s obtuse like a fox later. For now, let’s just understand what it is so that at our dinner party we’re at least not blurting out the wrong name, unprovoked.

What is the Purpose of Unit Testing?

Unit testing is testing, and testing a system is an activity to ensure quality. Right? Well, not so fast. Testing is, at its core, experimentation. We’re just so used to the hypothesis being that our code will work and the test confirming that to be the case (or proving that it isn’t, resulting in changes until it does) that we equate testing with its outcome–quality assurance. And we generally think of this occurring at the module or system level.

Testing at the unit level is about running experiments on classes and methods and capturing those experiments and their results via code. In this fashion, a sort of “state of the class” is constructed via the unit test suite for each unit-tested class. The test suite documents the behavior of the system at a very granular level and, in doing so, provides valuable feedback as to whether or not the class’s behavior is changing when changes are made to the system.

So unit testing is part of quality assurance, but it isn’t itself quality assurance, per se. Rather, it’s a way of documenting and locking in the behavior of the finest-grained units of your code–classes–in isolation. By understanding exactly how all of the smallest units of your code behave, it is straightforward to assemble them predictably into larger and larger components and thus construct a well designed system.

Some Basic Best Practices and Definitions

So now that you understand what unit testing is and why people do it, let’s look a little bit at some basic definitions and generally accepted practices surrounding unit tests. These are the sort of things you’d be expected to know if you claimed unit-testing experience.

  • Unit tests are just methods that are decorated with annotations (Java) or attributes (C#) to indicate to the unit test runner that they are unit tests. Each method is generally a test.
  • A unit test runner is simply a program that executes compiled test code and provides feedback on the results.
  • Tests will generally either pass or fail, but they might also be inconclusive or timeout, depending on the tooling that you use.
  • Unit tests (usually) have “Assert” statements in them. These statements are the backbone of unit tests and are the mechanism by which results are rendered. For instance, if there is some method in your unit test library Assert.IsTrue(bool x) and you pass it a true variable, the test will pass. A false variable will cause the test to fail.
  • The general anatomy of a unit test can be described using the mnemonic AAA, which stands for “Arrange, Act Assert.” What this means is that you start off the test by setting up the class instance in the state you want it (usually be instantiating it and then whatever else you want), executing your test, and then verifying that what happened is what you expected to happen.
  • There should be one assertion per test method the vast majority of the time, not multiple assertions. And there should (almost) always be an assertion.
  • Unit tests that do not assert will be considered passing, but they are also spurious since they don’t test anything. (There are exceptions to this, but that is an intermediate topic and in the early stages you’d be best served to pretend that there aren’t.)
  • Unit tests are simple and localized. They should not involve multi-threading, file I/O, database connections, web services, etc. If you’re writing these things, you’re not writing unit tests but rather integration tests.
  • Unit tests are fast. Your entire unit test suite should execute in seconds or quicker.
  • Unit tests run in isolation, and the order in which they’re run should not matter at all. If test one has to run before test two, you’re not writing unit tests.
  • Unit tests should not involve setting or depending on global application state such as public, static, stateful methods, singeltons, etc.

Lesson One Wrap-Up

I’m going to stop here and probably continue this as a series of posts. I’m going to be giving an “introduction to unit tests” talk, probably this week, so I’m doing double duty with blog posts and prepping for that talk. When finished, I’ll probably post the PowerPoint slides for anyone interested. Hopefully if you’ve been faking it all along with unit tests you can now at least fake it a little bit better by actually knowing what they are, how they work, why people write them, and how people use them. Perhaps together we can get you started adding some value by writing them, but at least for now this is a start.

By

Reverential Practice

During the 1950s, a then-unknown journalist named Hunter Thompson briefly held a job working for Time Magazine. During his tenure there, he used a typewriter to type the text, verbatim, of novels by Ernest Hemingway and F. Scott Fitzgerald in order to feel what it was like to write a novel in the literary voice of those great authors. Thompson would go on to write many articles and some books of his own, including the popular Fear and Loathing in Las Vegas, which was eventually made into a movie starring Johnny Depp and Benicio Del Toro. He is also the inventor of a wild, manic style of ‘reporting’ known as Gonzo Journalism in which the author is the story as much as he reports on the story.

HunterThompson

If you type out A Farewell to Arms or The Great Gatsby, I think it’s pretty unlikely that you’ll be the next Hemingway, Fitzgerald, or Thompson. I mean, no offense, but the odds are against that. But what you must have is an incredible amount of appreciation for your craft as an author and an intense desire to carefully study and learn the habits of success and ingenuity. And while you may not strike the big time, I have a hard time imagining that you’ll be worse off for doing so.

In our line of work as programmers, there’s a very important difference between writing the code for an application and writing a novel. A novel is intended to be utterly unique and creative, and it serves as its own end. A program is intended to get the job done and uniqueness is neither necessary nor particularly desirable. (It’s not undesirable–it just doesn’t matter). But in both cases, someone interested in getting inside the mind of success could do a full-on emulation of a renowned practitioner. As a programmer, you could do your best Hunter Thompson and bang out an early version of the Linux kernel, emulating Linus Torvalds and his success at crafting a product.

There are certainly barriers to doing this that don’t exist when it comes to novels. A sizable chunk of software is proprietary, so you can’t see the code to emulate it. The code, language, libraries and platform might be so hopelessly dated that finding hardware on which to run it proves interesting. And if you’re on the cusp of being fired for misappropriating company time, it probably isn’t advisable. But still, imagine what that would be like…

I call this “reverential practice,” though it isn’t practice in the true sense. It’s more of an homage. If you look up practice in the dictionary (the definition we’re using here anyway), you’ll see that it requires repetition for skill acquisition. This clearly isn’t that, since you’d be doing it only once. I say “reverential practice” as a nod and distinction from a term I like that pops up in software blogs and talks: “deliberate practice.” I like the term “deliberate practice” because it distinguishes between doing your job and honing your skills via drilling and focusing deliberately on areas that need improvement. (The term does skew a little heavy toward a borderline weird equation with software development as performance art, but who am I to begrudge people literary and dramatic devices?) And now, I like the term “reverential practice” because it invokes the same connotation of specific attempts to improve, but via unusual amounts of respect for the approach another has taken.

I’m not suggesting that you go out and do this, but it would certainly be an interesting experiment. If you did it, what would you learn from it? Can you do it on a more micro-level in which you just ask someone prominent in the field for a little application they’ve written and then write it yourself? Would it work with just the finished product, or would it be better to see a screencast of them coding and type along with them, extracting methods and deleting things that maybe weren’t right after all? And, most importantly, would it help you improve at all, basking in the reflected successes of another, or would it be a pure act of quasi-religious Programmer Work Ethic?

I don’t know, and there’s a pretty good chance that I’ll never know. But hopefully that’s some interesting food for thought on this Friday. Cheers!

By

Why Are Public Fields No Good, Again?

Today, a developer on my team, relatively new to C# and some of the finer points of OO, asked me to take a look at his code. I saw this:

public class Address
{
    public string Street1;
    public string Street2;
    public string City;
    public string State;
    public string Zip;
}

How would you react to this in my shoes? I bet like I did–with a quick “ooh, no good…try this:”

public class Address
{
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

Busy and pleased with myself for imparting this bit of wisdom, I started to move on. But then I realized that I was failing at mentoring. Nothing annoyed me more as a junior developer than hearing something like, “That’s just the way you do it.” And there I was saying it.

The creatures outside looked from pig to man, and from man to pig, and from pig to man again; but already it was impossible to say which was which.

AnimalFarm_1stEd

I stopped at this point and forced myself to talk about this–to come up with a reason. Obviously, I hadn’t been thinking about the subject of why public fields are icky, so slick terms like “encapsulation” and “breaking change” weren’t on the tip of my tongue. Instead, I found myself talking sort of obliquely and clumsily explaining encapsulation in terms of an example. Strangely, the first thing that popped into my head was threading and locking the field to make it thread-safe. So I said something along the lines of this:

With a public field, you simply have a variable that can be retrieved and set. With a property, you’re really hiding that variable behind a little method, and you have the ability to centralize operations on that variable as needed rather than forcing them on everyone who uses your code. So imagine that you want to make these fields thread-safe. In your version, you force all client code that uses your class to deal with this problem. In the property version, you can handle that for your clients so that they needn’t bother.

Yeah, things are always more awkward off the cuff. So I ruminated on this a bit and thought I’d offer it up in blog post format. Why not have public fields (aside from random thoughts about threading)?

The Basic Consideration: Encapsulation

As amused by my off-the-cuff threading example as I am, it does drive at a fundamental tenet of OOP, which is class-level encapsulation. If I’m writing a class, I want to expose a set of public functionality (methods and properties) that describe what an instance of the class will do while hiding how it will do it. If the address class is in some other assembly and I don’t decompile or anything, as a client, I have no idea if the City property is auto-implemented, if it wraps a simple property, or if all kinds of magic happens behind the scenes.

I know (hopefully) that if I set the City property to some value and then read it back, I get that value. But beyond that, I dunno. Maybe it implements lazy loading from some persistence store. Maybe it tracks all of the things you’ve set City to throughout its entire lifetime. I don’t know, and I don’t want to know if it isn’t part of the public API.

If you use public fields, you can’t offer consumers of your code that blissful ignorance. They know, ipso facto. And, worse, if they want any of the cool stuff I mentioned, like lazy loading or tracking previous values, they have to implement it themselves. When you don’t encapsulate, you fail to provide any kind of useful abstraction to me. If all you’re giving me is a field-bag, what use do I have for an instance of your class in mine? I can just have my own string variables, thank you very much, and I will hide them from my clients.

The Intermediate Consideration: Breaking Changes

There’s a more subtle problem at play here as well. Specifically, public properties and public fields look and seem pretty similar, if not identical, but they’re really not. Under the hood, a property is a method. A field is, well, a field. If you have a property and you decide you want to change its internal representation (going from something complex to simple property or automatic property), no harm done. If you want to switch between a field and a property though, even the trivial switch I showed above, there be dragons–especially going from field to property.

It seems like a pretty obvious case of YAGNI to start out with a field and move to a property if you need it, but things start to go wrong. Weird and subtle things. And they go wrong on you when you least expect them. Maybe you’re using the public members of the Address class as part of some kind of reflection. Well, now things are broken because properties and fields are completely different here. Perhaps you have a field that you were using as an out parameter somewhere. Oops. And, perhaps most insidiously of all, just changing a field to a property will cause runtime exceptions in dependent assemblies unless recompiled.

Let me put that another, scarier way. Let’s say that I write something called Erik’s Awesome DLL and I distribute it to to the world, where it gains wide adoption. Let’s also say that I created a bunch of public fields that the world uses as part of my DLL’s API. And finally, let’s say that in vNext, I decide that I want some encapsulation after all. Maybe I want to implement thread-safety. I implement and publish vNext to Nuget, and you update accordingly. The next time you hit F5, your application will blow up, and it will continue to blow up until you recompile. It may not sound like a big deal, but you’re definitely going to annoy users of your code with stuff like this.

So beware that you’re casting the die here more quickly and strongly than you might think. In most cases, YAGNI applies, but in this case, I’d say that you should assume you are going to need it. (And anything that stops people from using out parameters is good in my book)

The Advanced Consideration: Tell, Don’t Ask

I’ll leave off with a more controversial and definitely the most philosophical point. Here’s some interesting reading from the Pragmatic Bookshelf, and the quote I’m most interested in here describes the concept of “Tell, Don’t Ask.”

[You] should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do.

I’m calling this controversial because the context in which I’m mentioning it here is a bit strained. Address, above, is essentially a data transfer object whose only purpose is to store data. It will probably be bubbled up from somewhere like a database and make its way onto something like a form, with perhaps no actual plumbing code that ever does ask it anything. But then again, maybe not, so I’m mentioning it here.

Public fields are the epitome of “ask.” There’s no telling but not asking–they’re all “ask and tell.” Auto-properties are hardly different, but they’re a slight and important step in the right direction. Because with a property, awkward as it may be, we could get rid of the getter and leave only the setter. This would be a step toward factoring to a (less awkward) implementation in which we had a method that was setting something on the object, and suddenly we’re telling only and asking nothing.

“Tell, don’t ask” is really not about setting data properties and never reading them (which is inherently useless), but about a shift in thinking. So in this example, we’d probably need one more step to get from telling Address what its various fields are only to telling some other object to do something with that information. This is getting a little indirect, so I won’t belabor the point. But suffice it to say that code riddled with public fields tends to be about as far from “tell, don’t ask” as you can get. It’s a system design in which one piece of code sets things so that another can read it rather than a system design in which components communicate via instructions and commands.

So I feel clean and refreshed now. I didn’t shove off with “I’m in charge and I say so” or “because that’s just a best practice.” I took time to construct a carefully reasoned argument, which served double duty of keeping me in the practice of justifying my designs and decisions and staying sharp with my understanding of language and design principles.