Well the worst defect I've seen in a while is sitting unnoticed in a program I wrote and was specifically required by the owner after months of argument against it, references to scholarly works showing why it was stupid, explanations for how search engines work etc. etc.
Right now customers are happy, but I expect some time in the next year someone is going to get a big fine because they didn't find the data they were supposed to and didn't realize it, and then they will no longer be happy.
IMO, if the defect is because some functionality wasn't specified and there's an edge case, that's not the programmer's fault.
OTOH, if the required functionality is documented and it doesn't work, that is the programmer's fault.
> A lot of the nastiest bugs come from components that are all correct in isolation but interact in a dangerous way. This is called a Feature Interaction Bug.
IMO, the original programmers of those components are not at fault, but if someone uses those components together and doesn't verify that they're working as desired, that "someone" is at fault.
Sure, stricter discipline won't always help, but sometimes it will. Sometimes, the programmer really is just being lazy.
Some functionality should be common sense to a competent developer, and not have to be specified. As a developer, I'd expect working code to have the following behavior, whether or not it was explicitly written into the spec:
- If the credit card transaction fails, do not ship the product to the customer, and notify the customer of the failure.
- Don't let the customer order more items than are available in our inventory, or zero items, or a negative or non-integer number of items.
- A web-based system should correctly handle multiple concurrent transactions.
- If something goes wrong, show the user a meaningful error message in their selected language, not a Python stack trace.
If my excuse for my code not doing this was "it wasn't in the spec", I'd expect people to ridicule me.
"working as expected" doesn't preclude defects. As expected by whom? There's always a range of expectations to consider... take the iPhone4 antenna, was it a bug or just people "holding it wrong"/ intentionally squeezing the phone to demonstrate signal loss?
Or, take programming languages - is the null pointer "working as expected", or is it a "billion dollar mistake"?
I like to use this phrase for this sort of thing: "It's working as designed but the design was flawed."
That's pretty successful at getting the business/product people off the blame-assigning game and thinking productively towards resolving the interaction and defining a solution.
> some functionality wasn't specified and there's an edge case
Edge cases are extremely rare. If they weren't they'd not be an edge case. But, we should still try and build robust systems and not allow the system to be in an unknown state. It isn't, "I have to think of all the possible cases." No, it's "These are the possible cases, nothing else is allowed."
If the cases were not specified, as you mention, then that is not completely a dev issue.
Take a look at polytope. Edges are easier to hit than corners. If you start in a random place and move in any direction, you will eventually hit an edge. But you might never hit a corner.
In case anyone was wondering the actual definitions:
An edge case is a problem or situation that occurs only at an extreme (maximum or minimum) operating parameter. For example, a stereo speaker might noticeably distort audio when played at maximum volume, even in the absence of any other extreme setting or condition. [0]
A corner case (or pathological case) involves a problem or situation that occurs only outside of normal operating parameters—specifically one that manifests itself when multiple environmental variables or conditions are simultaneously at extreme levels, even though each parameter is within the specified range for that parameter. [1]
I think the discussion here is about how we should use the term "fault".
I don't think it's the case that we all have shared definition of exactly what "fault" means, leaving us only to deduce from that where the fault for defects lies.
So I don't think the maxim you stated is helpful here.
I see the flip side, the "cost" on being made the "escape goat" because the lens is only the developer is a fault. Verse the team owning the complete system. Which is still a toxic mindset to be trying to minimize perceived punishment. Or if the environment is not collectively responsible aka blaming then that is the problem.
But the high level point of framing I think is very on point. You can only justify altering that which you measure, and if you only blame/measure/inspect the developer, you are missing stuff.
That might or might not be accurate, but I still care about utility. There are a lot of truths that don't have any utility for me. I celebrate not knowing most of those truths.
I mean do you want to be on this call? ...
A: "One point seven million dung beetles can fit in my suitcase."
B: "Let's get back to work."
A: "Utility has no bearing on truthfulness."
I do not want to be on that call. However, if following this exchange, person B goes on to write a blog post titled "One point seven million dung beetles CAN NOT fit in my suitcase" and justifies this claim by saying that knowing how many dung beetles fit in a suitcase is not a useful thing to know, then despite how right they are about the utility, their conclusion is wrong.
Sure. They should claim "it doesn't matter," not "it's false." By the end of the article I felt the author said something like "substitute more design space and dev activity for blame analysis."
Defects are the fault of programmers but programmers are pressured to deliver quickly and judged on their ability to deliver quicker than another (in some cases, not all). Given this, the blame can be pushed onto the person setting a timeline if and only if they don't give the programmer enough time to evaluate a solution and implement based off of that evaluation.
If the programmer is given an opportunity to evaluate the scope of the work and allowed to communicate the time they think it will take, then any fault in the resulting work is the fault of the programmer.
What I've seen are programmers unwilling to spend the time necessary to fully understand the work they are doing and managers/sales people/whohaveyou who push for aggressive timelines.
Well, you can't know how to avoid it until you know how you got into it. Which is one form of "blame" -- though I would agree it's more productive not to direct it at a person, and instead blame features of the situation.
You're still putting the concept of "blame" into the water.
Blaming in the context of a situation is called "an excuse", and it's still defensive.
It's pretty nuanced after this though. If folk are defensive when a problem happens ("I don't know how this happened"="it wasn't me"), you have a blame culture.
So how does one figure out what happened without someone jumping on the blame-sword (or run screaming from it)? I haven't quite figured out the answer to that question, but one thing that helps is only ask what happened if it's truly a mystery that must be solved. If it was human error, quietly move on.
It's a mistake to think when something goes wrong there must be a single person (or rule) to blame.
If a program does not behave according to spec, then presumably the programmer made a mistake, unless satisfying the spec is impossible. But if the defect made it past qa, then the qa must be imperfect also. And if your programmers and qa team are all a bunch of fuckups, maybe your hiring process is flawed also. And so on.
Defects are the fault of a lot of factors. The reason our skyscrapers rarely fall down is that there is societal pressure against them falling down. So we create laws that enforce that buildings conform to a set of standards. Some places have more prescriptive standards than others, and the result is that there is less room for creativity by the architects and engineers, but it makes the average case easy to reason about. Some places have performance based standards which come with their own downsides, but the end result is that if a building comes down in a first world country, it's a pretty big deal.
We could do this with software, but the world would look very, very different. We haven't had too many major destructive acts outside of privacy leaks, so there isn't pressure and resulting political will to regulate the industry, so economic pressures dominate what software looks like. Things like CSP headers are a distant, distant afterthought. People can't even keep their OSes, libraries, and runtimes current.
Plus software is harder to reason about than a building. Things can squeeze in or out through the smallest crack and nobody on the planet really knows whats going on in a modern cellphone or self-driving car top to bottom.
That said, I take responsibility for the security holes and bugs I create. There is no other way of being a professional software developer, even if it is hard.
It seems like it would be a lot easier to say "Bob Martin is not my cup of tea." He's not mine either. I've read a bunch of his stuff. Some good ideas, some I definitely don't agree with. I don't love his writing style.
But you said "explain why we don't like Robert Martin", which to some extent says its a problem with the man himself, not his point of view on software defects. Starting your post that way undermines your conclusion and makes it seem as if you're post-hoc rationalizing your dislike of the mine rather than talking about software methods.
I wish more people would understand (and the article alludes to it) that testing is only one of the three general methods to achieve software quality (lack of defects), the other two being:
- Abstractions - preventing bugs by having a proper language in which to express the ideas
- Assertions - making sure that certain assumptions are satisfied during the actual execution of the code, AKA defensive programming
And in particular, unit testing is IMHO a rather weak method of testing, compared to property-based or integration testing. There is this famous quote "bad programmers worry about the code, good programmers worry about the data". Similarly, I believe we should spend more time testing the assumptions that we have about data (can this value be 0?) rather than testing the logic of the code under the same and possibly wrong (or unstated) assumptions.
It's not that it's wrong. It's just really, really unproductive.
Rust language and community capitalized greatly on taking the opposite approach after years of stupid attitude like this present in systems languages community.
BTW. Uncle Bob has his opinions, good for him. They are not much more worth than anyone's else.
The responsibility for bugs and software not behaving as specified falls on the dev. team. This sort of defects is their collective 'fault' or responsibility because even if a single dev. tries to cut corners or is not very thorough there should be internal processes to catch it.
Now, "behaving as specified", well, having clarity on that can often be a big problem. The dev. team does have some responsibility there as well because they should push for clarity instead of trying to interpret on their own. I've seen it many times: spec defines something in vague terms, dev implements it the way (s)he decides to interpret/imagine it without even asking for confirmation.
I agree, but only provided that the dev team has the authority to set their own processes for ensuring quality, to refuse inadequate specifications, to refuse timelines that don't leave enough time for ensuring zero defects. Take away any of those things, and they are no longer responsible.
React was invented because anytime developers at Facebook touched code regarding unread messages/notifications, they would introduce a defect. You'd find the error and then fix it, but it was fundamentally a minefield. So, they had to invent a new way of building frontends, and then all those defects went away.
Now, I do tend to believe, that there still is a lot of low-hanging fruit with regards to programmer discipline. Many juniors (and some seniors) just straight up don't pay attention to things, don't check things, don't think things through to their obvious conclusion. You can then try to add policy to try to force them to do these things, like, e.g. you must have 100% coverage on your code, maybe that'll force them to try running their code at least once before merging into master. But then of course that must be a blanket policy for everyone, and you know how the story goes.
About the hosting site (Buttondown) - does anyone know what "Unlock full access" does?
I liked the post, so I filled out the newsletter signup form with my email. It immediately switched to show a credit card form to "Unlock full access" for $25/month. I was annoyed by this bait-and-switch, only to find that apparently it's all already accessible [1] and I received a confirmation email with a link to subscribe. So now I'm just confused.
Buttondown lets you send emails to just paying subscribers of the newsletter, kinda like substack. I was playing around with the config and didn't realize it would ask people to pay! I am very dumb.
Anyway, I disabled it. There is no paying-subscriber-only content and I don't plan to offer any in the near future. Sorry about that!
There's some archive posts[1] that are only for newsletter subscribers. Those are usually first drafts of blog posts I'm writing and don't want to be shareable yet. But that's just a newsletter subscription and totally free.
42 comments
[ 5.0 ms ] story [ 68.7 ms ] threadRight now customers are happy, but I expect some time in the next year someone is going to get a big fine because they didn't find the data they were supposed to and didn't realize it, and then they will no longer be happy.
OTOH, if the required functionality is documented and it doesn't work, that is the programmer's fault.
> A lot of the nastiest bugs come from components that are all correct in isolation but interact in a dangerous way. This is called a Feature Interaction Bug.
IMO, the original programmers of those components are not at fault, but if someone uses those components together and doesn't verify that they're working as desired, that "someone" is at fault.
Sure, stricter discipline won't always help, but sometimes it will. Sometimes, the programmer really is just being lazy.
And I say this as a programmer.
- If the credit card transaction fails, do not ship the product to the customer, and notify the customer of the failure.
- Don't let the customer order more items than are available in our inventory, or zero items, or a negative or non-integer number of items.
- A web-based system should correctly handle multiple concurrent transactions.
- If something goes wrong, show the user a meaningful error message in their selected language, not a Python stack trace.
If my excuse for my code not doing this was "it wasn't in the spec", I'd expect people to ridicule me.
Or, take programming languages - is the null pointer "working as expected", or is it a "billion dollar mistake"?
That's pretty successful at getting the business/product people off the blame-assigning game and thinking productively towards resolving the interaction and defining a solution.
Edge cases are extremely rare. If they weren't they'd not be an edge case. But, we should still try and build robust systems and not allow the system to be in an unknown state. It isn't, "I have to think of all the possible cases." No, it's "These are the possible cases, nothing else is allowed."
If the cases were not specified, as you mention, then that is not completely a dev issue.
An edge case is a problem or situation that occurs only at an extreme (maximum or minimum) operating parameter. For example, a stereo speaker might noticeably distort audio when played at maximum volume, even in the absence of any other extreme setting or condition. [0]
A corner case (or pathological case) involves a problem or situation that occurs only outside of normal operating parameters—specifically one that manifests itself when multiple environmental variables or conditions are simultaneously at extreme levels, even though each parameter is within the specified range for that parameter. [1]
[0] https://en.wikipedia.org/wiki/Edge_case
[1] https://en.wikipedia.org/wiki/Corner_case
The utility you gain by believing or disbelieving a statement has no bearing on its truthfulness.
I don't think it's the case that we all have shared definition of exactly what "fault" means, leaving us only to deduce from that where the fault for defects lies.
So I don't think the maxim you stated is helpful here.
But the high level point of framing I think is very on point. You can only justify altering that which you measure, and if you only blame/measure/inspect the developer, you are missing stuff.
I mean do you want to be on this call? ... A: "One point seven million dung beetles can fit in my suitcase." B: "Let's get back to work." A: "Utility has no bearing on truthfulness."
If the programmer is given an opportunity to evaluate the scope of the work and allowed to communicate the time they think it will take, then any fault in the resulting work is the fault of the programmer.
What I've seen are programmers unwilling to spend the time necessary to fully understand the work they are doing and managers/sales people/whohaveyou who push for aggressive timelines.
It's unhealthy, IMO. Instead, I prefer to focus forward on what to do next and how to avoid this particular issue again... and simply move on.
Blaming in the context of a situation is called "an excuse", and it's still defensive.
It's pretty nuanced after this though. If folk are defensive when a problem happens ("I don't know how this happened"="it wasn't me"), you have a blame culture.
So how does one figure out what happened without someone jumping on the blame-sword (or run screaming from it)? I haven't quite figured out the answer to that question, but one thing that helps is only ask what happened if it's truly a mystery that must be solved. If it was human error, quietly move on.
We could do this with software, but the world would look very, very different. We haven't had too many major destructive acts outside of privacy leaks, so there isn't pressure and resulting political will to regulate the industry, so economic pressures dominate what software looks like. Things like CSP headers are a distant, distant afterthought. People can't even keep their OSes, libraries, and runtimes current.
Plus software is harder to reason about than a building. Things can squeeze in or out through the smallest crack and nobody on the planet really knows whats going on in a modern cellphone or self-driving car top to bottom.
That said, I take responsibility for the security holes and bugs I create. There is no other way of being a professional software developer, even if it is hard.
But you said "explain why we don't like Robert Martin", which to some extent says its a problem with the man himself, not his point of view on software defects. Starting your post that way undermines your conclusion and makes it seem as if you're post-hoc rationalizing your dislike of the mine rather than talking about software methods.
- Abstractions - preventing bugs by having a proper language in which to express the ideas
- Assertions - making sure that certain assumptions are satisfied during the actual execution of the code, AKA defensive programming
And in particular, unit testing is IMHO a rather weak method of testing, compared to property-based or integration testing. There is this famous quote "bad programmers worry about the code, good programmers worry about the data". Similarly, I believe we should spend more time testing the assumptions that we have about data (can this value be 0?) rather than testing the logic of the code under the same and possibly wrong (or unstated) assumptions.
Was it the last person to pull a piece out?
Maybe the person before, who barely managed to keep it upright?
Or is the tower doomed to fall by design?
It's not that it's wrong. It's just really, really unproductive.
Rust language and community capitalized greatly on taking the opposite approach after years of stupid attitude like this present in systems languages community.
BTW. Uncle Bob has his opinions, good for him. They are not much more worth than anyone's else.
Now, "behaving as specified", well, having clarity on that can often be a big problem. The dev. team does have some responsibility there as well because they should push for clarity instead of trying to interpret on their own. I've seen it many times: spec defines something in vague terms, dev implements it the way (s)he decides to interpret/imagine it without even asking for confirmation.
Issues can go beyond the dev team of course, but they cannot escape their responsibility.
Now, I do tend to believe, that there still is a lot of low-hanging fruit with regards to programmer discipline. Many juniors (and some seniors) just straight up don't pay attention to things, don't check things, don't think things through to their obvious conclusion. You can then try to add policy to try to force them to do these things, like, e.g. you must have 100% coverage on your code, maybe that'll force them to try running their code at least once before merging into master. But then of course that must be a blanket policy for everyone, and you know how the story goes.
I liked the post, so I filled out the newsletter signup form with my email. It immediately switched to show a credit card form to "Unlock full access" for $25/month. I was annoyed by this bait-and-switch, only to find that apparently it's all already accessible [1] and I received a confirmation email with a link to subscribe. So now I'm just confused.
[1] https://buttondown.email/hillelwayne/archive
Anyway, I disabled it. There is no paying-subscriber-only content and I don't plan to offer any in the near future. Sorry about that!
There's some archive posts[1] that are only for newsletter subscribers. Those are usually first drafts of blog posts I'm writing and don't want to be shareable yet. But that's just a newsletter subscription and totally free.
[1] https://buttondown.email/hillelwayne/archive