DEV Community

Cover image for Is “Defensive Programming” actually healthy?
Cubicle Buddha
Cubicle Buddha

Posted on • Originally published at cubiclebuddha.com on

Is “Defensive Programming” actually healthy?

I can’t solve this one, and I think I need the help of the DEV Community. So, a developer was responding to a code review comment I made and they simply asked me, “why would I do that?” I gave my standard, dusty answer: “because you have to code defensively— you don’t know what the future holds.” But I suddenly realized… am I proliferating a fear of the future? How could I code fearfully when I run CubicleBuddha.com where I blog so often about living happily in the present? I’ll share the specific code example with you. I’m hoping to hear from the community whether my solution is “coding in the moment” or if I am actually bowing down to the fear.

A classic defensive programming example

Part of the duty of reviewing a coworkers code is to try and see what they might have missed. This follows the standard definition of defensive programming:

Defensive programming is when a programmer anticipates problems and writes code to deal with them. (1)

So, imagine you were reviewing a pull request and the code was making some assumptions. At first glance, the code sample below looks innocuous. And maybe it is. But having spent decades fixing other people’s production bugs, my spider-sense was tingly with fear. A specific bug comes to mind (which I’ll be demonstrating it in the second coding sample below) which leaves me staring at the Github code review not knowing how to proceed. I’m trapped wondering if I should keep quiet to preserve a carefree relationship with my peer or if I should speak up and prevent the potential production bug. Am I being haunted by the early years of my career where I was relegated to only bug fixing? Or were my formative years an invaluable training ground that makes me who I am today?

“If you are caught in sorrow and regret about the past, or if you are anxious about what will happen to you in the future, then you are not really free to enjoy the many wonders of life that are available in the here and now.”

~ Thich Nhat Hanh

See for yourself if you can find where a bug can easily manifest. If you can’t see the bug, then I’m almost jealous that your past didn’t inform you of the potential nightmare. There is a bliss in not knowing. But sadly, users who experience production bugs don't care about your "bliss," they just want to finish what they were doing:

Okay, yea. No problems “in the present.” And one could argue (as my peer continues to do so) that since our program is only used in geographical regions that are limited to the three major traffic signals (red, yellow, and green) that we don’t have to worry about this right now. My peer is using one of my favorite phrases against me: “You Ain’t Gonna Need It” (YAGNI). And I get it. But do we really not care about expanding the software?

And this is the biggest internal conflict I struggle with between my coding style and my philosophical beliefs. Why build software if you don’t want it to be used by an expanding group of people? There’s no shame in hobbyist programming. But if you’re a professional programmer, you’re doing it to make money and/or to improve the lives of your customers.

So, can we be pragmatic? Can we try to be a buddha in a setting so sterile as a cubicle? Can we have one foot in commerce with another foot in calmness? The coding technique below will (in my opinion) help you to make way for the future while calmly focusing on the present.

Seeing the car crash of the future… and remaining calm

So consider the fact that when you get new users, you should hopefully be learning about the needs of your new customers. And new use cases means new features to write. And here’s the classic example. Today, we only deal with 3 lights. But what if we start selling the software in other states? For instance, the state that I live in has a blinking red light where you’re required to stop first before you go (kind of like a stop sign). Let’s see if the code that worked before has protected us from the future– can you spot the calamity that would occur?

Hold on a second, if the driver saw a red blinking light… wouldn’t that fall into the fall-through/else case? Wouldn’t they… oh no! Kaboom!!! Let’s see if we can prevent that future car crash but without having to do too much more work in the present.

Defending the future: the “never” type comes to the rescue!

Thankfully TypeScript has a language feature called the “never” type that allows the compiler to identify when every case in a union of types (or every case of an enum) has not been accounted for. As you can see below, by not allowing the series of if-elses to fall through to a default else, the compiler will tell us that we forgot to instruct the driver how to respond to the “red blinking light.”

