2 ms·
The only thing I design to extend is the data. That is, I get suspicious now when I take an existing method and try to parametrize it, because what I'm doing if
by chipsy 11y ago
The only thing I design to extend is the data. That is, I get suspicious now when I take an existing method and try to parametrize it, because what I'm doing if I do that is building up a richer all-purpose code edifice - and that is a Bad Idea when we know that code is disposable and data isn't. By default, all programmers start by planning and testing code towards one use-case, and then they add the other cases one by one. Code that is arbitrarily made "generic" or "extensible" imposes a testing regime that programmers do not arrive at within the natural edit-test-debug cycle, but instead have to consider separately.
With data this isn't really the case. Achieving both YAGNI and DRY at the same time is _easy_ with data. Data tends to become more generic and more fungible as it becomes more crude and simplistic, and less so as a defined schema builds up - for example, a Point class with "x" and "y" fields, vs. a numeric array of 2 or more entries. With the former, you can end up with your Point not being the same type as someone else's completely equivalent Point. With the latter, agreement mostly rests on which kind of numeric type you're using(which can be enforced by the language), and the ability to operate on many points as well as just one point is straightforward to support yet not strictly required. And so if the goal is reuse, you design downwards; if it's type safety, you design upwards. Achievement of both imposes an enormous code burden as you try to enumerate all the types and provide various translation layers and fallbacks - in turn creating more code paths, more situations where you can throw an exception or return a null value, etc. That can add up to a lot of lines of code, and a performance tax, and more failure modes, in exchange for sometimes eliminating some runtime errors.
I think we naturally tend towards overdefining our data, though, because it looks more "correct" to tightly define each block of code so that it says "point.x" instead of something like "x = point[index * 2]".
- moron4hire 11y agoYou say so much that I agree with, except your core thesis. YAGNI must be applied with DRY, yes. Code is disposable (maybe-leaning-to-yes), data is not, yes. But not properly defining your data is a huge mistake. Torvalds has been noted to say: "git actually has a simple design, with stable and reasonably well-documented data structures. In fact, I'm a huge proponent of designing your code around the data, rather than the other way around, and I think it's one of the reasons git has been fairly successful […] I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important." Well-documented data structures. Schema is data structure documentation. Your Point example is good. A Point is not a Size, but an array of numbers could be interpreted as either, or as a Latitude/Longitude pair, or as anything else. How do we know, looking at an array of numbers, what it is meant to be? But if we knew it was a Point (because the type expressed it as such), then we'd also get to assume we can do point-like things to it. Of course, YAGNI would suggest not creating a Point and Size classes until you need to differentiate between multiple types of ordered pairs. That's fine. If your system only ever handles point data, then it only ever handles point data and we state that up front, allowing us to assuming "oh, an array of numbers? Must be a point". But being able to handle different types of data is a feature, and if you prove you need it, then you need it. Write that Point and write that Size class.