130

Say I have a entity that has "type" attribute. There could be 20+ possible types.

Now I'm asked to implement something that would allow changing the type from A->B, which is the only use case.

So should I implement something that allows arbitrary changes of type as long as they are valid types? Or should I ONLY allow it to change from A->B per the requirement and reject any other type change such as B->A or A->C?

I can see the pros and cons from both sides, where a generic solution would mean less work in case a similar requirement comes up in the future, but it would also mean more chance of going wrong (though we 100% control the caller at this point).
A specific solution is less error prone, but requires more work in the future if a similar requirement arises.

I keep hearing that a good developer should try to anticipate the change and design the system so that it's easy to extend in the future, which sounds like a generic solution is the way to go?

Edit:

Adding more details to my not-so-specific example: The "generic" solution in this case requires less work than the "specific" solution, as the specific solution requires validation on the old type as well as new type, while the generic solution only need to validate the new type.

Billal Begueradj
  • 1,305
  • 5
  • 16
  • 31
LoveProgramming
  • 1,159
  • 2
  • 8
  • 8
  • 99
    That's a interesting question. I've had a discussion about something like that with my gramps (a very, _very_ old timer programmer) and his response was something in the lines of "the most generic thing that solves your specific problem", which boils down to a fancy "It depends". Blanket statements in Software Development rarely work - things should be taken always in a case-by-case basis. – T. Sar Nov 30 '17 at 18:38
  • 3
    @T.Sar add this as an answer and I'll upvote :-) – Christophe Nov 30 '17 at 18:43
  • 4
    What is a “generic solution” in your view? Also, please clarify what you mean by the “only use case”. Do you mean that only a transition from A->B is allowed or only that transition is specified, and it would not be an error condition to transition from any other state to any other state. As the developer you need to ask the author of the use case to clarify. If no other transition is allowed and your code allows it regardless of who controls the caller, your code has failed to meet the requirements. – RibaldEddie Nov 30 '17 at 19:34
  • 1
    Sometimes the generic solution is exactly the same code as the specific solution, only the types are different. In this case it is hard to argue either way. If the generic solution is at all different, it is better to use the specific until you need the generic. Don't try to predict the future, unless you already know it. – Frank Hileman Nov 30 '17 at 19:59
  • Relevant article from Code Simplicity: [Two is Too Many](http://www.codesimplicity.com/post/two-is-too-many/). – Wildcard Dec 01 '17 at 01:32
  • 5
    The principle that if X is a good idea, then only X all the time must be optimal, seems to have a particular appeal to developers (or at least a vocal group among them), but consider this: programming rules-of-thumb are like proverbs, in that you can often find a pair with opposite implications. This is a sign that you should use your judgement (as advocated by the answers here), and beware of the dogma. – sdenham Dec 01 '17 at 02:42
  • 1
    The body of this question actually asks: Should I put more programming work to contrain users of my app? Or leave them more freedom? – kubanczyk Dec 01 '17 at 03:37
  • @sdenham Yes, that is a very present problem in the field. Even old-time gurus end up deploying blanket statements as holy solutions more often than they should. – T. Sar Dec 01 '17 at 11:01
  • 2
    Premature Generalization can cause as much heartburn as Premature Optimization - you wind up writing a lot of code that may never get used. Solved for the specific problem first, then generalize as the need arises. – John Bode Dec 01 '17 at 15:34
  • "I keep hearing that a good developer should try to anticipate the change and design the system so that it's easy to extend in the future, which sounds like a generic solution is the way to go?" - Not quite. A "good" developer should try and anticipate what might change *and what probably won't*. Then generalize only those parts which he expects to be worth it while keeping the other parts specific for at least two reasons: a) specific = easier/faster to implement and b) specific = (often) easier to understand/maintain. – JimmyB Dec 01 '17 at 16:03
  • 1
    I think you should solve the problem that's at the most natural level of generality from an implementation POV (you can then restrict it at the business layer to just the case required). Sometimes that's exactly what is required and sometimes it's more general. For example, if you are required to develop a linked list of Customers, you might as well develop a generic linked list if nothing about a Customer is relevant to the list. But you shouldn't develop an entire collections framework. – Reinstate Monica Dec 01 '17 at 16:22
  • Should a question get as many answers as possible or as few answers as possible? –  Dec 01 '17 at 16:56
  • Should the answer to this question be generic? Or should it be specific? – kirkpatt Dec 01 '17 at 17:54
  • i would have said generic, but reading answers here has made me think. however, i have never regretted going generic. one time i added a new requirement just by using a function i already wrote and passing a different parameter value. i was terribly pleased with myself :p – inarilo Dec 02 '17 at 17:50
  • 1
    "Everything should be made as simple as possible, but no simpler." (usually attributed to Einstein, but he probably never actually said it). – Dawood ibn Kareem Dec 02 '17 at 22:27
  • 1
    I winder if this question deserves an answer that is as generic as possible or one that is as specific as possible ... – Hagen von Eitzen Dec 02 '17 at 22:41
  • Possible duplicate of [When to prefer a generalized solution over solving specific cases](https://softwareengineering.stackexchange.com/questions/161526/when-to-prefer-a-generalized-solution-over-solving-specific-cases). Feels the same, except mine has XKCD images. – AncientSwordRage Dec 04 '17 at 09:08
  • I don't understand the question; what on earth does "as generic *as possible*" mean? Is there some *maximum level of generality that can be obtained*? What is this maximum level of generality? – Eric Lippert Dec 04 '17 at 18:19
  • 2
    The most general-purpose thing is a programming language. – Casey Dec 04 '17 at 18:31
  • 2
    There's another point to add to the good answers here - your question seems to be one that could be cleared up by requirements. While the generic solution could be quick and useful, it may also allow behavior that is not intended. Before the exercise of deciding between a generic or specific solution, one should first determine whether there are hidden requirements that preclude the generic. – iheanyi Dec 04 '17 at 20:33
  • As said by many, there is no absolute answer, it depends, don't look for an absolute rule. I recommend to ask yourself questions along the lines of "Is the estimated likelihood that a generic solution will later prove useful worth the extra work needed to make it generic ?" (in my 30+ years of observations the answer is "yes" more often than most people like to admit). Or, more specifically to your case, "Are _arbitrary changes of type_ sufficiently likely to be needed in future ?". Things like these. It's all about figuring out the cost / benefit ratio (which is spectacularly difficult). – SantiBailors Dec 05 '17 at 12:17

14 Answers14

302

My rule of thumb:

  1. the first time you encounter the problem, only solve the specific problem (this is the YAGNI principle)
  2. the second time you run into the same problem, consider generalizing the first case, if it's not a lot of work
  3. once you have three specific cases where you'd be able to use the generalized version, then you should start really planning the generalized version -- by now, you should understand the problem well enough to actually be able to generalize it.

Of course, this is a guideline and not a hard-and-fast rule: the real answer is to use your best judgement, on a case by case basis.

Daniel Pryden
  • 3,268
  • 1
  • 21
  • 21
  • 1
    Can you explain what YAGNI is? – Bernhard Dec 01 '17 at 05:09
  • 8
    @Bernhard [You Aren't Gonna Need It](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it). A very useful software design principle. – Angew is no longer proud of SO Dec 01 '17 at 08:15
  • 4
    "At `thing1, thing2`, think about maybe using an array. At `thing1, thing2, thing3`, almost certainly use `thing[]` array instead." is a similar rule of thumb for deciding between multiple variables or a single array. – Joker_vD Dec 01 '17 at 23:19
  • 16
    Another way to state rule #1: "You can't generalize one thing." – Wayne Conrad Dec 04 '17 at 20:52
  • @WayneConrad et al. But you can (actually should) at least give a shot at estimating how likely it is that you will run into the same problem again and how often, and decide based on that, even the first time. Also, how can it not matter how much time and work is wasted by first writing the specific solution and then throwing it out and rewriting it to make it generic ? That's definitely another parameter that deserves much consideration, especially the first time. As it stands, rule 1 is bad and mostly an excuse not to think ahead and just start "coding". – SantiBailors Dec 05 '17 at 12:33
  • 3
    @SantiBailors: As Doc Brown says in his answer, we often vastly overestimate the amount of wasted effort we might expend by building the first one to throw away. In *The Mythical Man-Month*, Fred Brooks says: "Plan to throw one away -- you will, anyhow." That said: if you immediately encounter more than one use for something -- for example, by being delivered a set of requirements where you will clearly need to solve the same problem more than once -- then you already have more than one case to generalize from, and that's totally fine and not in conflict with my answer. – Daniel Pryden Dec 05 '17 at 13:12
  • @DanielPryden I was not thinking of a case where I immediately encounter more than one use for something, and not suggesting that there is anything in conflict with your answer. I just meant that it's wrong to blindly go for the specific solution just because it's the first time one encounters that problem. I maintain that even in that case one should wonder if that one case is likely going to be the first one of many, and this evaluation, together with the other one I mentioned, should be made before deciding, rather than having an absolute rule independent from the context. – SantiBailors Dec 05 '17 at 14:01
98

A specific solution [...] requires more work in the future if similar requirement is needed

I have heard this argument several dozen times, and - to my experience - it regularly turns out to be a fallacy. If you generalize now or later, when the second similar requirement arises, the work will almost be the same in total. So there is absolutely no point to invest additional effort in generalizing, when you don't know this effort will ever pay off.

(Obviously this does not apply when a more general solution is less complicated and requires less effort than a specific one, but to my experience, these are rare cases. Such a kind of scenario was edited afterwards into the question, and it is not the one my answer is about).

When the "second similar case" shows up, then it is time to start thinking about generalizing. It will be much easier to generalize correctly then, because this second requirement gives you a scenario where you can validate if you made the right things generic. When trying to generalize just for one case, you are shooting in the dark. Chances are high you overgeneralize certain things which don't need to be generalized, and miss other parts that should. And when then a second case arises and you realize you generalized the wrong things, then you have much more work to do to fix this.

So I recommend to delay any temptation for doing things "just in case". This approach will only lead to more work and maintenance efforts when you missed the occasion for generalizing three, four or more times and then have a pile of similar-looking (so duplicate) code to maintain.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • Completely agree: a generic solution with a specific implementation – William Perron Nov 30 '17 at 20:26
  • 2
    To me this answer doesn't make sense in OP's context. He's saying he'll spend less time generalizing now, and less time in the future, unless in the future there is a need for the implementation to be specific. You're arguing against "investing additional effort in generalizing", whereas OP will invest additional effort only if they _don't_ generalize. (think) .... weird :$ – msb Nov 30 '17 at 22:14
  • 2
    @msb: that context was added after I wrote my answer and goes contrary to what the OP was saying before, especially in the part I cited. See my edit. – Doc Brown Nov 30 '17 at 22:25
  • Oh I see. lol, I'm sorry. :$ – msb Nov 30 '17 at 22:27
  • 2
    Also, the generalized solution will be more complicated, so by the time you have to adapt it for the second case, it will take much longer to understand it again. – AndreKR Dec 01 '17 at 09:36
  • Writing for a specific case *and* writing for a general case is generally more work than just writing for just the general case. When you write for the general case, you have to either start over from scratch, or go try to change the code for the specific case and hope you don't break anything. – Acccumulation Dec 01 '17 at 22:10
  • @Acccumulation: one should not "hope" not to break anything, one should use proper unit testing instead. – Doc Brown Dec 01 '17 at 22:20
  • 4
    Doc, an aside, but never would have guessed you were a non-native speaker. Your answers are always well written and well argued. – user949300 Dec 01 '17 at 23:27
  • @user949300: your are lucky you cannot hear my german accent ;-) – Doc Brown Dec 02 '17 at 00:20
  • 2
    I'll imagine your accent when I'm reading through your future answers. :-) – user949300 Dec 02 '17 at 02:36
66

TL;DR: it depends on what you're trying to solve.

I've had a similar conversation with my Gramps about this, while we were talking about how Func and Action in C# are awesome. My Gramps is a very old timer programmer, that's been around source codes since software was run on computers that took a whole room.

He changed techs several times in his life. He wrote code in C, COBOL, Pascal, BASIC, Fortran, Smalltalk, Java and eventually started C# as a hobby. I learned how to program with him, sitting on his lap while I was but a devling, carving away my first lines of code on the blue editor of IBM's SideKick. By the time I was 20, I already had spent more time coding than playing outside.

Those are a bit of my memories, so excuse me if I'm not exactly practical while retelling them. I'm somewhat fond of those moments.

That's what he said to me:


"Should we go for the generalization of a problem, or solve it in the specific scope, do you ask? Well, that's a... question."

Gramps took a pause to think about it for a brief moment, while fixing the position of his glasses on his face. He was playing a match-3 game on his computer, while listening to a Deep Purple's LP on his old sound system.

"Well, that would depend on what problem you're trying to solve", he told me. "It is tempting to believe that a single, holy solution for all design choices exist, but there isn't one. Software Architecture is like cheese, you see."

"...Cheese, Gramps?"

"Doesn't matter what you think about your favorite, there will always be someone that thinks it is smelly".

I blinked in confusion for a moment, but before I could say anything Gramps went on.

"When you're building a car, how do you pick the material for a part?"

"I... I guess it depends on the costs involved and what the part should do, I suppose."

"It depends on the problem that part is trying to solve. You won't make a tire made of steel, or a windshield made of leather. You pick the material that best solves the problem you have at hand. Now, what is a generic solution? Or a specific one? To what problem, to what use case? Should you go with a full functional approach, to give maximum flexibility to a code that will be used only once? Should you write a very specialized, fragile code to a part of your system that will see lots and lots of uses, and possibly lots of changes? Design choices like those are like the materials you pick for a part in a car or the shape of the Lego brick you pick to build a little house. What Lego brick is the best one?"

The elderly programmer reached for a little Lego train model that he has on his table before continuing.

"You can only answer that if you know for what you need that brick. How the hell you'll know if the specific solution is better than the generic one, or vice versa, if you don't even know what problem you're trying to solve? You can't see past a choice that you don't understand."

"..Did you just quote The Matrix?"

"What?"

"Nothing, go on."

"Well, suppose you're trying to build something to the National Invoice System. You know how that hellish API and its thirty thousand lines XML file look like from the inside. How would a 'generic' solution for creating that file would even look like? The file is full of optional parameters, full of cases that only very specific branches of business should use. For most cases, you can safely ignore them. You don't need to create a generic invoice system if the only thing you'll ever sell is shoes. Just create a system for selling shoes and make it be the best darned shoe-selling invoice system out there. Now, if you had to create a invoice system for any type of client, on a more broad application - to be resold as a independent, generic sales system, for example - now it is interesting to implement those options that are only used for Gas, food or alcohol. Now those are possible use cases. Before they were just some hypothetical Don't use cases, and you don't want to implement Don't use cases. Don't use is the little brother of Don't need."

Gramps put the lego train back on its place and turned back to his match-3 game.

"So, to be able to pick a generic or a specific solution for a given problem you first need to understand what the hell that problem is. Otherwise you're just guessing, and guessing is the job of managers, not programmers. As almost everything in IT, it depends."


So, there you have it. "It depends". That's probably the most powerful two-word expression when thinking about software design.

T. Sar
  • 2,045
  • 1
  • 14
  • 20
  • 15
    I'm sure there's something useful in that story but it was too painful to read, sorry – GoatInTheMachine Dec 01 '17 at 10:47
  • @GoatInTheMachine Too painful? Well, thanks for the feedback. Could you explain a bit why do you think it is too painful? – T. Sar Dec 01 '17 at 10:58
  • 28
    Your first post was a short fun anecdote - and of course it's a matter of opinion - but unless I *really* like someones writing style I find long stories like this really self-indulgent and a bit inappropriate outside of a personal blog – GoatInTheMachine Dec 01 '17 at 11:05
  • @GoatInTheMachine Oh, I understand. Thanks for your feedback, I'll keep that in mind! – T. Sar Dec 01 '17 at 11:11
  • 16
    @T.Sar don't listen to the haters, your post is a gem of useful advice from a guru. +1 – LLlAMnYP Dec 01 '17 at 12:27
  • 7
    The "software architecture is like cheese" part was just pure awesome. Indeed, this answer is a gem! – Mathieu Guindon Dec 01 '17 at 13:10
  • 3
    Maybe this answer is like cheese too? ;) But please, "SmallTalk" should be "Smalltalk", and yes, I'm being _that_ guy, sorry. – fede s. Dec 01 '17 at 14:30
  • 2
    @fedes. I've fixed the Smalltalk problem - sorry about that! Now, about the answer - I know that this format of answer was somewhat of a bet and not everyone would like it. Still, I wanted to give it a shot. I feel that sometimes our viewpoints can be more easily transmitted if we give to others an insight about what made us think that way, like our past experiences. That's just me, tho. – T. Sar Dec 01 '17 at 15:27
  • 1
    It's an excellent story, I am curious to know how much is fact and how much is fiction though. – icc97 Dec 01 '17 at 16:27
  • Thanks! Storytelling is an effective way to get positive emotional involvement (that's important in learning) and appeal to imagination. I like the answer, just maybe it's not the best informative one. – fede s. Dec 01 '17 at 17:00
  • 1
    Great _story_, and correct answer! To implement specific or generic certainly depends on the problem/req you have, this is a fact. +1. PS: even if I didn't like the writing style, I'm still perfectly able to see the technical answer here :) – Emerson Cardoso Dec 01 '17 at 17:21
  • @EmersonCardoso Thank you for the feedback! I'm not sure if I understood you, but I have to ask - is there anything on my writing style that didn't work well? I could use some extra pointers to try to it better! – T. Sar Dec 01 '17 at 18:01
  • I really expected the first line after the horizontal rule to be "que sera sera". – Dawood ibn Kareem Dec 02 '17 at 22:22
  • 1
    I really don't like things like '"..Did you just quote The Matrix?"" - that's just one step too much for me. "Don't let the charakters in your jokes tell each other jokes" as my gramps would say. A close +1 from me :p – Philipp Dec 04 '17 at 11:38
