![[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)
From:no subject
Date: 2018-09-23 02:42 pm (UTC)(no subject)
From: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)
From:(no subject)
From:(no subject)
From: