DaedTech

Stories about Software

By

Intro to Unit Testing 8: Test Suite Management and Build Integration

It’s been over a month now since my last post in this series, and for that I sort of apologize. I think I’ve been channelling all of my instructive energy into my now-finished Pluralsight course, leaving the blog largely for opinions, screeds, and a random hiring announcement. So, let’s get back on track and wrap this thing up. I have this post and another one slated and then we can call it a day.

So far, I’ve talked quite a lot about how and when (and when not) to write unit tests. I’ve offered up some techniques for helping you isolate the classes that you want to test, including the use of test doubles. And finally, I offered some advice on how to get people to leave you alone and let you write tests. So now I’d like to turn and offer some advice beyond just writing the things. You need to live with them, manage them and leverage them over the course of time.

Managing the Suite

You’ve built them. So, now what? At some point, you’ll wonder exactly when you’re getting started. For the first few or even few dozen classes you test, you’ll alternate between some exasperation at spending extra time doing something new and satisfaction at, well, doing something new. But then, at some point, you’ll be sitting around and notice that your test suite has like 400 tests and think, “wow, that’s a lot of code… do I really want all this?”

That feeling will hit you even harder when you go to change something under a tight deadline and your real quick change makes a test go red. You’re pretty sure the test is broken because it was testing the old way of doing things, so you really just want to comment out the test and you wonder why it’s such a pain to change the code. Why do you have to waste so much time to change one line of code?

The answer to these questions lies in practice but also effective test suite management. If you let the unit test suite become a boat anchor, it will drag you down. Your frustration will be real and reasonable, rather than just a temporary product of you being in a hurry and unfamiliar with working in a code base under test. You need to take care to prevent this from happening, and I’m going to tell you how in this section.

Name Your Tests Clearly and Be Wordy

When you’re writing a unit test, you’re looking at code. But when you’re running your test suite, you aren’t most of the time, and when you’re trying to understand why a run or a build failed, you’re never looking at code. When the test suite is failing, you don’t want to waste time figuring out why. And having to open the IDE, navigate to the test, read the code and figure out the problem is a waste of time.

Don’t give your test methods names like “Test24” or “CustomerTest” or something. Instead, give them names like “Customer_IsValid_Returns_False_When_Customer_SocialSecurityNumber_Is_Empty”. That method name may seem ridiculous, especially if you’re used to giving methods short names, but trust me, you’ll be thankful for it. When your build is failing, which of these method names would you rather see an X next to? Would you rather be saying “looks like test 24 is failing,” or would you rather be saying, “oh, I wonder why someone made it so that an empty SSN is now considered valid?” If you say the first one, you’re lying.

This may seem unimportant in the scheme of things, but it’s the difference between associating frustration and confusion with your test suite and viewing it as a warning system for potentially undesirable changes. The test suite needs to be communicating clearly to you what’s wrong. Descriptive test names help do that and they help you identify whether it’s your code or the test itself that needs to be changed in the face of changing requirements.

Make Your Test Suite Fast

Ruthlessly delete and cull out slow tests. I can’t say it more plainly than that. A good test suite runs in seconds, max. If yours starts to take minutes, or God forbid, hours, then it’s rotting and becoming useless to you. Think of it this way — if it takes several minutes to run the test suite, how often are you going to do it? Every time you make a change, or just when you check in? If it takes hours, will you ever run it voluntarily?

If your test suite takes a long time to run, nobody will run it. Short feedback loops are of paramount importance to developers, and we optimize for efficiency. If the unit test suite is inefficient, we’ll find other ways to get feedback. As such, it is incredibly important to ensure that your test suite always runs quickly. Treat it as if the rest of your team were waiting for any legitimate excuse not to use the test suite, and don’t let inefficiency be that excuse.

Test Code is First Class Code

A common mistake that I see among those relatively new to testing is test code that’s something of a mess. The code will be brittle, heavily duplicated, weird, and hard to read. In short, your tests and test classes will contain code that you wouldn’t be caught dead putting into production.

