DaedTech

Stories about Software

By

Visualization Mnemonics for Software Principles

Whether it’s because you want to be able to participate in software engineering discussions without having to surreptitiously look things up on your phone, or whether it’s because you have an interview coming up with a firm that wants you to be some kind of expert in OOP or something, you probably have at least some desire to be knowledgeable about development terms. This is probably doubly true of you, since ipso facto, you read blogs about software.

Toward that end, I’m writing this post. My goal is to provide you with a series of somewhat vivid ways to remember software concepts so that you’ll have a fighting chance at remembering what they’re about sometime later. I’m going to do this by telling a series of stories. So, I’ll get right to it.

Law of Demeter

Last week I was on a driving trip and I stopped by a gas station to get myself a Mountain Dew for the sake or road alertness. I grabbed the soda from the cooler and plopped it down on the counter, prompting the clerk to say, “that’ll be $1.95.” At this point, naturally, I removed my pants and the guy started screaming at me about police and indecent exposure. Confused, I said, “look, I’m just trying to pay you — I’ll hand you my pants and you go rummaging around in my pockets until you find my wallet, which you’ll take out and go looking through for cash. If I’m due change put it back into the wallet, unless it’s a coin, and then just put it in my pocket, and give me back the pants.” He pulled a shotgun out from behind the counter and told me that in his store, people obey the Law of Demeter or else.

PantlessLawOfDemeter

So what does the Law of Demeter say? Well, anecdotally, it says “give collaborators exactly what they’re asking for and don’t give them something they’ll have to go picking through to get what they want.” There’s a reason we don’t hand the clerk our pants (or even our wallet) at the store and just hand them money instead; it’s inappropriate to send them hunting for the money. The Law of Demeter encourages you to think this way about your code. Don’t return Pants and force clients of your method to get what they want by invoking Pants.Pockets[1].Wallet.Money — just give them a Money. And, if you’re the clerk, don’t accept someone handing you a Pants and you going to look for the money — demand the money or show them your shotgun.

Single Responsibility Principle

My girlfriend and I recently bought an investment property a couple of hours away. It’s a little house on a lake that was built in the 1950’s and, while cozy and pleasant, it doesn’t have all of the modern amenities that I might want, resulting in a series of home improvement projects to re-tile floors, build some things out and knock some things down. That kind of stuff.

One such project was installing a garbage disposal, which has two components: plumbing and electrical. The plumbing part is pretty straightforward in that you just need to remove the existing drain pipe and insert the disposal between the drain and the drainage pipe. The electrical is a little more interesting in that you need to run wiring from a switch to the disposal so that you can turn it on and off. Now, naturally, I didn’t want to go to all the hubub of creating a whole different switch, so I decided just to use one that was already there. The front patio light switch had the responsibility for turning the front patio light on and off, but I added a little to its burden, asking it also to control the garbage disposal.

That’s worked pretty well. So far the only mishap occurred when I was rinsing off some dishes and dropped a spoon in the drain while, at the same time, my girlfriend turned the front light on for visitors we were expecting. Luckily, I had only a minor scrape and a mangled spoon, and that’s a small price to pay to avoid creating a whole new light switch. And really, what’s the worst that could happen?

Well, I think you know the worst thing that could happen is that someone loses a hand due to this absurd design. When you run afoul of the Single Responsibility Principle, which could loosely be described as saying “do one thing only and do that thing well” or “have only one reason to change.” In my house, we have two reasons to change the state of the switch: turning on the disposal and turning on the light, and this creates an obvious problem. The parallel situation in code is true as well. If you have a class that needs to be changed whenever schema updates occur and whenever GUI changes occur, then you have a class that serves two masters and the possibility for changes to one thing to affect the other. Disk space is cheap and classes/namespaces/modules are renewable resources. When in doubt, create another one.

Open/Closed Principle

I don’t have a ton of time for TV these days, and that’s mainly because TV is so time consuming. It was a lot simpler when I just had a TV that got an analog signal over the air. But then, things went digital, so I had to take apart my TV and rewire it to handle digital signals. Next, we got cable and, of course, there I am, disassembling the TV again so that we can wire it up to get a cable signal. The worst part of that was that when I became furious with the cable provider and we switched to Dish, I was right back to work on the TV. Now, we have a Nintendo Wii, a DVD player, and a Roku, but who has the time to take the television apart and rewire it to handle each of these additional items? And if that weren’t bad enough, I tried hooking up an old school Sega Genesis last year, and my Dish stopped working.

