Anyone apply deliberate practice to software development? I would be interested to hear applications.
I can imagine data structures and algorithms and coding competitions, but I've always struggled with the concept of drills for improving skills as a software developer.
Old time programmers often wrote everything down before inputting it and engaged with their code instead of with tests and the debugger. Not to say tests and the debugger don't have their place. Here's the handwritten sheets for the Algol 58 compiler that Don Knuth wrote in assembly for the Burroughs 205.
He's famous as a programmer who got things right the first time. And that's been consistent over his career. A lot of what's in there is obsolete, but notice that even in 1960 he was doing something on paper like the literate programming that he developed later.
That's just because you had to program on punch cards and run your progran over hours to see if it worked. Now its faster to just run the tests and see how it went rather than spend ages reading the code trying to spot issues.
A lot of novices think that, but it turns out that there are many problems that are easy to find by code reviews that are hard to find by testing. (And vice versa — testing is very valuable, especially with things like Hypothesis.)
I'm skeptical that the scope of things you can get working at all by mindlessly banging on tests without thinking about the problem is very large, and when you add other aspects of the software development lifecycle, your prospects seem very slender indeed. If reading your code takes "ages", how are you going to figure out how to extend it next week when you need to change what it does? How are you going to debug it when it crashes once you put in production?
I have written compilers in a somewhat test-driven fashion, and I am especially skeptical that you can purely TDD a compiler. Certainly I've never seen it done. I'm far more skeptical that you could TDD a compiler written in assembly language that achieves the kind of performance this scenario implies. (But tests, especially regression tests, are extremely valuable for writing compilers.)
The particular compiler we're talking about here was one Knuth wrote during a summer-long road trip for US$5500 (roughly US$120 000 today). So the situation was actually considerably more extreme than you're imagining — not only did he not have punch cards when he wrote the compiler, he'd never seen the computer. So he had to spend a few months debugging it after he actually finished driving to the other side of the continent where the computer was.
In 2019 you could build a compiler in your free time for free so that probably says something about modern development practicices.
I have been working on my current codebase for almost a year now and still haven't seen half of it. There is no just read over it because its massive. Tests are the only thing that makes it manageable. When I want to change something I can find the part that needs to change but I have no idea what features I don't know about that depend on that feature doing something. Tests allow me to very quickly automatically scan the code base to show what things changed.
Knuth had previously built a couple of compilers in 1957 and 1958, as you can read in the interview I linked, but it's sort of ambiguous as to whether it was "in his free time" — his job gave him and his friends access to a computer but I don't think writing compilers was declared to be part of their responsibilities. It wasn't for free, though. I think what that says about modern development practices is that we have cheap computers.
I agree that tests are very valuable for the reason you describe, as well as for other reasons. What codebase are you working on?
Every time I hear a concept I don't know I make a flashcard and quiz myself daily. I also have a collection of problems to keep myself challenged, some are interview questions and others are common patterns.
Whenever I code something moderately challenging, I come back and do it again, and maybe again until it’s good and proper. I love history rewrite in mercurial.
The code gets better, but the business outcome remains unchanged so it may look like waste. But it’s not waste - the micro skills acquired in the exercise accumulate.
It makes sense to me - you get both deliberate practice and creative practice. I don't know if it's the best, perhaps someone who is good at cooking can provide a more informed opinion.
I was also told that cooking from good ingredients is easy, the hard part is figuring out what to cook given the ingredients you have on a given day.
The code gets better, but the business outcome remains unchanged so it may look like waste.
Herein lies the issues for many of us in the corporate world. Unless I am get the rewrite done in the current testing (assuming the feature I'm working on isn't already test complete), convincing the testers to re-test is basically impossible (to say nothing of the project/product managers).
I think I've gradually developed something like deliberate practice as I go along, in that I increasingly approach greenfielding with an "idiomatic muscle memory solution" like "write approximately this style of loop", or "make approximately this data structure", and then start adjusting the outcome of that to fit the true nature of the problem, because that gets me through at least two iterations, while if I just sit at a blank page thinking, I produce code that is usually not really better and still needs adjustment. For some things architecture planning does help, but a great deal of it can be written as a pseudocode comment where i list the idioms i plan to use to address the problem. Often I do that, spot the flaws, and get in another iteration that way without having to go to the compiler. When I'm really stuck I go back to basic philosophy-of-truth reasoning and look for "a set of ideas that are coherent with each other" - one idea being the problem being solved, and the remainder being techniques to apply. Often this process reveals a need for research into unknowns, and hence another source of iterative feedback.
And so if I were to codify all the types of common idioms as if they were scales - which I haven't done - I would be able to drill those, but I suspect that there's a lot of them that are different between different domains of programming, and that's a major source of expertise.
Likewise my approach to naming has seen some iterative improvement, and it's definitely a thing I am training as I go. Names are like comments: you don't really want too many of them in your program, because they can obscure other information. Standardized variable names are great, and dense code is great when you can get it without sacrificing much readibility. I spent a little while evaluating what length of variable is sufficient to avoid collision and maintain readability while getting good density where needed:
1 letter - fine for local variables using detailed mathematics, where each variable is necessarily used in a dense, non-obvious way and it would be reasonable to have an explainer comment.
2 letters - hard to use well. As abbreviations they tend to collide too often to be recommended, and they are hard to recognize.
3 letters - a sweet spot for density and low collision rates, but still often unreadable.
4 letters - sufficient for most abbreviations and many full words.
5+ letters - at this point a majority of single words you would use will fit, and so you may as well consider this as "I am spelling out a whole word", with the next step up being multiple words and phrases, and at that point code density is the main tradeoff being made.
A common naming idiom I practice is labelling array-like values such as min/max bounds numerically, e.g. an axis-aligned box defined with x0, x1, y0, y1. For a while I used "result" as the variable for all returned values, after seeing this idiom in Pascal. Then I realized that I could use "ans" (answer) instead and get much denser code in a lot of instances. It's little things like that, which add up to a pleasant, lower-friction experience.
I can imagine data structures and algorithms and coding competitions, but I've always struggled with the concept of drills for improving skills as a software developer.
Would love to hear ideas from others.