In Defense of Defensive Programming
Tags: react, javascript, typescript, codequality
Published: 2020-10-02 03:40:31

[**NOTE:** In this article I reference a validation library that I wrote called `allow`.  It's now in an NPM package that can be found here: https://www.npmjs.com/package/@toolz/allow]

My regular readers (both of them) know that I've written a lot about the integrity of values that are passed between different parts of an application.  Sometimes, we add manual validations.  Sometimes, these values aren't checked at all.  Sometimes, we check them at compile time, but we _assume_ they'll be correct at runtime (I'm looking dead at you, TypeScript).  

Whatever the approach, I've only recently become aware that the term "defensive programming" is generally used as a pejorative by many programmers.  My impression is that "defensive programming" is often interpreted as "jumping through a ridiculous number of hoops to validate data - data that probably doesn't really need to be validated at all."  And I don't _entirely_ disagree with this assessment.  But I fear some may have become so averse to the idea of _defensive programming_ that they don't recognize the other loopholes they're incorporating into their own code.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nqdaiesqm60oczhdhku9.jpg)
## Basic Assumptions
Let's ensure that we're all on "the same page" here.  I'm sure there are multiple definitions for _defensive programming_.  So, for the sake of _this_ article, this is the definition I'll be using:
<br/>

> **Defensive Programming:** The practice of treating all _inputs_ to a program as "unknown" - _hostile_, even.  This practice guards such inputs from the main application flow until they've been validated as conforming to the "expected" type/value/format.  

<br/>I'm focusing on _inputs_.  It would be possible to validate data within the same code block where it was defined.  And such a practice would certainly be _defensive_.  But it would also be extreme.  And silly.  

But inputs represent the strongest case for defensive programming.  Because inputs come from... somewhere _else_.  And you don't want _this_ program to be aware of the inner workings of _another_ program for it to do its business.  You want _this_ program to be a standalone unit.  But if this program stands alone, then it must also assume that any input to the program is potentially _hostile_.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hegc9azjm6wjb3v88wyn.jpeg)
## Validation Hell
This is where "defensive programming" becomes a dirty word.  When we talk about validating _all_ of our inputs, we fear it will lead to something like this:

```javascript
const calculatePassAttemptsPerGame = (passAttempts = 0, gamesPlayed = 0) => {
  if (isNaN(passAttempts)) {
  	console.log('passAttempts must be a number.');
    return;
  }
  if (isNaN(gamesPlayed)) {
  	console.log('gamesPlayed must be a number.');
    return;
  }
  if (gamesPlayed === 0) {
    console.log('Cannot calculate attempts-per-game before a single game has been played.');
    return;
  } 
  return passAttempts / gamesPlayed;
}
```

The function has _inputs_.  And the function shouldn't be aware of where those inputs originated.  Therefore, from the perspective _of the function_, the inputs are all potentially dangerous.  

That's why this function already has some significant baggage attached to it.  We can't necessarily trust that `passAttempts` or `gamesPlayed` are numbers.  Because `passAttempts` and `gamesPlayed` are _inputs_ to this _program_.  And if we feel the need to program "defensively", we end up stuffing extra validations inside our program.

Honestly, the validations shown above aren't even adequate, as far as I'm concerned.  Because, while we're ensuring that the inputs are _numbers_.  We're not validating that they're the _right kind_ of numbers.

Think about this:  If we're logging the pass attempts per game, does it make sense that either could be _negative_?  Would it make sense if either of them are _fractional_??  I can't remember the last time a player threw 19.32 passes in a single game.  I can't remember the last time a player played in -4 games.  And if we want to ensure that our function is truly equipped to always provide the most logical returns, we should also ensure that it is always given the most logical inputs.  So if we really wanted to go all-in on defensive programming techniques, we'd add even _more_ validations to ensure that the inputs are _non-negative integers_.

But who really wants to do all of that??  All we wanted was a simple function that returns the result of `passAttempts` divided by `gamesPlayed`, and we ended up with a bloated mess of code.  Writing all of those _defensive_ validations feels laborious and pointless.  