14

Primarily, you should try to anticipate whether it is likely that such a change will occur - not just a remote possibility somewhere down the line.

If not, it's usually better to go for the simple solution now and extend it later. It's quite possible that you'll have a much clearer picture of what is needed then.

doubleYou
  • 2,737
  • 1
  • 11
  • 25
13

If you're working in domain that's new to you, than the Rule of three that Daniel Pryden mentioned definitely should be applied. After all, how are you supposed to build useful abstractions if you're a novice in that area? People are often self-assured in their ability to catch abstractions, though it's seldom the case. To my experience, premature abstraction is no less of an evil than code duplication. Wrong abstractions are really painful to comprehend. Sometimes even more painful to refactor.

There is a book addressing my point about an unknown area developer is working in. Is consists of specific domains with extracted useful abstractions.

Vadim Samokhin
  • 2,158
  • 1
  • 12
  • 17
  • The question is clear about a concrete problem. – RibaldEddie Nov 30 '17 at 19:28
  • 4
    The answer is generic enough to address this concrete problem. And the question is not specific enough to be given a concrete receipt: as you can see, no domain details are mentioned in a question. Even class names are not specified (A and B don't count). – Vadim Samokhin Nov 30 '17 at 19:35
  • 7
    "Should a stackoverflow answer be as generic as possible or as specific as possible?" – Frames Catherine White Dec 01 '17 at 01:59
  • @LyndonWhite should a stackoverflow question be as generic as possible (too broad!) or as specific as possible (whatever that failure is called!)? Ha. –  Dec 01 '17 at 16:51
4

Given the nature of the body of your question, assuming I understood it correctly, I actually see this as a design question of central system features more than a question about generic vs. specific solutions.

And when it comes to central system features and capabilities, the most reliable ones are the ones that aren't there. It pays to err on the side of minimalism, especially considering that it's generally easier to add functionality centrally, long desired, than it is to remove problematic functionality, long undesired, with numerous dependencies because it has made working with the system so much more difficult than it needs to be while raising endless design questions with each new feature.

In fact, lacking a strong anticipation of whether this will be needed frequently in the future, I would seek to avoid looking at this as a type replacement from A to B if possible, and instead just seek it as a way to transform A's state . For example, set some fields in A to make it morph and appear like B to the user without actually changing into a different 'type' of thing -- you could potentially make A store B privately using composition and call functions in B when A's state is set to indicate that it should mimic B to simplify the implementation if possible. That should be a very simple and minimally invasive solution.

So anyway, echoing many others, I'd suggest to err on the side of avoiding generic solutions in this case, but more so because I assume this is in consideration of adding a very bold capability to the central system, and there I'd suggest to err on the side of leaving it out, especially for now.

  • "you miss all the bugs you don't code." (from basketball poster: you miss all the shots you don't take) –  Dec 01 '17 at 16:53
3

It's difficult to give a generic answer to this specific problem ;-)

The more generic it is, the more time you'll gain for future changes. For example for this reason many game programmes use the entity component pattern instead of building a very elaborate but rigid type system of the characters and objects in the game.

On the other hand, making something generic requires an up-front time and effort investment in design which is much higher than for something very specific. This bears the risk of over-engineering, and even of getting lost in potential future requirements.

It's always worth to see if there's a natural generalisation that will get you a breakthrough. However, in the end, it's be a question of balance, between the effort that you can spend now and the effort that you might need in the future.

Christophe
  • 74,672
  • 10
  • 115
  • 187
3
  1. Hybrid. This doesn't have to be an either/or question. You could design the API for general type conversions while implementing only the specific conversion you need right now. (Just make sure that if someone calls your general API with an unsupported conversion, it fails with a "not-supported" error status.)

  2. Testing. For the A->B conversion, I'm going to have to write one (or a small number) of tests. For a generic x->y conversion, I might have to write a whole matrix of tests. That's substantially more work, even if all the conversions share a single generic implementation.

    If, on the other hand, there turns out to be a generic way to test all possible conversions, then there isn't much more work and I might be inclined to go to a generic solution sooner.

  3. Coupling. A converter from A to B might necessarily need to know implementation details about A's and B's (tight coupling). If A's and B's are still evolving, this means I might have to keep revisiting the converter (and its tests), which sucks, but at least it's limited to A's and B's.

    If I'd gone with a generic solution that needs access to the details of all types, then even as C's and D's evolve, I may need to keep tweaking the generic converter (and a bunch of tests), which can slow me down even though nobody yet needs to convert to a C or a D.

    If both the generic and the specific conversions can be implemented in a way that's only loosely coupled to the details of the types, then I wouldn't worry about this. If one of those can be done in a loosely coupled way but the other requires tight coupling, then that's a strong argument for the loosely-coupled approach right out of the gate.

Adrian McCarthy
  • 981
  • 5
  • 8
  • 2
    Testing is a good point. It's a big advantage if you design the generic solutions based on concepts from [category theory](https://en.wikipedia.org/wiki/Category_theory), because then you often get [free theorems](https://bartoszmilewski.com/2014/09/22/parametricity-money-for-nothing-and-theorems-for-free/) which prove that the only possible implementation for a signature (that the compiler's type checker accepts) is _the correct one_, or at least that if the algorithm works for any particular type it must also work for all other types. – leftaroundabout Nov 30 '17 at 23:13
  • I'd guess that there is a domain-specific reason why only one type conversion has been asked for, meaning that most type conversions would be invalid actions in the problem domain, and for that reason, should not be supported by the app until they are officially allowed. This answers comes closest to that argumentation. – Ralf Kleberhoff Dec 01 '17 at 10:25
3

Should the solution be as generic as possible or as specific as possible?

That's not an answerable question.

The best you can reasonably get is a few heuristics to decide how general or specific to make a given solution. Working through something like the process below, usually the first-order approximation is correct (or good enough). When it isn't, the reason is likely to be too domain-specific to be usefully covered in detail here.

  1. First-order approximation: the usual YAGNI rule of three as described by Daniel Pryden, Doc Brown, et al.

    This is a generally-useful heuristic because it's probably the best you can do that doesn't depend on domain and other variables.

    So, the initial presumption is: we do the most specific thing.

  2. Second-order approximation: based on your expert knowledge of the solution domain, you say

    The "generic" solution in this case requires less work than the "specific" solution

    so we might re-interpret YAGNI as recommending we avoid unnecessary work, rather than avoiding unnecessary generality. So, we might modify our initial presumption and instead do the easiest thing.

    However if your solution domain knowledge indicate that the easiest solution is likely to open up a lot of bugs, or be hard to adequately test, or cause any other problem, then being easier to code isn't necessarily a good enough reason to change our original choice.

  3. Third-order approximation: does your problem domain knowledge suggest the easiest solution is actually correct, or are you allowing lots of transitions you know to be meaningless or wrong?

    If the easy-but-generic solution looks problematic, or you're not confident in your ability to judge these risks, doing the extra work and staying with your initial guess is probably better.

  4. Fourth-order approximation: does your knowledge of customer behaviour, or how this feature relates to others, or project management priorities, or ... any other not-strictly technical considerations modify your current working decision?

Useless
  • 12,380
  • 2
  • 34
  • 46
2

This is not an easy question to answer with a simple answer. Many answers have given heuristics built around a rule of 3, or something similar. Stepping beyond such rules of thumb is difficult.

To really really answer your question, you have to consider that your job is most likely not to implement something that changes A->B. If you're a contractor, maybe that's the requirement, but if you're an employee, you were hired on to do many many smaller tasks for the company. Changing A->B is only one of those tasks. Your company is going to care about how well the future changes can be made as well, even if that is not stated in the request. To find "TheBestImplementation(tm)," you have to look at the larger picture of what you're really being asked to do, and then use that to interpret the small request you've been given to change A->B.

If you are a low level entry programmer fresh out of college, it's often advisable to do exactly what you were told to do. If you were hired on as a software architect with 15 years experience, it's typically advisable to think about big picture things. Every actual job is going to fall somewhere between "do exactly what the narrow task is" and "think about the big picture." You'll feel out where your job fits in that spectrum if you talk to people enough and do enough work for them.

I can give a few concrete examples where your question has a definitive answer based on context. Consider the case where you are writing safety-critical software. This means you have a test team stood up to ensure the product performs as promised. Some of these test teams are required to test every single possible path through the code. If you talk with them, you may find that if you generalize the behavior, you'll add $30,000 to their testing costs because they'll have to test all of those extra paths. In that case, do not add generalized functionality, even if you have to duplicate work 7 or 8 times because of it. Save the company money and do exactly what the request stated.

On the other side, consider that you're making an API to permit customers to access data in a database program that your company makes. A customer requests to permit changes A->B. APIs typically have an aspect of golden handcuffs to them: once you add functionality to an API, you're typically not supposed to remove that functionality (until the next major version number). Many of your customers may not be willing to pay for the cost of upgrading to the next major version number, so you may be stuck with whatever solution you choose for a long time. In this case, I highly recommend creating the generic solution from the start. You really don't want to develop a bad API full of one-off behaviors.

Cort Ammon
  • 10,840
  • 3
  • 23
  • 32
1

Hmm... not much context to go on for an answer... echoing earlier answers, "It depends".

In a sense, you have to fall back on your experience. If not yours, then someone more senior in the domain. You could quibble about what the acceptance criteria state. If it's something along the lines of 'the user should be able to change the type from "A" to "B"' versus 'the user should be able to change the type from it's current value to any allowed alternate value'.

Frequently acceptance criteria are subject to interpretation, but good QA staff can write criteria appropriate to the task at hand, minimizing the interpretation needed.

Are there domain restrictions that don't allow changing from "A" to "C" or any other option, but only "A" to "B"? Or is this just a narrowly spec'ed requirement that isn't "forward thinking"?

If the general case were more difficult I'd go ask before starting the work, but in your case if I could 'predict' that other 'type' change requests would be coming in the future, I'd be tempted to: a) write something reusable for the general case, and b) wrap it in a conditional that only allows A -> B for now.