… said no one, ever. And the reason no one has ever said this is that televisions that you purchase follow the Open/Closed Principle, which basically says that you should create components that are closed to modification, but open for extension. Televisions you purchased aren’t made to be disassembled by you, and certainly no one expects you to hack into the guts of the TV just to plug some device into it. That’s what the Coax/RCA/Component/HDMI/etc feeds are for. With the inputs and the sealed-under-warranty case, your television is open for extension, but closed for modification. You can extend its functionality by plugging anything you like into it, including things not even out yet, like an X-Box 12 or something. Follow this same concept for flexible code. When you write code, you strive to maximize flexibility by facilitating maintenance via extension and new code. If you program to interfaces or allow overriding of behavior via inheritance, life is a lot easier when it comes time to change functionality. So favor that over writing some juggernaut class that you have to go in and modify literally every sprint. That’s icky, and you’ll learn to hate that class and the design around it the same way you’d hate the television I just described.

Liskov Substitution Principle

I’m someone that usually eats a pretty unremarkable salad with dinner. You know, standard stuff: lettuce, tomatoes, crutons, scallions, carrots, and hemlock. One thing that I seem to do differently than most, however, is that I examine each individual item in the salad to see whether or not it will kill me before I put it into my mouth (a lot of other salad consumers seem to play pretty fast and loose with their lives, sheesh). I have a pretty simple algorithm for this. If the item is not hemlock, I eat it. If it is hemlock, I put it onto my plate to throw out later. I highly recommend eating your hemlock salad this way.

Or, you could bear in mind the Liskov Substitution Principle, which basically says that if you’re going to have an inheritance relationship, then derived types should be seamlessly swappable for their base type. So, if I have a salad full of Edibles, I shouldn’t have some derived type, Hemlock, that doesn’t behave the way other Edibles do. Another way to think of this is that if you have a heterogeneous collection of things in an inheritance hierarchy, you shouldn’t go through them one by one and say, “let’s see which type this is and treat it specially.” So, obey the LSP and don’t make hemlock salads for people. You’ll have cleaner code and avoid jail.

Interface Segregation Principle

Thank goodness for web page caching — it’s a life saver. Whenever I go to my favorite dictionary site, expertbeginnerdictionary.com (not a real site if you were thinking of trying it), it prompts me for a word to lookup and, when I type in the word and hit enter, it sends me the dictionary over HTTP, at which time I can search the page text with Ctrl-F to find my word. It takes such a long time for my browser to load the entire English dictionary that I’d be really up a creek without page caching. The only trouble is, whenever a word changes and the cache is invalidated, my next lookup takes forever while the browser re-downloads the dictionary. If only there were a better way…

… and there is. Don’t give me the entire dictionary when I want to look up a word. Just give me that word. If I want to know what “zebra” means, I don’t care what “aardvark” means, and my zebra lookup experience shouldn’t be affected and put at risk by changes to “aardvark.” I should only be depending on the words and definitions that I actually use, rather than the entire dictionary. Likewise, if you’re defining public interfaces in your code for clients, break them into minimum composable segments and let your clients assemble them as needed, rather than forcing the kitchen sink (or dictionary) on them.  The Interface Segregation Principle says that clients of an interface shouldn’t be forced to depend on methods that they don’t use because of the excess, pointless baggage that comes along.  Give clients the minimum that they need.

Dependency Inversion Principle

Have you ever been to an automobile factory?  It’s amazing to watch how these things are made.  They start with a car, and the car assembles its own engine, seats, steering wheel, etc.  It’s pretty amazing to watch.  And, for a real treat, you can watch these sub-parts assemble their own internals.  The engine builds its own alternator, battery, transmission, etc — a breathtaking feat of engineering.  Of course, there’s a downside to everything, and, as cool as this is, it can be frustrating that the people in the factory have no control over what kind of engine the car builds for itself.  All they can do is say, “I want a car” and the car does the rest.