And now the driver won’t get into a car crash when we decide to start handling blinking red lights… because we literally couldn’t compile the code until we instructed the driver how to respond to this new case. In the original example, the code would have told the driver to just “go.” That doesn’t seem mindful to me.

The beauty of this defensive programming technique is that it costs almost no time to add exhaustive type checking to your code. The experienced programmer part of my brain knows that coding defensively is the simplest and best way to look out for the user’s needs. But, I worry sometimes that my career prevents me from truly acting like a Buddhist. Hopefully techniques like this “assert never” approach will allow me to strike a balance. After all, I’m just human. And Buddhism teaches us to love our humanity and to accept our emotions.


But what do you think? I’d love to hear your thoughts on Twitter and Dev.to about your thoughts on the healthiness of defensive programming. Do you think it’s worrying too much about the future? Should you only concentrate on what the software needs to do today? Or do you think it’s okay to code defensively?

Oldest comments (125)

Collapse
 
samuraiseoul profile image
Sophie The Lionhart

The never type is nice, and this is a nice example of it, but I feel YAGNI really should apply to features, or the idea of a feature down the pipeline.

This is more of using a bad a data structure I feel. There should not be an anonymous union type as a return, and since it's a map, I think an explicit map makes more sense to use here.

This all said, the kind of YAGNI that it is, I say fine, but make a tech debt issue or something if they don't want to deal with it right now. I get the idea that you sometimes just want done with a ticket or PR.

But a never case is nice for sure. It fixes problems of people using switches poorly too. Switches work well for things that have a defined range of values that won't change (Days of the week I hope...) but not things like traffic light states.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Good points. I especially appreciate your pragmatic point about making a tech debt ticket.

This is more of using a bad a data structure I feel. There should not be an anonymous union type as a return

Yea I must admit that I used the string union type because I think it’s fun (and a type that is only available in TypeScript and Haskell from what I understand), but I could have used an enum to illustrate the same point. And I feel like this kind of defensive programming is even more important for enums since values can be added at any time.

But yea, never is a fun type and very valuable here.

Collapse
 
ssimontis profile image
Scott Simontis

Obscure knowledge from my first job to the rescue! Flashing/blinking red actually exists everywhere...there are two systems monitoring the output of the traffic signal controller: the conflict monitor unit and the master malfunction unit. The CMU detects if the controller outputs an invalid signal, like giving a green light to every direction of traffic at the same time, and the MMU monitors the CMU for failures and also looks out for general failures of the entire cabinet's traffic hardware setup.

If either unit detects a failure, the traffic light controller attempts to reset itself. While the controller attempts to clear the error condition, the output signal is transitioned over to a series of relays which cause the red lights to flash. This continues until the error condition clears or a technician is able to fix the cabinet issues.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

That’s amazing! I never would have known this. Isn’t the internet fantastic that I get wonderfully surprising comments like this? 🥰

Collapse
 
jochemstoel profile image
Jochem Stoel

This is an interesting topic to chat about sometimes. (hint hint) I completely get how you think the two 'mentalities' of yours contradict.

There is nothing inherently wrong with 'defensive programming'. It is part of your job description as lead developer to see potential problems before they arise and predict to some extent what the future could bring.

I would like to suggest though that you stop calling it defensive programming and call it anticipatory programming or something.
Anticipatory programming takes the negative vibration of defensiveness and turns it into something positive; the suggestion that you know what you're doing. You take the future into account even when developing in/for the present.

Based on where I think you are now as a developer, I suggest you have a look at functional programming in JavaScript and the application design patterns that come with it. In my experience functional programming eradicates issues like this alltogether. This guy @joelnet wrote some stuff about it, good place to start.

Collapse
 
workingwebsites profile image
Lisa Armstrong

My coding paradigm: some poor bastard is going to have to work with this some day, and it will probably be me. Code accordingly.