Easy enough to verify for the current case in automated testing, and easy enough to open up to other options later if/when different use cases arise.

railsdog
  • 129
  • 3
1

For me, a guideline I established a while ago is: "For hypothetical requirements only write hypothetical code." That is - if you anticipate additional requirements, you should think a little about how to implement them, and structure your current code so that it doesn't block that way.

But don't write actual code for these now - just think a little about what you'd do. Otherwise you'll usually make things unnecessarily complex, and will probably be annoyed later when actual requirements come that differ from what you anticipated.

Four your example: if you have all the usages of the convert method under your control, you can just call it convertAToB for now and plan to use a "rename method" refactoring in the IDE to rename it if you need more general functionality later. However, if the conversion method is part of a public API, this could be quite different: being that specific would block the generalization later, since it's hard to rename things in that case.

Dr. Hans-Peter Störr
  • 1,393
  • 2
  • 10
  • 10
0

I keep hearing that a good developer should try to anticipate the change and design the system so that it's easy to extend in the future,

In principle, yes. But this does not necessarily lead to generic solutions.

There are two types of topics in software development, as far as I'm concerned, where you should anticipate future change:

  • Libraries intended to be used by 3rd parties, and
  • overall software architecture.

The first case is solved by watching your cohesion/coupling, dependency injection or whatever. The second case is on a more abstract level, for example picking a service oriented architecture instead of a big monolothic blob of code for a large application.