I bet you can picture the code base I’m describing.  A long time ago, I went into detail about this piece of imagery, but I’ll summarize by saying that this is “command and control” programming where constructors of objects instantiate all of the object’s dependencies — FooService instantiates its own logger.  This runs afoul of the Dependency Inversion Principle, which holds that high level modules, like Car, should not depend directly on lower level modules, like Engine, but rather that both should depend on an abstraction of the Car-Engine interaction.  This allows the car and the engine to vary independently meaning that our automobile factory workers actually could have control over which engines go in which cars.  And, as described in the linked post, a code base making heavy use of the Dependency Inversion Principle tends to be composable whereas a command and control style code base is not, favoring instead the “car, build thyself” approach.  So to remember and understand Dependency Inversion principle ask yourself who should control what parts go in your car — the people building the car, or the car itself?  Only one of those ideas is preposterous.

By the way, if you liked this post and you're new here, check out this page as a good place to start for more content that you might enjoy.

By

TDD Chess Game Part 7: Cleaning up the Bishop

I bet you thought I’d forgotten this series and the video-cast leg of my burgeoning multimedia empire, but fear not, because I’m back following a long vacation, bookended by content publication announcements. I took previous feedback to heart and increased the font size of the spreadsheet as I worked, and decided to make this video a little shorter. Trending closer to the 10 minute mark than 20 is partly selfish because the 20 minute videos take an absolutely staggering amount of time to produce in my video editing software, but I also think it might be more consumable this way. So, here’s what I accomplish:

  • Extended test coverage on Bishop.GetMovesFrom() a bit.
  • Cleaned up Bishop.GetMovesFrom().
  • Moved Bishop to its own file.

And here are lessons to take away:

  • It’s fun to be cute with your todo list, but the effect wears off after a break — better perhaps to keep it simple and descriptive.
  • When you see duplication or repetition of patterns, ask yourself what’s the variable or variables in the repetition and see if you can parameterize it for an extracted method.
  • Use the refactor phase to ask yourself, “how would this method read to someone who’d never seen this code before,” and to focus on how to make it clearer.
  • This is pretty heavy on personal opinion, but I think favoring declarative (functional, Linq-y stuff) over imperative (loops and control flow) semantics promotes readability.

By

The Zen of Rejection: Let Companies Go In That Other Direction

The Courtship

There’s nothing like the beginning of a job search. It’s often born out of a time of transition or frustration, and perhaps uncertainty and worry, but no matter how it starts, there’s a rush and glow as you start to peruse the jobs that are out there. Why do you feel a rush and get a glow, no matter how unhappy you’d been feeling up to that point? Well, it’s because the world comes alive with possibilities as you look at Dice or Stackoverflow Careers or whatever. These aren’t just jobs — they’re the next chapter in your life.

And oh, how appealing they are. And it’s not just because you’re unemployed or sick of your current situation or just wanting a change of pace — it’s because they’re wonderful. They do Agile to your Waterfall and let you flex your hours instead of a rigid 9 to 5. That 401k match sure looks nice too, and they’re using the latest version of the language and the IDE. But, oh, there’s so much more. Casual dress. Free food and soda. Why, they even have a chef to cook you gourmet lunches and stationary bike-desks that you can use to exercise while you work. You can bring your dog in. They have X-Boxes and Playstations and Wiis. It’s really more like going on a cruise than going to work, and they’re beckoning to you, welcoming you, and telling you to become part of their exclusive club — nay, their family. You too can be one of the group of smiling, diverse people pictured in the “Careers” section on their website, having the absolute time of your life.

DreamJob

You submit your application. Really, how could you not? This is your chance at happiness, but now, the nerves set in. What if they don’t call you? But then they do, and the nerves only increase. What if you’re stumped in the phone screen? What if you screw up and tell them that you’re leaving because you don’t like your boss instead of what you’re supposed to say: “I’m just excited at the prospect of joining your organization?” But whew, you manage to avoid too much honesty and to secure an invitation to talk in person. Now, at this point, you put on your absolute best outfit, get in your car to drive over and make sure you get there very early, all the while telling yourself, “whatever you do, don’t be yourself — be that confident, diplomatic, smooth-talking version of yourself that will make you want to collapse from exhaustion the moment you leave.”

The interview passes in a surreal whirlwind as you meet with 8 different people, improvise on strange questions, answer with relief when you know what to say, pivot subtly when you don’t know what to say, remember not to fidget, and smile the whole time. You walk out into the parking lot, sweating under your nice clothes as you heave a huge sigh of relief, make yourself comfortable in your car, and get ready to plop down on your couch at home with a beer. But even that relief is short-lived, because now you have to wait to hear back, which makes the days seem like weeks, and weeks seem like years.