Seriously, experience is a good teacher. It's not always the deciding factor, but it is something to listen to.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Yes, there’s no replacement for experience. :) The challenge I find as a senior dev is how to help give junior devs the opportunity to fail safely so they too can gain experience. It’s a tough balancing act. :/

Collapse
 
jonathanhiggs profile image
Jonathan Higgs

Assume the next person to look at your code is a psychopath and knows where you live

Collapse
 
ferricoxide profile image
Thomas H Jones II • Edited

Never really called this "defensive programming" – or fear-based programming. A good programmer/engineer/architect/etc. is forward-thinking and seeks to author solutions that neither paint themselves nor their successors nor their customer into a corner. If the incremental cost of adding anticipatory code-paths is low and otherwise obvious, it makes little sense to take a shortcut now that will bone you, your successors or your customers in the future (see "technical debt").

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

That’s great to hear. I also feel that way. What have you done to persuade the less-experienced members of your team that the incremental cost is low?

I find that I can explain it once. I can remind them again in a code review. But if they ignore it the third time... that means that the developer really doesn’t want to code like (as you put it) “a good programmer.” That’s always sad for me to watch when a developer doesn’t want to take the extra step to help the customer from surprise bugs, but part of my growth as a senior dev has been learning to “advise” not “force” good behavior. I must admit that I’m finding it challenging. I’ll take any and all advice! :)

Collapse
 
ferricoxide profile image
Thomas H Jones II

What have you done to persuade the less-experienced members of your team that the incremental cost is low?

Usually, it's a dialogue that usually depends on the personalities involved. My approach is generally to frame things as questions. Usually something along the lines of "that's a good approach to the direct problem, but how would you extend this to meet ". The other thing that helps is to remember that showing usually helps more than just telling.

but part of my growth as a senior dev has been learning to “advise” not “force” good behavior. I must admit that I’m finding it challenging. I’ll take any and all advice! :)

Ultimately, whether you're advising or explicitly-mandating a change, you're exercising a degree of force (inasmuch as you're causing someone to do something they wouldn't have otherwise done). The degree of force appropriate to a given situation will depend on the person you're interacting and the importance an visibility of the deliverable.

The other thing to bear in mind when evaluating the force applied is that, at the end of the day, when you sign off on a PR, your name is now on that code, too. Whoever looks at that commit history can rightly interpret that you were ok with the state of things. Generally, I'm all for letting people do things how they see fit. However, I have to feel comfortable lending my reputation and my employer's reputation on a given chunk of code.

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Ooh I’m definitely gonna use this line of yours:

“that's a good approach to the direct problem, but how would you extend this”

Thank you so much for your response. I think choosing my battles is the hardest part of being a lead/senior dev. It’s great to get feedback from others. :)

Thread Thread
 
danjconn profile image
Dan Conn

I've had devs in code reviews that are grateful for the advice, but others that thank you for the input but know better. Sometimes if the impact is minimal and the bug reversible I've let code to go through that isn't great so that the person learns from it. I wouldn't recommend that for every problem though!!!

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Yea absolutely As you pointed out, it’s really tough the draw the line between code that needs to be corrected and which one can slide. But I think that the fact that devs like us try to think about he distinction at all makes us better servant leaders. So congrats on being a thoughtful dev! :)

Thread Thread
 
danjconn profile image
Dan Conn

Thanks and congrats to you too!

Collapse
 
nialljoemaher profile image
Niall Maher

Totally agree with this statement except for when you code for scenarios that might never exist. We had a severe problem of simple tasks taking days longer because every engineer is trying to fix every edge case. Honestly I think a more proactive approach is making your code clean and tested so it's easy to expand later. So the real gotcha here is, how much is too much "defensive programming"

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Yes indeed. It’s all about knowing the right balance between future proofing and what is needed today by the user. Just out of curiosity, did you find this “exhaustiveness checking” approach to be within the balance? I.e. would you use it every time you write a switch case statement or an if statement that checks all cases? Because I use it every time but I am curious what others think.