In your case, you are asking for a specific solution for a specific problem, which has no foreseeable impact whatsoever on the future. In this case, YAGNI and DRY are good mottos to shoot for:

  • YAGNI (you ain't gonna need it) tells you to implement the absolutely minimum, basic stuff that you need and use right now. It means to implement the minimum that makes your current test suite turn from red to green, if you should use TDD/BDD/FDD style development. Not a single line more.
  • DRY (don't repeat yourself) means if you come about a similar problem again, then you take a good hard look at whether you need a generic solution.

Combined with other modern practices (like good test coverage to be able to refactor safely) this means that you end up with quickly written, lean, mean code that grows as needed.

which sounds like a generic solution is the way to go?

No, it sounds like you should have programming environments, languages, and tooling that make it easy and fun to refactor when you need it. Generic solutions don't provide that; they decouple the application from the actual domain.

Take a look at modern ORMs or MVC frameworks, for example Ruby on Rails; on the application level, all focus is on doing non-generic work. The rails libraries themselves are obviously nearly 100% generic, but the domain code (which your question is about) should be doing minimal shenanigans in that aspect.

AnoE
  • 5,614
  • 1
  • 13
  • 17
0

A different way to think about the problem is to consider what makes sense.

For example, there was application I was developing that had there sections which did almost the same thing but had inconsistent permission rules. As there was no reason to keep them different, when I refactored that section, I made them all do permissions the same way. The made the overall code smaller, simpler and the interface was more consistent.

When management decided to allow other people access to a feature, we were able to do it by just changing a flag.

Obviousily it makes sense to make the specific type conversion. Does it also make sense to make addintional type conversions?

Keep in mind, if the general solution is faster to implement, then the apecific case is easy too, just check that it is the only type conversion you are allowing.

If the application is in a highly regulated area (a medical or financial application) try to involve more people in you design.

Robert Baron
  • 1,132
  • 7
  • 10