DaedTech

Stories about Software

By

ChessTDD 12: Blind Alleys

This is one last post of loose ends and looking at what I’ve done so far before I move on to new functionality.  It wasn’t necessarily my finest hour as a programmer and TDD practitioner, but I’m including the good with the bad because nobody is always on when programming.  Also, I wasn’t insanely on in terms of my audio recording either becasue my throat was a little scratchy today.  You might hear me stealing quiet sips of ice water from time to time.  Anyway, here’s what I accomplish:

  • Reworked the knight’s GetMovesFrom() method
  • A fair amount of design flailing

Here are some lessons to take away:

  • An interesting side effect of a long time spent at TDD is a tendency to look to unit tests when wanting to dive into and explore code functionality.  The reason for this is that you know the unit tests will show you how the class’s public API is being used.  So, it’s like finding the class instantiated in-situ in the code, except that in the test class, it’s going to have a bunch of methods with descriptive names telling you how things work.
  • When doing exploratory refactorings, try to keep things compiling as much as you can and check with as much granularity as possible that you’re still passing.  Some of my flailing with tuples of ints wasn’t really necessary, but I kept my eye on the tests anyway so as not to get stuck in the middle of a refactoring where tests are red, things are broken, and I don’t know how to go forward or back.
  • Check out around 9:50.  Copy and paste will always burn you.  Even when it’s tiny.  Even when you’re using TDD.
  • Sometimes you wind up in a blind alley.  That’s okay.  Go back to where things were working and tackle the problem again later.  This isn’t just punting; it’s an important skill.  You need to ship code and you can get lost in trying to perfect it if you aren’t careful.  So, let it go, think on it later — in the shower or while eating dinner or something — the answer will smack you in the forehead like a ton a bricks at some point.

By

Fun with ILDASM: Extension vs Static Methods

I recently had a comment on a very old blog post that led to a conversation between the commenter and me. During the conversation, I made the comment that extension methods in C# are just syntactic sugar over plain ol’ static methods. He asked me to do a post exploring this in further detail, which is what I’m doing today. However, there isn’t a ton more to explain in this regard anymore than explaining that (2 + 2) and 4 are representations of the same concept. So, I thought I’d “show, don’t tell” and, in doing so, introduce you to the useful concept of using a disassembler to allow you to examine .NET’s IL byte code.

I won’t go into a ton of detail about virtual machines and just in time compilation or anything, but I will describe just enough for the purpose of this post. When you write C# code and perform a build, the compiler turns this into what’s called “IL” (intermediate language). Java works in the same way, and its intermediate product is generally referred to as byte code. When an IL executable is executed, the .NET framework is running, and this intermediate code is compiled, on the fly, into machine code. So what you have with both .NET in Java is a 2 stage compilation process: source code to intermediate code, and intermediate code to machine code.

