3 ms·
I think this is a bit overthought. There are 2 classes of errors: Errors that can manifest in a problem (we usually call these "bugs") and errors that can not
by mikekchar 9y ago
I think this is a bit overthought. There are 2 classes of errors: Errors that can manifest in a problem (we usually call these "bugs") and errors that can not manifest in a problem (these are usually called "software errors"). The second class of error is not really an issue until you change the code (or some other factor) and the error manifests as a problem (and then it is a bug). You might imagine some off-by-one error, where by chance it doesn't affect the result. However, if you modify the code, then the off-by-one suddenly matters and you get a bug.
Apart from that, software either has some functionality or it does not. It's actually not particularly meaningful to discuss whether or not software was originally supposed to have functionality, unless you just want to finger point. There may be a bug that stops the functionality from working correctly or it may be that you got the requirements wrong and implemented the wrong thing, or it may be that you never even tried to implement something. It doesn't matter -- the functionality doesn't exist.
When you are designing code, or implementing, or refactoring it, or whatever, then you need to consider how easy it is to modify the result. It's tempting to look ahead and assume that you know what you will need in the future, but you should also consider that you might be wrong (The YAGNI (You Aren't Gunna Need It) rule is surprisingly effective). Generally speaking, as a programmer, your goal should be high throughput of development throughout the lifetime of the project. Attempts to cut corners are usually repaid with interest later down the road. Similarly YAGNI work not only ends up being wasted, it complicates the code base and slows you down later. Because of this, you should usually attempt to just keep the code as simple as possible to modify in general. You should also make the functionality you have developed as accessible as possible (i.e. hiding your functionality under 1000 layers of abstraction is usually a recipe for slowing you down later, even if it looks cool now).
Worrying overmuch about if your code is wrong is probably a class of YAGNI. As long as it is easy and obvious to fix, then it doesn't really matter if it is wrong. Concentrate on making sure that it produces the result that you want and that you can modify it if it turns out to be wrong. Incidentally, it will be wrong. Eventually. You can pretty much count on it.
- algesten 9y agoI totally agree with you. I especially think this is counter productive advice: > Many programs which are correct when viewed at Level 2 are not correct when viewed at Level 3, because they rely on behavior which is not guaranteed to hold in all future versions. Some level of modularization/abstraction perhaps just keep the system clean, or maybe assist mocking for tests. Maybe even to prepare for some likely changes down the line, but absolutely not "guaranteed to hold in all future versions."
- algesten 9y agoFrom a couple of days ago: https://news.ycombinator.com/item?id=16138708 https://news.ycombinator.com/item?id=16138708 Those antivirus companies patching early windows kernel calls to do their thing sounds pretty horrendous today, but on the other hand, at the time, they made a bad situation better (suing microsoft later was just ridiculous though). But I think this is point in case why both the SimCity example in this article is flawed. Malloc on DOS behaved that way. Windows 95 kernel calls were patchable. If they couldn't "rely on behavior which is not guaranteed to hold in all future versions", had any of this software seen the light of day?
- rdtsc 9y ago> Because of this, you should usually attempt to just keep the code as simple as possible to modify in general. I like that phrase, that's the key really. Usually the simplest code to modify is the smallest amount of code. But not always, a good abstraction will also make the code easy to modify. Having good tests, with decent coverage, which are easy to update and add to, probably not overly tied to specific implementation, also helps make code simple to modify. If you work in a specific domain for long enough, you might be able to predict _how_ requirements will change. Say you are required to handle one widget at time and you're told to implement a process_widget(a_widget) function. But you have been around long enough to know that next week they'll come back and say, "actually, can we process N widgets at a time?" so instead you write a function that is process_widgets(list_of_widgets), and next week you just start accepting a list of more than one item, with the system already tested and implemented to handle the rest. Some might say this is overdoing it, and in most cases it is, but I've seen this happen with more experienced engineers - they'd be a step ahead of the manager and the customer requirements.
- mannykannot 9y ago> As long as it is easy and obvious to fix, then it doesn't really matter if it is wrong. If only there was a way to exclude all the bugs that don't fit into this category. > Incidentally, it will be wrong. Eventually. You can pretty much count on it. Especially if the theory (and its assumptions) behind how it works is flawed. And there always is such a theory, even though it is rarely ever stated explicitly in any form but the source code. You build that theory with each statement and expression you write, from the considerations and decisions that led you to writing that particular line (BTW, included in those decisions are the choices you made about what to test for (and, implicitly, what not to test), and how each test is implemented.) That is the point the author is making.
- crdoconnor 9y agoI agree. I thought with the SimCity bug that, while it may have seemed hacky, the way both Windows and Maxis teams handled it was actually optimal. Had Maxis put a lot of extra effort into "proving" that the software wasn't going to break in 8 years that would have likely led to schedule slippage for no particular end-user benefit.
- TimJYoung 9y agoThis is fine if the bug is only triggered when you modify the code. Many bugs that classify as #3 (from the article) are not only triggered by modifications to the code. For example, many lower-level languages have runtimes with sub-allocators that grab chunks of memory from the OS and dole them out in smaller bits to the calling code. This means that an off-by-one error on the read of an array can cause absolutely zero problems when you run the same binary 1000+ different ways, but have the user change the order of operations in the application in one particular way and boom !, now you get an AV/segfault in your application. It's all down to the how the OS MM and the runtime sub-allocator work and what the user does (and in what order). The solution is to make sure to use a sub-allocator that allows you to test your application with rigorous allocation tracking, which will catch these types of bugs, but not all developers are so thorough.
- Retric 9y agoIt's also possible for two off by one errors to cancel out. The code is not correct as implemented, but you need to fix both locations to avoid noticeable bugs.