Why You Shouldn't Nest Your Code

preview_player
Показать описание
I'm a Never Nester and you should too.

Correction: At 2:20 the inversion should be "less than or equal", not "less than"
Рекомендации по теме
Комментарии
Автор

I once nested 20 layers deep. Much like inception, time reading the code becomes exponentially longer the deeper you get... It's a miracle I'm even here today.

tristanneal
Автор

I will admit to being an out-of-control nester, but I'm recovering. It really does make code easier to understand.

PrimerBlobs
Автор

I think this is actually crucial to teach to beginners. Holding exceptions and conditions in your head makes a code base really hard for others to read.

aleceastman
Автор

A few of you, rightly so, pointed out that the inversion at 2:20 should be `<=` instead of `<`. I'll need to start unit testing my videos

CodeAesthetic
Автор

Update: He fixed it. Nice! :-)

I'd suggest using a lighter background when highlighting code and darkening the rest instead of blurring the rest, so that it's faster to find the line.

jonesmartins
Автор

There's actually a small error at 2:20, when you change the "top > bottom" to "top < bottom", as they are not exactly opposites. This actually changes the output of the function when "bottom == top"

Komil
Автор

I'm a huge fan of "guard clauses" (simple if's at the start of the function which basically just return because the return value is obvious due to a special case input). So I was VERY bewildered when in my early programming classes (In 2020!) it kept being blanketly repeated that "a function should never have multiple returns". If you have one return 50 lines in and another 100 lines in, sure... but then you probably haven't split up the function correctly to begin with.

You can write much simpler and cleaner code when you filter out all the special cases at the beginning as much as possible.
One simple example would be a power function:
pow(base, exponent)
if(base == 0 && exponent == 0)
throw Exception; (if you'd want to treat it as undefined)
if(exponent == 0)
return 1;
if(exponent == 1)
return base;

...rest of function to actually calculate the power

Now this definitely goes against a different technique called branchless programming, but unless your function is called very very often in some heavy computing task, the readability far outweighs the speed.

kilianbalter
Автор

Just code for clarity. Sometimes that means breaking complex methods in several simpler routines, other times that means combining several micro routines into one larger method. Sometimes nesting gets really confusing, other times nesting adds clarity to the code. Sticking to any one type of code structure religiously just adds complexity in different ways.

techman
Автор

I just want to applaud the visual design of this video, especially the code. The blurring, previewing important definitions, uncluttered diagrams, use of color, typewriting, introducing motion to draw/hold attention. It really communicates the intent of the code better than I've seen anywhere else.

jakobbarger
Автор

I totally agree. This was a nice example of refactoring code. When I start a new piece of code I always force myself to not go into the details right away but create a fundament that outlines what I'm trying to build. Extracting routines and give the names of what I need to do in them and handle any results. This way I can start to think about the bigger picture before diving into the details. It helps me tackle the whole problem more easy. When I get to the nitty gritty of the details I may need to refactor some of the outline I made but most of the time it is not needed. Chopping the problem up like this lets me focus on each little detail independent of the whole problem. When I find that an extraction is becoming too big I create a little more outline before diving into the details again. Works very well for me. Sometimes I find that the way I thought about the fundament is totally wrong. No problem. I didn't write much code so far, so starting over is not very costly and I've learned a lot setting it up the wrong way.

PaulSinnema
Автор

I particularly like Extraction because then you can give the function really descriptive names, then when you read thru the top function it's almost like you're just reading a book, instead of interpreting the logical flow of the code. I often combine Extraction and Inversion, in that after I invert, I'll extract the inverted code into its own (descriptive) function.

toddbarton
Автор

Just wanted to point out a common error when inverting logic. At 2:20, the inversion technique is demonstrated. Before, the "happy" case was (top > bottom), which made the "sad" case (top <= bottom). After, the sad case was (top < bottom), making the happy case (top >= bottom). This creates a classic "off by one" error. In the case where top and bottom are equal, the former returned 0 and the latter returns one iteration of the sum equal to the result of filterNumber().

I know that isn't really the point of the example, but you do need to be careful when using logic inversion.

johnheaney
Автор

Out of control nesting is definitely bad but sometimes when the guts of the code are extracted into smaller and smaller functions it also makes following the flow of the logic hard as well. You still have to try and keep track of what the parent of the parent is doing. At that point you are still in a nesting scenario, you’ve just shifted into smaller chunks of separated code. It’s a fine balance. Guard clauses help to get rid of unnecessary if/else conditions.

jsonlee
Автор

At 2:24 in your inversion, "if (top > bottom)" doesn't invert to "if (top < bottom)", that potentially introduces a bug. It probably should be "if (top <= bottom)". While I agree with you generally, be aware that inverting logic can introduce additional issues.

shanehebert
Автор

I think that decomposing your mega function into a bunch of single responsibility micro functions definitely makes unit testing a lot more straightforward. But one thing that I have found is that I still need to write the mega function first. Once it's all down and working, I can then refactor it into something more sensible, but trying to write out all the single responsibility functions first, takes me more time than doing a "first draft" and then editing. It's like premature optimisation. Don't do it until you know that you have something you need to fix.

I'm also a big fan of kicking out bad paths at the start. Not only is it easier to read, but when you discover a new unexpected bad path, you can just add another concise return block instead of having to nest something deeper in the function, and possibly missing a spot where you also needed to return rather than execute.

erichobbs
Автор

Reminder to not go overboard with extractions as it creates a readability issue having to excessively jump from function to function. Especially if they’re not placed near each other and/or poorly named.

SJrad
Автор

I was already doing this but not aware of all the benefits outlined here. I've always used extraction and inversion (without knowing their names) simply because it's cleaner and easier to read. Great video.

jmilen
Автор

In general I agree. I especially like early returns and splitting conditions/validation from operations. But sometimes nesting is better. For example when you have algorithm that is inherently nested (like you have 4 tight for loops indexing some arrays etc.). Better to have the whole looping/indexing in one function and extract the "doing" into another function than to extract part of looping to another function. Because it makes the algorithm less clear and if you do 4 loops with complex indexing you better be aware what you're doing.

ajuc
Автор

The "validation gatekeeping section of the code" can be summarised as "guard clauses". We use it all the time, especially in the backend of our stack where requests must be validated. I didn't think I was a never nester before this video but I've got a bit of a problem with long line lengths so avoid unnecessary nesting if possible.

cc
Автор

Extraction should be done carefully. I've often seen code with lots of once-used & poorly named extracted functions, just to hide nesting. So the code is just as nested as before but you have to jump from function to function to understand whats going on.

tobene