Ask HN: How to make fewer mistakes?
67 points| bmomb | 8 years ago
My code is clean and follow the guide-lines, but i feel that i make to much mistakes, there is always a corner case that i don't think about or some test that is trivial to the QA team but i didn't do, and my code end up being broke.
This 'behavior' also happen with maths, i know the theorems and the content that i need to know to solve questions but i always miss calculate something and end up with the wrong result, one clear example of this was my last "calculus 2" exam, all the steps were right, but all the results were wrong;
TL, DR: I know my stack and the tools but i make silly mistakes that end up breaking my code; how can one get better at this?
0xfaded|8 years ago
Since making that final walk around a habit, nothing. I started doing final everythings, one last check of the radios, one last check of the instruments, etc, etc. It's amazing how many times you pick up on something so obvious that is wrong.
The key is to change your mindset before doing the final check. Acknowledge you are done, go get a tea, and go through the motions.
Somethings I do related to code.
Use `git add -p` to go through changes one by one. Remove stuff that shouldn't be there.
Do a self code review before submitting the code for others to review.
Obvious one, but always run tests.
For what it's worth, I find the same things with maths. I've been working on SLAM systems, so lots of differential geometry and matrix calculus. I'll spend a morning with my notebook trying to figure something out, go to lunch, then come back and go over what I've done.
In university I was horrible at maths exams, because like you I knew everything but made so many stupid mistakes. I think the problem was that during an exam, I could never dial down my mindset from OMG I need to triple check everything right now!
Haul4ss|8 years ago
If you learn to fly, you will learn to follow checklists. Many, many checklists.
A good instructor will tell you that while you do need to follow the checklists, it never hurts to do one last check. One final walk-around before getting in the cockpit, one last check of radios/transponder, one final scan across your engine instruments before takeoff, etc.
If you do these things enough times, you'll find the occasional instance where you forgot something obvious, or accidentally skipped a checklist item.
To OP, as you miss each edge case or QA thing you should have found, you add it to your checklist. Before you check in a new piece of functionality, run it through your checklist. Also, do one last walk-around of the code. How will the user interact with it, what are the API dependencies, etc.
You're not second-guessing yourself, but you always verify even your own work before calling it done. Over time, you'll learn to follow both the best practices, and your own intuition built from years of learning from your mistakes.
Starwatcher2001|8 years ago
Changing your mindset is another good piece of advice. When you're done coding, stop thinking like a programmer and think like a user - a bored, fed-up, malicious user who's going to have a go at breaking your software. He's not going to do the things you expect him to, he's going to try to enter ridiculous data, in the wrong order, press stuff that he shouldn't, etc. Be curious. "I wonder what happens if I do this..."
Offer to buy a beer for every bug your colleague finds in your code. Don't want to pay out? Make it hard for him to find any, and any that he does find will be well worth the cost.
I've been coding for nearly 40 years and still manage to write some crap on occasion!
sevensor|8 years ago
Strom|8 years ago
appleiigs|8 years ago
As you mention, mindset and habits are key. My mindset for tasks theses days is "focus, and dont be lazy". That allows me to not half ass the checks/checklist.
bmomb|8 years ago
PS. `git add -p` is amazing, thank you for this too.
_nalply|8 years ago
itamarst|8 years ago
His advice for learning from mistakes is basically to look for the cues or patterns you missed that caused your mistake. That way you find the hints that would've helped you catch it in time.
I've been doing this myself, and writing up results in a weekly email (https://codewithoutrules.com/softwareclown/) and it really improves how much I learn from mistakes. As in, I go from "well that was stupid of me" to "ohhhhh, I should've noticed that."
Example: early in career I submitted code with bad variable names (foo, bar) to customer who requested I write a library. She pointed that out as bad practice, I was super embarrassed over my bad code. My takeaway in one of initial emails was obvious one of "write readable code."
But later I revisited using the cues/patterns approach, and realized deeper mistake was not listening to what she wanted—in this case, source code. If deliverable wasn't source code then variable names wouldn't have mattered and it would've been fine.
hutzlibu|8 years ago
I think it doesn't matter if you give the source code or not, but you should allways "write readable code".
Sure there is a difference between throw away code and a library, but also for youself it is a good habit to understand later, what you have been doing. And so much more for other people ...
psyc|8 years ago
bmomb|8 years ago
In the beginning i learn to ignore it because the mentality of "moving fast and break things", and really worked because my tasks were fairly simple and it would have fewer critical points, but thinking now that is harmful when doing more complex things, i should slow down and trust in my 'feeling' more.
dundercoder|8 years ago
Making mistakes properly is progress. Make the exact same mistake multiple times? You might need to write them down as you find them.
At one startup we had a motto: Measure once, cut twice. Being fix the bug, then write code to mitigate that risk in the future. Fix it twice.
Now in critical systems like traffic lights and the like, mistakes kill people, so you test the everloving crap out of everything, then another person does, and another.
If your code doesn’t kill people, ease up on yourself a bit.
ams6110|8 years ago
bmomb|8 years ago
I can learn from my mistakes, but some of them are really trivial, i can find better examples in math, like:
In a calculus question, I need to find the volume inside some functions, this type of question is not new, i know how to solve, but somewhere deep in the calculation i change 'integral of sin' to be `cos` instead of `-cos` making the everything more hard to solve and obviously getting the wrong answer.
This kind of thing is currently my biggest problem and i cant find a real way to improve.
segmondy|8 years ago
Part of making fewer mistakes is going for simplicity over complexity.
in9|8 years ago
So what seems to help me is:
- slow down, more time equals more time to think
- write specifications as neatly as possible and review it with the requester (even if it is only your self, review it many times). The more bullet points (checks, and tasks) the better. (Come to think of it, the number of checkable bullet points in an specification may be a good proxy for how much you are thinking on corner cases and etc)
- write extensible tests, read some nice TDD books (I haven't done that myself, but having some canon way of thinking about tests, together with reviewing tests can help you)
- incorporate the expectation that there will be mistakes onto yourself, onto the client and into your planning
- practice practice practice
bmomb|8 years ago
Do you have any recommendations on TDD books?
sclangdon|8 years ago
I've noticed that some developers rely way too much on tools to do their job and they just don't spend a lot of time thinking. Think about the big picture as well as the details. Think about how your code interacts with its surroundings. Think about what you know to be true at any given line (and write an assertion), and whether or not that thing may ever change. Think about what every line means in isolation, and what it means to the surrounding code. Think about the names you give things.
Maybe it's because of my age, but when I started there were barely any tools to help. Even syntax highlighting wasn't readily available. Debuggers were pretty crap, or didn't exist at all. The best thing we could do to prevent bugs was to really question every bit of code. "What does this line do if x is not what we think it is?", "Can x ever not be what we think it is?", etc
I feel like a lot of developers today don't think enough about what they're doing, and care too much about their stack and their tools. Instead of moving fast and breaking things, perhaps instead try moving carefully, and try not to break things.
"The most effective debugging tool is still careful thought, coupled with judiciously placed print statements" - Brian Kernighan (replace print with assert for my interpretation!)
brent_noorda|8 years ago
Most language offer a way to run in DEBUG mode, or something like it, use that, along with assert (which only runs in DEBUG mode) to make sure all your assumptions are correct. You're positive a value can never be NULL? assert that! You're absolutely positive an integer addition will not overflow? assert that! You're absolutely sure that your super-fast table returns the same results as the complete algorithm? Run them both in debug mode and assert that!
henrik_w|8 years ago
More info here: https://henrikwarne.com/2016/04/28/learning-from-your-bugs/
and here: https://henrikwarne.com/2016/06/16/18-lessons-from-13-years-...
noxToken|8 years ago
If you go by average time spent on a given task, I'm one of the slowest developers in my shop. I'm pretty sure I'm in the bottom 25% for speed. What I lack in speed I make up for in correctness and readability. Rarely is anything bounced back to me for a bug, defect, or missing edge case. If I start getting sloppy, I have no leverage, because now I'm one of the slowest developers who make just as many (or more) mistakes than everyone else.
Start by fully understanding the problem. If you ever have any doubts about anything, stop and ask. As you're breaking down the problem, create a set of abstract notes. You could write it in paragraph form, a set of steps with checkboxes, sticky notes[0] - whatever helps you get the problem from plain business requirements to something a bit more concrete and actionable.
Then start writing code. Try to stick finishing in the order of your notes. It doesn't have to be pretty, but make sure it works according to spec. Constantly execute your code to test, because it's possible to add a conditional that renders one of your passing scenarios as a failure. If you can get a specific lists of tests that will be ran, use that as your framework. Your number one priority would be ensuring that the entire list passes deterministically.
After you've got working code, clean it up. Make sure your variables and functions have good names. getValue() is a very generic name, while getTotalCost() indicates much more without looking at another line of code. Use a linter, formatter, and static analysis tools to search for common bugs. If there are any glaring optimizations, make them.
[0]: https://news.ycombinator.com/item?id=16212374
mattgreenrocks|8 years ago
Most of my code reading is in my editor. I'm in a certain mode of thought there, mostly detail-oriented as I piece things together. When committing code, I use git diff to inspect everything going into the commit, making sure I agree with it. I do this in a terminal (I don't use a terminal-based editor) with a different color scheme. Lastly, I'll re-read it again (especially if I'm not 100% sure) on a web-based git server (Github/Gitlab) as a way to get some closure on the day/commit.
This works well for me because:
* Most committed code is read at least twice
* Each reading context is different, at least visually
* Time passes between each reading, which gives your brain space to surface doubts
Doubt is your friend here. With enough experience, you'll know when you've nailed something.
matt_s|8 years ago
I would suggest checking your work might help. Put it down and do something else, then come back to it and pretend you are doing a code review of it.
When you state final quality of code, your work doesn't need to be perfect before another human looks at it. That is why we work in teams. I'm not saying be sloppy on purpose but if QA is coming up with trivial test additions, just add them. People should have feedback on code reviews.
If the mistakes you are making are trivial, we've all made them and all still make them. Syntax errors and mis-typed variables trip me up all the time. Those are usually easy to find because the code doesn't work. If the mistakes are more design related then get some software design reviews from a senior dev before going deep into having code written.
Annatar|8 years ago
2. run the code and purposely try to break it with all the edge cases you can think of;
3. run the code as if you are an end user who knows absolutely nothing about it;
4. have your colleagues try to break your code before you commit it.
https://wiki.illumos.org/display/illumos/On+the+Quality+Deat...
https://davepacheco.github.io/se411/release/fcs-quality-all-...
AstralStorm|8 years ago
2. And 3. Does not help when you miss the edge cases.
4. It's called code review.
And my own: run the logic through an automated theorem prover, for example Isabelle. They will instantly tell you if any invariant causes a contradiction (mistake, corner case) or it cannot deduce behaviour given prerequisites. (missing specification, probably corner case)
Since it makes you write and read the code twice and forces you to think of it as necessary conditions and invariants, that alone might help.
stinos|8 years ago
For me, for basically every line of code written, and also for bigger pieces of code, I'll try to think of all possible corner cases which might prevent the the pre- and postconditions from being established. And make as few assumptions as possible.
It takes a while to develop that mindset, and it takes even longer to learn to write code which doesn't have problems in the first place. And I wouldn't even consider myself being really good at it.
Note that once you get a hold of it, you'll automatically start to write code which adheres to most 'best practices' like DRY, proper naming, short functions, you name it. These go hand in hand with fewer mistakes. In the beginning this will cost you time. But that is not wasted, as it'll teach you to do the right thing in the first place, and once you do that you'll have to spend less time debugging and the code will be clearer in the first place so less time lost reading and understanding it.
As a simple example take code from a person who does lack that mindset (despite having programmed decades more than I have) which I reviewed just today: if you just write bar[1].Foo() and don't have a strategy to either make sure bar does have enough elements, or else a strategy to properly deal with the error (as in, not just segfault, unless it has been established that is the way to deal with it) which will occur if you try to access that element anyway, the code is not good. So when I write even the simplest thing like bar[1].Foo() I automatically will wonder about that, ask myself 'can this occur'? If it's supposed to never happen I'll likely insert an assert. If it can be totally acceptable, wrap it in an if statement. If it's a user error, complain. Etc. But never ever just assume it'll be ok. And to do that, realize you must always reflect about everything you can think of.
blanche_|8 years ago
You mind find this interesting: https://routley.io/tech/2017/11/23/logbook.html
muzani|8 years ago
Pick a very small but important part to improve in. The most effective training I did for myself was to practice pressing shift, semicolons, and brackets accurately.
Wherever you have the most trouble with, narrow it down as small as possible. Practice writing tests. Practice your algorithms, or working with a very specific part of the stack. Things like Rx can be quite a lot to grasp.
It's not enough to know something once. You should be achieving fluency in all the little parts.
tomerbd|8 years ago
1. The more time you put on a task, more mistakes you find and fix.
2. When finished with a task, come back to it after some time you will find more mistakes with fresh thoughts.
3. This means you have other good qualities like imagination and creativity, which is a side effect of mistakes (internal mind random mistakes).
4. Find mistakes ancestors - patterns - write them down in a short list, my top 10 mistakes patterns. When finished with a task review that you didn't repeat those 10 mistake patterns.
fredsted|8 years ago
Also, I'm a big fan of and practice "defensive" programming. There's lots of articles written about that, but in short it kind of changes your mindset a little and steers it towards more robust code. What can fail? How can I limit and validate parameters most efficiently?
AstralStorm|8 years ago
Mixing the three is bad.
snarf21|8 years ago
jjp|8 years ago
AstralStorm|8 years ago
ThomPete|8 years ago
thisisit|8 years ago
markfer|8 years ago
It is important that you figure out why the mistake was made, and how to not recreate it in the future.
I tell my employees that creating 1000 individual mistakes is fine, so long as none of them are repeats.
reacweb|8 years ago
candu|8 years ago
Double-checking. To take an example: say you take the integral of `sin x` and get `cos x` instead of `-cos x`. You can double-check this by differentiating `cos x`, which will give you `-sin x`. Not only does this tell you you're slightly off, it suggests exactly how to fix the problem. Many problems can be double-checked: you get a result, then run the steps backwards to see if you can get back to the starting point.
Incremental testing. I've recently been learning basic robotics / electronics as part of a side job. One thing I found enormously helpful was to build up progressively larger circuits on a breadboard - I'd even test the breadboard itself (e.g. do the two halves of each row connect? Well, there's an easy way to find out...) to make sure I had the correct mental model of how my components were working.
Isolation. When confronted with a problem, try to find the minimal example that reproduces the problem. I do this a lot when building out interfaces / applications, where I need to add some non-trivial piece into a larger application. I'll build a test page that contains just that piece, get it working, then figure out how to integrate that working example with the application as a whole. As a side benefit, I've broken my seemingly hard problem down into two smaller, easier problems.
Informed questioning. You will inevitably hit something beyond your level of expertise. Half of experience is learning when to identify that this has happened, and knowing how to coherently explain your mental model of the problem. Your mental model doesn't have to be right, but it should exist, and you should know how to describe it to someone who can pick it apart and improve it. Part of this is a sort of "egolessness" mindset: having the humility to admit you don't understand everything, and to seek expert help once you've tried what you can. (We're all bad at this last step, myself included; developing this mindset is itself a learning process.)
Finally, and IMHO most importantly: failure normalization. Failure is normal when learning a new tool, framework, language, skillset, etc. In this situation, one of the first things I do is to explore what failure looks like. I'll deliberately introduce syntax / logic errors: what sorts of errors does the compiler / toolchain spit out? How can I use the debugger to step into the problem, and what does that process look like? Do my tools give me any kind of visual / tactile / etc. feedback on my error? (If not: where can I find better tools?)
PhilipA|8 years ago
tw1010|8 years ago
comstock|8 years ago
If it comes to it write two solutions (possibly one optimized, one not) that solve the same problem in different ways, and confirm the results against each other.
Don’t count on just picking good edge cases.
Protostome|8 years ago
After finishing writing a functional piece of code, spend half an hour or so (maybe more?) trying to break it.
kleer001|8 years ago
segmondy|8 years ago
bookofjoe|8 years ago
chvid|8 years ago
cnkk|8 years ago
Tomte|8 years ago
kldfjgh|8 years ago
corobo|8 years ago
How to make fewer mistakes.
luxpsycho|8 years ago
kapauldo|8 years ago
[deleted]
charleshmorse|8 years ago
[deleted]