Don’t do that. Treat your test code as if it were any other code. Eliminate duplication. Factor common functionality out into methods. Be descriptive with naming and with the flow of the method. Keep that code clean. I get that there’s a desire when it comes to testing to make as much of a mess as possible in the “bug bash” sense of throwing chaos at the situation and proving that your code can handle it, but the chaos needs to be controlled, and you can control it by keeping your test code clean and maintainable. If the tests are clean and easy to maintain, people won’t mind going in periodically to make an adjustment. If they’re unruly, people will get annoyed and comment them out or stop running them.

Have a Single Assertion per Test

This is a subtle one, but it also goes toward maintainability. If you start writing tests that have 20 asserts in them, you may feel good that you’re exercising a whole section of the code, but really you’re making things hard for yourself later. If all 20 tests pass (or at least the first 19), then all will be executed. But if the first one fails, none of the rest get executed. This means that in test methods with lots of asserts, it’s not always clear where they’re failing, which means it’s not always clear what’s going wrong.

In order for your test suite to be an asset, it has to be a clear indicator of what’s going wrong. Which would you find more useful in your car: a series of many different lights with helpful diagrams that lit up to indicate a problem, or one unlabeled red light that came on whenever anything at all was wrong? If you had that latter light and it could mean anything from your gas being low to you being out of wiper fluid to imminent destruction of your transmission, I bet you’d just start ignoring it after a while.

Don’t Share State Between Your Tests

There is no more surefire way to drive yourself insane at some future date than by storing some kind of application state among unit tests being executed. What I mean is if you have some test A that declares sets a global counter variable to 1, and then you have another test B that depends on the global counter being set to 1 in order for it to succeed, you are in for a world of hurt.

The problem is that there is no guarantee that the unit test runner will execute the tests in any particular order. What’s likely to happen is that your tests get executed in a particular order whenever you run them on your machine, so everything goes fine. But when the build machine runs them they fail. Weird. So you check them on your friend Bob’s machine, and they pass there. But on Alice’s machine, they fail. If you didn’t already know why this was happening because I just told you, can you imagine how much of your hair you’d pull out? You’d probably be checking the IDE version on those machines, compiler information, OS settings, and God only knows what else. It’d be a wild goose chase.

And imagine if it worked on everyone’s machine initially and then six months later started failing occasionally on the build machine. Machine isn’t the only failing dimension — there’s also time. So please, whatever you do, do not have your unit tests depend on the execution of a previous test. This practice, more than any other, is likely to lead to a rage-quitting of unit testing as a practice where you simply take all of them out of the build.

Encourage Others and Keep Them Invested

This sounds like a strange one to round out the section, but it’s important. If you’re the only one fighting the good fight with unit tests, it becomes daunting and exasperating. Everyone else’s reaction to failing tests is annoyance and they’re waiting for excuses just to stop altogether. You wind up feeling that you’re in an adversarial relationship with the team (I speak from experience here). But if you get others to buy in, you’re not shouldering the burden alone and you have help keeping the suite healthy and helpful.

Build Integration

When you first start out unit testing, the tests will be sort of disorganized and haphazard. You’ll write a few to get the hang of it and then maybe discard them. After a bit of that, you’ll start checking them into your solution (unless you’re an incorrigible weirdo or a liar). You do that, and the suite grows and, ideally, everyone is running it locally to keep things clean and be notified of potential breaking changes.

But you have to take it beyond that at some point if you want to realize the full value of the unit tests. They can’t just be a thing everyone remembers to do locally on pain of nagging emails or because someone will buy the team donuts or some other peer-pressure-oriented demerit system. Failing unit tests have to have real (read: automated) consequences. And the best way to do this is to make it so that failing unit tests mean a failing build.