Thread Thread
 
nialljoemaher profile image
Niall Maher

I think we need to do our due diligence but if you're working on an app and you know the user base will be quite small (internal software for companies or marketing sites, even niche software) then a lot of the scenarios engineers dream up just won't happen and if they do then you'll have the extra cash to address them. Just make your code clean, so the next engineer hates you a little less 😉

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

I don’t see new cases as being something “engineers dream up.” Happens every day. That’s why the never type says “with what I know now, the code will never experience a different case at runtime.” Seems like an easy thing to apply every time regardless of the size of the user base. Because the user base will grow. And when the user base grows, so will the requested functionality.

Thread Thread
 
nialljoemaher profile image
Niall Maher

I definitely do, but without solid samples it's just us bike shedding 😂

Collapse
 
ferricoxide profile image
Thomas H Jones II

Yeah. It's definitely a balance. And it's a "balance" that comes both from general experience and knowledge of your particular customers' or target userbase's proclivities.

One thing that helps with tempering a tendency towards sacrificing adequate delivery-speed on the altar of future-proofing is realizing that "future-proofing" is often an stand-in for "idiot-proofing" ...and that there will always be a better idiot out there. =)

Collapse
 
danjconn profile image
Dan Conn • Edited

I would say it depends what you are programming and who for. If you're writing software for something where failure will cost someone's life (eg air traffic control, railway switchers, radiation therapy treatment for cancer), I would argue be as defensive and thorough as possible to avoid unexpected conditions.

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Defensive programming is a must if you wish to obtain high-quality software. The main thing it does it stop bugs from propagating far through the code.

Your example is a complex one, because it depends a lot on the language. There are numerous simpler examples that can demonstrate the value of defensive coding. That is, just because there are situations where it may not be warranted, doesn't mean it isn't warranted as a whole.

Checking enums via if's is generally the wrong thing to do. If you intended to cover all cases than some kind of switch is expected -- which then many languages will catch new conditions being added which aren't covered. If the language does not support a switch statement, then having a catch-all final case is the only sane option. This is because a series of if-else conveys a different meaning than a switch statement, but you actually intended to have a switch statement.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

A lot of people avoid switch statements since it’s too hard to enforce good behavior like avoiding fall through. But I get your point.

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Not all languages are broken like classic C. Even C/C++ compilers can provide warnings on fall-through and missing case's now. Using those languages without warnings turned on, and heeding all warnings, is crazy.

C# requires full coverage I believe.

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Two points of correction/clarification:
1) the solution I provided in the article works for switch statements too
2) the solution I provided gives you feedback at compile time. C# would only help you out at runtime. See here: stackoverflow.com/a/20759116/706768

Thread Thread
 
mortoray profile image
edA‑qa mort‑ora‑y

Oh, I didn't mean to imply there's something wrong with your approach, only that there are simpler cases to convince people of the need for defensive programming. Your solution is fine for the languages you're using.

Hmm, I wonder what language it was, if not C#? I recall one of them would produce an error (and of course C++ with the warning enabled).

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha • Edited

Point taken. As for your question, I’m not sure which language besides TS supports checking exhaustiveness. Btw, I had copied the gist link incorrectly in the solution part of the article. You can now see the use of the never type. Woopsie!

Thread Thread
 
pitweetie profile image
Andy Lamb

@edA-qa, in the .Net world are you thinking of F# maybe?

Collapse
 
yaser profile image
Yaser Al-Najjar

I can't see the point in the example you provided since a simple dictionary is gonna do the whole if's thing:

  1. in a faster manner.
  2. gonna throw an exception if no key found.

And voila, defensive coding achieved!

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

I’m not sure I see how a dictionary would solve this. Could you provide a code example so I could see and understand?

Collapse
 
yaser profile image
Yaser Al-Najjar

Sure, I'm using Python generally but this could apply to any lang:

def respond_to_traffic_signal(signal):
    signals_meanings = {
        'green': 'go', 
        'red': 'stop', 
        'yellow': 'pause'
    }

    return signals_meanings[signal]

try:
    print(respond_to_traffic_signal('green'))
except KeyError:
    print('not found')

As a general rule of thumb, whenever I see many if's, I just think directly: "Can I use a dictionary here instead?"

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

The problem with that dictionary solution is that it relies on throwing an error at runtime. There’s no information provided to the compiler to catch it sooner. Which means that you’ll only find out if you forgot a case (or a key the dictionary) if you write a unit test that tests for exhaustiveness. But rather than write a unit tests, wouldn’t it be better to find out at compilation that a mistake was made?

So I would recommend that you give the article another read and then try out the samples I provided in the article in a REPL. It’s probably not apparent the advantages without seeing how quickly the error pops up in a TypeScript playground. You’ll find that they allow you to discover bugs much faster (since you don’t have to run the code).

Thread Thread
 
yaser profile image
Yaser Al-Najjar

Ah, now I get your point...

You're trying to get things checked at compile time, but I'm not sure if the compiler is really suited to check the app logic (the compilers' job is to check syntax and such).

wouldn’t it be better to find out at compilation that a mistake was made?

Not really, I trust my tests more than the compiler, cuz the app logic might break one day (one way or another), and the beauty of tests is to get that logic checked everytime you go into the building stage.

Thread Thread
 
jvanbruegge profile image
Jan van Brügge • Edited

@yaser

the compilers' job is to check syntax and such

I completely disagree here, ideally the compiler would check all your app logic too, the more you can get checked by the compiler, the less tests you need to maintain. Languages like Haskell are popular just because the compiler can help you a lot.

I trust my tests more than the compiler

Tests can never show the absence of bugs, only the presence. Having a type system cut the possible inputs down to (in this case) a finite amount of values is far more valuable than testing the 4 values you mention in your unit tests.

@Cubicle

The problem with that dictionary solution is that it relies on throwing an error at runtime

Not with TypeScript, the following code will throw a compiler error if you change the TrafficLight type, without adding something to the object:

type TrafficLight = "red" | "yellow" | "green";
type TrafficAction = "stop" | "go" | "pause";

type TrafficResponse = {
    [k in TrafficLight]: TrafficAction;
};

function respondToTrafficSignal(signal: TrafficLight): TrafficAction {
    const mapping: TrafficResponse = {
        red: "stop",
        yellow: "pause",
        green: "go"
    };
    return mapping[signal];
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Great work Yan. Yes, your type would require each key to be present. It’s another thing I love about TypeScript. There are so many ways to express concepts. :)

Thread Thread
 
yaser profile image
Yaser Al-Najjar • Edited

I agree with you in this TypeScript scenario.

But, as for Python (or any similar lang), I'm not sure this would be the case since this even goes against the Python moto: "let the exceptions fly and catch them later".

So, defensive programming model might be different from a lang to another.

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Interesting. I tried to find an article about Python and “letting the exceptions fly” but I couldn’t find anything.

One should always choose the best tool for the job. Sometimes that might be throwing/catching an error, and other times it might mean preventing it with the type system. Why limit yourself to one tool?

“When all you have is a hammer, every problem starts to look like a nail.”

Thread Thread
 
yaser profile image
Yaser Al-Najjar

I think the last time I heard it about was in a video or so, but the correct idiom is "Easier to ask for forgiveness than permission"

This video explains it in a nice way: youtube.com/watch?v=x3v9zMX1s4s
And this article summarizes things: devblogs.microsoft.com/python/idio...

Sometimes that might be throwing/catching an error, and other times it might mean preventing it with the type system

I can totally relate after I saw how TypeScript goes (I never used it before, just the old normal JS).

Collapse
 
itscoderslife profile image
Coder

I liked your handle very much and made me follow you here. Your website is nice too. Now reading the articles. 👍 😅

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

That’s very kind of you to say. 😊 And thank you for the follow. 🥰

Collapse
 
cecilelebleu profile image
Cécile Lebleu

Being still relatively a beginner, I would have added green as an else if, and then an error message in the else. Why? Because I’m always stumbling on unexpected bugs or behaviors that I never could have anticipated (because of lack of experience); so I’m always thinking “what could go wrong?” with every line I code.

Also, where I come from, the traffic light could be red, green, yellow, or turned off because of a power outage, so I would have thought of that specific scenario as having an important reason not to be else: green. I would do else: red, so if something unexpected comes up, the response is “stop” and not “go”.

Now, I wonder if this train of thought could be limiting my problem solving, or if by being extra-careful I’m being a better programmer? 🤔

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

It’s always better to be explicit. So even if you wanted to have a case for “power off” you should have a specific if statement for that. Falling through hides complexity and makes for code that looks simple but has hidden complexity. So please don’t have anything in the else other than:

  • this never pattern described in the article
  • or if you don’t have the never type (like if you’re not in TypeScript) then you should throw in the else. Like throw new Error(“unexpected scenario”)
Collapse
 
jvanbruegge profile image
Jan van Brügge

For me there is a distinction to be made when it comes to defensive programming. On the type level you should be as defensive as possible. Your types should result in compiler errors if you do not handle new cases in the future. Make your types as small as possible, just like you did in your article (use a sum of three distinct values instead of simply a string).

On value level on the other hand, I really dislike defensive programming, usually because it means that your compiler's type system is not strong enough to check these at compiler time. The prime example here is the classic null. TypeScript has non-nullable types, but for example Java programmers have to put if(x != null) everywhere, just in case.

I would write this code snippet like this:

type TrafficLight = "red" | "yellow" | "green";
type TrafficAction = "stop" | "go" | "pause";

type TrafficResponse = {
    [k in TrafficLight]: TrafficAction;
};

function respondToTrafficSignal(signal: TrafficLight): TrafficAction {
    const mapping: TrafficResponse = {
        red: "stop",
        yellow: "pause",
        green: "go"
    };
    return mapping[signal];
}

The value level is short and concise, it only contains what I want to do (ie mapping a signal to some action) with not much boilerplate syntax. The type level code is almost the same length, guaranteeing that adding future cases will result in a compiler error.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

OP: This is another great solution ^

Collapse
 
thomasjunkos profile image
Thomas Junkツ

If you want to write "defensively", you have to ask yourselves: what is the target, I want to defend against. The problem with defensive code is, that relative what your targets are, workload increases.

Regarding your traffic light example, I would argue, that there is no need to implement the "never"-strategy. Simply write tests - which you would do anyways - and before it goes to production, the according test would fail, you would detect the booboo and fix it. No harm was done.

But I agree, it is hard to assess the risk for errors correctly.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Hi Thomas. While I appreciate your response, I needed to share that at the time you commented, I had a copy paste error and wasn’t showing the actual solution in the final part of the article. So if you get the chance, you can see how the never type is used and how it helps me to not have to write a test at all.

I think it’s much nicer to not have to write a test for this kind of thing.

That being said, I’m thankful that there are people out there like yourself who want to write automated tests at all. Me too! Let’s keep spreading the test-writing love. :)

