![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
I tweeted this in one sentence, but I thought it deserved revisiting.
In many ways, this is obvious, but I didn't have it all laid down in my head together till now.
When you write a program, there's any number of ways it might change. You might have included provisional "fail when input is invalid" but need to make that be handled more gracefully. You might have hard-coded an integer size, and maybe need to expand that later. You might need to change it from a stand-alone program to a library that other programs can link against, or vice versa.
The point is that for any of these cases, there's a spectrum of choices. You can ignore the problem, you can write the program so that the expansion is easy to add on later, or you can add the expansion in right now.
Sometimes "do it right now" is right. Sometimes the 'right' way is just as easy to write as the 'wrong' way, and equally clear even to inexperienced programmers, so you should just do it always.
I've previously talked about the times when the middle path is right. If you're not sure, it's usually a good call.
And there's always a trade-off between quickness now and maintenance cost.
But today, I want to talk about ignoring the problem. If the cost of making it extensible is real, and the chance of needing it is low, ignoring the problem is likely correct. Think carefully before tying yourself to 16-bit integers, if they may one day overflow and lead to much pain. But accepting 32-bit integers is, a lot of the time, fine, and the cost of making each integer into a type which could hold larger integers is that a lot of code becomes less clear (and in many languages, slower).
Mark Dominus pointed this out in both code and documentation for "small" libraries, especially provided as part of a language's standard set of libraries. The cost of any change, even just explaining that something isn't there, it that all users of the library take *slightly* longer to understand it. And if unchecked, eventually all the "you should clarify this" or "this change is really small, why not?" mount up and turn a small, lightweight library into a heavy, general purpose library. And then maybe the cycle starts again.
He also pointed out that when he was writing small command line utilities mostly for his own use, he often added extra command line options, because when he was originally writing it, the options were clearer in his mind. But he didn't *implement* them, because he'd probably never use them.
I was thinking of the habit of writing coding exercises. My instinct always used to be to look for the "right" solution. But actually, the right solution depends not on the current state of the program, but it's future evolution. If it's ACTUALLY not likely to change, leaving it clearly imperfect may be the right solution. If it will acquire many more users, spending developer effort on making the interface cleaner may become worthwhile. If much future code will be build to interface with it, get the interface right NOW, or it will become locked in.
It's not a matter of what TO do, but equally much what NOT to do.
In many ways, this is obvious, but I didn't have it all laid down in my head together till now.
When you write a program, there's any number of ways it might change. You might have included provisional "fail when input is invalid" but need to make that be handled more gracefully. You might have hard-coded an integer size, and maybe need to expand that later. You might need to change it from a stand-alone program to a library that other programs can link against, or vice versa.
The point is that for any of these cases, there's a spectrum of choices. You can ignore the problem, you can write the program so that the expansion is easy to add on later, or you can add the expansion in right now.
Sometimes "do it right now" is right. Sometimes the 'right' way is just as easy to write as the 'wrong' way, and equally clear even to inexperienced programmers, so you should just do it always.
I've previously talked about the times when the middle path is right. If you're not sure, it's usually a good call.
And there's always a trade-off between quickness now and maintenance cost.
But today, I want to talk about ignoring the problem. If the cost of making it extensible is real, and the chance of needing it is low, ignoring the problem is likely correct. Think carefully before tying yourself to 16-bit integers, if they may one day overflow and lead to much pain. But accepting 32-bit integers is, a lot of the time, fine, and the cost of making each integer into a type which could hold larger integers is that a lot of code becomes less clear (and in many languages, slower).
Mark Dominus pointed this out in both code and documentation for "small" libraries, especially provided as part of a language's standard set of libraries. The cost of any change, even just explaining that something isn't there, it that all users of the library take *slightly* longer to understand it. And if unchecked, eventually all the "you should clarify this" or "this change is really small, why not?" mount up and turn a small, lightweight library into a heavy, general purpose library. And then maybe the cycle starts again.
He also pointed out that when he was writing small command line utilities mostly for his own use, he often added extra command line options, because when he was originally writing it, the options were clearer in his mind. But he didn't *implement* them, because he'd probably never use them.
I was thinking of the habit of writing coding exercises. My instinct always used to be to look for the "right" solution. But actually, the right solution depends not on the current state of the program, but it's future evolution. If it's ACTUALLY not likely to change, leaving it clearly imperfect may be the right solution. If it will acquire many more users, spending developer effort on making the interface cleaner may become worthwhile. If much future code will be build to interface with it, get the interface right NOW, or it will become locked in.
It's not a matter of what TO do, but equally much what NOT to do.
no subject
Date: 2018-09-23 01:51 pm (UTC)tl;dr: use the same function call in two places not just because they do the same thing now, but because you're confident they'll want to continue doing the same thing in future.
no subject
Date: 2018-09-25 03:05 pm (UTC)I remember having that argument once, yes, these two pieces of functionality HAPPEN to be the same, but they're likely to change, they shouldn't be combined. I won, although I didn't have the vocabulary at the time to explain that the important thing about removing duplication is not the duplication in code, but the duplication in having to change it in two places if it ever changes (or more to the point, failing to do so).
no subject
Date: 2018-09-23 02:42 pm (UTC)no subject
Date: 2018-09-25 03:05 pm (UTC)no subject
Date: 2018-09-23 03:42 pm (UTC)It's also important to avoid the trap of coding something in a more complex way for an imagined benefit. E.g., the naive implementation might be imperceptibly slower on your data than using some cunning algorithm or data structure: in which case, just do it simply so that the code is at least more obviously correct. If it does need fixing later, at least what's already there will be easily understood so the fix can more confidently not break existing behavior.
no subject
Date: 2018-09-25 03:09 pm (UTC)so that everything uses the appropriate variable now, and if they diverge in future it's easy to change one of them and not need to separate out which uses need to change.
And conversely, if other parts of code start to depend on them being the same, it's easy to search-and-replace and make them all the same variable, if we decide we want to give up that functionality to avoid doing extra work to keep it true.
no subject
Date: 2018-09-25 04:38 pm (UTC)My standard example of this is 'always make a new puzzle-collection game default to a rectangular rather than square grid'. If you do all your early testing on the 10×10 setting, you'll never spot the bugs in which you absentmindedly wrote the wrong one of
width
andheight
. Test on 12×10 and you catch them much faster!no subject
Date: 2018-09-25 08:53 pm (UTC)