If you’re in a shop that’s not as formal, this may be difficult at first. One handicap may be that you’re reading this and saying “what do you mean by ‘the build?'” If what you do is write code and take some kind of executable out of your project’s output directory on your machine and push it to a server or to your users, you’ve got some work to do before you think about integrating unit tests. You need a build. A build is an automated process by which your source code is turned into a production-ready, deployable package. And it’s automated in the sense that it doesn’t involve you hitting Ctrl-Shift-B or Ctrl-F6 or whatever you do manually in your IDE to build. The Build, with a capital B, is a process that checks your code out of source control, builds it, runs checks and whatever else is necessary, perhaps increments the versioning of the executables, etc., and then spits out the final product that will be pushed to a server or burned onto a DVD or whatever. If you want to read more about build tools, you can google around about TeamCity, CruiseControl, TFS, FinalBuilder, Jenkins etc. And you don’t have to use a product like that — you can create your own using shell scripts or code if you choose.

Because of all the different options when it comes to programming languages, unit test technologies and build tools, I’m not going to offer a tutorial on how to integrate unit tests into your build. To be comprehensive, I’d need to give dozens of such tutorials. But what I will say is that your integration is going to take the same basic format no matter what tools you’re using. The build is a series of steps that passes if everything goes smoothly and the deliverables are ultimately generated. If a step in the build fails, then the build itself fails. What you need to do is add a step that involves running the unit tests. With this in place, you’re creating a situation where any failing unit test means that the entire build fails.

Conceptually, this is pretty straightforward. Unit test runners can be run in command line fashion and they’ll generate a return value of some kind. So the build tool needs to examine the test runner’s output for an error code. If it finds one, it puts the brakes on the whole operation.

It may seem extreme at first to torpedo the whole build because of a failing unit test, but when you think about it, what else should possibly happen? Why would you want a process that allowed you to ship code knowing that it was defective in a way that it didn’t used to be? That’s amateur hour. And, what’s more is that if your team starts understanding that failed unit tests mean a failed build they’ll be sure to run the tests before check-in so that they don’t fail. It will become a natural part of your process, and the quality of your software will be dramatically improved for it.

By

Notes on Job Hopping Part 4: Free Agency

I’ve been very busy of late, so I had let this series slip a bit by the wayside. But I was taking a break from recording my Pluralsight course to sort of mindlessly read tweets when I noticed this one:

 

Juxtapositions as Outrage Factories

There is a fine line in life when it comes to juxtapositions. On one side of the line is genuine profundity, and on the other side is schmaltzy non sequitur and/or demagoguery. And I’d argue that the line is not entirely subjective, as it might seem. There is a Tupac Shakur lyric that is sort of raw and powerful that says, “they’ve got money for wars but can’t feed the poor.” If you’re a bleeding heart type, you’ll probably agree wholeheartedly. If you’re more of a pragmatist, you might say, “well, there are degrees of poverty and need, and what good is feeding people if some foreign entity is lobbing cruise missiles at them?” But you have to admit that he’s comparing apples to apples, so to speak. There is some finite pool of money, and some of that money is being spent on blowing one another up instead of feeding people that are hungry.

On the other side of this line of juxtaposition lie tiresome canards like “they can send a man to the moon but they can’t make a computer that doesn’t crash?!?” Yeah, because those are the same engineering problem. Or how about “a country can’t run a fiscal deficit because I don’t run my own budget that way at home with the groceries and the car payments!” Right, because macroeconomics is like your budget spreadsheet. And you also have the ability to print money to pay off your debts.

Back to the tweet. I think it sits right on the juxtaposition line. On the one hand, it seems to be sort of garden-variety line-employee populism (“pointy-haired bosses bad, knowledge workers good”) that ignores an important difference between athlete-coach and employee-manager — namely the inverted money and fame related power dynamic of athletes and coaches as compared to line employees and managers. LeBron James, not Erik Spoelstra, puts butts in seats and gets paid a king’s ransom, even if Spoelstra does theoretically call the shots. But it does raise an interesting question that isn’t the one that’s literally being asked (the answer to the tweet’s question being easy: managers make more money, have offices and get to boss people around).