Where things get interesting for our purposes is that the disassembled intermediate code is legible and you can work with it, reading it or even writing it directly (which is how AOP IL-Weaving tools like Postsharp work their magic). What gets even more interesting is that all of the .NET languages (C#, VB, F#, etc) compile to the same IL code and are treated the same by the framework when compiled into machine code. This is what is meant by languages “targeting the .NET framework” — there is a compiler that resolves these languages into .NET IL. If you’ve ever wondered why it’s possible to have things like “Iron Python” that can exist in the .NET ecosystem, this is why. Someone has written code that will parse Python source code and generate .NET IL (you’ll also see this idea referred to as “Common Language Infrastructure” or CLI).

Anyway, what better way to look at the differences or lack thereof between static methods and extension methods. Let’s write them and see what the IL looks like! But, in order to do that, we need to do a little prep work first. We’re going to need easy access to a tool that can read .NET exe and dll files and produce the assembly in a readable, text-file form. So, here’s what we’re going to do.

  1. In Visual Studio, go to Tools->External Tools.
  2. Click “Add” and you will be prompted to fill out the text boxes below.
  3. Fill them out as shown here (you may have to search for ILDASM.exe, but it should be in Microsoft SDKs under Program Files (x86):ILDASM
  4. Click “Apply.”  ILDASM will now appear as a menu option in the Tools menu.

Now, let’s get to work.  I’m going to create a new project that’s as simple as can be. It’s a class library with one class called “Adder.” Here’s the code:

public static class Adder
{
    public static int AddTwoNumbers(int first, int second)
    {
        return first + second;
    }
}

Let no one accuse me of code bloat! That’s it. That’s the only class/method in the solution. So, let’s run ILDASM on it and see what happens. To do that, select “ILDASM” from the Tools menu, and it will launch a window with nothing in it. Go to “File->Open” (or Ctrl-O) and it will launch you in your project’s output directory. (This is why I had you add “$(TargetDir)” in the external tools window. Click the DLL, and you’ll be treated to a hierarchical makeup of your assembly, as shown here:

ILDASMPopulated

So, let’s see what the method looks like in IL Code (just double click it):

.method public hidebysig static int32  AddTwoNumbers(int32 first, int32 second) cil managed
{
  // Code size       9 (0x9)
  .maxstack  2
  .locals init ([0] int32 CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  stloc.0
  IL_0005:  br.s       IL_0007
  IL_0007:  ldloc.0
  IL_0008:  ret
} // end of method Adder::AddTwoNumbers

Alright… thinking back to my days using assembly language, this looks vaguely familiar. Load the arguments into registers or something, add them, you get the gist.

So, let’s see what happens when we change the source code to use an extension method. Here’s the new code:

public static class Adder
{
    public static int AddTwoNumbers(this int first, int second)
    {
        return first + second;
    }
}

Note, the only difference is the addition of “this” before “int first.” That is what turns this into an extension method and alters the calling semantics (though you can still call extension methods the same way you would normal static methods).

So, let’s see what the IL code looks like for that:

.method public hidebysig static int32  AddTwoNumbers(int32 first, int32 second) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       9 (0x9)
  .maxstack  2
  .locals init ([0] int32 CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  stloc.0
  IL_0005:  br.s       IL_0007
  IL_0007:  ldloc.0
  IL_0008:  ret
} // end of method Adder::AddTwoNumbers

The only difference between this and the plain static version is the presence of the line:

.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )

The “this” keyword results in the generation of this attribute, and its purpose is to allow the compiler to flag it as an extension method. (For more on this, see this old post from Scott Hanselman: How do Extension Methods work and why was a new CLR not required?). The actual substance of the method is completely identical.

So, there you have it. As far as the compiler is concerned, the difference between static and extension methods is “extension methods are static methods with an extension method attribute.” Now, I could go into my opinion on which should be used, when, and how, but it would be just that: my opinion. And your mileage and opinion may vary. The fact of the matter is that, from the compiler’s perspective, they’re the same, so when and how you use one versus the other is really just a matter of your team’s preferences, comfort levels, and ideas about readability.

By

Recall, Retrieval, and the Scientific Method

Improving Readability with Small Things

In my series on building a Chess game using TDD I’ve defined a value type called BoardCoordinate that I introduced instead of passing around X and Y coordinate integer primitives everywhere. It’s a simple enough construct:

public struct BoardCoordinate
{
    private readonly int _x;
    public int X { get { return _x; } }

    private readonly int _y;
    public int Y { get { return _y; } }

    public BoardCoordinate(int x, int y)
    {
        _x = x;
        _y = y;
    }

    public bool IsCoordinateValidForBoardSize(int boardSize)
    {
        return IsDimensionValidForBoardSize(X, boardSize) && IsDimensionValidForBoardSize(Y, boardSize);
    }

    private static bool IsDimensionValidForBoardSize(int dimensionValue, int boardSize)
    {
        return dimensionValue > 0 && dimensionValue <= boardSize;
    }
}

This was a win early on the series to get me away from a trend toward Primitive Obsession, and I haven't really revisited it since. However, I've found myself in the series starting to think that I want a semantically intuitive way to express equality among BoardCoordinates. Here's why:

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_1_2_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 1 && bc.Y == 2));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_2_2_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 2 && bc.Y == 2));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_3_3_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 3 && bc.Y == 3));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Does_Not_Return_0_0_From_1_1()
{
    Assert.IsFalse(MovesFrom11.Any(bc => bc.X == 0 || bc.Y == 0));
}