Finally, it comes: the offer. Now, it’s time for giddiness, assuming the pay, benefits and title are in line. You’ve been invited to Shangri La, and you’ve gratefully accepted. You gear up for the first day there, not knowing quite what to expect. And really, how could you? You’ve gone to great lengths to show them a completely air-brushed version of yourself in response to the unrealistic utopia they’ve shown you. So now, starting on your first day, you can see what the other looks like when it’s no longer the evening of the grand ball. Oh, but you’re already married. Better hope no one’s coach turns into a pumpkin (but here’s the catch: to some degree or another, it will, on both sides).

And that’s if everything goes really well. But what if it doesn’t? What if they decide to “go in another direction?” Oh, the rejection, disappointment, angst, and heartbreak.

Culture Shock

I’ve sort of mulled over how to make this point without sounding like a conceited jerk, but that’ll be hard, so I think I’ll just power through it and hope that most of my readership can relate. We’re programmers, and programmers are generally pretty intelligent. I’d imagine that most of you reading are no strangers to overachieving.

When I was growing up and attending school, test time was my time to shine. Whether it was getting into the advanced track math classes, taking standardized tests, trying out for clubs, applying to colleges, or pretty much anything else you can think of, those were things where I showed up and won. I got straight A’s in high school and graduated valedictorian. I won academic scholarships. I was never even cut from a high school sports team. In the sieve of primary and secondary school stratification, I was always the one that received the metaphorical job offer, and I’m sure it was the same for many of you. We grew up in a culture where a bunch of kids in a room working on a test meant you were about to get some accolades and general validation. If the real world were like high school, we’d all have had to rent storage to house all of our offer letters. Wake-ups would come later.

The first one I got, personally, was in my orientation week at college. The Carnegie Mellon CS department is pretty selective, and to prove that we were no longer in the minor leagues, so to speak, they asked all of us in our entering class of 150 people or so to raise our hands if we’d been the valedictorian of our high school. Something like a third of the hands in the room went up, and my jaw dropped. I wasn’t in Kansas anymore, and I no longer won anything just by showing up.

But if college was tough in this circumstance and some that would follow, the “real world” was downright depressing and seemed to have no interest in a fresh computer science grad (graduating at the end of 2001 as the dotcom bubble was bursting sure didn’t help). I failed job interviews before they even talked to me. I sent out resumes and they crossed the event horizon of some HR black hole. I’d get the occasional phone interview and then a “no thanks,” email if they bothered responding at all. In my life growing up, I’d barely ever heard, “I’m sorry, but we’re going in a different direction,” and now I was lucky to hear it because at least I was hearing something. The effect on my self esteem was profound. At the time, I thought my degree to be useless and I felt that some sort of confusing betrayal had occurred, rendering past academic success entirely non-predictive of continued success in this cruel new world I had entered.

Returned Mojo, Anger, and Preemptive Rejection

Eventually, I did get a job. I somehow managed to convince someone to hire me, and I was incredibly grateful and terrified. I was grateful because I could stop moonlighting and taking retail jobs to make ends meet, but terrified because if I’d learned anything from the job search it was that I was great at school but bad at life. But I made it in the real world. In fact, I more than made it. After a stumbling out of the gate, once I was employed, I consistently earned excellent performance reviews and was rapidly advanced and given greater and greater responsibility.

I started to regain some dignity and then some swagger. I was no longer “kid with no industry experience” — I was a software engineer that took business trips, met with clients and, most importantly, got things done. During this time, I also started earning my MS degree in Computer Science via night school, and by the time I hit the job market again in coming years, I was ready.

In fact, I was more than ready. I had learned my lessons from the previous job search and hit the ground running when I wanted a change, doing all of the right things. Interviews were easy to get for someone with my (now miraculously no longer useless) degree pedigree and years of experience in several programming languages, and I did well enough to receive offers. And this time, I’d figured out an important fact: any company that didn’t make me an offer clearly had a flawed interview process. Any form of rejection I haughtily interpreted as ipso facto process failure on their part. This arrogant self-righteousness, I believe, was misdirected resentment at my culture shock from the first go-round. I had done well in school, and then done well in the business world, and was getting offers from most companies with which I interviewed, so how dare those other companies and the ones from years ago make me doubt myself the way I had, very deeply, as a new grad?