Overvaluing Management

The interesting question that is raised is “why are line managers and other overhead personnel overvalued?” Let me say here that I’m not going to spend this post talking in much detail about that. I poured a lot of time and thought into it for the concluding part of my Expert Beginner E-Book, so I’ll summarize here by saying that there is a bit of a societal pyramid scheme that occurs when it comes to work. We collectively agree on a system where 20-somethings that are new to the workforce do all the grunt work for little pay and that, over the course of our careers, we accumulate vacation time, bigger salaries, larger desks and offices, and the ‘right’ to tell others what to do rather than doing it ourselves. We pat ourselves on the back for ‘earning’ it, but often that assessment is pretty questionable. It’s more a matter of waiting our turn.

Managers are overvalued in organizations because of a collective endorsement of the same kind of reasoning that drives social security — disproportionate dues paying now, followed by disproportionate dues receiving later. The entry level folks tolerate overvalued mid-level management either because they have no choice or because they someday want to be that overvalued mid-level management which is, of course, a fairly sweet deal once you get into the club.

But the overvaluation of management goes deeper than that and finds its real roots in the undervaluation of non-management. In other words, if the Product X Team, consisting of five knowledge workers and a line manager, delivers a spectacular and wildly profitable success, is this owed to the team or to the manager? I’d say that, in a very real way, this is comparable to sports teams in that a good manager will account for some variance and some wins here and there by managing egos, strategizing and motivating, but at the end of the day no one is going to manage a hopelessly underqualified team into serious contention. But, unlike sports teams, the spotlight in the corporate world often falls onto the manager because the manager is in a position to seize it and because it’s simply easier to give credit to the “leader” than to parcel it out in equal portions to the team.

So why do young athletes aspire to be LeBron James and not Eric Spoelstra while young academics want to be the boss rather than the inventor? Because Steve Jobs. Because Warren Buffet. Because bosses, like athletes, represent the competitive pinnacle. It’s really the same thing in the end — a desire to dominate. Athletes, CEOs and, to a lesser extent, line managers, have gotten to where they are by defeating foes in direct competition, and that’s appealing to children in an environment where peer competition is natural and amplified (grade school).

Positive Sum Makers

The overvaluation of managers (and athletes, actually) is a zero-sum outlook on the world. You become a success by competing against others and causing them to fail. There are winners and losers and the glory lies in being a winner in this game. But there is another avenue, which is that taken by who I’d call the Inventor or the Maker — the positive sum player. Makers (and I use this umbrella to describe the overwhelming majority of line-level programmers as well as engineers and other people who produce work product) shine by making the world a better place for all. They transmute their creativity and ambition into products and services that improve the standard of living and better people’s lives. There doesn’t need to be a loser in this game for the Maker to be a winner.

Make no mistake — there is certainly competition among Makers. But it is a healthy, positive sum competition. They compete against one another and themselves to invent great things, to do it quickly, and to do it well. I may want to be the best programmer on earth and that desire may drive me to some late nights hitting the books and cranking out code, but I can produce helpful things without “defeating” some other programmer in some kind of game or competition for position.

The interplay between Makers and what I’ll call Competitors has historically been an uneasy balance. In the time before widespread knowledge workers in corporations, you had the mad-scientist/inventor archetype as your Makers: the Edisons and Teslas of the world. Then with the technological growth of the 20th century, the Makers were kind of funneled into working as Organization Man where they went from being valued professionals in the 50s to eventually being Dilbert in more recent times — toiling under the inept stewardship of a pointy haired boss that also happened to be a marginally victorious Competitor.

Makers were in a difficult position, as they really just wanted to make. Working your way up the corporate ladder as Competitor requires stopping Making and engaging in zero sum gamesmanship that doesn’t interest that archetype, but not doing so meant obeying the micromanaging and often incompetent Competitors and having the fun sucked out of the act of making. Finding organizations that got this right has become so rare that places like Valve and Github are the stuff of legends. Historically, Makers could try going off on their own, but that meant giving up a lot of the Making as well, since they’d then have to worry about their own sales, marketing, accounting, etc.

