3 ms·
>Don’t worry about breaking things. If the new structuring you’ve thought of is better, get stuck in and change the code. You will introduce bugs this way, but
by Groxx 4y ago
>Don’t worry about breaking things. If the new structuring you’ve thought of is better, get stuck in and change the code. You will introduce bugs this way, but because you’re using the editor every day to write itself, you’ll find them quickly (in fact, almost by definition, you’ll tend to find them more quickly the more important they are) and your policy of always working on whatever needs fixing right now will make sure they’re fixed promptly.
Solid advice in the early-ish days of any project.
It's not like your code was perfect before the refactor - you just hadn't found some of the bugs yet. You will have bugs after too, but now you have more experience and will probably find most of them quickly. Don't get too attached to the maybe-bugs you currently have.
Also:
>When you’ve got a while (true) loop that searches a tree, it’s gonna throw you an infinite loop every now and then.
A surprisingly effective strategy I've found for development purposes: never have infinite loops.
Nothing is actually infinite. You think X might loop 100x? Put a limit of 10,000 ("absurd and impossible"), and crash your debug builds when it happens. With enough of this, you'll get hits, and they're extremely effective at catching pathological cases that you didn't realize could happen. "It's a bit slow" is often too hard to notice.
- fbdab103 4y ago>You think X might loop 100x? Put a limit of 10,000 ("absurd and impossible")... This feels like a lot of line noise for those rare-until-you-hit-them bugs. Just capping a loop with a fixed iteration size deserves a comment as to the rationale. Do you do it everywhere? Just IO? The idea has merit, but in practice, I am not sure where I would implement it. Plus, there would always be the temptation to remove that do-nothing code.
- tmtvl 4y ago> This feels like a lot of line noise for those rare-until-you-hit-them bugs. Yeah, but if doing so can eliminate an entire class of bugs... ...you know something? I think I'm starting to see where those Rust guys are coming from.
- oneeyedpigeon 4y ago> Plus, there would always be the temptation to remove that do-nothing code. It's not 'do-nothing' code, though. Isn't the temptation just the same as the temptation to remove 'do-nothing' exception handling or 'do-nothing' unit tests?
- vidarh 4y agoIf it's much "do nothing" code to remove, perhaps it'd be better to move to a more expressive language. E.g. for Ruby at least, block arguments means it's trivial to do this: def at_most(num) num.times { yield } raise "Should never get here" # or "binding.irb"/"binding.pry" to drop you into a repl, perhaps contingent on a command line flag. end and do: at_most(5) do .. logic end If the number isn't obviously absurdly high, I agree it'd be worth documenting why the precise number was chosen, but then it might be worth documenting why as an indication of why you thought an infinite loop was appropriate anyway.
- Groxx 4y agoYep. In essentially every language there's a reasonably efficient way to do it in one or two lines at most. It's really not much noise, and it serves as (mechanically checked!) documentation of the expected size of a loop. In some ways that's an improvement, since it's just a loopy assert.
- Groxx 4y agoAs a counter-argument: why are we generally ok with allowing things to be infinite when they're expected to be finite? Precise bounds are obviously best, but sometimes it's not efficiently calculable or not worth the effort - don't choose the literal worst possible value there (infinity), pick something that'll stop itself when it goes insane. And no, it's a tool. Use it where it makes sense.
- ericmcer 4y agoIn personal project’s refactors are dangerous. Not because of bugs, but because they tax your motivation more heavily than other tasks. Personal projects don’t fail until you decide they do, so maintaining your enthusiasm is key.
- madeofpalk 4y agoDepends what the refactor is. I think personal/side projects are a great place to give into the attractive nuisances we encounter in programming.
- jakelazaroff 4y agoI guess it depends on what motivates you! I find refactors a zen-like experience. Figuring out the way to make everything fit together elegantly is a really satisfying task for me. Also, "personal projects don't fail until you decide they do" is a great insight :)
- IlliOnato 4y agoI actually love refactoring. I really like to keep my code "elegant" and so I tend to constantly refactor it (which also makes a typical refactoring step small, and thus not a chore). At work it's sometimes a hindrance: I tend to overdo the clean-up stuff; for me it's hard to resist a temptation to have even simple throw-away code clean and maintainable, and it is difficult for me to cut corners when working on something urgent. Of course sometimes throw-away code is not thrown away and is maintained for years and years, and cutting corners accumulates code debt (which you later on pay with interest), but overall I tend to be slow with small projects, although reasonably fast (if you include debugging time) with large ones. But when working on a personal project, and can tidy the code up to my heart content! It actually helps to keep up my enthusiasm, not the other way around. I guess different people have different preferences and predilections :-)
- vasco 4y agoMany things are infinite from the perspective of the program, for some classes of programs. An IoT device generally is always doing the same loop and responding to interrupt signals for anything else. Game loops work in a similar way. I guess you can have a limit on the loop, have it break out to make sure you're still listening to interrupts do some logging and go back to looping, but in an environment where interrupt signals are native, an infinite loop can and is fine. Glad I got my bikeshedding allowance out for the day.
- vidarh 4y agoMost of the things that are infinite from the perspective of the program tend to be event loops of different forms, though, where that perspective is "unless the right event arrives". There are rarely many of them in the same application, and they tend to be high level. If you find yourself writing an infinite event loop at a lower level, odds are something is off (e.g. an infinite loop waiting for a reply packet over the network could do with a timeout) I agree it's usually fine to use infinite loops for high level event loops, as it's usually easy to test that you can break out of them by triggering the right event (though sometimes it might be worth thinking about pathological cases if you can do something about them, such as whether something is wrong if there's no interrupt for a long enough period; and you can also prevent writing an infinite loop even for event loops by making the break condition whether a suitable "exit"/"restart" event has been received - very few things should be without one or the other of those two even if intended to run forever in normal circumstances). The advice, I think, is more important for algorithms working on input data that may well lock up the application if the logic is wrong. E.g. a sort should eventually complete; if it takes more passes through an outer loop than the theoretical worst case for the algorithm, something is wrong (be it your implementation or your understanding of the worst case...). A habit of estimating worst cases and treating them as invariants to check and make assertions about can be useful.
- IlliOnato 4y agoWell, many document-processing tasks also require something like an infinite loop. For example, parsing of an XML document naturally resembles an event loop, with each token being an equivalent of an event. Although usually there is a natural end to this loop, so you have not "while(1)" but "while (not EOF)" or something, which helps avoid some bugs, but I believe a compiler turns it into an infinite loop anyway, and breaks it on these conditions.
- DannyBee 4y agoFor GCC I just added a debug counter facility. You could tell it what value a given debug counter should stop on from the command line. This also made automatic minimization of bugs much easier and faster. One thing that other (applicable). projects dont seem to do that llvm and gcc were quite good at was using automatic tooling to minimize test cases for bugs. Find a large test case causing an issue. Run automatic minimizer and leave it alone for a bit, come back to minimal test case that reproduces your issue