This is a series of unit tests of the "Queen" class that represents, not surprisingly, the Queen piece in chess. The definition of "MovesFrom11" is elided, but it's a collection of BoardCoordinate that represents the possible moves a queen has from piece 1, 1 on the chess board.

This series of tests was my TDD footprint for driving the functionality of determining the queen's moves. So, I started out saying that she should be able to move from (1,1) to (1,2), then had her also able to move to (2,2), etc. If you read the test, what I'm doing is saying that this collection of BoardCoordinates to which she can move should have in it one that has X coordinate of 1 and Y coordinate of 2, for instance.

What I don't like here and am making mental note to change is this "and". That's not as clear as it could be. I don't want to say, "there should be a coordinate in this collection with X property of such and such and Y property of such and such." I want to say, "the collection should contain this coordinate." This may seem like a small semantic difference, but I value readability to the utmost. And readability is a journey, not a destination -- the more you practice it, the more naturally you'll write readable code. So, I never let my foot off the gas.

During the course of the series, this nagging readability hiccup has caused me to note and refer to a TODO of implementing some kind of concept of equals. In the latest post, Sten asks in the comments, referring to my desire to implement equals, "isn't that unnecessary since structs that doesn't contain reference type members does a byte-by-byte in-memory comparison as default Equals implementation?" It is this question I'd like to address here in this post.

Not directly, mind you, because the assessment is absolutely spot on. According to
MSDN:

If none of the fields of the current instance and obj are reference types, the Equals method performs a byte-by-byte comparison of the two objects in memory. Otherwise, it uses reflection to compare the corresponding fields of obj and this instance.

So, the actual answer to that question is simply, "yes," with nothing more to say about it. But I want to provide my answer to that question as it occurred to me off the cuff. I'm a TDD practitioner and a C# veteran, for context.

Answering Questions on Language Knowledge

My answer, when I read the question was, "I don't remember what the default behavior of Equals is for value types -- I have to look that up." What surprised me wasn't my lack of knowledge on this subject (I don't find myself using value types very often), but rather my lack of any feeling that I should have known that. I mean, C# has been my main language for the last 4 years, and I've worked with it for more years than that besides. Surely, I just failed some hypothetical job interview somewhere, with a cabal of senior developers reviewing my quiz answers and saying, "for shame, he doesn't even know the default Equals behavior for value types." I'd be laughed off of stack overflow's C# section, to be certain.

