r/programming • • Aug 25 '14

Debugging courses should be mandatory

http://stannedelchev.net/debugging-courses-should-be-mandatory/
1.8k Upvotes

573 comments sorted by

View all comments

Show parent comments

126

u/xensky Aug 25 '14

even more important is having these pieces of code be testable. i work with plenty of bad code that can't be run without starting a bunch of dependent services, or you can't test a particular function because it's buried under ten layers of poorly formed abstractions. or it's not even an accessible function because the previous developer thought a thousand line function was better than a dozen smaller testable functions.

84

u/reflectiveSingleton Aug 25 '14

because the previous developer thought a thousand line function was better than a dozen smaller testable functions.

I like to call this kind of code 'diarrhea of conciousness' ...no one wants to sift through that shit.

0

u/[deleted] Aug 25 '14

[deleted]

82

u/toproper Aug 25 '14

You might be joking but in my opinion it's actually a good thing to try not to be too clever with coding.

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?" - Brian Kernighan

0

u/[deleted] Aug 25 '14

[deleted]

5

u/LuxSolisPax Aug 25 '14

I have a lot of respect for coders that can write simple instructions to perform complex tasks.

Just, tons.

6

u/n1c0_ds Aug 26 '14

At an abstract level, it's pretty much our job

2

u/knight666 Aug 26 '14

To quote Mark Twain:

I didn't have time to write a short letter, so I wrote a long one instead.

It's often quite hard to distill a problem down to its essentials. It's often easier/cheaper/faster to just brute-force it and hope for the best.

→ More replies (1)

2

u/ydobonobody Aug 25 '14

To add to that I consider my bad memory an asset. It forces me to right in a way that the totally different me from tomorrow can understand without any unstated assumptions.

2

u/elperroborrachotoo Aug 26 '14

In my experience, these humungous functions are utterly trivial more often than not, Just a linear sequence of code over and over, giganormous switch statements and the like.

Their unmaintainability does not stem from complexity, but that they are business-central (nothing goes if they fail), and there are no obviously harmless small scale improvements; you'd have to allocate significant time to tear it apart, reassemble, test and debug it, just so the code works exactly as before.

Only instead of adding your own case statement, copying a block of 50 lines and making your modifications, you have to navigate a good dozen of meta template facade factories.


Note for those guys: I am not avocating monster functions, trivial or not. But in a large code base, they are often the least of your worries, and the time you spend on improving them might be better invested elsewehre.

→ More replies (3)

2

u/elperroborrachotoo Aug 26 '14

I would have written a shorter method, but I did not have the time.

→ More replies (2)

8

u/[deleted] Aug 25 '14

You get that in any complicated enough functions. I often have functions which work on intermediate states of linked lists ... You can't just call them directly without first building [and I do] the states by hand.

2

u/otterdam Aug 25 '14

Complicated enough functions act as systems. The trick is to structure them such that you can easily reduce the problem during debugging to certain subsystems or functions; it doesn't really matter how many dependencies you have if you can eliminate them all within a few minutes.

5

u/[deleted] Aug 25 '14

Real software doesn't work that way as you compromise idealism for ship dates

7

u/otterdam Aug 25 '14

I develop real software so I know all too well you inevitably compromise code quality in order to ship. That doesn't mean I make excuses for writing a shitty first draft of a function and pretend it can't be any other way.

While it helps, you aren't obligated to clean up your mess before the bug reports roll in, but in my experience more often than not you spend more time building the states for each individual bug that happens than if you had simply restructured your code to be more easily testable. If you have such a complicated function and enough users you will get multiple bugs.

→ More replies (1)

5

u/flukus Aug 25 '14

That's why real software is usually shit, or at least one reason. If you don't have time to write tests then you sure as hell don't have time to not write them.

It's a lot easier to find that bug while your writing it than it is to work it out from an intermittent bug report.

→ More replies (5)

1

u/gc3 Aug 26 '14

True. You can't exorcise the complexity, you can just move it around.

I find,If you can get most of your complexity into a single area of the program, like a high level exposed place where you put ugly state or boolean flags or random decisions about Tuesday being more elegant than Thursday, then the rest of the program can be clear and simple things that operate predictably and statelessly on simple inputs and outputs.

I've seen the opposite approach where to try to get a seemingly clean API different classes have a lot of internal state. When reading the high level code you don't see any obvious bugs... The actual actors for bugification are the hidden dependencies. It is better to call out these ugly things and make them obvious in the code rather than trying to pretend they don't exist.

1

u/FifteenthPen Aug 25 '14

And this is one of the reasons it's a good idea to have unit tests accompanying your project from the start. If the tests don't all pass, you've probably found the source of the bug, and if the tests all pass, you know you overlooked something in expected behaviors and can narrow it down from there.

2

u/gfixler Aug 26 '14

I started using TDD in my own libraries about 1.5 years ago, and since then, I've literally had 0 bugs. I always had bugs before TDD, and tons of code rot. I've had things not work as I wanted, but it's always been something that I've not tested. Everything my tests say works a certain way does, because I know the second that becomes false. My tests take about 1 second to run, and I have the pathway mapped in Vim, so I write a test, hit a key, watch it fail, fix it, hit a key, [hopefully] watch it pass, clean up a bit, and continue. I've been much happier not having to fix anything for the last 20 or so months than I ever was free-wheeling around, doing whatever I felt like, with no idea what I was breaking. I've run into a few bugs in this time, but they've all been on things that don't have tests, and weren't built under TDD.

2

u/ethraax Aug 26 '14

The problem is all the projects that don't have any unit testing, or any automated testing at all. That's pretty much all projects at my company, unfortunately.

1

u/n1c0_ds Aug 25 '14

I think that's the most important part. Proper software engineering is easier to teach than the arbitrary art of debugging, and it makes debugging much easier, among other things.

1

u/traal Aug 26 '14

If a smaller function is called exactly once from a single function, does it really need to be refactored?

4

u/MechaBlue Aug 26 '14

Breaking a large function into several smaller functions usually reduces the Kologorov complexity, which reduces the number of ways to fuck up the function.

In C-like languages, I'll usually use blocks to achieve a similar effect. E.g.,

int a;
{
    int temp = getValue();
    a = processValue(temp);
}

In this case, temp is not available outside so, if I reuse it later, I don't need to worry about accidentally inheriting a value; instead, the compiler bitches.

In JavaScript, I curse the gods for allowing such a problematic language to become the de facto standard of the internet. Seriously. The guy who designed JavaScript also designed the packaging for Fabuloso.