I am more stunned by the tone your response. Why are you implying there's a "struggle" or "fundamental problem"? Sorry but your language is very condescending.
Sometimes it's easiest to hit Cmd+Z a few times to check out how something looked at an earlier stage of a change. And then it's easy to Redo everything back to the current state. Rewinding changes back can even jog your memory as to why you did something. It's fast, simple, and effective. What's so bad about that?
The closest imaginable thing I might do is wrapping some block of code in an if(false) {..} or #ifdef it out, when I'm refactoring and feeling unsure about my changes. But is that even the same thing that we're talking about here?
Hello from the bizarro universe of people who do this all the time and can't imagine the opposite. Do you never rewrite some code only to realize it didn't precisely capture all the edge cases of the thing you rewrote? In those cases I use the git gutter in VSCode to view what the old code looked like and compare and contrast to the new version to see what I missed. It seems to me that that's the same as what these engineers are doing, just with a bit more primitive tooling.
Can't imagine how anyone doesn't do this - unless there's just a race of God-programmers I've never encountered that write everything perfect the first go. But I don't think so. :-)
Exactly. Sometimes you start heading down a change path, and as you discover more, you realize there is a better solution. You want to save some of your concepts, but discard others. That's what this helps solve.
Git stash can be helpful. Stash your "wrong direction" change, then apply just the good chunks from your stash.
TDD (test driven development) really help in solidifying interfaces ahead of time and also to capture regressions.
Yes, git diffs help, but once I’ve passed a parameterized suite of unit tests, never needed to undo/redo. Maybe not perfect the first go, but only need one/two tries to working code. Then refactor for readability.
TDD is definitely helpful, but I don't ever use it as a metric for code fitness. At best it proves a limited number of inputs produce a limited number of expected outputs.
Consider, for example, a bug recently introduced during a refactor at my work:
The programmer optimized a conditional based on a regex by transforming it into a simple string compare. All the tests passed, code/branch coverage was good. Except that he missed that the regular expression tested case-insensitively, and our test suite didn't test upper and lower case scenarios.
This simple mistake outlines a few flaws:
- coverage was not robust, because it didn't take into consideration the branches inside the regex (which one could interpret as a form of macro expansion)
- the limited number of inputs used to test failed to capture the broader domain of possible input
- the test did not reflect a successful refactor
Obviously this is a fairly complicated example despite a pretty simple change, and several pieces had to fail in order for this change to fail. But it affirms that even basic changes to code aren't necessarily adequately covered by TDD.
Yes, the developer who forgot to test different letter cases made a mistake. Yes, regular expressions bring their own problems. But fundamentally, the result was that passing the tests did not affirm fitness of the change. Rather, it only proved a limited subset of conditions were error-free.
Effectively, tests are loaded with false negatives, so trusting them to identify problems should be done with a massive grain of salt.
If the developer had simply copy/pasted the code he changed and compared the two, then it's exceedingly more likely that he would have noticed that his code didn't capture the full breadth of conditions in the previous code.
A bug existed in the test suite, to start, and then a bug was introduced into the codebase. A human being looking at the two lines of code as it was rewritten probably would have noticed the regression. But even a code review missed it because it was a fundamentally small change among the other, more "make sure this looks good" code.
Personally, I very much value copy/paste/compare changes and never treat the test suite for anything other than "well we haven't broken anything in any exceedingly obvious ways." Maybe you're a superhuman programmer, but I'd lean more towards "you've probably added more bugs than you realize".
There’s def. ways to write really good tests as well to avoid the limited input issue - write the test in a property-based test manner, like haskell’s quickcheck, and the test can catch entire classes of bugs.
The caps vs. no caps issue would be easily caught using randomString() as the test case.
Yeah, sure it happens. Then I type “git diff”. But never have I unraveled the entire undo buffer stack only to peek at it and re-push the entire contents. That’s super weird to me.
What’s weird about it? For some - to do git diff might take more keystrokes than hitting undo 5 times.
I do all of these. I’d use undo/redo when I want to look at something I /just/ did, meaning while in the process of writing something. The git gutter when I stumble upon some change I want to see what was there before. Or Sourcetree for a full diff.
you cant do it on a first go. i write a draft of what has to happen. often i draw drafts of the solution on a4 sheets of paper(my own blackboard, the best tool i know to make software). i do research and update the drafts to a point where i type in the code. code is the last thing i do. i laught that i make coloring books and not coding :) i am quite fast and i have little problems with switching contexts. the code is much better as i "see" the whole solution. i have much better abstractions. in a recent task i have found with this way of coding, many places with bad code and made them better as they didnt fit the cleanliness of the soulution. they just stand out like the eifel tower in paris.
If there’s a specific implementation that’s especially hairy, I’ll just copy that block and comment it out before continuing. Instant reference. And if I’m rewriting more than 10 or so lines at a time of complex business logic, something is wrong.
Thought the same, copy-pasting valuable blocks as a reference into a comment worked fine. If it exceeds a one block rewrite, make small dirty commits to keep track of things.
On one hand it's nice that there are tools to support devs who get lost in their undo-redo history, on the other I feel like it's a matter of good habits to not even have this problem.
of course changing habits is hard, so maybe tooling is justified in this case. I'm just happy I don't have nother "history" type mental model to deal with.
It’s amazing how many developers don’t have a working knowledge of fundamental tools - like version control and debuggers. I honestly don’t know how you could get hired without this basic knowledge.
Undo/redo have nothing to do with this though. Undo/redo is for looking at changes you did while in the middle of writing something. Before committing.
This is from an example I did just yesterday.
Let’s say you’re moving a piece of code by cut n paste. You cut it but in the middle of changing you copied something else, maybe accidentally. Undo and cut it again. Redo and it’s now back in your clipboard.
You could copy from the diff but you’d get extra tokens to clean up.
I too am struggling to understand this use of undo.
If I'm reading this correctly it means applying a large number of undos to get to a previous editor state, followed by an equally large number of redos to then get back to where you started.
I can't recall every doing something like this.
The only time I seem to use undo is when I genuinely make some sort of editing mistake.
One reason I know I don't subconsciously do this is because I never struggle to execute an undo action.
However, on the rare occasions I need to do a redo I tend to struggle with the short cut key and usually go to the menu instead.
Are most modern developers struggling to write code without this? If so, there is a fundamental problem that has nothing to do with levels of undo.