And yet, I don't really care that I don't know that (of course, now I do know the answer, but you get what I'm saying). I find myself having an attitude of "I'll figure things out when I need to know them, and hopefully I'll remember them." Pursuing encyclopedic knowledge of a language's behavior doesn't much interest me, particularly since those goalposts may move, or I may wind up coding in an entirely different language next month. But there's something deeper going on here because I don't care now, but that wasn't always true -- I used to.

The Scientific Method

When I began to think back on this, I think the drop off in valuing this type of knowledge correlated with my adoption of TDD. It then became obvious to me why my attitude had changed. One of the more subtle value propositions of TDD is that it basically turns your programming into an exercise in the Scientific Method with extremely rapid feedback. Think of what TDD has you doing. You look at the code and think something along the lines of, "I want it to do X, but it doesn't -- why not?" You then write a test that fails. Next, you look at the code and hypothesize about what would make it pass. You then do that (experimentation) and see if your test goes green (testing). Afterward, you conduct analysis (do other tests pass, do you want to refactor, etc).

ScientificMethod

Now you're probably thinking (and correctly) that this isn't unique to TDD. I mean, if you write no unit tests ever, you still presumably write code for a while and then fire up the application to see if it's doing what you hypothesized that it would while writing it. Same thing, right?

Well, no, I'd argue. With TDD, the feedback loop is tight and the experiments are more controlled and, more importantly, isolated. When you fire up the GUI to check things out after 10 minutes of coding, you've doubtless economized by making a number of changes. When you see a test go green in TDD, you've made only one specific, focused change. The modify and verify application behavior method has too many simultaneous variables to be scientific in approach.

Okay, fine, but what does this have to do with whether or not I value encyclopedic language knowledge? That's a question with a slightly more nuanced answer. After years of programming according to this mini-scientific method, what's happened is that I've devalued anything but "proof is in the pudding" without even realizing it. In other words, I sort of think to myself, "none of us really knows the answer until there's a green test proving it to all of us." So, my proud answer to questions like, "wouldn't it work to use the default equals method for value types" has become, "dunno for certain, let's write a test and see."

False Certainty

Why proud? Well, I'll tell you a brief story about a user group I attended a while back. The presenter was doing a demonstration on Linq, closures, and deferred execution and he made the presentation interactive. He'd show us methods that exposed subtle, lesser known behaviors of the language in this context and the (well made) point was that these things were complex and trying to get the answers right was humbling.

It's generally knowledgeable people that attend user groups and often even more knowledgeable people that brave the crowd to go out on a limb and answer questions. So, pretty smart C# experts were shouting out their answers to "what will this method return" and they were getting it completely wrong because it was hard and it required too much knowledge of too many edge cases in too short a period of time. A friend of mine said something like, "man, I don't know -- slap a unit test on it and see." And... he's absolutely right, in my opinion. We're not language authors, much less compilers and runtimes, and thus the most expedient answer to the question comes not from applying amassed language knowledge but from experimentation.

Think now of the world of programming over the last 50 years. In times where compiles and executions were extremely costly or lengthy, you needed to be quite sure that you got everything right ahead of time. And doing so required careful analysis that could only be done well with a lot of knowledge. Without prodigious knowledge of the libraries and languages you were using, you would struggle mightily. But that's really no longer true. We're living in an age of abundant hardware power and lightning fast feedback where knowing where to get the answers quickly and accurately is more valuable than knowing them. It's like we've been given the math textbook with the answers in the back and the only thing that matters is coming up with the answers. Yeah, it's great that you're enough of a hotshot to get 95% of the answers right by hand, but guess what -- I can get 100% of them right and much, much faster than you can. And if the need to solve new problems arises, it's still entirely possible for me to work out a good way to do it by using the answer to deduce how the calculation process works.

Caveats

In the course of writing this, I can think of two valid objections/comments that people might have critiquing what I'm saying, so I'd like to address them. First of all, I'm not saying that you should write production unit tests to answer questions about how the framework/language works. Unit testing the libraries and languages that you use is an anti-pattern. I'm talking about writing tests to see how your code will behave as it uses the frameworks and languages. (Although, a written and then deleted unit test is a great, fast-feedback way to clarify language behavior to yourself.)

Secondly, I'm not devaluing knowledge of the language/framework nor am I taking pride in my ignorance of it. I didn't know how the default Equals behavior worked for value types yesterday and today I do. That's an improvement. The reason it's an improvement is that the knowledge is now stored in a more responsive cache. I maintain having the knowledge is trumped by knowing how to acquire it, and I look at reaching into my own personal memory stores as like having it in a CPU cache versus the memory of writing a quick test to see versus the disk space location of looking it up on the internet or asking a friend.

The more knowledge you have of the way the languages and frameworks you use work, the less time you'll have to sink into proving behaviors to yourself, so that's clearly a win. To continue the metaphor, what I'm saying is that there's no value or sense in going out preemptively and loading as much as you can from disk into the CPU cache so that you can show others that it's there. In our world, memory and disk lookups are just no longer expensive enough to make that desirable.

By

Chess TDD 11: Tying Up Loose Ends

Now that all of the pieces are implemented, I decided to change it up a little and get rid of a few yellow items in one fell swoop. I thought it would also be a good time to
Here’s what I accomplish in this clip:

  • Move Knight into it’s own class.
  • Separated test and production into their own assemblies.
  • Got rid of the DefaultBoardSize constant that had been defined in many test classes by consolidating it into one place.
  • Went back to earlier test classes and implemented “MovesFromXX” pattern that I had settled on.

Here are some lessons to take away:

  • If Ctrl-M, O stops working (or any normal VS shortcut), it might be because some other process has claimed part or all of the sequence and is preempting Studio.  Kill processes or reboot.
  • If you have some mundane but necessary tasks (e.g. moving a bunch of classes into a new namespace), this can be a good way to “warm up” when you haven’t looked at a code base in a while.  Generally speaking, look for tasks that can ease you back into the code base while being productive.
  • Even duplication that is seemingly mundane should be avoided.  The DRY principle states that systems should have a single point of definition for all pieces of knowledge, and that includes things like database schemas, config files, and yes, even your unit test classes.  You need to maintain your unit tests, and duplication creates maintenance pain.
  • Pay attention to your test method names as you’re making changes.  These are documentation as to how your code should behave, so if you change the behavior, make sure to change the names to reflect those changes.
  • “Lean on the compiler” to help you with your refactorings.  If you’re getting rid of a bunch of references to a variable, delete the definition of the variable and the compiler will then force you to get rid of all references.  If you do it in the opposite order, you might miss references and waste time.  As a more general rule, you should always favor failing early and doing things in such a way that mistakes will result in non-compiling.
  • When you do large refactorings, it might not hurt to run all of the unit tests explicitly in the test runner instead of relying on the dots in NCrunch (or any continuous testing tool that you’re using).

By

How To Quit Your Job

Before you get any ideas, this isn’t about Amway; I’m not going to follow “how to quit your job” with an ellipsis and then a bunch of promises about how you can make a 7 figure income emailing pictures of cats to people. In fact, this post has nothing whatsoever to do with whether or not you depend on having a job. I’m not offering advice on how not to need a job or how to “quit the game” or anything like that. I’m speaking quite literally about how to resign from your job when the time comes (usually when you’ve accepted an offer from another organization) and all of the considerations around that. I’ve received a surprising (to me anyway) number of requests for advice on this topic, so I thought I’d share my thoughts. As someone who has managed people and who has moved around some, I certainly have perspective on the matter that, perhaps, you will find worthwhile.

This "How to Quit Your Job" post is not about cat memes, like the one pictured here.

Before I get right down to it, I’d like to make a quick note about “rage-quitting.” Don’t rage quit; be a grownup. Okay, moving on.

Prelude to Quitting

Before you decide to quit a job (consciously, anyway) you generally dip your toe into the job market, sending off some resumes, doing some phone interviews, etc. This is the phase in which it feels like you’re doing something kind of wrong but exhilarating — cheating on your current employer (or at least flirting). You’ve grown tired of your situation and you’re starting to daydream about a new one where they don’t follow such a stupid development methodology, you won’t have to deal with Steve from two rows over anymore, and they even have ping pong tables. Ping pong — do you hear me?! So why does this make you feel a little guilty? Well, it should — the game is setup this way. You have to sneak around, claiming to be sick when you’re not and doing other quasi-unscrupulous things. It’s a bummer, but it’s the way the game is played, unfortunately. Perhaps a better system will come along and obviate this practice.

Until that happens, however, you have to make do. It might be tempting to tell your employer what you’re doing either for the sake of honesty or to make them sweat, but resist this impulse. You don’t want to tip your hand at all because it’s option-limiting for you. If you throw in their face that you’re out job-hunting, they may scramble to please/keep you in the short term, but they’ll certainly start forming contingency plans in the long term. And, it also makes you sound like a prima donna.

If you think that your employer can correct whatever is causing you to want to look elsewhere, state your case without threats. If you’re paid below market value, come in with data from salary.com, your last few performance reviews, and a level-headed pitch for more money. Same general idea if you think you should have more responsibility or a better title. Don’t bring threats into it — and make no mistake, that’s what telling them you’re going to look for other jobs is — instead, sell yourself. If it doesn’t work, thank them for their consideration, tell them they’ve actually convinced you that they have the right of it, and start lining up interviews. Hey, you gave them a chance.

When you’re lining up interviews, space them out and separate the wheat from the chaff. (This is mainly applicable to programmers, who will be inundated with interview requests in today’s economy.) It can be tempting, especially if you’re disgruntled, to book 12 interviews over a Monday-through-Friday span, but what plausible, non-suspicious reason do you have for a run of absence like that? Your supply of mornings to come in late, afternoons to sneak out early, and random “sick” days is going to be quite limited, particularly if you’ve just made a pitch for a better title and been refused. Use these wisely. Filter out all but the best opportunities. Don’t take ‘interviews’ with recruiters (they’ll cave — just tell ’em you’ll call a different recruiter). Feel free to push back on things and let them go for a week or two. It’ll be okay.

A bird in the hand is worth two in the bush, and your current job is in hand, earning you money. Until you have an offer, it’s clearly your best prospect.

Dotting I’s and Crossing T’s

After a bit of sneaking around, your activities paid off. You managed to snag a good offer and you’re ready to march in and let everyone know that you’re moving on to bigger and better things. Settle down, because you have work to do first. First of all, you need to sign the offer and have it returned to you, counter-signed, before you have anything of substance (and theoretically, an employer can even go back on a signed offer letter — it’s not a contract, per se — even though it would be bad faith). Next, you need to see what the offer is contingent upon.

Are they going to call your references? Do a background check? Credit check? Drug test? Something you’ve never heard of before like an obstacle course or feats of strength? These things aren’t mere formalities — they’re grounds for rescinding the offer. Do not give notice at your current job until all of these things are taken care of. Never done any drugs in your life and nothing in your background to hide, you say? Great, but that doesn’t mean a false positive is impossible. It’d certainly be a bummer if your offer got yanked because some other Joe Smith made the local police blotter for stealing a car. It’s a situation that could likely be straightened out, but you’re in no-man’s-land until it is.

Make sure there are no possible obstacles before you take an irrevocable step with your current employer who is, still, even with contingent offer in hand, your best prospect.

Actual Resignation

Alright, so no one dropped a bunch of poppy seeds into your urine and no one sharing your name stole any cars recently, so everything went smoothly with the offer and contingencies. Now, you’re ready to make it official. But, what to do? Tell your manager in passing? A phone call? An email, since that’ll be an awkward conversation? How do you drop this bombshell? None of the above. You’re going to type up a letter of resignation, sign it, and bring it along with you to a one on one meeting that you’ll request with your direct supervisor (perhaps someone above that person in the chain in an odd situation such as your boss being on vacation for a week or something).

Yikes, so what to type in the letter? Not much. Make it very short, polite and unremarkable. Here’s more or less what I use:

Dear {Boss}:

Please accept this as my formal resignation from the position of {your official title} at {official company name}, with my last day being {date of last day}. I very much appreciate you giving me the opportunity to work for {informal/abbreviated company name}.

This decision was made very difficult by the fact that I have thoroughly enjoyed my time and experience here. Please let me know how I can be of assistance with any knowledge transfer or final work over the next {duration of notice period}.

Again, thank you very much for the opportunity to work for you.

Sincerely,

{your first and last name}

Put a header with your address on top of it on the right and the company’s address, C/O your boss on the left below it, leave 5 lines between Sincerely and your name, and sign it once you print it. This is not the venue for a soliloquy on how you’ve grown with the company, and it’s not the place to skewer people you hate. This is basically just going to go in a folder somewhere as proof for HR that you left voluntarily rather than them firing you. It’s not likely anyone will even read it.

With that in hand, schedule some time with your boss to talk and, when in there, get to the point. Say something like, “I just want to let you know that I’m going to be resigning, effective X date, and I’m happy to help with any knowledge transfer or whatever you need, and here’s a letter to that effect.” Don’t send an email or make a phone call or leave a note or something. Grab a few minutes of the manager’s time, look him or her in the eye, and offer your resignation.

Also, offer two to three weeks of notice. Two weeks is pretty standard, and most prospective new employers will understand up to three weeks of notice before you start (a little extra notice and maybe a long weekend for you to decompress or something). Giving less than two weeks is poor form (and no new boss should expect this of you). Giving more than three weeks seems like it’d be a really courteous thing to do, but in reality, it’s just kind of awkward. You take on a dead man/woman walking status once people know that you’re leaving and things get kind of weird. You really don’t want to drag this out for too long.

Keep it Classy

Once you’ve handed in your resignation, the reaction you face is likely to vary. If you’re close with your manager and have a good working relationship, they probably expect it. If not, you’re going to be catching the person off guard, so expect reactions that range from disappointment to dismay to anger to sadness. On occasion, you might even get happiness, if they don’t like you (or if they really like you and don’t like the company very much). Having been on the manager’s end of the table, I can tell you that you’re almost never expecting someone dropping by your office to say, “hey, I quit,” so expect a bit of unguarded emotion from the manager before they get their bearings.

All that said, it’s fairly unusual for a manager to have a real outburst of any kind. The most common reaction will be to ask you why you’re quitting (even if they already know), and at this point, it’s vital to stay classy. There’s nothing for you to gain by launching a volley of negativity at them — just say that you have an opportunity that you think is a better fit or a chance to advance your career or whatever. Be positive about your new gig — not negative about the current one.

On the rare occasion that you are subject to a hissy fit, take the high road. Deep breaths, calming thoughts, and level-headed coolness are your allies. If it gets too heated, you can always leave the room (I mean, what are they going to do to you?). Go in knowing that yours is a position of strength and don’t worry about things like, “what if they fire me on the spot?” Not to say that it would never happen, but a manager or higher-up berating or even firing a quitting employee is spectacularly stupid. If they’re doing that they might as well hang a sign in the building that says, “if you’re going to quit, you’re better off doing it without notice!” Managers and leaders need notice to arrange a replacement, line up knowledge transfer, craft a message about the departure, etc. Knowing all of that should help you stay calm in the face of whatever happens. But, nothing much will probably happen beyond the obligatory, “sure hate to see ya go, but good luck!”

AngryArch

The Counter-Offer

Assuming that your boss or someone higher in the food chain hasn’t acted like an idiot in reaction to your quitting, a common thing to which you’ll be exposed is the counter-offer. Michael Lopp calls this an attempt at a “diving save.” On Monday, you tell your boss that you quit, and later that afternoon or evening, boss has a meeting with the CTO, some other managers, and maybe even the CEO in which they discuss your offer of notice. Since programmers are so hard to hire and keep these days, it’s decided that they have to try to keep you. So, Tuesday morning when you come in, you have a meeting invite asking you to come to that conference room way across the building with the leather chairs and the little fridge with sodas in it, and there on the meeting roster are a bunch of people that have always been too important to be in meetings with the likes of you. Until today, that is.

In this room, they smile and offer you a soda, and they tell you how important you are to the company. They tell you that they have your best interests at heart, and they warn you that a lot of people who leave wind up being unhappy elsewhere. Then, they lay the good stuff on you. They’ll bump you from Software Engineer III to Software Engineer IV and bump your pay by 9, count-em 9 thousand dollars per year. You lick your lips and do a little quick math, realizing that’s $750 extra dollars per month and, even after tax, a pretty nice car payment for the new car you’ve been needing. Heck, it’s even more of a bump than the offer you got from the new company. Should you take it?

If you want to hear arguments as to why counter-offers are a bad idea, just ask any recruiter to whom you talk. Recruiters work by taking a percentage of your first year’s salary from the company that hires you, and they lose that cut if you sign and then decide to stay. Counter-offers are recruiters’ mortal enemy, so if you ask them about whether you should accept a counter offer, you will be treated to a polished, well-rehearsed, convincing argument that counter-offers are such a terrible idea that it’s not unheard of for them to lead to cancer. They’ll tell you about how the company is in a bind but once you agree they’ll start trying to replace you. They’ll tell you that bosses don’t like to have a gun placed to their head and will resent you. They’ll tell you about the dreaded HR matrix and how if you accept a counter offer you won’t get a promotion for 8 years.

But here’s something I’ve never heard them say, and the reason I tend to think that accepting counter offers is a bad idea. Returning to the relationship metaphor from earlier when I mentioned the idea of “sneaking around” on your employer, consider what a resignation is. You’ve had kind of a vacant stare for a while and a feeling that something just isn’t working. You go out, flirt a little with other companies and then decide, “you know what — it’s over — time for a change!” You muster up the courage to take that plunge and have that difficult conversation, and your partner is then hurt and perhaps a bit in denial. After sleeping on it, though, bargaining starts the next day. “Alright, I know you said that you aren’t happy, so here’s what I’m going to do: you get to pick the pizza topping every Saturday instead of us alternating, you never have to come to my parents’ house except on Christmas, and I’ll take over the dishes and the vacuuming.” You’ve initiated a breakup due to existential unhappiness and the other party responds with an appealing set of superficial improvements. So do you then say, “Gosh, I was pretty unhappy, but really, every Saturday night we can have mushrooms and pepperoni? Hello, relationship bliss!” If so, how long before the blank stare comes back?

In the article I linked, Lopp says the following of the diving save:

Diving Saves are usually a sign of poor leadership. People rarely just up and leave. There are a slew of obvious warning signs I’ve documented elsewhere, but the real first question you have to ask yourself once you get over the shock of an unexpected resignation is: “Did you really not see it coming? Really?”

The question then becomes whether you really want to work at a place that only addresses your discontentment at the absolute last conceivable moment. Do you want to work at a place that’s only interested in your happiness when faced with your eminent departure? There’s probably a reason that you want to go, and that reason is probably tied heavily into a corporate culture where you’ve felt that no one who mattered was interested in championing your cause. You’re going to get that $750 and buy a car, and then it won’t be an awesome sum of money but rather ho-hum, just what you make. Your life will be basically the same as it was before they shoveled that money your way in a desperate attempt to keep you. They’re not going to consider you almost leaving a wake-up call to stop taking you for granted; they’re going to consider you a problem solved via bribe.

The Awkward Two Weeks And Exit Interview

Let’s assume you’ve politely declined any counter-offers that are made and you’re just riding out your last two weeks. First of all, continue staying classy and maintain a positive attitude. This is not the time to tell off people that you don’t like or make a lot of snarky comments. Be polite and helpful with knowledge transfer activities and finishing up any last work. If you disagree with who is going to be replacing you on things, keep that to yourself. These shouldn’t be hard things to do and it shouldn’t be hard to stay optimistic — you’re going to be free soon and there’s a light shining very clearly at the end of the tunnel.

At some point, HR will want to bring you in for an exit interview in which they ask all sorts of candid questions about your time with the company. Let me be clear about this — there is zero upside for you when it comes to being honest about criticism of the company. The smartest course of action is for you to walk in there, smile, and tell them how wonderful the company is and that you really hate to leave but it’s just too good an opportunity to pass up. Why? Well, what’s going to happen is that your responses are going to be reviewed by various manager and VP types as matters of feedback for improving the company. So the very people that might later offer you references or even hire you back later (stranger things do happen) are going to be seeing your feedback. And, those people are humans who probably won’t enjoy reading about how they’re “completely clueless” or “incompetent” or whatever you might feel like saying.

I’m not telling you not to be honest. I’m just telling you that there’s no benefit to you in being honest beyond how momentarily cathartic it might be to tell someone what you’ve really thought this whole time. It might be that you have friends working there and someone giving feedback about how everyone hates some activity or has real problems with some person might help your friends that will continue to work there. Fine, great, go for it if you want. Just understand that there’s no upside for you and there’s definite potential for downside.

Last Day and Beyond

Your last day will probably be the weirdest. You should spend some time making the rounds, saying your goodbyes, exchanging contact info, and wishing people well. I would also recommend an offer, if appropriate and within the bounds of your new employment agreement, to consult here and there if your former group/boss needs it. Typically this sort of consultation is easy, extra money for you and ensures a good professional relationship on an ongoing basis. Even just the offer will probably be well received, even if politely declined.

Once you’ve left, keep contact with your friends if so desired, and perhaps go for a drink or meal now and then. But on top of that, I’d keep in touch with former managers or people in authority positions as well. At minimum, if they like you, they can be excellent references. But on top of that, they may leave and go elsewhere and want to hire people. Or the landscape may change at your former employer and it might be worth considering again for you. Or maybe none of that is the case, but hey, what does it cost you to be friendly and exchange an email with someone every now and then? I’ve been around for a while in this industry and in a variety of roles and I never once have found myself thinking, “ugh, I wish I hadn’t stayed in touch with that guy!”

Throughout the whole bizarre and awkward process of leaving a job, the main thing to remember is to be classy and professional and always to take the high road. It can be tempting to do otherwise and leave in a blaze of sour grapes, telling people what you really think. But your career is really a fancy wrapper around the concept of your own earning power, and your earning power is your ticket to comfort and security throughout your life. It’s not worth jeopardizing for a youtube-able moment that might go mildly viral for a few days or something. Take the long view and do your best to leave organizations with an even more positive view of you than they had when they made you the initial offer.