So how do we avoid the nuisances of defensive programming?  Well, here are the approaches (excuses) that I most frequently encounter.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8dcp6ep6dd0cpvqy3n2d.jpg)
## Missing The Forest For The Trees
Is the picture above a bunch of trees?  Or is it a single forest?  Of course, depending upon your frame of reference, it may be either (or both).  But it can be dangerous to assume that the picture above shows no "trees" and only shows a single "forest".

Similarly, what do you see when you look at code like this?

```javascript
const calculatePassAttemptsPerGame = (passAttempts = 0, gamesPlayed = 0) => {
	//...
}

const calculateYardsPerAttempt = (totalYards = 0, passAttempts = 0) => {
	//...
}

const getPlayerName = (playerId = '') => {
	//...
}

const getTeamName = (teamId = '') => {
  //...
}
```

Is this _one_ program (a "forest")?  Or is it a bunch of individual programs ("trees")??  

On one hand, they're presented in a single code example.  And they all seem related to some kind of central player/team/sport app.  And it's entirely possible that these functions will only ever be _invoked_ in a single runtime.  So... they're all part of a single program (a "forest"), _right_??

Well, if we think beyond our overly-simplistic example, the simple fact is that we should always be trying to write our functions as "universally" as possible.  

This means that the function might only ever be used in the context of this particular example.  But the function also might be referenced dozens of different times across the app.  In fact, some functions prove to be so utilitarian that we end up using them across multiple applications.

This is why the best functions operate as standalone, _atomic_ units.  They are their own "thing".  And as such, they should be able to operate irrespective of the broader app from which they're called.  For this reason, I believe, religiously, that:
<br/>

> Every single function is: _a program_.

<br/>Of course, not everyone agrees with me on that front.  They argue that each function is a tree.  And they only need to worry about the _inputs_ that are provided to their overall program (the forest).  

This gives devs a convenient way to avoid the headaches of acid-testing their code.  They look at the example above and they say things like, "No one will ever pass a Boolean into `getPlayerName()` because `getPlayerName()` is only ever called from _within my program_ and I know that I'll never pass something stupid into it - like a Boolean."  Or they say, "No one will ever pass a negative number into `calculateYardsPerAttempt()` because `calculateYardsPerAttempt()` is only ever called from _within my program_ and I know that I'll never pass something stupid into it - like a negative number."

If you're familiar with _logical fallacies_, these counterarguments basically fall under **Appeal to Authority**.  These devs treat _the program_ as the "authority".  And they simply assume that, as long as the input is provided from somewhere else _within the same program_, there will never be any problems.  In other words, they say, "The inputs to this function will be fine _because 'the program' says they're fine_."

And that _is_ fine - as long as your app is miniscule .  But as soon as your app grows to the point that it's a "real", robust app, this appeal falls flat.  I don't know how many times I've had to troubleshoot code (often... _my_ code), when I realized that something was failing because the wrong "kind" of data was passed into a function - even though the data came from somewhere else _inside the same program_.

If there are (or will ever be) two-or-more devs on the project, this "logic" is woefully insufficient.  Because it relies on the silly idea that anyone _else_ who works on the project will never ever call a function in the "wrong" way.  

If the project is (or will ever be) large enough that it's impractical to expect a single developer to have the entire program _in their head_, this "logic" is, again, woefully insufficient.  If an end-user can put ridiculous values in a form field, then it's equally true that another programmer can try to call your function in a ridiculous way.  And if the logic _inside_ your function is so brittle that it blows up whenever it receives bad data - then your function _sucks_.

So before we move on, I want to make this crystal clear:  If your excuse for not validating your function inputs is simply to lean on the fact that _you_ know all the ways the function will be called _by you_ in _your_ app, then we really never need to be on the same dev team.  Because you don't code in a way that is conducive to team development.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/f8m9lm2ekqqyl7mmkc9c.jpg)
## The Testing Shell Game
I've found that many devs don't try to solve the problem of brittle inputs by writing a bunch of defensive code.  They "solve" it by writing a metric crap-ton (technical term) of tests.  

They'll write something like this:

```javascript
const calculatePassAttemptsPerGame = (passAttempts = 0, gamesPlayed = 0) => {
  return passAttempts / gamesPlayed;
}
```