Collapse
 
thomasjunkos profile image
Thomas Junkツ

I had a copy paste error and wasn’t showing the actual solution in the final part of the article.

Happens ;)

I think it’s much nicer to not have to write a test for this kind of thing.

To put it in another way:
Write tests to observe changing behaviour of your application.
If your code has to change due to an error, write a test covering that changing behaviour.

Do not try to cover each and every cornercase.

Collapse
 
tauteo profile image
tauteo

I think it is valuable to not only think of defensive / anticipatory / forward thinking programming in terms of the code only, but to also think about it in terms of the domain.
In other words: when this code fails, will it result in a safe / acceptable condition in the physical domain? Disregarding all other safeguards, this code would fail with "green" (which is probably the least desirable outcome with regards to the domain), but can be easily changed to fail with "red". If there is no buy-in to change from a code perspective, at least convince them to make a change that will fail to a safe domain condition.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Interesting thought. I suppose the whole idea is that I don’t think we can ever know what the future holds. So how can we (as you say) “fail to a safe domain condition?” Because we don’t know what is safe for a case that we haven’t discovered yet. So for instance, the safest response is to stop. But if you stop at a blinking yellow, you might enrage the driver behind you who was expecting to pause. I’m sort of joking. But yes, I will meditate on what you’ve suggested. I think you might be on to something. :)