But the Makers are going to have the last laugh.

“Developernomics”

For Makers that happen to be software developers, times are pretty good, and they’ve been pretty good for years. Most developers complain about how annoying it is that recruiters won’t stop calling them to try to get them to go to interviews for jobs that will pay them more money. Let me repeat that. Developers complain that companies won’t leave them alone when it comes to offering them work. The reason that this is happening is that the demand for programming is absolutely exploding as we transition into a world where the juggernaut of endless automation has finally lurched up to ramming speed and is methodically wiping out other types of jobs at an unbelievable rate. Quite simply, they days of any company not being a “software company” are drawing to a close, as described in an article entitled “Developernomics.

Flush with opportunities, developer job hopping is accelerating rapidly. Companies are ceasing to bother with asking developers why their resumes feature so much job hopping — they’re so hard up for programmers that they don’t bother to ask. More and more, developers are seizing on any excuse or any annoyance to fly the coop and go work on something else. This might be organizational stupidities, but it also might simply be something like “I’m tired of C# and want to try out Ruby for a while.” The mobility within the job market for developers is pretty much unprecedented for a Maker position.

In fact, it’s starting to look a lot more like another type of profession that is also not zero sum: high skill jobs for which there is virtually inelastic demand. Two that come to mind are doctors and lawyers. As long as the world has sick people it needs doctors, and as long as there are disputes (and lawyers making laws requiring lawyers for things), it needs lawyers (at least until the automation juggernaut automates both of these professions — I put an upper bound of 50 years on the full automation of the medical profession making human doctors obsolete). Both doctors and lawyers have a different sort of work model than most Competitors and Makers. They get a lot of education and then they start their own practices, band together in small groups, or join a large existing practice. But whichever option they choose, their affiliations tend to be very fluid and their ability to work for themselves almost a given, if they so choose.

Let’s stop for a second now. These high skill knowledge workers are highly in demand, have a great deal of fluidity in their working relationships and association, and often work for themselves. Doesn’t that sound familiar? Doesn’t it sound like software these days where some people freelance, some people bounce around among startups, and others just job hop between larger corporations to get themselves promoted and paid quickly? And doesn’t it seem like more and more developers are working at shops that are just software development consultancies? Isn’t it starting to seem like the question shouldn’t be “is job hopping okay,” but rather be “for how much longer are we going to bother with typical corporate jobs from which to hop?”

I think the handwriting is on the wall for the future of software development. I think that we’re careening toward a future where developers working for corporations for any length of time is such an anachronism that it isn’t considered a serious possibility. Developers aren’t going to job hop at all because they won’t have traditional corporate jobs. Due to increased globalization, networking, and interconnectivity, developers have sort of a de facto guild — their association with the global network of developers. Promotion, marketing, sales, and even some other aspects of managing one’s own consultancies are collectivized to a degree as developers have a network of friends to help them with such things. They often become moot points because who needs to bother with sales and marketing when business is banging down your door already?

So to return to the sports world and metaphor that started all of this, I’d say the future of developers doesn’t involve an ebb in “job hopping” but rather the opposite: a codified establishment of extreme job hopping as the status quo. Developers are going to become free agents like athletes who drift from team to team as their title aspirations and salary negotiations dictate. Like mercenary athletes, developers are not especially interested in things like culture, domain knowledge, corporate slogans and mission statements and all of that company man, corporate identity stuff. They’re interested in challenging projects, learning, making a few bucks and heading out to the next gig. In a previous post in this series, I had said the answer to the question “should I job hop” is “probably.” In the future, I think the answer to this question for developers will be another question: “uh, as opposed to what?”

By

Beware of Mindless Automation