As the “chip on the shoulder” mentality started to fade a bit (with me maturing and realizing this was petulant and comically egotistical), left in its place was a more antiseptic tendency to preemptively reject certain companies. I didn’t like rejection any better than at any time in my life — who does — but I didn’t view it as an insult or a mistake, either. I came closer to seeing it this way: “if I don’t apply, I’ll never be rejected, but if I apply and am rejected, I’d always have to say, if asked under oath, that I wasn’t good enough for that company.” By this time, I had become a good sport about “thanks but no thanks,” but I sought to avoid it. If, based on the job requirements or phone screen, it seemed like an interview might not go well, I told the company, “no thanks,” before they could tell me the same. Aha! The rejector has become the rejected! Take that! I would only play in games where I felt the deck had been stacked in my favor, which is good for a small king in a small kingdom, but bad if you ever want to reach and push yourself.

The Reality and Rejection Zen

I spent a lot of years going through this progression of my view on interviews. The feelings of inferiority, regaining my confidence but compensating with righteous indignation, and picking and choosing my spots to minimize rejection all played into an internal mantra of “there’s so much wrong with the interview process and it’s so reductionist, and I won’t do that to people.” If I were a character in Animal Farm, I would be Napolean, however. Like him, I wound up becoming that against which I railed. As my career wound on and I was in a position to make hiring decisions, I surprised myself by being reductionist. “Oh, that was the woman who didn’t really understand what unit tests were,” or “that was the guy who stumbled weirdly when trying to explain why he liked ASP MVC instead of ASP Webforms.” Human lives — and probably imminently capable humans — reduced to a single mistake they had made or passed over in consideration for someone who had happened to impress that day.

It was from this perspective that I realized the sad reality of the interview process. I had been viewing it wrong all along; you’re not really “rejected” from jobs and the interview process isn’t an evaluation of your intrinsic worth in the same way that something like the SAT purports to be (even at companies that make it a point to try to make their interview process exactly that). At best (and probably more in the realm of ideal), it’s an evaluation of mutual fit — something like, “we have a team with characteristics X and Y, and we’re looking for compatibility with X and Y, but also someone who brings Z.” You can be a wonderful human, full of X and Y, but not familiar with or interested in Z, and the position won’t be a good mutual fit. For instance, if I’m hiring someone to be a DBA and your interest is in client side web programming, neither of us pursuing things past the phone interview phase is not a rejection.

What I finally understood, particularly after seeing how the sausage is made and then making it when it comes to candidate selection, is that an interview is really more like you calling up a buddy and saying, “hey, wanna go see that new X-Men movie tonight?” Most likely, the answer will be no because there are a million reasons it could be no. Your buddy might be sick, he might be working late, he might have a date, he might be watching a game on TV, he might be annoyed with you, he might not like movies… the list goes on forever. Some of his reasons could be related to you and some completely based on other factors.

So it is with the interview process. A company may think you’re great and perfectly able to do the work, but have someone else in mind that’s just an absolute perfect match. Or the CEO might be forcing them to hire his nephew. Or they might have decided not to hire after all. In fact, it could be that they never intended to hire, and the whole process was a sham to humor some muckety muck (and before you doubt me, I’ve seen this happen). Again, as many reasons as stars in the night sky.

Once I wrapped my head around this, I stopped fearing or much caring about interview “rejection” beyond simple disappointment that I didn’t get a job about which I was excited or perhaps regret at the fruitless time investment. It was as if I’d called my friend about the movie and he’d said offhandedly, “meh, not tonight.” The response then becomes, “okie dokie, maybe some other time.” A little disappointment is in order if you were hoping to go together but, hey, you have other friends and you could always go on your own.

This is how I got over the feeling that interviews are some measure of your competence or worth — a feeling that I have no doubt is quite pervasive among those reading as well, even if you tell yourself or others that you’re fine with it. So next time you don’t get a job offer, imagine saying in your head, “well, maybe we’ll catch the movie work together some other time or maybe I’ll just invite someone else apply somewhere else.” It seems silly, but I invite you to try it. I imagine that you’ll find it takes the sting out of that call/email/letter/non-response to some degree, if not totally. At the very least, I hope my story might help your confidence not be shaken.

This is the end of the first part of this series, but I anticipate several more. Stay tuned for next time when I tell you why this zen state that I’ve reached depresses me in a kind of detached way and as I delve into how seriously, seriously dysfunctional I think the employee-employer pairing process is in our society (including the likely controversial assertion that the interview process might conceivably not be worth doing). By the end of the series along these lines, I’m hoping to collect my thoughts enough to end on an up note, offering ideas as to how things could be improved.