Collapse
 
jonathanhiggs profile image
Jonathan Higgs

I had exactly the same thought when I saw the example, and can't stress enough how much I think this is a great approach

Collapse
 
danazar96 profile image
Aidan Walters-Williams

Great article! Defensive programming should be the de facto. It's not being scared of the future, it's actually being optimistic about the future in the sense that because you're handling this potential future scenarios, future you will not be hindered by them and will be able to develop freely.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Wow, you nailed it. You perfectly summarized why I continue to use defensive programming. It’s like we’re “paying it forward” even though the person we’re helping out might be future us.

future you will not be hindered by them and will be able to develop freely.

Well put. :)

Collapse
 
danazar96 profile image
Aidan Walters-Williams

Thank you very much, it was a great article. Look forward to hearing more of your stuff!

Collapse
 
nepeckman profile image
nepeckman

I agree that done well, defensive programming is helpful. Being mindful of the future is generally a good thing, especially in software engineering where things change so frequently! However, in the interest of making this a more interesting discussion, I posit defensive programming can be done wrong. I once contributed to a JavaScript codebase that took defensive programming to an extreme. Every function checked that all the arguments were the right type and were non null. While this might be desirable for some functions (especially functions consuming user input or exposed in an api), the proliferation of defensive checks had a obfuscating effect. I couldn't tell which functions should actually expect and handle null input, and what functions had just cargo culted the checks. We structure our code primarily to communicate to our future selves and teammates. This code was hiding valuable information about the flow of data through the codebase by introducing unnecessary fear.

We later transitioned to TypeScript and deleting all of those defensive checks (replacing them with types) was very satisfying.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

Omg this was totally the way that I wrote JS before TypeScript was invented:

Every function checked that all the arguments were the right type and were non null.

And this was also me:

We later transitioned to TypeScript and deleting all of those defensive checks (replacing them with types) was very satisfying.

It’s so nice to meet others who have had similar journeys. And thank you for sharing your perspective on the clutter of duck typing. I never thought of that so it was really valuable to hear. :)

Collapse
 
burdettelamar profile image
Burdette Lamar

Well, others notwithstanding, I like the term "defensive programming," as long as we understand that the one being defended is the user, not the programmer.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

haha funny and true

Collapse
 
megazear7 profile image
megazear7

When I review pull requests I like to make my comments open ended. "What do you think about...", "Do you see a benefit with...", "How difficult would it be to..."

This helps draw out expertise from the other developer, fosters open discussion, and helps maintain code quality and future proofing while allowing the developer that is actually going to write it to say that it isn't worth the time - and gives them a chance to provide a reason for that opinion.

Collapse
 
cubiclebuddha profile image
Cubicle Buddha

That’s a great idea. I’ve always appreciated the Socratic method. Question for you: does this work well for you in remote teams? I feel like this might work better as an in person code review. I say this because questions can be perceived as passive aggressive in text.

Collapse
 
megazear7 profile image
megazear7

I work almost entirely remote and most of the people I work with are non native English speakers. So it definitely works but you are absolutely right. I make sure to go out of my way to prevent a accidental passive aggressive tone.

Thread Thread
 
cubiclebuddha profile image
Cubicle Buddha

Yea totally. Remote working is a skill unto itself.