And then they shrug off the brittle nature of this function by pointing to the incredible pile of integration tests they wrote to ensure that this function is only ever called in the "right" way.  

To be clear, this approach isn't necessarily _wrong_.  But it only shunts the real work of ensuring proper application function to a set of tests _that don't exist at runtime_.  

For example, maybe `calculatePassAttemptsPerGame()` is _only_ ever called from the `PlayerProfile` component.  Therefore, we could try to craft a whole series of integration tests that ensure this function is never actually invoked with anything other than the "right" data.

But this approach is tragically limited.

First, as I've already pointed out, tests don't exist at runtime.  They're typically only run/checked prior to a deployment.  As such, they are still subject to developer oversight.

And speaking of developer oversight... trying to acid-test this function through integration tests implies that we can think of all the possible ways/places where the function can be called.  This is prone to short-sightedness.  

It's much simpler (in the code) to include the validations _at the point where the data needs to be validated_.  This means that there are usually fewer oversights when we include the validations directly in-or-after the function signature.  So let me spell this out simply:
<br/>

> Tests are great.  But they are never a one-for-one replacement for data validation.

<br/>Obviously, I'm not telling you to eschew unit/integration tests.  But if you're writing a pile of tests just to ensure proper functionality _when a function's inputs are "bad"_, then you're just doing a shell-game with your validation logic.  You're trying to keep your application "clean" - by shoveling all of the validation into the tests.  And as your application grows in complexity (meaning that: there are more conceivable ways for each function to be called), your tests must keep pace - or you end up with glaring blindspots in your testing strategy.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fqs5dhqw792jjjbjcr0l.jpg)
## The TypeScript Delusion
There's a large subset of Dev.to readers who would read this with a cocky smirk and think, "Well, obviously - _this_ is why you use TypeScript!"  And for those cocky devs I'd say, "Yeah, ummm... sorta."

My regular readers (both of them) know that I've had some real "adventures" over the last half-year-or-so with TS.  And I'm not _against_ TS.  But I'm also wary of the over-the-top promises made by TS acolytes.  Before you label me as a Grade-A TypeScript Haterrr, lemme be clear about where TS _shines_. 

When you are passing data _within your own app_, TS is incredibly helpful.  So for example, when you have a helper function that's only ever utilized _within a given app_, and you know that the data (its _arguments_) only ever emanate from _within the app_, TS is incredible.  You pretty much catch **_all_** of the critical bugs that might occur throughout the app whenever that helper function is called.

The utility of this is pretty obvious.  If the helper function requires an input of type `number` and, at any point in the rest of the app, you try to call that function with an argument of type `string`, TS will immediately complain.  If you're using any kind of modern IDE, that also means that your coding environment will immediately complain.  So you'll probably know, immediately, when you're trying to write something that just doesn't "work".

Pretty cool, _right_???

Except... when that data emanates from _outside_ the app.  If you're dealing with API data, you can write all the comforting TS type definitions that you want - but it can still blow up _at runtime_ if the wrong data is received.  Ditto if you're dealing with user input.  Ditto if you're dealing with some types of database inputs.  In those cases, you're still resigned to either A) writing brittle functions, or B) adding additional runtime validations inside your function.

This isn't some knock on TS.  Even strongly-typed OO languages like Java or C# are susceptible to runtime failures if they don't include the proper error handling.  

The problem I'm noticing is that far-too-many TS devs write their data "definitions" inside the function signature - or inside their interfaces - and then... they're done.  That's it.  They feel like they've "done the work" - even though those gorgeous type definitions _don't even exist_ at runtime.  

TS definitions are also (severely) limited by the basic data types available in JS itself.  For example, in the code shown above, there is no native TS data type that says `passAttempts` must be _a non-negative integer_.  You can denote `passAttempts` as a `number`, but that's a weak validation - one which is still vulnerable to the function being called the "wrong" way.  So if you really want to ensure that `passAttempts` is the "right" kind of data, you'll still end up writing additional, manual validations.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/q2ews8iq1kn07wrsvdse.jpg)
## The Try-Catch Hail Mary
There is _one_ more avenue we could explore to avoid defensive programming: the try-catch.

