Why don't schools teach debugging? (2014)
danluu.com
danluu.com
It's also actually two skills:
Analysing a state. What do you see, what is there, or isn't. What was expected.
Backtracing. Getting from point y to point x by figuring out how y happened.
I'd say at least in my country we also actually learn these skills. But when we learn the methods its in a completely different context, totally unrelated to life. In real life, you hit a problem, you get overwhelmed by the complexity, and then your brain needs to start applying the method. In school you learn the method, no idea why. Then you apply it to totally theoretical problems, no idea why. Then you get tested on how well you execute the method, no idea why. Then nobody ever talks about it again. Now, how should your brain in moment of being overwhelmed remember that method A it learned 20 years ago may apply here? There's no way.
Teaching shouldn't be: theory->method->application->test. It should be theory->enforce_problem->wait_for_questions->explain_how_theory_can_be_applied->review_of_application->goto:enforce_problem.
The best way to find and fix the toughest heisenbugs is to a) form a hypothesis as to what might be happening, b) figure out what would falsify your hypothesis, c) run an experiment that must make the bug happen reliably iff your hypothesis is correct, and finally d) figure out the solution.
Steps a-c are precisely the scientific method.
Fine. But when the course is just about making "whatever you want," then that really valuable time of learning these debugging skills gets missed, 'cause you can always pivot and do something else. The most valuable lessons I've learned about coding -- and the ones which, like you wisely stated, are applicable elsewhere in life -- had everything to do with needing to work through a tough computational challenge.
That taught me how to know I don't know something, how to poke at an issue in such a way as it would reveal something about the system (or my own understanding), the oh-so-currently-hyped skill called "grit", etc.
Every thinker or scientist I deeply respect has a treasure trove of examples of being stuck and working through hard and challenging problems even as little kids. That somehow we think an avenue for a more educated populous consists of presenting fewer experiences like that to youngsters is beyond me.
It's a mindset issue. In the military, a soldier is a tool to get a specific job done. The soldier must understand this and shed away his ego and any ideas he has about his "self."
He must understand that he is a cog and his only job is to be the perfect cog.
This could be applied to programming to increase efficiency, but I'm certain most programmers (and the HN crowd in general) won't be happy with what's to be expected of them.
The success of the mission rests on appointing a competent commander (CEO/Manager/Etc.) that'll make all the final decisions and having soldiers (employees) that can zero-in on doing exactly what the commander tells them to, even at the sacrifice of their comfort.
There's a lot of ego and self-identity in the programming world which collides with the very essence of the "military way of doing things."
Additionally, your software engineers aren't indentured servants. In the military, you often can't go home until a problem is fixed and if you quit, you go to jail.
The military burns through a lot of good people, most get out after their initial term.
I bet you can teach that without all the bad associations people have about the military.
In the debugging context there are some more specific applications of the general principles, which should also be taught.
And herein lies Dan Luu's fundamental misconception: that universities see themselves as places where students come to learn skills, i.e. as places of certification producing skilled workers for industry. Most universities see themselves as academic places of learning for the pursuit of knowledge for knowledge's sake, which only incidentally graduates people whom industry happens to find their knowledge at least somewhat useful. If the university professor never needed to debug, use version control, or write clean designs in order to succeed in academia - why should he consider it worthy of being taught? If the university professor never benefitted from remedial review on his academic path towards becoming a professor, why should he consider it worth his time? Of course he's going to dismiss the notion to the tone of "some people just can't hack it in engineering." It's academic survival bias.
People talk about the STEM pipeline like some kind of demigod figure designed it from on high specifically with the intent of producing skilled technical workers, albeit with various flaws related to sexism and skill relevance etc. Of course, this is a ridiculous way of looking at the STEM pipeline, so it shouldn't surprise anyone that different actors in the pipeline have contrary motivations.
Dan Luu is describing a method that would help people achieve a better success in their exams and classes, he's not even pointing their usefulness in the workplace. Therefore, your point about him thinking that universities are here to produce skilled workers is a strawman.
The author demonstrated this point by showing how his classes could help students learn better.
We can then debate about whether having successful students is a positive thing to have for universities, but Dan Luu clearly seems to think it is.
The article doesn't press this point very hard, but I take the central argument to be in favor of 'systematically approach[ing] problems [of the sort that debugging instantiates]' which is just as much 'academy' as 'industry'.
In Portugal that place belongs to Polytechnics.
Universities are to learn to learn.
Showing our work reminds us that solving problems is an iterative work, and when we get the wrong answer, it lets us (with or without the help of the teacher) step through and figure out what we did wrong. For teachers and professors, it probably becomes second nature, and maybe they forget who explained this to them, or when they realized it themselves.
Even once we're out of school, I think we need to remind ourselves that this is the same reason we don't write a 1000 line function, rather we break it up into smaller parts, each designed to solve a specific part of the overall problem.
Now, what I really wish we had had in university was a CS course on using gdb and/or WinDbg - not just to understand how to analyze a core/dump file or step through running code, but also to help drill it into our head what compilers do to our code, and how processors really work. --It's amazing how often I have to remind people that debuggers will often lie to you, and you often have to look into the registers for the real data!
Its absence in any course is conspicuous.
But at the same time more than half of the professional web developers I know don't really use a debugger. Equally problematic.
I wonder how much that has to do with tooling? When programming in python I run everything in a debugger basically all the time. When doing JavaScript front-end work it's back to print debugging.
In these cases, the Feynman method is sometimes all that you can apply. I have already spent plenty of hours with a stacktrace on one screen and the source code on the other screen, stepping through the source code in my mind (usually backwards from the end of the stacktrace).
The caveat obviously being that debugging code written using some JS frameworks, like Polymer, is a nightmare.
PHP and JavaScripy are great for debugging. Even Node is awesome. Rails and mainstream JavaScript frameworks... not so much. Nothing about the languages.... The people who design the frameworks and build tools just don't seem to value it.
https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_...
Debuggers are complicated and create dependency. How many developers do you see every day just sitting at their computer tapping "step" ... "step" ... "step" for hours.
I'll break out a debugger if all else fails, but it's not my first choice.
Er...very few? A bad programmer will be a bad programmer with a debugger and that bad programmer will be a bad programmer who prints out state after every line of code.
Breakpoints and watches are strictly superior to print statements for understanding the system you're working with. You drop a breakpoint where, as you yourself put it, "the bug is likely to be," you look around, you kill the process and you make a change. It doesn't "create dependency", it's just better at solving the problem. Anyone can fall back to println debugging if necessary. It's not some difficult thing. But it's wasting your time.
If I'm 90% sure I know what the bug is and it's a system where it's easier to add a print than to enter the debugger (yes, those types of systems exist), I'll do that first.
If you've written something in a language where you don't have a debugger...that's your own damned fault. :-P
It's often hard to run a stepping debugger when you have an embedded hardware at hand. It's equally hard to run such a debugger when you have multi-threaded code. And the same stands for virtually any system that works with network directly, where timeouts are plentiful.
Also, good luck with a debugger in this modern architecture of microservices that happens to be hype nowadays.
Totally true. But that's a pretty rare situation.
> It's equally hard to run such a debugger when you have multi-threaded code.
Maybe your experience is different, but Every piece of multi-threaded code I've ever written has fallen into one of two buckets: task-pooled parallelism or interlocking features that shouldn't block one another. The former is trivial; reduce the task pool size to a single thread. The latter is definitely harder, but modern tooling (I'm partial to VS2017's Threads window) makes this a much more achievable thing; you can break across all of them and step between them effectively.
> And the same stands for virtually any system that works with network directly, where timeouts are plentiful.
I find myself debugging in environments where I can control timeouts, but if you can't, yeah, this is totally a problem. Leaving a service paused for ~3-5 minutes while I poke at it doesn't drop connections with my dev configs, though.
> Also, good luck with a debugger in this modern architecture of microservices that happens to be hype nowadays.
"Doctor, it hurts when I do this..."
If you can't effectively stub out an environment around your microservice so that you can run it in a debugger-friendly environment, you don't have a microservice--you have a monolith that communicates over pipes rather than function calls, and that's strictly worse in the first place.
> Totally true. But that's a pretty rare situation.
I know it's rare to work with embedded systems if you don't work with embedded systems. Though when you do work with them, this "rare situation" suddenly becomes prevalent.
>> It's equally hard to run such a debugger when you have multi-threaded code.
> Maybe your experience is different, but Every piece of multi-threaded code I've ever written has fallen into one of two buckets: task-pooled parallelism or interlocking features that shouldn't block one another.
It all works well until it doesn't. That is, until you need two threads to communicate with each other (otherwise, why are they even running in the same process?).
>> And the same stands for virtually any system that works with network directly, where timeouts are plentiful.
> I find myself debugging in environments where I can control timeouts, [...]
All of them? My simplest cases have dozen timeouts one behind the other, at different levels of the stack. Even though I potentially can set them all, it's all very tedious. Not to mention that the timeouts can interact in non-trivial ways, so it's often hard to track all of them.
>> Also, good luck with a debugger in this modern architecture of microservices that happens to be hype nowadays.
> If you can't effectively stub out an environment around your microservice so that you can run it in a debugger-friendly environment, you don't have a microservice
https://en.wikipedia.org/wiki/No_true_Scotsman
Also, I haven't said that "you can't effectively stub out an environment". There are other difficulties around debugging microservices, like mentioned before controlling timeouts everywhere.
I rarely use the step functionality, though, because it just takes too long. More frequently when I hit a breakpoint, I examine the current state, figure out the next place that I am interested in stopping and then add a breakpoint there before resuming.
These days it would certainly be much easier to teach students how to use a debugger - most students probably aren't doing their work on mainframes anymore where resources might be limited (anyone else remember getting emails asking you to release your semaphores because the server is running low?), but professors still have to decide which topics are the most important to cover in a limited amount of time.
I touched on the PL ML in a type systems course. I can't speak for how common it is though. It's certainly much more likely for someone who takes a course on type systems or PL theory though, presumably given that choice it's pretty common.
--
> Is an ML course now a common part of an undergraduate degree programme, though?
There were several available to me. It's not really a particular interest of mine, but I took an introductory course anyway, since it would have seemed a bit remiss in today's world to have graduated without knowing an NN from an SVM.
Unfortunately for my grades, performance usually reflects interest... I still don't think I actually regret it though.
I rather focused on compiler design and graphical programming options instead, but I remember that all the basis for neuron learning algorithms were already part of the content.
What do you mean by this?
I wouldn't consider this to be a very common use case though, it's a niche within niche.
Luckily debugging is not merely the act of using a debugger !
Still not a reason enough to avoid using a debugger.
My advice on this is to, before even thinking about using a debugger, think carefully about and write down if necessary the conditions and values expected at certain points in the code. Then, and only then, should you use the debugger to confirm or deny those hypotheses. In practice, with those I've taught, guiding them through this procedure, they often realise the source of the bug before using the debugger.
They're both great, like having two sets of teeth to grind problems down to a fine digestible paste.
My software engineering prof encouraged us to use both approaches to programming. The analogy to teeth, however, is my own.
1. Read the code and think about what it's really doing.
2. If that leads nowhere, write (more?) unit/integration tests.
3. Only if the above two fail, run the debugger.
"Contemplating" code has another benefit: since you're looking at the whole picture rather than jumping line-by-line it develops your intuition about why things fail.
The added benefit is that when you actually break out the debugger to squash a bug you better understand what might be going wrong as you can try and pattern match from the time it worked perfectly.
I treat the debugger as a tool for understanding code, buggy or otherwise, no "debugger-induced tunnel vision" for me, it widens my field of view :)
I served as a TA on that course for the first few years, and after the first year, I lobbied for a few adjustments. The most important one was to include a specific assignment on debugging, where instead of asking the student to implement a new program, an existing program is presented that has both syntax and semantic errors. Students are expected to turn in a list of all the errors that they find.
[1] A crash course, actually, but still way better than nothing.
[2] Nowadays they do Python, I hear.
The thing that makes debugging hard is multiple hypotheses (or amazingly, exhausting all hypotheses). Your crash can be caused by errors on many different abstraction levels, so your debugging process has to be thought about on many levels. On one level, you have an illegal access via pointer. On another, you have a user whose information is incomplete.
Once you find a bug it's often hard to trace back exactly what the original state (including your state of mind) was, because you'll have taken the thing apart in various ways, added logging, looked at the call stack, memory, etc. And once you know the issue you shave down the explanation so that all those tedious steps that didn't lead to the answer seem to vanish.
One thing that got me thinking was this idea that people are expected to fail. To me, such a process is incredibly wasteful, and itself a failure. Are engineering students immune to ordinary psychology? If you tell people half of them will give up, you'll lose a lot of them, even ones who could pass.
You can make a course difficult, but don't tell people they will fail. I was never confronted with such statements, yet after my first year about 10% of the engineers had been booted due to failing exams.
His other point seems to be that courses are difficult because they aren't taught to people with the prerequisites. That should never be the case. Where I went the prereqs were a very standard set of math courses, and you could access most non-math courses knowing just those.
If you're doing C (or any other language where that particular problem exists), it's always a nice exercise to have students write a program (or a function) that takes the radius of a circle and outputs its volume. Most students will write:
double volume(double radius) {
return 4 / 3 * M_PI * radius * radius * radius;
}
Can you see the problem? ;)(And technically, it's the other way around. A circle is the 2-dimensional special case of a (hyper)sphere.)
A nice example for debugging, and for understanding the value of languages with better typing discipline.
> The thing that makes debugging hard is multiple hypotheses (or amazingly, exhausting all hypotheses).
You don't have to start out with an example that features all possible error conditions. It's totally fine to use a made-up classroom assignment for teaching. There is no need to put all the complexity in a single example, better to partition it into steps that can be attempted individually.
Moving around in a debugger, setting breakpoints and watches, knowing where to set breakpoints and watches, tracking a value on its way through the program, adding logging to get a view of varying state over time, localizing a problem within a large code base. Those are all skills that build on top of each other, and can and should be learned over a series of successively more complex examples, each focusing on a particular aspect, while reinforcing the previous lessons.
Then you can let the students loose on a wild goose chase through dozens of libraries calling into each other across language boundaries using asynchronous communication, to find the one instance where someone cast a void* to an int* to do arithmetic on it, when it should have been a float* .
Have bugs with index bugs like:
for(var i = 1; i < len; i++)
substr(name, indexof(name, ' '))
i.e. Find out why it's not showing all the items (i = 1 instead of 0), find out what's going on with the formatting of the lastname (there's an extra space at the beginning of the name).For SQL use a JOIN where it needs a LEFT JOIN.
I can think of others off the top of my head. Leave out an optional parameter that's been populated from the front-end, but don't pass it to the method call. In OO languages, have a constructor accidentally call itself instead of the alternative with more parameters (infinite loop). Nest two for loops and accidentally use i instead of j inside the inner loop. Have an if statement that looks sorta right but is logically impossible to satisfy.
For the "only sometimes crashes" bugs, div by zero is an easy example.
All the sort of accidents you instinctively correct now are probably good beginner debugger exercises.
How do you abstract away a problem so you can individually identify and isolate the parts and test them individually? The same general techniques apply whether you're debugging a c program or repairing a car.
Just on the basis of interacting with coworkers, when I see people fumbling around in the dark, it's usually just because they're not using basic troubleshooting, they're just googling (at best!) and trying random fixes in a scattershot approach.
Quite a few times, I've helped people who were far more knowledgeable than me about, for example, Linux, by just asking them questions and trying different experiments to see if we could eliminate possibilities and so on. At the end of it, the problem is fixed and they thank me for the help, even though they knew everything they needed to know to fix the problem to begin with.
Here's a high-level idea that might be doable in the right circumstances.
Get one or two hardware guys from an engineering company near the institution to come out with an old version of their silicon that has a fairly rapidly (but not necessarily trivially) triggerable bug. Set the system up, run over how it works, explain the ins and outs of the debug environment/setup, make the bug fire, and spend the next $class_length getting the students to try and figure the bug out.
If the guys can't come back for repeat sessions, they explain it at the end.
I suggest previous-gen silicon in the hope this would decrease the NDA requirement. Then again, even that may not be enough; obviously this would be CPU dependent. But even if the chip was otherwise NDA'd, if it uses a popular (or at least non-NDA'd) core and the bug is in there, that could work too - maybe you could do a partnership type thing with a custom build of the debugger that has NDA'd components hidden.
Maybe you can set up VNC or RDP from student laptops back to the test setup up the front, maybe the students can just backseat-drive. But just describing this, I'm excited, I'd totally want to go to a class like this! (Which was why I tried to think of ways to get around the NDA issue)
They do tend to be 10x as annoying to debug though. Not more difficult - just tedious - especially because you often end up triaging what turns out to be somebody else's bug (i.e. a checker on your block fails, so you need to trace it back to somebody else, and often they also trace it outwards to somebody else).
And this is where using it as a class example gets really difficult - even people on the design team are specialists in their own blocks. Giving students all the knowledge they'd need to debug a problem would be incredibly difficult.
It should be alright in a classroom setting. I remember one particular piece of an assignment that I received a 0 on many years ago.
We were doing String manipulation, and the iterative pieces of the assignment culminated in us building a simplistic cipher. The part I did not receive credit for was recursively reversing the String. I just called the reverse method. Same result, so no harm no foul right? Wrong.
The point wasn't to reverse the String so that the cipher could be created. It was to incorporate programming basics such as recursion into an assignment. It was a shoe-horned method to get us to use recursion, but it got the point across.
The problem is that debugging is hard, and will tend to very quickly manifest any flaws in a novice's understanding. When I teach it, I describe it as a manifestation of the scientific method: observe the phenomenon (my program isn't doing what I expect), hypothesis (it might be this), experiment (if that's the problem then doing this should result in...). Repeat ad frustratum.
Unfortunately, doing that effectively required some pre-requisites: (1) An accurate mental model of how the program should behave, with sufficient detail to hypothesize about why it's not behaving that way, (2) Available tools and instrumentation that allow you to inspect a running program (I'm old enough to remember debugging by adding commands to print state to the console), (3) sufficient experience and sophisticated understanding of the system to be able to make valid hypotheses and design tests.
Only the second item easily lends itself to the classroom, and the third item is critical: After I help a student identify and isolate a bug, they often ask me how I knew what to try. The answer is, almost always, that I've seen that before. Hell, there used to be a type of memory fault in Borland C++ that I could diagnose from across the room just by the icon and size of the error dialog. (You get a lot of experience seeing error messages by teaching Freshman programming).
So, short answer, the reason we don't do a good job teaching debugging to novices is at least partly because they're not ready to learn it yet.
"(1) An accurate mental model of how the program should behave, with sufficient detail to hypothesize about why it's not behaving that way" -- While working as a firmware engineer, I once got a bug from a customer, he reported that they were in Argentina and the device with the firmware would lose an hour in the time displayed every time they had a power loss and that the problem goes away when they put the time-zone to Brazil, this much was enough to know that the problem was with the day light saving time part of code. Some engineer hypothesized that problem might be with the NTP server but his hypothesis was wrong because NTP server was dealing in UTC.
"(2) Available tools and instrumentation that allow you to inspect a running program (I'm old enough to remember debugging by adding commands to print state to the console)" -- I dealt with code which installs the firmware, frequent power cycles made keeping a debugger attached an exercise in futility so I just continuously logged the top two frames of the stack by using a system call. Crude way to trace the control flow but helped me in understanding the installation process a lot.
"(3) sufficient experience and sophisticated understanding of the system to be able to make valid hypotheses and design tests." -- This was even more important while dealing with firmware hangs, dealing with hangs made dealing with crashes look easy. Crashes were the messy criminals leaving a lot of clues behind. Hangs are the type of criminals who would have to be caught while committing the crime.
I would also like to add one more --
"(4) When debuggers fail you, git-bisect is your only true friend." -- I once was dealing with a memory corruption bug, in a piece of memory which was shared by two systems, the number of processes with access to that piece of memory was bonkers. I decided to brute-force my way out, it worked in the last release, it stopped working this release, I just kept doing a git-bisect between clean and dirty states and found the code which caused the bug. This way was definitely not the clever way to do it, but brute-force is sometimes the only option left.
Using the interactive debugger Reading log files and adding more logging/trace. Writing unit tests Making some sort of test program or script to exercise the problem area so it's easier to reproduce the problem. Depending on your environment, tools and problem, different combinations of the above will make more sense.
The number one criteria we have when hiring a fresh graduate is the quantification of how much time we can devote to training them their first year. Demonstration of unit tests in sample code signals lessened requirements on us. We use it as one of our proxies (a real internship is an example of another.)
If not students will conclude that writing the spec. and debugging the program are simply equal valued steps in a stepwise refinement process. Of course it sometimes is that way and can be no other, but generally it is better to think first and act afterwards.
Make them use a JTAG debugger such as the Ronetix PEEDI. Make them debug timing-sensitive boot code, such as SDRAM detection routines that use GPIO pins to bit-bang SMBus protocol to the SPD EEPROM. Have a hardware watchdog enabled when this code is hit; it should reset the CPU after a few seconds.
Another suggestion is to use the debugger that is included with IDA Pro. Make them connect IDA Pro to a remote system, then debug a kernel driver without source code. The "trace replay" capability may come in handy.
Those taking that class have spent years trying to learn algebra and still don't get it so we are already trying this.
> I’m no great teacher, but I was able to get all but one of the office hour regulars up to speed over the course of the semester.
Most likely they just memorized the new algebra rules as well. There are lots of studies showing how hard it is to teach students anything tangible, instead most will just try to memorize everything you say.
See for example: https://www.amazon.com/Academically-Adrift-Limited-Learning-...
Troubleshooting is really just a special application of general 'problem solving', which is something schools aim to teach. I'm not sure why troubleshooting skills are not taught, especially something like "divide and conquer" [1]. Even in very complex systems, so long as you have the right access/visibility/logging to see what's going on at various places, it usually doesn't take very long to isolate a problem down to a particular component.
[1] https://en.wikipedia.org/wiki/Troubleshooting#Half-splitting
He also used intuitively divide&conquer to find faults or leaks in wirings and pipes and so on.
It is really something that should get more study and practice outside of IT or science, and I am sure that mechanics and other craftsmen have their own set of algorithms that can be used in various situations.
But that's not how things happened, and today people pretty much do not think about writing (and thus do not end up debugging) the programs they use.
Instead we are all users now, and for many the troubleshooting algorithm is to:
1. google it, or
2. reboot it, or, failing that too,
3. throw it away, or, failing even that,
4. complain and curse.
It's amusing to think that this model is now sometimes used by programmers as well (though hopefully mostly just #1, and sometimes #2).
I've found the best way to learn debugging is to should-surf someone who is good at debugging. For instance, I'd spent six months at Apple before I watched a resident wizard for an hour in the low-level debugger, and what I learned in that hour improved my skills dramatically. Repeat for two other companies: Encounter a tough problem (for you), find a wizard to help out, and pay attention.
I think the sentiment in schools is that sufficiently challenging coursework will force you to learn debugging techniques on your own, as a natural outcome of you writing code :-)
At best they'll talk about best practice and will give you an exercice that implies you should use one of those skills.
The amount of useful stuff that are not taugh in school is enormous.
"We excitedly explained our newly discovered technique to those around us, walking them through a couple steps. No one had trouble; not even people who’d struggled with every previous assignment. Within an hour, the group of folks within earshot of us had finished, and we went home."
This is great, I wish more people at my school did this, learning should be collaborative (as long as you don't copy paste code). There seems to be an obsession with secrecy and not sharing any ideas on a project as though there is only one way to solve a Java homework problem.
as long as you don't copy paste code
That's the million dollar question: how do you know whether a classmate is being earnest or just copying you? Especially in highly competitive environments, cheaters are extremely adept at leveraging your work without substantially contributing anything back.The way it works in law school is that students (at least the social ones) break up into study groups early on. They may or may not be friends outside the study group. The key is that the study groups span multiple classes, and sometimes last the entire three years. In other words, it's an iterated game that discourages cheating from the outset. Being ostracized from a group is a huge liability, especially if you're a cheater.
This strategy doesn't work as well in undergraduate programs. There's too much churn and variability (in the student population, in their intelligence, in the number of subject classes, in the number of professors teaching the same subject), which means it's easier for cheaters to come and go without having to suffer repercussions.
I'm not a collaborator. To learn (and retain) I need to work through things alone, especially on difficult problems. Talking with other people about a problem is usually a distraction, and almost guarantees I won't internalize and retain the material. The above descriptions of group dynamics is my outsider's perspective.
Cheating was solved in an engineering class I took that had us write a program in our own time, but the assessment was a test where we were asked to modify it and produce the correct output. If you had copied the code without understanding it, you wouldn't be able to pass the test.
What if I'm very good at one thing and very bad at another? That would probably work with the study group just fine, assuming they know it and adjust accordingly, but it's still 'cheating' in the sense that I can get away with only a partial understanding.
Everywhere I've studied, that alone would have counted as academic dishonesty. If it wasn't an explicit group project, there was clear language about working independently; you couldn't just decide to pair up. Almost everyone, even my graduating class's valedictorian, did it anyway. I was the honest chump and my GPA suffered in comparison.
Discussion of the problem definition was condoned, but any talk about solutions was off-limits. Many professors would include homework sets with a final question asking you to confirm that you worked alone and received no help outside the professor or TA. Some allowed you to admit to receiving help, with no punishment beyond 0 points for the assignment.
I shall assume this guy had an atypically lenient professor for his first engineering class.
Yeah, this is horrible and irresponsible on the university and professors' part. But that's the way it seems to be in most large US universities (anecdotally based on talking to friends in grad schools).
However, decent small liberal arts colleges often do exactly what you prescribe, they correctly try to teach it to everyone and generally succeed. Big box schools are just exploiting undergrads as cash cows, don't attend them or send your children to them.
There's a need for all types.
"Hackworth was a forger, Dr. X was a honer. The distinction was at least as old as the digital computer. Forgers created a new technology and then forged on to the next project, having explored only the outlines of its potential. Honers got less respect because they appeared to sit still technologically, playing around with systems that were no longer start, hacking them for all they were worth, getting them to do things the forgers had never envisioned."
That makes you a good programmer, too. ;)
I know that The story isn't about debugging, but sometimes showing "right" inputs can be very efficient way how to explain the bug to other person.
To give another example, anyone who's working in a programming context can boost their productivity by knowing how to set up their aliases, learning the basics of unix tools such as grep, and mastering a good text-editor/IDE such as vim/emacs/intellij. And yet, after 4 years in college, I didn't know a single thing about any of the above, and slowly picked up bits and pieces randomly over the course of my career. If a professor could have taught the above in a intro-to-engineering course, it would have saved us countless hours and allowed us to hit the ground running.
While it focuses on time tradeoffs and such during resolving outages, one of the major themes is that engineers/developers tend to use heuristics and past experiences to drive problem solving. I think as developers we tend to do the same, we look for common patterns for types of problems we've encountered. The debugger is just a representation of log analysis.
So how do you teach those experiences? I think a lab-based approach with examples is probably the right course.
Sun Microsystems used to have a Fault Analysis class they taught for Solaris.. It was essentially just a lab of 50 broken systems/scenarios that you had to diagnose and fix. I think more of that sort of thing earlier on would be helpful.
When something is not working right and you have to debug it, you need to re-evaluate your abstractions and drop down a level in your technology stack.
In Dan's case, he had to look at, and think about how the logic gates worked.
In a more modern case, when your REST calls aren't working right, you may have to debug HTTPS stuff.
If your HTTPS stuff isn't working you may have to look at key negotiation and exchange, etc etc.
If your Entity Framework isn't working, you may have to (horrors!) look at what's going on with the database.
If the database isn't working, you may have to (gasp!) look at the hardware it's running on.
Too often people look at surrounding code or state, and do not verify the abstractions of lower-level code or hardware. Those things have "always worked" - it's never the compiler's fault, until it is (or a bad assumption of how the compiler works)
I've seen people here talk about iterating, which is fine and good for many problems. For many debugging problems iterating will take you until the heat death of the universe to solve.
I don't really think "fullstack" devs are always the best solution, or a scale-able approach to development; but a fullstack dev with a debugging background is hard to beat when you have problems.
Edit: To put it another way: If your technology isn't working, you have to examine the engineering under it. If the engineering isn't working, you have to examine the science. If the science isn't working, you have bad assumptions, or you've found new science! (you probably have bad assumptions)
> There's no authoritative definition, but I see it as "I can trace a visual problem down through CSS styles, debug the JavaScript code that sets them, intercept the AJAX call with the problem-data, debug the backend language that is run, diagnose the SQL that finds the malformed row... And apply a fix on any of those levels where it is needed."
> You might not like the code you wade through, but...
Does the author really advocate that the instructor of advanced college course needs to spend his time on explaining the difference between f(x+a) and f(x)+a to his students?
I don't have any idea how someone with such an understanding of algebra could even get into a college even if he wanted to learn humanities. If a college student doesn't get it, it's the college mission to fail him as fast and possible, not to spend time trying teach him something middle school should have.
But I guess it depends on the country. US math education, from what I hear, never ceases to amaze me - the contrast between the abysmal level of your schools and the best CS departments in the world is just astonishing.
Schools' income depends on the number of students (either a budget allocated according to student population, or students paying directly). Therefore, the incentive is to delay failing people as long as possible, as long as it doesn't affect the quality of the graduates.
In two ways, even: first, if you select good students, they show good test results and place good jobs anyway, because they're smart. But also (and this is most important for me as a potential student) your school's _product_ is not only interaction with instructors, but also interactions with other students. The better your peers, the better education you get - even if you disregard the "school network" factor.
Those aren't mutually exclusive. It's much easier to write a test for an issue if you know what the issue is.
Instead they focus on /writing/ code. In the process of writing code, at least some incidental debugging experience is gained (this article is actually an example of that -- the debugging wasn't formally taught, but the assignment clearly forced the author to learn it a bit), but almost no /reading/ code experience. It took me a while in industry to make up for this deficit.
This pernicious attitude, at least in my alma mater, could be traced back to E.W. Dijkstra, e.g.
http://www.cs.utexas.edu/users/EWD/transcriptions/EWD02xx/EW... http://www.cs.utexas.edu/users/EWD/transcriptions/EWD03xx/EW...
And he may not have been entirely wrong; it's not uncommon to see people spend insufficient time on reasoning about the correctness of their code from first principle, and jump directly into testing/debugging.
On the other hand, using correctness proofs as the sole and sufficient means of writing code is just not a workable approach for most non-trivial problems.
Another way which we have used is to engage students in open source projects. Look at bug trackers to find a few issues and then try to solve them. This also teaches students a lot of other things like: - How to work in existing projects - How to ignore large parts of a code base to focus on a given issue - How to write test cases - How to create patches that match project code style - How to communicate and participate in projects.
If there were, I can believe some universities might at least be interested in offering an optional course.
Maybe I was just lucky to have a good teacher. I still remember him not liking my solution to a problem when I was extremely proud of how smart I'd made an object, so that it reacted properly based on whatever was passed in. Instead of arguing with me he offered an extra credit assignment to take all of the logic out of the object and see if I could get it working that way...I did it and realized how much simpler his way was.
Hindsight makes you appreciate the patience that took.
The IDE (and debugging experience) is as important if not more than the language in my opinion.
Also I like debugging via print much better than with a debugger, because it's easier for me to spot the problem. Breakpoints are great though.
It's interesting that I see them saying "the more talented the engineer, the more likely they are to hit a debugging wall outside of school". I was only ever a good engineer in school because I was able to debug. You don't understand something til you tear it apart.
Turned out the program we stole didn't quite work and the process of debugging it was at least as educational as starting from scratch.
college is an absolutely horrible environment to learn programming.
programming should have a journeyman apprentice program like electricians.
I learned more from a mentor than I ever did in school.. it honestly was a waste of money.
The real world is different.
But I think there might be some time to at least walk students through the basics of profiling, it can be very valuable to have some exposure.
Then when it comes to memory management, there's a fine line between debugging and profiling.
I was playing with left leaning red-black binary trees yesterday as a refresher and got a memory corruption due to a bug. With valgrind (a tool I generally use to catch memory leaks) I caught it immediately, very helpful tool.