Failure is a Teacher; Success, a Deceiver
By: Daniel Villa
Tags: Coding, Software Devlopment, Software Engineering, Career Advice

Failure is a Teacher; Success, a Deceiver

The erroneously-passing unit tests are perhaps the evilest of all written code.

Photo by Brusk Dede on Unsplash

tldr;

Just because you’ve had one success, do not assume you know the way.

Stay humble. Any time you encounter a new idea, understand it first before you criticize it or promote your way.

Deceitfully Successful Code

The erroneously-passing unit tests are perhaps the evilest of all written code. Albeit once, I did stumble across a test one time that went something like this.

Obviously, this is a child of the Evil One. If you were looking at the test output it looks as if the function is tested. A Q/A, reviewing the report from the CI/CD tool might not notice. However, much more often I see something more like this.

The bug is subtle and takes many forms. This test passes via Jest in its current state. However, if you added a second expect statement of: expect(username).toBe("Mullen"); the test would fail. If you write code TAD fashion (Test After Development) you will someday get as far as the toBeDefined() and call it good. In fact, you've created a lier and it was not good. He will lie to all that he can until finally at some end to end test a Q/A gets cranky.

Side Note: This is why TDD takes such a scientific approach of “Make an assumption, then validate it”. In TDD, your first test is more likely to be expect(username).toBe("Mullen"); rather than expect(username).toBeDefined() . It is not perfect, but it is better.

Second Side Note: the bug is that you need await user.getUsername() to allow the promise to resolve. This is far more common in code that uses the done callback. done may be called when none of the expect() statements ran.

Photo by Joshua Earle on Unsplash

Deceitfully Successful Experiences

Consider another example in the realm of teams. When I was on a particular project we spent years creating trust with stakeholders and rigor around our dev process. We had made leadership proud on a few occasions and had slowly increased the levels of automation and tooling over a multi-year period. We did design documents, we had checklists for code reviews, and we had lots of automated CI/CD for the project and supporting libraries. In this environment, my role as a leader was fairly uncomplicated and low pressure. I felt accomplished. I took the TAD approach and thought, “Well this seems to be working, I’ll make an assumption that this is the way”.

But success is like a local maximum. Perched on my little hill, through many retrospectives, I had been to both sides of the hill and both led downward. Reinforcing my position that at this little height, at this particular graphical zoom level, I was on The Peak.

One day I found myself on a new project with a number of challenges. I began to operate in the same fashion that had worked well on the previous project expecting to subdue the challenges with the proven strength of my existing ways. The only thing that was proved was what a poor match my methods were for this new environment. The trust was hardly there in the new team and the rigor had to start from 0, not the 100 that I attempted to dive into.

My past success had lied to me. I lasted about 5 weeks on the project before the team and I understood that a different approach would be needed. I split off to do a side project with one of the developers a different lead came in and took the horns taking a fairly different approach, but one that worked rather well.

The Problem of Success

Here is the way I think about this. When we succeed at something, we don’t really know why we actually succeeded. We might have ideas, but it’s not a scientific calculation. In the few business advice books I’ve read they caveat so much of what they say with “you will still need luck”. Luck they say is an essential ingredient. Why? Because if you take the success of one person and duplicate the steps for another person in similar situation you are likely to NOT get a successful outcome. Each individual and each circumstance are just too unique for a detailed formulae of success.

Let’s compare the concrete act of landing a plan with the fluid act of project management. Landing a plan successfully requires thousands of parts and processes to work in sync. We can say the success depends on each part doing its defined and limited function at the right time in the right way. Of the few unpredictable variables, the weather might be the highest risk factor to success since we can’t control it to the degree we can control our own systems designs. A successful project may have a similar definition of success. However, on the project we cannot possibly deconstruct the system to understand the parts — namely, the people. We cannot fully understand how Bob interacts with Alice, or how Alice covers for Bob on a sick day, or how Alice and Bob shared the same background and so never had a communication problem. As a leader, you may have a successful project and you may look at it, like the airplane, and think that you understand the parts and processes that make up the success. But, Success is lying to you. You may have thought it was that peer-review process, but in fact, it was Bob. You may have thought it was Bob, but in fact, it was the peer-review process.

Photo by Dev Asangbam on Unsplash

The Call of Failure

Failure on the other hand demands to be known. He demands the truth. Failure is the rock in your shoe that you cannot ignore until you’ve stopped, taken off your shoe, and shook him out. In the engineering world, we have Root Cause Analysis, we have the 5 Why’s Method, we have weird fishbone charts and test frameworks, we have tools built around tools to address a failure. But we can only address it once it occurs.

So then, we must create a failure in order to pursue it. Unlike success, we can in most cases get to a definitive answer as to why the failure occurred. We can then begin to address it.

Combating Success

Doctors do mock-surgeries, the military does mock-missions (and makes them as real as possible), the trades do basically one long paired programming session until you get the journey-man badge. Developers do… um, we do… TDD. Ok, well we say we do TDD. But only when we have to and someone is watching… Leaders just tend to fail first and then hopefully learn and have the gumption to keep at it and not get demoralized.

They say in some areas of the military, “if you are not failing you are not trying hard enough”. The idea is to create a culture of creative testing and anti-complacency. Failing in a supportive environment is perhaps the quickest way to competence. The quickest way to weed out Success’s lies and learn from Failures truths.

We must find ways to successfully fail. Here, I will avoid talking about failures as they pertain to a person’s character. Let’s stay focused on honest mistakes, naïveté, and skill paths.

We must stay humble and not let our ego (a close friend of success as a liar) tell us that failure is a sign of weakness. We must not let our ego play with our imposter syndrome and tempt us to hide or avoid failures.

There is an old saying that “if you aim at nothing you will hit it every time”. At the other end of the spectrum is aiming at too much and creating catastrophic problems that you cannot recover from. So I suggest a TDD approach to code and to life. Create small assumptions or small experiments. Create small environments to test and measure those assumptions and experiments. At times, if you can do a hackathon and try something completely new and dangerous in a Nevada Test Site — then by all means do it! In fact, take the opportunity to try something opposite of what you normally would do. But do it safely, for the sake of your code and your loved ones.