By

Failed to Open storage.ide

I don’t make a lot of “lessons learned” posts anymore, but here’s hoping this quick post saves some of you out there a headache when you get this message and google it.

A few weeks back, I had an issue with the Git source code provider in Visual Studio and tweeted about it here (though in my haste/ire, I incorrectly described it as “tfs-git”):

It was a weird problem, because I’d been hitting that code base from 3 machines, each with the same version of Visual Studio installed. One of them never had any problems, and the other two always bombed out with this message, forcing me to go into the command line and committing from there, which worked. I have no idea what’s different among these machines and why it would work on one but not the others. Weird.

A long way down in the reply chain, Buck Hodges provided the answer that worked:

Sure enough, I went in and added this to my .ignore file, and it did the trick. This never would have occurred to me, however, as a possible solution. That whole directory and all of the files in it were created by the IDE-Source control apparatus anyway. It’s not like I went in there and created those files, and it’s not like I was using them for anything. I actually would have assumed they would already be in that auto-generated .ignore file. I mean, this is sort of like the IDE saying to me, “I can’t check in your code because this file I created without your knowledge is being used… by me.” (When troubleshooting, I investigated which process was using the file, and the only one was Visual Studio).

Nevertheless, I was grateful for the fast response and the fix that worked, so I won’t look a gift horse in the mouth.

By

Starting to Unit Test: Not as Hard as You’d Think

I happened to read a post by Dror Helper the other day, in which he said:

I believe TDD and “unit tests” has been done a great injustice by not giving it a cooler name – preferable one that doesn’t have the word “test” in it – because it’s a PR disaster!

Wow, that had never occurred to me, and yet he’s spot on. “Unit test” is a wretched name for anything. It seems somehow to combine all the worst elements of propagating uncertainty in arithmetic with lab measurements and double checking all of your answers on a scantron exam. I just bored myself half to death typing that sentence, so it’s little wonder that the concept of a “unit test” is often met with a visceral “ugh.” I mean, we could at the least call the test suite a “verification checklist” or something, implying that you’re marking progress as you complete things. It’s not exactly a skydiving trip, but it has to beat “unit test.” But I digress.

The lack of appeal of the name, the feeling of already being pressed for time without taking something else on, and the natural resistance to the unknown all create barriers to entry when it comes to unit testing. In order to get started yourself, or especially to convince those around you to do the same, it’s necessary to overcome those barriers. I have a good bit of experience with this in a variety of environments and from a variety of roles. Over the last year, I did a string of blog posts that were essentially my talking scripts for a series of power point/demo talks I gave. These were meant to be an introduction to unit testing.

The series actually became pretty long, and as I was finishing it, I decided to put it into E-Book format. So, with the help of my editor, we turned it into fluid book, and with the help my publisher, we published it in all major E-Book formats for a cost of $4.99. Here is the book on the publisher’s site, and here is a link directly to Amazon
for Kindle readers (full disclosure: I’m experimenting with affiliate links, and that’s why I’m specifically linking Amazon directly).

The title of the book is “Starting to Unit Test: Not as Hard as You Think,” and I feel that the title really captures it. My goal was to trade practice purity for reducing the barriers to entry. In other words, the message was, “don’t feel like you have to start full bore with 100% coverage, TDD, and anything less is a failure — if you just introduce a few tests into a few places in your code, consider that a win.” I also did something else that I haven’t seen others do as much, which is to explain that some types of frameworks and code present unit testing nightmares, and that newbie unit testers should avoid them until they reach a higher belt.” What I’d like to see people take away from this book is a feeling of satisfaction from experiencing a sequence of small but real wins.

For you mythology buffs, this is Sysiphus actually making it to the top of the hill.

For you mythology buffs, this is Sysiphus actually making it to the top of the hill.

By and large, you could get this content by reading through my blog posts on the subject, but if you want it in a compact format, here it is. Not to mention, if you’re trying to sell your team on the merits of unit testing, a book is probably going to have more cachet than a series of blog posts, and the one link to the book is going to be easier to distribute than the giant collection of links for the individual posts/chapters. So get it if you’re interested, and encourage your team to get it if you’re trying to introduce the concept, especially since I close out the book by making a business case for unit testing (this making cases for best practices is actually going to be a theme of mine in the future).

Enjoy, and thanks for reading (the blog and, hopefully, the book)!