Try-catch obviously has its place in JS/TS programming.  But it's quite limited as a tool for defensive programming when it comes to validating inputs.  This happens because try-catch is really only meaningful when JS itself _throws an error_.  But when we're dealing with aberrant inputs, there are frequently use-cases where the "bad" data doesn't result in an outright _error_.  It just provides some kind of unexpected/undesired output.

Consider the following example:

```javascript
const calculatePassAttemptsPerGame = (passAttempts = 0, gamesPlayed = 0) => {
  try {
  	return passAttempts / gamesPlayed;
  } catch (error) {
  	console.log('something went wrong:', error);
  }
}

const attemptsPerGame = calculatePassAttemptsPerGame(true, 48);
console.log(attemptsPerGame); // 0.0208333333
```

The try-catch is never triggered, because `true / 48` doesn't throw an error.  JS "helpfully" interprets `true` as `1` and the function returns the result of `1 / 48`.  
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8x323x6iujjmvedvnbjb.jpg)
## It's Not That Hard
At this point, for those still reading, you're probably thinking, "Well then... there's no good answer to this.  _Defensive programming_ is cumbersome and slow.  Other techniques are prone to oversights and failures.  So... what's to be done???"

My answer is that defensive programming doesn't _need to be_ so hard.  Some people read "defensive programming" as "validate **ALL** inputs" - and they jump to the conclusion that validating **ALL** inputs must, by definition, be a nightmare.  But that's not the case.

I've written before about how **_I_** do runtime validation on **ALL** of my functions that accept inputs.  And for me, _it's easy_.  (If you'd like to read about that, the article is here: https://dev.to/bytebodger/better-typescript-with-javascript-4ke5)  

The key is to make the inline validations fast, easy, _and concise_.  No one wants to clutter every one of their functions with 30 additional LoC of validations.  But - you don't have to.  

To give you a tangible example of my approach, consider the following:

```javascript
import allow from 'allow';

const calculatePassAttemptsPerGame = (passAttempts = 0, gamesPlayed = 0) => {
  allow.anInteger(passAttempts, 0).anInteger(gamesPlayed, 1);
  return passAttempts / gamesPlayed;
}
```

The entire _runtime_ validation for this function is handled in a single line:

* `passAttempts` must be an integer, with a minimum value of `0`.
* `gamesPlayed` must also be an integer, with a minimum value of `1`.

That's it.  No TS needed.  No fancy libraries.  No spaghetti code crammed into every function to manually validate all of the arguments.  Just a single call to `allow`, that can be chained if there are two-or-more arguments expected in the function.

To be absolutely clear, this is **_not_** some kind of (long-winded) advertisement for my silly, little, homegrown validation library.  I couldn't care less _which_ library you use - or whether you roll your own.  The point is that _runtime_ validation doesn't need to be that hard.  It doesn't need to be verbose.  And it can provide much greater overall security to your app than any kind of compile-time-only tool.
<br/><br/>

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ecacvlq1p7n79a0l8rfw.jpg)
## The Arrogance of the Entrenched
So should _you_ reconsider any aversions you have to "defensive programming"??  Well, umm... probably not.

I understand that, you probably already have a job where you're paid to program.  And in that job, you probably already work with other programmers who set all of their coding ideas in stone _years ago_.  They've already allowed those programming bromides to sink deep into their soul.  And if you question any of that, you'll _probably_ be shot down - and quietly scorned.

Don't believe me?  Just take a look at the article that I linked to above.  There was some nice feedback in the comments.  But one, umm... "gentleman" decided to respond with nothing but: "Yuck..."

That's it.  No constructive feedback.  No rational logic.  Just:  "Yuck..."

And _that_ is basically what soooo much of programming comes down to these days.  You could develop a way to do nuclear fusion merely by writing JavaScript code.  But someone will come along, with no additional explanation, and just say, "Yuck..."

So... I _get_ it.  I really do.  Keep writing your TS.  And your copious tests.  And keep refusing to validate your function inputs.  Because that would be "defensive programming".  And defensive programming is _bad_, mmmmkay????

And I'll keep writing applications that are _more_ fault-tolerant, with _fewer_ lines of code.