Something I’ve seen a lot over the years is a tendency to locally maximize when it comes to automating processes. We’re software developers, and thus automation is what we do. But not all automation is created equally, and some of it can be fairly obtuse and misguided if we aren’t careful. And the worst part is that it’s pretty easy to fall into this trap.

For example, let’s say that you observe some people in your organization following a process. They have some Microsoft Word template that they’ve stored somewhere and they regularly open it up and fill it out with data that they pull from an internal system. They populate things like today’s date and various data points and then they do some light formatting based upon various criteria, such as putting items in red if they fall below a certain threshold. When finished, they print out the result, drop it in an envelope, and mail it to another office location of the company. At that location, they process the data and put it into the system — you don’t know too much about that system because it’s not your office location, but that’s the general gist of it.

So, what do you do if you have some spare time and empathy for manual process on your hands and are looking to make a name for yourself? Do you automate this process for them, to their many thanks and heaped praise? And, assuming you do, how do you do it? Do you write some code that pulls the necessary data from your internal system, fires up MS Word interop, and starts automatically generating the documents they’re using? Then, flush with success from that project, do you also automate the printing of the envelopes and metering of the postage?

If you do, how does that go as a function of time? I bet the users are very grateful at first, but then they come to rely on it. And, what’s more, they like the system less and less over the course of time. Every time the USPS changes the price of postage you have to go into this system and made changes, and, what’s worse is that the part that generates the documents seems to break every time there’s a new version or even an update to Word. And when the format of the documents that the other office is requesting changes, suddenly you’ve got a real project on your hands, since automating intricate, form Word documents is about as much fun as spending the afternoon trying to cram a decade of your life onto a one-page resume. Wasn’t this supposed to be helpful? Weren’t you the hero? Does no good deed go unpunished?

Let’s go back to the point where you decided to help. Was the automation as you conceived it worth doing or was it sort of marginal? I mean, you’re probably saving a few minutes for people and some fat-fingering opportunities, but what you still have is sort of an involved, manual process. What if you had stopped to think about the process and the larger goal: getting data from one system into another? Might you not have been talking about things like “web service” or at least “file transfer” instead of things like “Word interop” and “postage?”

Here’s the rub. When your users are solving your problems, they think like users and not like software developers. As such, they come up with non-programming, user solutions. Normal computer users understand MS Word and sending things via mail (or at least email), so they come up with processes that feature those tools. You’re a programmer. By all means, automate, but don’t mindlessly automate whatever they happen to be doing. That’s an optimization tweak. Real software engineering is about using software to create simple solutions to problems. I’ve seen many people fall into this trap and have fallen into it myself. When you’re writing software, asking “why” is invariably more important than asking “how.”

By

I Don’t Really Do That Anymore

The title of this post has become my answer to a surprising number of things lately and something of an unsatisfying conversation ender. For instance, I was having dinner with some friends the other night, and one of them mentioned a bizarre ‘pattern’ in the code he was working one where people would have a switch statement with a single case (and no default) instead of the more traditional if statement. There was some discussion of this approach as an anti-pattern, and when it was my turn to weigh in, I realized with some embarrassment that I couldn’t really remember off the top exactly how enums behaved in all situations. You see, I don’t really do that anymore. I don’t think I’ve typed “public enum Something {” in well over a year.

This comes up in other situations that I can think of. Someone was telling me about duplication in unit tests. Someone was asking me about cursors in stored procedures. Someone was showing me unit testing troubles they were having around a logger with a public static API. There seems to be an endless list of things to which my response is something along the lines of, “that was a point of ickiness for me too, so I wound up avoiding the whole mess altogether.” Or, more succinctly, “I don’t really do that anymore.”

It’s hard to know when saying something like that if you’re being off-putting and sounding like snot, and, more subtly, it’s hard to know whether or not you might be copping out. I tend to think I’m not, however. I don’t generally shy away from attempts at mastery and generally working toward understanding something. I find that usually I shy away from things that result in flailing without mastery — pain without gain. I never found that using enums resulted in cleaner, better, more defensible code, but I definitely found that using them was a surefire way to make me regret the decision later.

I’ll keep this post short and sweet (as an aside, I’m insanely busy, so I’m trying to gravitate a little more toward posting vignettes). Pay attention to what you find yourself avoiding and see if you can put some reason to it. If there’s some programming practice or construct that makes your fingers subconsciously clench every time you think about doing it, ask yourself why. It may be that a very good explanation or discovery awaits you at the end of some brainstorming and white-boarding. Likewise, if your friends, coworkers or peers in general seem to have developed an aversion toward something, ask them about it or do a little research for largely the same reasons.

We all learned hard lessons as children. After the first time you touched the hot stove, you surely never did it again (at least not on purpose). See if you can pull some wisdom from that stark, simple lesson, and put some solid rationale behind your aversions.

By

Wrapping Up and an E-Book: The Tragedy of the Expert Beginner

It’s been a pretty busy week for me, which is why I haven’t posted in over a week. I’m in the midst of my next Pluralsight course, and I spent the last week getting ready for closing, then actually closing, on a house. On top of that, the Expert Beginner e-book is now available!

ExpertBeginner

Here is the start of the final post in the series and the conclusion of the book:

The real, deeper sadness of the Expert Beginner’s story lurks beneath the surface. The sinking of the Titanic is sharply sad because hubris and carelessness led to a loss of life, but the sinking is also sad in a deeper, more dull and aching way because human nature will cause that same sort of tragedy over and over again. The sharp sadness in the Expert Beginner saga is that careers stagnate, culminating in miserable life events like dead-end jobs or terminations. The dull ache is endlessly mounting deficit between potential and reality, aggregated over organizations, communities and even nations. We live in a world of “ehhh, that’s probably good enough,” or, perhaps more precisely, “if it ain’t broke, don’t fix it.”

There is no shortage of literature on the subject of “work-life balance,” nor of people seeking to split the difference between the stereotypical, ruthless executive with no time for family and the “aim low,” committed family type that pushes a mop instead of following his dream, making it so that his children can follow theirs. The juxtaposition of these archetypes is the stuff that awful romantic dramas starring Katherine Heigl or Jennifer Lopez are made of. But that isn’t what I’m talking about here. One can intellectually stagnate just as easily working eighty-hour weeks or intellectually flourish working twenty-five-hour ones.

I’m talking about the very fabric of Expert Beginnerism as I defined it earlier: a voluntary cessation of meaningful improvement. Call it coasting or plateauing if you like, but it’s the idea that the Expert Beginner opts out of improvement and into permanent resting on one’s (often questionable) laurels. And it’s ubiquitous in our society, in large part because it’s encouraged in subtle ways. To understand what I mean, consider institutions like fraternities and sororities, institutions granting tenure, multi-level marketing outfits, and often corporate politics with a bias toward rewarding loyalty. Besides some form of “newbie hazing,” what do these institutions have in common? Well, the idea that you put in some furious and serious effort up front (pay your dues) to reap the benefits later.

To read the entire conclusion, or if you like this series in general and want to support it, please consider buying the e-book. It is available right now on Amazon and will be available soon in other e-book stores as well. The price in all stores is $4.99. Here it is on the publisher’s site, where you will be able to find links to everywhere that it’s available. (As an aside, any of you with a blog should take a look at Blog Into Book, an 1871 startup that can help you generate an e-book from a book-worthy string of posts on your blog.)

In the interest of full disclosure, I will publish this last post in its entirety around the end of the year. This series of posts is, well, a series of posts, and I certainly don’t want to penalize regular readers of the blog by withholding content. But the e-book is more than just the posts strung together — it is complete with some additional content, better segues, and a more continuous flow. So I’d encourage you to get it if you want to see my conclusion sooner rather than later or if you’d like to read the series as a single work.

Also, I’d like to thank Amanda Muledy for editing and illustrating the book, as she does with my site.