How do I know how reusable my methods should be?











up vote
105
down vote

favorite
31












I am minding my own business at home and my wife comes to me and says




Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




And I am super happy because that was what I had been waiting for my whole life with my Java experience and come up with:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}


But then she says she was just testing me whether I was a ethically-trained software engineer, and tells me it looks like I am not since (taken from here)..




It should be noted that no ethically-trained software engineer would
ever consent to write a DestroyBaghdad procedure. Basic professional
ethics would instead require him to write a DestroyCity procedure, to
which Baghdad could be given as a parameter.




And I am like, fine, ok, you got me.. Pass any year you like, here you go:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..


But how do I know how much (and what) to parameterize? After all, she might say..




  • she wants to pass a custom string formatter, maybe she does not like the format I am already printing in: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]


void dayLightSavings(int year, DateTimeFormatter dtf)




  • she is interested in only certain month periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)




  • she is interested in certain hour periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)



If you are looking for a concrete question:



If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?



After all, I can first call it with Action.DESTROY then Action.REBUILD, isn't it?



But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? After all, I do not want to call:



takeActionOnCity(Action.DESTORY, City.BAGHDAD);


then



takeActionOnCity(Action.DESTORY, City.ERBIL);


and so on when I can do:



takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);


p.s. I only built my question around the quote I mentioned, I have nothing against any country, religion, race or whatsoever in the world. I am just trying to make a point.










share|improve this question




















  • 5




    Possible duplicate of Rule of thumb for cost vs. savings for code re-use
    – gnat
    Nov 27 at 6:29






  • 61




    The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
    – Eric Lippert
    Nov 27 at 15:02






  • 24




    Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
    – Eric Lippert
    Nov 27 at 18:36






  • 6




    Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
    – Tezra
    Nov 27 at 18:44








  • 18




    I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
    – Eric Lippert
    Nov 27 at 19:14















up vote
105
down vote

favorite
31












I am minding my own business at home and my wife comes to me and says




Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




And I am super happy because that was what I had been waiting for my whole life with my Java experience and come up with:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}


But then she says she was just testing me whether I was a ethically-trained software engineer, and tells me it looks like I am not since (taken from here)..




It should be noted that no ethically-trained software engineer would
ever consent to write a DestroyBaghdad procedure. Basic professional
ethics would instead require him to write a DestroyCity procedure, to
which Baghdad could be given as a parameter.




And I am like, fine, ok, you got me.. Pass any year you like, here you go:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..


But how do I know how much (and what) to parameterize? After all, she might say..




  • she wants to pass a custom string formatter, maybe she does not like the format I am already printing in: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]


void dayLightSavings(int year, DateTimeFormatter dtf)




  • she is interested in only certain month periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)




  • she is interested in certain hour periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)



If you are looking for a concrete question:



If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?



After all, I can first call it with Action.DESTROY then Action.REBUILD, isn't it?



But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? After all, I do not want to call:



takeActionOnCity(Action.DESTORY, City.BAGHDAD);


then



takeActionOnCity(Action.DESTORY, City.ERBIL);


and so on when I can do:



takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);


p.s. I only built my question around the quote I mentioned, I have nothing against any country, religion, race or whatsoever in the world. I am just trying to make a point.










share|improve this question




















  • 5




    Possible duplicate of Rule of thumb for cost vs. savings for code re-use
    – gnat
    Nov 27 at 6:29






  • 61




    The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
    – Eric Lippert
    Nov 27 at 15:02






  • 24




    Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
    – Eric Lippert
    Nov 27 at 18:36






  • 6




    Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
    – Tezra
    Nov 27 at 18:44








  • 18




    I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
    – Eric Lippert
    Nov 27 at 19:14













up vote
105
down vote

favorite
31









up vote
105
down vote

favorite
31






31





I am minding my own business at home and my wife comes to me and says




Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




And I am super happy because that was what I had been waiting for my whole life with my Java experience and come up with:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}


But then she says she was just testing me whether I was a ethically-trained software engineer, and tells me it looks like I am not since (taken from here)..




It should be noted that no ethically-trained software engineer would
ever consent to write a DestroyBaghdad procedure. Basic professional
ethics would instead require him to write a DestroyCity procedure, to
which Baghdad could be given as a parameter.




And I am like, fine, ok, you got me.. Pass any year you like, here you go:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..


But how do I know how much (and what) to parameterize? After all, she might say..




  • she wants to pass a custom string formatter, maybe she does not like the format I am already printing in: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]


void dayLightSavings(int year, DateTimeFormatter dtf)




  • she is interested in only certain month periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)




  • she is interested in certain hour periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)



If you are looking for a concrete question:



If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?



After all, I can first call it with Action.DESTROY then Action.REBUILD, isn't it?



But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? After all, I do not want to call:



takeActionOnCity(Action.DESTORY, City.BAGHDAD);


then



takeActionOnCity(Action.DESTORY, City.ERBIL);


and so on when I can do:



takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);


p.s. I only built my question around the quote I mentioned, I have nothing against any country, religion, race or whatsoever in the world. I am just trying to make a point.










share|improve this question















I am minding my own business at home and my wife comes to me and says




Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




And I am super happy because that was what I had been waiting for my whole life with my Java experience and come up with:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}


But then she says she was just testing me whether I was a ethically-trained software engineer, and tells me it looks like I am not since (taken from here)..




It should be noted that no ethically-trained software engineer would
ever consent to write a DestroyBaghdad procedure. Basic professional
ethics would instead require him to write a DestroyCity procedure, to
which Baghdad could be given as a parameter.




And I am like, fine, ok, you got me.. Pass any year you like, here you go:



import java.time.*;
import java.util.Set;

class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..


But how do I know how much (and what) to parameterize? After all, she might say..




  • she wants to pass a custom string formatter, maybe she does not like the format I am already printing in: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]


void dayLightSavings(int year, DateTimeFormatter dtf)




  • she is interested in only certain month periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)




  • she is interested in certain hour periods


void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)



If you are looking for a concrete question:



If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?



After all, I can first call it with Action.DESTROY then Action.REBUILD, isn't it?



But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? After all, I do not want to call:



takeActionOnCity(Action.DESTORY, City.BAGHDAD);


then



takeActionOnCity(Action.DESTORY, City.ERBIL);


and so on when I can do:



takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);


p.s. I only built my question around the quote I mentioned, I have nothing against any country, religion, race or whatsoever in the world. I am just trying to make a point.







design programming-practices clean-code






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited yesterday

























asked Nov 27 at 3:18









Koray Tugay

7333714




7333714








  • 5




    Possible duplicate of Rule of thumb for cost vs. savings for code re-use
    – gnat
    Nov 27 at 6:29






  • 61




    The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
    – Eric Lippert
    Nov 27 at 15:02






  • 24




    Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
    – Eric Lippert
    Nov 27 at 18:36






  • 6




    Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
    – Tezra
    Nov 27 at 18:44








  • 18




    I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
    – Eric Lippert
    Nov 27 at 19:14














  • 5




    Possible duplicate of Rule of thumb for cost vs. savings for code re-use
    – gnat
    Nov 27 at 6:29






  • 61




    The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
    – Eric Lippert
    Nov 27 at 15:02






  • 24




    Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
    – Eric Lippert
    Nov 27 at 18:36






  • 6




    Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
    – Tezra
    Nov 27 at 18:44








  • 18




    I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
    – Eric Lippert
    Nov 27 at 19:14








5




5




Possible duplicate of Rule of thumb for cost vs. savings for code re-use
– gnat
Nov 27 at 6:29




Possible duplicate of Rule of thumb for cost vs. savings for code re-use
– gnat
Nov 27 at 6:29




61




61




The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
– Eric Lippert
Nov 27 at 15:02




The point you're making here is one I have tried to express many times: generality is expensive, and so must be justified by specific, clear benefits. But it goes deeper than that; programming languages are created by their designers to make some kinds of generality easier than others, and that influences our choices as developers. It is easy to parameterize a method by a value, and when that's the easiest tool you have in your toolbox, the temptation is to use it regardless of whether it makes sense for the user.
– Eric Lippert
Nov 27 at 15:02




24




24




Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
– Eric Lippert
Nov 27 at 18:36




Re-use is not something you want for its own sake. We prioritize re-use because we have a belief that code artifacts are expensive to build and therefore should be usable in as many scenarios as possible, to amortize that cost across those scenarios. This belief is frequently not justified by observations, and the advice to design for reusability is therefore frequently misapplied. Design your code to lower the total cost of the application.
– Eric Lippert
Nov 27 at 18:36




6




6




Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
– Tezra
Nov 27 at 18:44






Your wife is the unethical one for wasting your time by lying to you. She asked for an answer, and gave a suggested medium; By that contract, how you obtain that output is only between you and yourself. Also, destroyCity(target) is way more unethical than destroyBagdad()! What kind of monster writes a program to wipe out a city, let alone any city in the world? What if the system was compromised?! Also, what does time/resource management (effort invested) have to do with ethics? As long as the verbal/written contract was completed as agreed upon.
– Tezra
Nov 27 at 18:44






18




18




I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
– Eric Lippert
Nov 27 at 19:14




I think you might be reading too much into this joke. It's a joke about how computer programmers come to make bad ethical decisions, because they prioritize technical considerations over the effects of their work on humans. It's not intended to be good advice about program design.
– Eric Lippert
Nov 27 at 19:14










13 Answers
13






active

oldest

votes

















up vote
95
down vote



accepted










It's turtles all the way down.



Or abstractions in this case.



Good practice coding is something that can be infinitely applied, and at some point you're abstracting for the sake of abstracting, which means you've taken it too far. Finding that line is not something that's easy to put into a rule of thumb, as it very much depends on your environment.



For example, we've had customers who were known to ask for simple applications first but then ask for expansions. We've also had customers that ask what they want and generally never come back to us for an expansion.

Your approach will vary per customer. For the first customer, it will pay to pre-emptively abstract the code because you're reasonably certain that you'll need to revisit this code in the future. For the second customer, you may not want to invest that extra effort if you're expecting them to not want to expand the application at any point (note: this doesn't mean that you don't follow any good practice, but simply that you avoiding doing any more than is currently necessary.



How do I know which features to implement?



The reason I mention the above is because you've already fallen in this trap:




But how do I know how much (and what) to parameterize? After all, she might say.




"She might say" is not a current business requirement. It's a guess at a future business requirement. As a general rule, do not base yourself on guesses, only develop what's currently required.



However, context applies here. I don't know your wife. Maybe you accurately gauged that she will in fact want this. But you should still confirm with the customer that this is indeed what they want, because otherwise you're going to spend time developing a feature that you're never going to end up using.



How do I know which architecture to implement?



This is trickier. The customer doesn't care about the internal code, so you can't ask them if they need it. Their opinion on the matter is mostly irrelevant.



However, you can still confirm the necessity of doing so by asking the right questions to the customer. Instead of asking about the architecture, ask them about their expectations of future development or expansions to the codebase. You can also ask if the current goal has a deadline, because you may not be able to implement your fancy architecture in the timeframe necessary.



How do I know when to abstract my code further?



I don't know where I read it (if anyone knows, let me know and I'll give credit), but a good rule of thumb is that developers should count like a caveman: one, two many.



enter image description hereXKCD #764



In other words, when a certain algorithm/pattern is being used for a third time, it should be abstracted so that it is reusable (= usable many times).



Just to be clear, I'm not implying that you shouldn't write reusable code when there's only two instances of the algorithm being used. Of course you can abstract that as well, but the rule should be that for three instances you must abstract.



Again, this factors in your expectations. If you already know that you need three or more instances, of course you can immediately abstract. But if you only guess that you might want to implement it more times, the correctness of implementing the abstraction fully relies on the correctness of your guess.

If you guessed correctly, you saved yourself some time. If you guessed wrongly, you wasted some of your time and effort and possibly compromised your architecture to implement something you end up not needing.




If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?




That very much depends on multiple things:




  • Are there multiple actions that can be taken on any city?

  • Can these actions be used interchangeably? Because if the "destroy" and "rebuild" actions have completely different executions, then there's no point in merging the two in a single takeActionOnCity method.


Also be aware that if you recursively abstract this, you're going to end up with a method that's so abstract that it's nothing more than a container to run another method in, which means you've made your method irrelevant and meaningless.

If your entire takeActionOnCity(Action action, City city) method body ends up being nothing more than action.TakeOn(city);, you should wonder if the takeActionOnCity method truly has a purpose or isn't just an extra layer that adds nothing of value.




But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?




The same question pops up here:




  • Do you have a use case for geographical regions?

  • Is the execution of an action on a city and a region the same?

  • Can any action be taken on any region/city?


If you can definitively answer "yes" to all three, then an abstraction is warranted.






share|improve this answer



















  • 15




    I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
    – Christian Sauer
    Nov 28 at 8:39






  • 2




    >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
    – Rune
    2 days ago








  • 6




    The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
    – Filip Milovanović
    2 days ago






  • 1




    Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
    – Filip Milovanović
    2 days ago






  • 1




    Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
    – Wayne Werner
    yesterday


















up vote
39
down vote













Practice



This is Software Engineering SE, but crafting software is a lot more art than engineering. There's no universal algorithm to follow or measurement to take to figure out how much reusability is enough. Like with anything, the more practice you get designing programs the better you will get at it. You'll get a better feel for what is "enough" because you'll see what goes wrong and how it goes wrong when you parameterize too much or too little.



That's not very helpful now though, so how about some guidelines?



Look back at your question. There's a lot of "she might say" and "I could". A lot of statements theorizing about some future need. Humans are shite at predicting the future. And you (most likely) are a human. The overwhelming problem of software design is trying to account for a future you don't know.



Guideline 1: You Ain't Gonna Need It



Seriously. Just stop. More often than not, that imagined future problem doesn't show up - and it certainly won't show up just like you imagined it.



Guideline 2: Cost/Benefit



Cool, that little program took you a few hours to write maybe? So what if your wife does come back and ask for those things? Worst case, you spend a few more hours tossing together another program to do it. For this case, it's not too much time to make this program more flexible. And it's not going to add much to the runtime speed or memory usage. But non-trivial programs have different answers. Different scenarios have different answers. At some point, the costs are clearly not worth the benefit even with imperfect future telling skills.



Guideline 3: Focus on constants



Look back at the question. In your original code, there's a lot of constant ints. 2018, 1. Constant ints, constant strings... They're the most likely things to need to be not-constant. Better yet, they take only a little time to parameterize (or at least define as actual constants). But another thing to be wary of is constant behavior. The System.out.println for example. That sort of assumption about use tends to be something that changes in the future and tends to be very costly to fix. Not only that, but IO like this makes the function impure (along with the timezone fetching somewhat). Parameterizing that behavior can make the function more pure leading to increased flexibility and testability. Big benefits with minimal cost (especially if you make an overload that uses System.out by default).






share|improve this answer

















  • 1




    It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
    – Telastyn
    Nov 27 at 4:50






  • 5




    @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
    – Doc Brown
    Nov 27 at 4:55








  • 7




    @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
    – Jörg W Mittag
    Nov 27 at 7:21






  • 3




    As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
    – Flater
    Nov 27 at 7:52








  • 4




    @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
    – Flater
    Nov 27 at 8:12


















up vote
24
down vote













Firstly: No security minded software developer would write a DestroyCity method without passing an Authorisation Token for any reason.



I too can write anything as an imperative which has evident wisdom without it being applicable in another context. Why is it necessary to authorise a string concatenation?



Secondly: All code when executed must be fully specified.



It does not matter whether the decision was hard coded in place, or deferred to another layer. At some point there is a piece of code in some language that knows both what is to be destroyed and how to instruct it.



That could be in the same object file destroyCity(xyz), and it could be in a configuration file: destroy {"city": "XYZ"}", or it might be a series of clicks and keypresses in a UI.



Thirdly:




Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




is a very different set of requirements to:




she wants to pass a custom string formatter, ... interested in only certain month periods, ... [and] interested in certain hour periods...




Now the second set of requirements obviously makes for a more flexible tool. It has a broader target audience, and a broader realm of application. The danger here is that the most flexible application in the world is in fact a compiler for machine code. It is literally a program so generic it can build anything to make the computer whatever you need it to be (within the constraints of its hardware).



Generally speaking people who need software do not want something generic; they want something specific. By giving more options you are in fact making their lives more complicated. If they wanted that complexity, they would instead be using a compiler, not asking you.



Your wife was asking for functionality, and under-specified her requirements to you. In this case it was seemingly on purpose, and in general it's because they don't know any better. Otherwise they would have just used the compiler themselves. So the first problem is you didn't ask for more details about exactly what she wanted to do. Did she want to run this for several different years? Did she want it in a CSV file? You didn't find out what decisions she wanted to make herself, and what she was asking you to figure out and decide for her. Once you've figured out what decisions need to be deferred you can figure out how to communicate those decisions through parameters (and other configurable means).



That being said, most clients miss-communicate, presume, or are ignorant of certain details (aka. decisions) that they really would like to make themselves, or that they really didn't want to make (but it sounds awesome). This is why work methods like PDSA (plan-develop-study-act) are important. You've planned the work in line with the requirements, and then you developed a set of decisions (code). Now it's time to study it, either by yourself or with your client and learn new things, and these inform your thinking going forward. Finally act on your new insights - update the requirements, refine the process, get new tools, etc... Then start planning again. This would have revealed any hidden requirements over time, and proves progress to many clients.



Finally. Your time is important; it is very real and very finite. Every decision you make entails many other hidden decisions, and this is what developing software is about. Delaying a decision as an argument may make the current function simpler, but it does make somewhere else more complex. Is that decision relevant in that other location? Is it more relevant here? Whose decision is it really to make? You are deciding this; this is coding. If you repeat sets of decision frequently, there is a very real benefit in codifying them inside some abstraction. XKCD has a useful perspective here. And this is relevant at the level of a system be it a function, module, program, etc.



The advice at the start implies that decisions your function has no right to make should be passed in as an argument. The problem is that a DestroyBaghdad function might actually be the function that has that right.






share|improve this answer























  • +1 love the part about the compiler!
    – Lee
    Nov 28 at 9:10


















up vote
4
down vote













There's a lot of long winded answers here, but honestly I think it's super simple




Any hard coded information you have in your function that isn't part
of the function name should be a parameter.




so in your function



class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
});
}
}


You have:



The zoneIds
2018, 1, 1
System.out


So I would move all these to parameters in one form or another. You could argue that the zoneIds are implicit in the function name, maybe you would want to make that even more so by changing it to "DaylightSavingsAroundTheWorld" or something



You don't have a format string, so adding one is a feature request and you should refer your wife to your family Jira instance. It can be put on the backlog and prioritised at the appropriate project management committee meeting.






share|improve this answer

















  • 1




    You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
    – David Conrad
    2 days ago










  • the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
    – Ewan
    2 days ago






  • 1




    Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
    – David Conrad
    2 days ago












  • an ourput stream is a consumer
    – Ewan
    2 days ago










  • No, an OutputStream is not a Consumer.
    – David Conrad
    2 days ago


















up vote
3
down vote













Experience, Domain Knowledge, and Code Reviews.



And, regardless of how much or how little experience, domain knowledge, or team you have, you cannot avoid the need to refactor as-needed.





With Experience, you'll start to recognize patterns in the domain-nonspecific methods (and classes) you write. And, if you're at all interested in DRY code, you'll feel bad feelings when you're about a write a method that you instinctively know you'll write variations of in the future. So, you'll intuitively write a parametrized least common denominator instead.



(This experience may transfer over instinctively into some your domain objects and methods too.)



With Domain Knowledge, you'll have a sense for which business concepts are closely related, which concepts have variables, which are fairly static, etc..



With Code Reviews, under- and over-parametrization will more likely be caught before it becomes production code, because your peers will (hopefully) have unique experiences and perspectives, both on the domain and coding in general.





That said, new developers won't generally have these Spidey Senses or an experienced group of peers to lean on right away. And, even experienced developers benefit from a basic discipline to guide them through new requirements — or through brain-foggy days. So, here's what I'd suggest as a start:




  • Start with the naive implementation, with minimal parametrization.
    (Include any parameters you already know you'll need, obviously ...)

  • Remove magic numbers and strings, moving them to configs and/or parameters

  • Factor "large" methods down into smaller, well-named methods

  • Refactor highly redundant methods (if convenient) into a common denominator, parametrizing the differences.


These steps don't necessarily occur in the stated order. If you sit down to write a method you already know to be highly redundant with an existing method, jump straight into refactoring if it's convenient. (If it's not going to take significantly more time to refactor than it would be to just write, test, and maintain two methods.)



But, apart from just having lots of experience and so forth, I advise pretty minimalistic code DRY-ing. It's not hard to refactor obvious violations. And, if you're too zealous, you can end up with "over-DRY" code that's even harder to read, understand, and maintain than the "WET" equivalent.






share|improve this answer

















  • 2




    So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
    – Koray Tugay
    Nov 27 at 17:45










  • Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
    – svidgen
    Nov 27 at 20:28












  • I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
    – svidgen
    Nov 27 at 20:31








  • 1




    So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
    – Koray Tugay
    Nov 27 at 20:52










  • Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
    – svidgen
    Nov 27 at 21:44


















up vote
3
down vote













In short, don't engineer your software for reusability because no end user cares if your functions can be reused. Instead, engineer for design comprehensibility -- is my code easy for someone else or my future forgetful self to understand? -- and design flexibility -- when I inevitably have to fix bugs, add features, or otherwise modify functionality, how much will my code resist the changes? The only thing your customer cares about is how quickly you can respond when she reports a bug or asks for a change. Asking these questions about your design incidentally tends to result in code that is reusable, but this approach keeps you focused on avoiding the real problems you will face over the life of that code so you can better serve the end user rather than pursuing lofty, impractical "engineering" ideals to please the neck-beards.



For something as simple as the example you provided, your initial implementation is fine because of how small it is, but this straightforward design will become hard to understand and brittle if you try to jam too much functional flexibility (as opposed to design flexibility) into one procedure. Below is my explanation of my preferred approach to designing complex systems for comprehensibility and flexibility which I hope will demonstrate what I mean by them. I would not employ this strategy for something that could be written in fewer than 20 lines in a single procedure because something so small already meets my criteria for comprehensibility and flexibility as it is.





Objects, not Procedures



Rather than using classes like old-school modules with a bunch of routines you call to execute the things your software should do, consider modeling the domain as objects which interact and cooperate to accomplish the task at hand. Methods in an Object-Oriented paradigm were originally created to be signals between objects so that Object1 could tell Object2 to do its thing, whatever that is, and possibly receive a return signal. This is because the Object-Oriented paradigm is inherently about modeling your domain objects and their interactions rather than a fancy way to organize the same old functions and procedures of the Imperative paradigm. In the case of the void destroyBaghdad example, instead of trying to write a context-less generic method to handle the destruction of Baghdad or any other thing (which could quickly grow complex, hard to understand, and brittle), every thing that can be destroyed should be responsible for understanding how to destroy itself. For example, you have an interface that describes the behavior of things that can be destroyed:



interface Destroyable {
void destroy();
}


Then you have a city which implements this interface:



class City implements Destroyable {
@Override
public void destroy() {
...code that destroys the city
}
}


Nothing that calls for the destruction of an instance of City will ever care how that happens, so there is no reason for that code to exist anywhere outside of City::destroy, and indeed, intimate knowledge of the inner workings of City outside of itself would be tight coupling which reduces felxibility since you have to consider those outside elements should you ever need to modify the behavior of City. This is the true purpose behind encapsulation. Think of it like every object has its own API which should enable you to do anything you need to with it so you can let it worry about fulfilling your requests.



Delegation, not "Control"



Now, whether your implementing class is City or Baghdad depends on how generic the process of destroying the city turns out to be. In all probability, a City will be composed of smaller pieces that will need to be destroyed individually to accomplish the total destruction of the city, so in that case, each of those pieces would also implement Destroyable, and they would each be instructed by the City to destroy themselves in the same way someone from outside requested the City to destroy itself.



interface Part extends Destroyable {
...part-specific methods
}

class Building implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}

class Street implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}

class City implements Destroyable {
public List<Part> parts() {...}

@Override
public void destroy() {
parts().forEach(Destroyable::destroy);
}
}


If you want to get really crazy and implement the idea of a Bomb that is dropped on a location and destroys everything within a certain radius, it might look something like this:



class Bomb {
private final Integer radius;

public Bomb(final Integer radius) {
this.radius = radius;
}

public void drop(final Grid grid, final Coordinate target) {
new ObjectsByRadius(
grid,
target,
this.radius
).forEach(Destroyable::destroy);
}
}


ObjectsByRadius represents a set of objects that is calculated for the Bomb from the inputs because the Bomb does not care how that calculation is made so long as it can work with the objects. This is reusable incidentally, but the main goal is to isolate the calculation from the processes of dropping the Bomb and destroying the objects so you can comprehend each piece and how they fit together and change the behavior of an individual piece without having to reshape the entire algorithm.



Interactions, not Algorithms



Instead of trying to guess at the right number of parameters for a complex algorithm, it makes more sense to model the process as a set of interacting objects, each with extremely narrow roles, since it will give you the ability to model the complexity of your process through the interactions between these well-defined, easy to comprehend, and nearly unchanging objects. When done correctly, this makes even some of the most complex modifications as trivial as implementing an interface or two and reworking which objects are instantiated in your main() method.



I'd give you something to your original example, but I honestly can't figure out what it means to "print... Day Light Savings." What I can say about that category of problem is that any time you are performing a calculation, the result of which could be formatted a number of ways, my preferred way to break that down is like this:



interface Result {
String print();
}

class Caclulation {
private final Parameter paramater1;

private final Parameter parameter2;

public Calculation(final Parameter parameter1, final Parameter parameter2) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}

public Result calculate() {
...calculate the result
}
}

class FormattedResult {
private final Result result;

public FormattedResult(final Result result) {
this.result = result;
}

@Override
public String print() {
...interact with this.result to format it and return the formatted String
}
}


Since your example uses classes from the Java library which don't support this design, you could just use the API of ZonedDateTime directly. The idea here is that each calculation is encapsulated within its own object. It makes no assumptions about how many times it should run or how it should format the result. It is exclusively concerned with performing the simplest form of the calculation. This makes it both easy to understand and flexible to change. Likewise, the Result is exclusively concerned with encapsulating the result of the calculation, and the FormattedResult is exclusively concerned with interacting with the Result to format it according to the rules we define. In this way, we can find the perfect number of arguments for each of our methods since they each have a well-defined task. It's also much simpler to modify moving forward so long as the interfaces don't change (which they aren't as likely to do if you've properly minimized the responsibilities of your objects). Our main() method might look like this:



class App {
public static void main(String args) {
final List<Set<Paramater>> parameters = ...instantiated from args
parameters.forEach(set -> {
System.out.println(
new FormattedResult(
new Calculation(
set.get(0),
set.get(1)
).calculate()
).print()
);
});
}
}


As a matter of fact, Object-Oriented Programming was invented specifically as a solution to the complexity/flexibility problem of the Imperative paradigm because there is just no good answer (that everyone can agree on or arrive at independently, anyhow) to how to optimally specify Imperative functions and procedures within the idiom.






share|improve this answer























  • This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
    – maple_shaft
    2 days ago










  • @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
    – Stuporman
    2 days ago










  • @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
    – Stuporman
    2 days ago










  • @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
    – Stuporman
    2 days ago






  • 1




    Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
    – Stephen J
    yesterday


















up vote
1
down vote













There's a clear process you can follow:




  • Write a failing test for a single feature which is in itself a "thing" (i.e., not some arbitrary split of a feature where neither half really makes sense).

  • Write the absolute minimum code to make it pass green, not a line more.

  • Rinse and repeat.

  • (Refactor relentlessly if necessary, which should be easy due to the great test coverage.)


This turns up with - at least in the opinion of some people - pretty much optimal code, since it is as small as possible, each finished feature takes as little time as possible (which might or might not be true if you look at the finished product after refactoring), and it has very good test coverage. It also noticeably avoids over-engineered too-generic methods or classes.



This also gives you very clear instructions when to make things generic and when to specialize.



I find your city example weird; I would very likely never ever hardcode a city name. It is so obvious that additional cities will be included later, whatever it is you're doing. But another example would be colors. In some circumstances, hardcoding "red" or "green" would be a possibility. For example, traffic lights are such an ubiquitous color that you can just get away with it (and you can always refactor). The difference is that "red" and "green" have universal, "hardcoded" meaning in our world, it is incredibly unlikely that it will ever change, and there is not really an alternative either.



Your first daylight savings method is simply broken. While it conforms to the specifications, the hardcoded 2018 is particularly bad because a) it is not mentioned in the technical "contract" (in the method name, in this case), and b) it will be out of date soon, so breakage is included from the get-go. For things that are time/date related, it would very seldomly make sense to hardcode a specific value since, well, time moves on. But apart from that, everything else is up for discussion. If you give it a simple year and then always calculate the complete year, go ahead. Most of the things you listed (formatting, choice of a smaller range, etc.) screams that your method is doing too much, and it should instead probably return a list/array of values so the caller can do the formatting/filtering themselves.



But at the end of the day, most of this is opinion, taste, experience and personal bias, so don't fret too much about it.






share|improve this answer























  • Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
    – dwizum
    2 days ago










  • @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
    – AnoE
    yesterday


















up vote
0
down vote













A good rule of thumb is: your method should be as reusable as… reusable.



If you expect that you will call your method only in one place, it should have only parameters that are known to the call site and that are not available to this method.



If you have more callers, you can introduce new parameters as long as other callers may pass those parameters; otherwise you need new method.



As the number of callers may grow in time, you need to be prepared for refactoring or overloading. In many cases it means that you should feel safe to select expression and run “extract parameter” action of your IDE.






share|improve this answer




























    up vote
    0
    down vote













    Ultra short answer: The less coupling or dependency to other code your generic module has the more reusable it can be.



    Your example only depends on



    import java.time.*;
    import java.util.Set;


    so in theory it can be highly reusable.



    In practise i donot think that you will ever have a second usecase that needs this code so following yagni principle i would not make it reusable if there are not more than 3 different projets that need this code.



    Other aspects of reusability are ease of use and doocumentation which corelate with Test Driven Development: It is helpfull if you have a simple unit-test that demonstrates/documents an easy use of your generic module as a coding example for users of your lib.






    share|improve this answer






























      up vote
      0
      down vote













      I've come to the opinion that there are two sorts of reusable code:




      • Code which is reusable because it's such a fundamental, basic thing.

      • Code which is reusable because it has parameters, overrides and hooks for everywhere.


      The first sort of reusability is often a good idea. It applies to things like lists, hashmaps, key/value stores, string matchers (e.g. regex, glob, ...), tuples, unification, search trees (depth-first, breadth-first, iterative-deepening, ...), parser combinators, caches/memoisers, data format readers/writers (s-expressions, XML, JSON, protobuf, ...), task queues, etc.



      These things are so general, in a very abstract way, that they're re-used all over the place in day to day programming. If you find yourself writing special-purpose code that would be simpler if it were made more abstract/general (e.g. if we have "a list of customer orders", we could throw away the "customer order" stuff to get "a list") then it might be a good idea to pull that out. Even if it doesn't get re-used, it lets us decouple unrelated functionality.



      The second sort is where we have some concrete code, which solves a real issue, but does so by making a whole bunch of decisions. We can make it more general/reusable by "soft-coding" those decisions, e.g. turning them into parameters, complicating the implementation and baking in even more concrete details (i.e. knowledge of which hooks we might want for overrides). Your example seems to be of this sort. The problem with this sort of reusability is that we may end up trying to guess at the use-cases of other people, or our future selves. Eventually we might end up having so many parameters that our code isn't usable, let alone reusable! In other words, when calling it takes more effort than just writing our own version. This is where YAGNI (You Ain't Gonna Need It) is important. Many times, such attempts at "reusable" code end up not being reused, since it may be incompatable with those use-cases more fundamentally than parameters can account for, or those potential users would rather roll their own (heck, look at all the standards and libraries out there whose authors prefixed with the word "Simple", to distinguish them from the predecessors!).



      This second form of "reusability" should basically be done on an as-needed basis. Sure, you can stick some "obvious" parameters in there, but don't start trying to predict the future. YAGNI.






      share|improve this answer





















      • Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
        – Koray Tugay
        yesterday










      • Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
        – Warbo
        yesterday










      • We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
        – Warbo
        yesterday










      • I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
        – Koray Tugay
        14 hours ago


















      up vote
      0
      down vote













      This is a good opportunity to state a rule I coined recently:



      Being a good programmer means being able to predict the future.



      Of course, this is strictly impossible!  After all, you never know for sure what generalisations you'll find useful later on, which related tasks you'll want to perform, what new features your users will want, &c.  But experience sometimes gives you a rough idea of what might come in handy.



      The other factors you have to balance that against are how much extra time and effort would be involved, and how much more complex would it make your code.  Sometimes you're lucky, and solving the more general problem is actually simpler!  (At least conceptually, if not in the amount of code.)  But more often, there's a complexity cost as well as one of time and effort.



      So if you think a generalisation is very likely to be needed, it's often worth doing (unless it adds a lot of work or complexity); but if it seems much less likely, then it's probably not (unless it's very easy and/or simplifies the code).



      (For a recent example: last week I was given a spec. for actions a system should take exactly 2 days after something expired.  So of course I made the 2-day period a parameter.  This week the business folks were delighted, as they were about to ask for that enhancement!  I was lucky: it was an easy change, and I guessed it was quite likely to be wanted.  Often it's harder to judge.  But it's still worth trying to predict, and experience is often a good guide.)






      share|improve this answer




























        up vote
        0
        down vote













        Firstly, the best answer to 'How do I know how reusable my methods should be?" is "experience." Do this a few thousand times, and you typically get the right answer. But as a teaser, I can give you the last line of this answer: Your customer will tell you how much flexibility and how many layers of generalization you should seek.



        Many of these answers have specific pieces of advice. I wanted to give something more generic... because the irony is too much fun to pass up!



        As some of the answers have noted, generality is expensive. However, it really isn't. Not always. Understanding the expense is essential to playing the reusability game.



        I focus on putting things on a scale from "irreversable" to "reversable." It's a smooth scale. The only truly irreversable thing is "time spent on the project." You'll never get those resources back. Slightly less reversable might be "golden handcuffs" situations such as the Windows API. Deprecated features remain in that API for decades because Microsoft's business model calls for it. If you have customers whose relationship would be permanently damaged by undoing some API feature, then that should be treated as irreversable. Looking at the other end of the scale, you have things like prototype code. If you don't like where it goes, you can simply throw it away. Slightly less reversable might be internal use APIs. They can be refactored without bothering a customer, but they may cost more time (the most irreversable resource of all!)



        So put these on a scale. Now you can apply a heuristic: the more reversable something is, the more you can use it for future looking activities. If something is irreversable, only use it for concrete customer-driven tasks. This is why you see principles like those from extreme programming which suggest only doing what the customer asks for and nothing more. These principles are good at making sure you don't do something you regret.



        Things like the DRY principle suggest a way to move that balance. If you find yourself repeating yourself, that's an opportunity to create what's basically an internal API. No customer sees it, so you can always change it. Once you have this internal API, now you can start playing with forward looking things. How many different timezone based tasks do you think your wife is going to give you? Do you have any other customers who might want timezone based tasks? Your flexibility here is bought by the concrete demands of your current customers, and it supports the potential future demands of future customers.



        This layered thinking approach, which comes naturally from DRY naturally provides the generalization you want without waste. But is there a limit? Of course there is. But to see it, you have to see the forest for the trees.



        If you have many layers of flexiblity, they often lead to a lack of direct control of the layers that face your customers. I've had software where I've had the brutal task of explaining to a customer why they can't have what they want due to flexibility built in 10 layers down which they were never supposed to see. We wrote ourselves into a corner. We tied ourselves in a knot with all the flexibility we thought we needed.



        So when you're doing this generalization/DRY trick, always keep a pulse on your customer. What do you think your wife is going to ask for next? Are you putting yourself in a position to fulfil those needs? If you have the knack, the customer will effectively tell you their future needs. If you don't have the knack, well, most of us just rely on guesswork! (especially with spouses!) Some customers will want great flexibility, and be willing to accept the extra cost of you developing with all these layers because they directly benefit from those layers' flexibility. Other customers have rather fixed unwavering requirements, and they prefer development be more direct. Your customer will tell you how much flexibility and how many layers of generalization you should seek.






        share|improve this answer





















        • Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
          – Koray Tugay
          13 hours ago










        • @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
          – Cort Ammon
          13 hours ago










        • In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
          – Cort Ammon
          13 hours ago












        • So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
          – Koray Tugay
          13 hours ago










        • @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
          – Cort Ammon
          11 hours ago


















        up vote
        -3
        down vote













        If you are using at least java 8, you would write the WorldTimeZones class to provide what in essence appears to be a collection of time zones.



        Then add a filter(Predicate filter) method to the WorldTimeZones class. This provides the ability for the caller to filter on anything they want by passing a lambda expression as the parameter.



        In essence, the single filter method supports filtering on anything contained in the value passed to the predicate.



        Alternately, add a stream() method to your WorldTimeZones class that produces a stream of time zones when called. Then the caller can filter, map and reduce as desired without you writing any specializations at all.






        share|improve this answer

















        • 3




          These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
          – maple_shaft
          2 days ago










        • So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
          – Rodney P. Barbati
          yesterday










        protected by gnat Nov 28 at 5:18



        Thank you for your interest in this question.
        Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



        Would you like to answer one of these unanswered questions instead?














        13 Answers
        13






        active

        oldest

        votes








        13 Answers
        13






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes








        up vote
        95
        down vote



        accepted










        It's turtles all the way down.



        Or abstractions in this case.



        Good practice coding is something that can be infinitely applied, and at some point you're abstracting for the sake of abstracting, which means you've taken it too far. Finding that line is not something that's easy to put into a rule of thumb, as it very much depends on your environment.



        For example, we've had customers who were known to ask for simple applications first but then ask for expansions. We've also had customers that ask what they want and generally never come back to us for an expansion.

        Your approach will vary per customer. For the first customer, it will pay to pre-emptively abstract the code because you're reasonably certain that you'll need to revisit this code in the future. For the second customer, you may not want to invest that extra effort if you're expecting them to not want to expand the application at any point (note: this doesn't mean that you don't follow any good practice, but simply that you avoiding doing any more than is currently necessary.



        How do I know which features to implement?



        The reason I mention the above is because you've already fallen in this trap:




        But how do I know how much (and what) to parameterize? After all, she might say.




        "She might say" is not a current business requirement. It's a guess at a future business requirement. As a general rule, do not base yourself on guesses, only develop what's currently required.



        However, context applies here. I don't know your wife. Maybe you accurately gauged that she will in fact want this. But you should still confirm with the customer that this is indeed what they want, because otherwise you're going to spend time developing a feature that you're never going to end up using.



        How do I know which architecture to implement?



        This is trickier. The customer doesn't care about the internal code, so you can't ask them if they need it. Their opinion on the matter is mostly irrelevant.



        However, you can still confirm the necessity of doing so by asking the right questions to the customer. Instead of asking about the architecture, ask them about their expectations of future development or expansions to the codebase. You can also ask if the current goal has a deadline, because you may not be able to implement your fancy architecture in the timeframe necessary.



        How do I know when to abstract my code further?



        I don't know where I read it (if anyone knows, let me know and I'll give credit), but a good rule of thumb is that developers should count like a caveman: one, two many.



        enter image description hereXKCD #764



        In other words, when a certain algorithm/pattern is being used for a third time, it should be abstracted so that it is reusable (= usable many times).



        Just to be clear, I'm not implying that you shouldn't write reusable code when there's only two instances of the algorithm being used. Of course you can abstract that as well, but the rule should be that for three instances you must abstract.



        Again, this factors in your expectations. If you already know that you need three or more instances, of course you can immediately abstract. But if you only guess that you might want to implement it more times, the correctness of implementing the abstraction fully relies on the correctness of your guess.

        If you guessed correctly, you saved yourself some time. If you guessed wrongly, you wasted some of your time and effort and possibly compromised your architecture to implement something you end up not needing.




        If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?




        That very much depends on multiple things:




        • Are there multiple actions that can be taken on any city?

        • Can these actions be used interchangeably? Because if the "destroy" and "rebuild" actions have completely different executions, then there's no point in merging the two in a single takeActionOnCity method.


        Also be aware that if you recursively abstract this, you're going to end up with a method that's so abstract that it's nothing more than a container to run another method in, which means you've made your method irrelevant and meaningless.

        If your entire takeActionOnCity(Action action, City city) method body ends up being nothing more than action.TakeOn(city);, you should wonder if the takeActionOnCity method truly has a purpose or isn't just an extra layer that adds nothing of value.




        But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?




        The same question pops up here:




        • Do you have a use case for geographical regions?

        • Is the execution of an action on a city and a region the same?

        • Can any action be taken on any region/city?


        If you can definitively answer "yes" to all three, then an abstraction is warranted.






        share|improve this answer



















        • 15




          I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
          – Christian Sauer
          Nov 28 at 8:39






        • 2




          >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
          – Rune
          2 days ago








        • 6




          The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
          – Filip Milovanović
          2 days ago






        • 1




          Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
          – Filip Milovanović
          2 days ago






        • 1




          Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
          – Wayne Werner
          yesterday















        up vote
        95
        down vote



        accepted










        It's turtles all the way down.



        Or abstractions in this case.



        Good practice coding is something that can be infinitely applied, and at some point you're abstracting for the sake of abstracting, which means you've taken it too far. Finding that line is not something that's easy to put into a rule of thumb, as it very much depends on your environment.



        For example, we've had customers who were known to ask for simple applications first but then ask for expansions. We've also had customers that ask what they want and generally never come back to us for an expansion.

        Your approach will vary per customer. For the first customer, it will pay to pre-emptively abstract the code because you're reasonably certain that you'll need to revisit this code in the future. For the second customer, you may not want to invest that extra effort if you're expecting them to not want to expand the application at any point (note: this doesn't mean that you don't follow any good practice, but simply that you avoiding doing any more than is currently necessary.



        How do I know which features to implement?



        The reason I mention the above is because you've already fallen in this trap:




        But how do I know how much (and what) to parameterize? After all, she might say.




        "She might say" is not a current business requirement. It's a guess at a future business requirement. As a general rule, do not base yourself on guesses, only develop what's currently required.



        However, context applies here. I don't know your wife. Maybe you accurately gauged that she will in fact want this. But you should still confirm with the customer that this is indeed what they want, because otherwise you're going to spend time developing a feature that you're never going to end up using.



        How do I know which architecture to implement?



        This is trickier. The customer doesn't care about the internal code, so you can't ask them if they need it. Their opinion on the matter is mostly irrelevant.



        However, you can still confirm the necessity of doing so by asking the right questions to the customer. Instead of asking about the architecture, ask them about their expectations of future development or expansions to the codebase. You can also ask if the current goal has a deadline, because you may not be able to implement your fancy architecture in the timeframe necessary.



        How do I know when to abstract my code further?



        I don't know where I read it (if anyone knows, let me know and I'll give credit), but a good rule of thumb is that developers should count like a caveman: one, two many.



        enter image description hereXKCD #764



        In other words, when a certain algorithm/pattern is being used for a third time, it should be abstracted so that it is reusable (= usable many times).



        Just to be clear, I'm not implying that you shouldn't write reusable code when there's only two instances of the algorithm being used. Of course you can abstract that as well, but the rule should be that for three instances you must abstract.



        Again, this factors in your expectations. If you already know that you need three or more instances, of course you can immediately abstract. But if you only guess that you might want to implement it more times, the correctness of implementing the abstraction fully relies on the correctness of your guess.

        If you guessed correctly, you saved yourself some time. If you guessed wrongly, you wasted some of your time and effort and possibly compromised your architecture to implement something you end up not needing.




        If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?




        That very much depends on multiple things:




        • Are there multiple actions that can be taken on any city?

        • Can these actions be used interchangeably? Because if the "destroy" and "rebuild" actions have completely different executions, then there's no point in merging the two in a single takeActionOnCity method.


        Also be aware that if you recursively abstract this, you're going to end up with a method that's so abstract that it's nothing more than a container to run another method in, which means you've made your method irrelevant and meaningless.

        If your entire takeActionOnCity(Action action, City city) method body ends up being nothing more than action.TakeOn(city);, you should wonder if the takeActionOnCity method truly has a purpose or isn't just an extra layer that adds nothing of value.




        But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?




        The same question pops up here:




        • Do you have a use case for geographical regions?

        • Is the execution of an action on a city and a region the same?

        • Can any action be taken on any region/city?


        If you can definitively answer "yes" to all three, then an abstraction is warranted.






        share|improve this answer



















        • 15




          I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
          – Christian Sauer
          Nov 28 at 8:39






        • 2




          >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
          – Rune
          2 days ago








        • 6




          The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
          – Filip Milovanović
          2 days ago






        • 1




          Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
          – Filip Milovanović
          2 days ago






        • 1




          Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
          – Wayne Werner
          yesterday













        up vote
        95
        down vote



        accepted







        up vote
        95
        down vote



        accepted






        It's turtles all the way down.



        Or abstractions in this case.



        Good practice coding is something that can be infinitely applied, and at some point you're abstracting for the sake of abstracting, which means you've taken it too far. Finding that line is not something that's easy to put into a rule of thumb, as it very much depends on your environment.



        For example, we've had customers who were known to ask for simple applications first but then ask for expansions. We've also had customers that ask what they want and generally never come back to us for an expansion.

        Your approach will vary per customer. For the first customer, it will pay to pre-emptively abstract the code because you're reasonably certain that you'll need to revisit this code in the future. For the second customer, you may not want to invest that extra effort if you're expecting them to not want to expand the application at any point (note: this doesn't mean that you don't follow any good practice, but simply that you avoiding doing any more than is currently necessary.



        How do I know which features to implement?



        The reason I mention the above is because you've already fallen in this trap:




        But how do I know how much (and what) to parameterize? After all, she might say.




        "She might say" is not a current business requirement. It's a guess at a future business requirement. As a general rule, do not base yourself on guesses, only develop what's currently required.



        However, context applies here. I don't know your wife. Maybe you accurately gauged that she will in fact want this. But you should still confirm with the customer that this is indeed what they want, because otherwise you're going to spend time developing a feature that you're never going to end up using.



        How do I know which architecture to implement?



        This is trickier. The customer doesn't care about the internal code, so you can't ask them if they need it. Their opinion on the matter is mostly irrelevant.



        However, you can still confirm the necessity of doing so by asking the right questions to the customer. Instead of asking about the architecture, ask them about their expectations of future development or expansions to the codebase. You can also ask if the current goal has a deadline, because you may not be able to implement your fancy architecture in the timeframe necessary.



        How do I know when to abstract my code further?



        I don't know where I read it (if anyone knows, let me know and I'll give credit), but a good rule of thumb is that developers should count like a caveman: one, two many.



        enter image description hereXKCD #764



        In other words, when a certain algorithm/pattern is being used for a third time, it should be abstracted so that it is reusable (= usable many times).



        Just to be clear, I'm not implying that you shouldn't write reusable code when there's only two instances of the algorithm being used. Of course you can abstract that as well, but the rule should be that for three instances you must abstract.



        Again, this factors in your expectations. If you already know that you need three or more instances, of course you can immediately abstract. But if you only guess that you might want to implement it more times, the correctness of implementing the abstraction fully relies on the correctness of your guess.

        If you guessed correctly, you saved yourself some time. If you guessed wrongly, you wasted some of your time and effort and possibly compromised your architecture to implement something you end up not needing.




        If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?




        That very much depends on multiple things:




        • Are there multiple actions that can be taken on any city?

        • Can these actions be used interchangeably? Because if the "destroy" and "rebuild" actions have completely different executions, then there's no point in merging the two in a single takeActionOnCity method.


        Also be aware that if you recursively abstract this, you're going to end up with a method that's so abstract that it's nothing more than a container to run another method in, which means you've made your method irrelevant and meaningless.

        If your entire takeActionOnCity(Action action, City city) method body ends up being nothing more than action.TakeOn(city);, you should wonder if the takeActionOnCity method truly has a purpose or isn't just an extra layer that adds nothing of value.




        But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?




        The same question pops up here:




        • Do you have a use case for geographical regions?

        • Is the execution of an action on a city and a region the same?

        • Can any action be taken on any region/city?


        If you can definitively answer "yes" to all three, then an abstraction is warranted.






        share|improve this answer














        It's turtles all the way down.



        Or abstractions in this case.



        Good practice coding is something that can be infinitely applied, and at some point you're abstracting for the sake of abstracting, which means you've taken it too far. Finding that line is not something that's easy to put into a rule of thumb, as it very much depends on your environment.



        For example, we've had customers who were known to ask for simple applications first but then ask for expansions. We've also had customers that ask what they want and generally never come back to us for an expansion.

        Your approach will vary per customer. For the first customer, it will pay to pre-emptively abstract the code because you're reasonably certain that you'll need to revisit this code in the future. For the second customer, you may not want to invest that extra effort if you're expecting them to not want to expand the application at any point (note: this doesn't mean that you don't follow any good practice, but simply that you avoiding doing any more than is currently necessary.



        How do I know which features to implement?



        The reason I mention the above is because you've already fallen in this trap:




        But how do I know how much (and what) to parameterize? After all, she might say.




        "She might say" is not a current business requirement. It's a guess at a future business requirement. As a general rule, do not base yourself on guesses, only develop what's currently required.



        However, context applies here. I don't know your wife. Maybe you accurately gauged that she will in fact want this. But you should still confirm with the customer that this is indeed what they want, because otherwise you're going to spend time developing a feature that you're never going to end up using.



        How do I know which architecture to implement?



        This is trickier. The customer doesn't care about the internal code, so you can't ask them if they need it. Their opinion on the matter is mostly irrelevant.



        However, you can still confirm the necessity of doing so by asking the right questions to the customer. Instead of asking about the architecture, ask them about their expectations of future development or expansions to the codebase. You can also ask if the current goal has a deadline, because you may not be able to implement your fancy architecture in the timeframe necessary.



        How do I know when to abstract my code further?



        I don't know where I read it (if anyone knows, let me know and I'll give credit), but a good rule of thumb is that developers should count like a caveman: one, two many.



        enter image description hereXKCD #764



        In other words, when a certain algorithm/pattern is being used for a third time, it should be abstracted so that it is reusable (= usable many times).



        Just to be clear, I'm not implying that you shouldn't write reusable code when there's only two instances of the algorithm being used. Of course you can abstract that as well, but the rule should be that for three instances you must abstract.



        Again, this factors in your expectations. If you already know that you need three or more instances, of course you can immediately abstract. But if you only guess that you might want to implement it more times, the correctness of implementing the abstraction fully relies on the correctness of your guess.

        If you guessed correctly, you saved yourself some time. If you guessed wrongly, you wasted some of your time and effort and possibly compromised your architecture to implement something you end up not needing.




        If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?




        That very much depends on multiple things:




        • Are there multiple actions that can be taken on any city?

        • Can these actions be used interchangeably? Because if the "destroy" and "rebuild" actions have completely different executions, then there's no point in merging the two in a single takeActionOnCity method.


        Also be aware that if you recursively abstract this, you're going to end up with a method that's so abstract that it's nothing more than a container to run another method in, which means you've made your method irrelevant and meaningless.

        If your entire takeActionOnCity(Action action, City city) method body ends up being nothing more than action.TakeOn(city);, you should wonder if the takeActionOnCity method truly has a purpose or isn't just an extra layer that adds nothing of value.




        But taking actions on cities is not enough for me, how about takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?




        The same question pops up here:




        • Do you have a use case for geographical regions?

        • Is the execution of an action on a city and a region the same?

        • Can any action be taken on any region/city?


        If you can definitively answer "yes" to all three, then an abstraction is warranted.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 27 at 7:56

























        answered Nov 27 at 7:51









        Flater

        6,33011220




        6,33011220








        • 15




          I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
          – Christian Sauer
          Nov 28 at 8:39






        • 2




          >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
          – Rune
          2 days ago








        • 6




          The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
          – Filip Milovanović
          2 days ago






        • 1




          Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
          – Filip Milovanović
          2 days ago






        • 1




          Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
          – Wayne Werner
          yesterday














        • 15




          I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
          – Christian Sauer
          Nov 28 at 8:39






        • 2




          >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
          – Rune
          2 days ago








        • 6




          The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
          – Filip Milovanović
          2 days ago






        • 1




          Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
          – Filip Milovanović
          2 days ago






        • 1




          Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
          – Wayne Werner
          yesterday








        15




        15




        I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
        – Christian Sauer
        Nov 28 at 8:39




        I cannot stress the "one, two, many" rule enough. There are infinite possibilities to abstract /parametrize something, but the useful subset is small, often zero. Knowing exactly which variant has value can most often only be determined in retrospect. So stick to the immediate requirements* and add complexity as needed by new requirements or hindsight. * Sometimes you know the problem space well, then it might be ok to add something because you know you need it tomorrow. But use this power wisely, it can also lead to ruin.
        – Christian Sauer
        Nov 28 at 8:39




        2




        2




        >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
        – Rune
        2 days ago






        >"I don't know where I read it [..]". You may have been reading Coding Horror: The Rule of Three.
        – Rune
        2 days ago






        6




        6




        The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
        – Filip Milovanović
        2 days ago




        The "one, two, many rule" is really there to prevent you from building the wrong abstraction by applying DRY blindly. The thing is, two pieces of code can start out looking almost exactly the same, so it's tempting to abstract the differences away; but early on, you don't know which parts of the code are stable, and which are not; besides, it could turn out that they actually need to evolve independently (different change patterns, different sets of responsibilities). In either case, a wrong abstraction works against you and gets in the way.
        – Filip Milovanović
        2 days ago




        1




        1




        Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
        – Filip Milovanović
        2 days ago




        Waiting for more than two examples of "the same logic" allows you to be a better judge of what should be abstracted, and how (and really, it's about managing dependencies/coupling between code with different change patterns).
        – Filip Milovanović
        2 days ago




        1




        1




        Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
        – Wayne Werner
        yesterday




        Turns out that if you make something customizable enough, it's Turing complete - congratulations, you just wrote a new programming language!
        – Wayne Werner
        yesterday












        up vote
        39
        down vote













        Practice



        This is Software Engineering SE, but crafting software is a lot more art than engineering. There's no universal algorithm to follow or measurement to take to figure out how much reusability is enough. Like with anything, the more practice you get designing programs the better you will get at it. You'll get a better feel for what is "enough" because you'll see what goes wrong and how it goes wrong when you parameterize too much or too little.



        That's not very helpful now though, so how about some guidelines?



        Look back at your question. There's a lot of "she might say" and "I could". A lot of statements theorizing about some future need. Humans are shite at predicting the future. And you (most likely) are a human. The overwhelming problem of software design is trying to account for a future you don't know.



        Guideline 1: You Ain't Gonna Need It



        Seriously. Just stop. More often than not, that imagined future problem doesn't show up - and it certainly won't show up just like you imagined it.



        Guideline 2: Cost/Benefit



        Cool, that little program took you a few hours to write maybe? So what if your wife does come back and ask for those things? Worst case, you spend a few more hours tossing together another program to do it. For this case, it's not too much time to make this program more flexible. And it's not going to add much to the runtime speed or memory usage. But non-trivial programs have different answers. Different scenarios have different answers. At some point, the costs are clearly not worth the benefit even with imperfect future telling skills.



        Guideline 3: Focus on constants



        Look back at the question. In your original code, there's a lot of constant ints. 2018, 1. Constant ints, constant strings... They're the most likely things to need to be not-constant. Better yet, they take only a little time to parameterize (or at least define as actual constants). But another thing to be wary of is constant behavior. The System.out.println for example. That sort of assumption about use tends to be something that changes in the future and tends to be very costly to fix. Not only that, but IO like this makes the function impure (along with the timezone fetching somewhat). Parameterizing that behavior can make the function more pure leading to increased flexibility and testability. Big benefits with minimal cost (especially if you make an overload that uses System.out by default).






        share|improve this answer

















        • 1




          It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
          – Telastyn
          Nov 27 at 4:50






        • 5




          @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
          – Doc Brown
          Nov 27 at 4:55








        • 7




          @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
          – Jörg W Mittag
          Nov 27 at 7:21






        • 3




          As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
          – Flater
          Nov 27 at 7:52








        • 4




          @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
          – Flater
          Nov 27 at 8:12















        up vote
        39
        down vote













        Practice



        This is Software Engineering SE, but crafting software is a lot more art than engineering. There's no universal algorithm to follow or measurement to take to figure out how much reusability is enough. Like with anything, the more practice you get designing programs the better you will get at it. You'll get a better feel for what is "enough" because you'll see what goes wrong and how it goes wrong when you parameterize too much or too little.



        That's not very helpful now though, so how about some guidelines?



        Look back at your question. There's a lot of "she might say" and "I could". A lot of statements theorizing about some future need. Humans are shite at predicting the future. And you (most likely) are a human. The overwhelming problem of software design is trying to account for a future you don't know.



        Guideline 1: You Ain't Gonna Need It



        Seriously. Just stop. More often than not, that imagined future problem doesn't show up - and it certainly won't show up just like you imagined it.



        Guideline 2: Cost/Benefit



        Cool, that little program took you a few hours to write maybe? So what if your wife does come back and ask for those things? Worst case, you spend a few more hours tossing together another program to do it. For this case, it's not too much time to make this program more flexible. And it's not going to add much to the runtime speed or memory usage. But non-trivial programs have different answers. Different scenarios have different answers. At some point, the costs are clearly not worth the benefit even with imperfect future telling skills.



        Guideline 3: Focus on constants



        Look back at the question. In your original code, there's a lot of constant ints. 2018, 1. Constant ints, constant strings... They're the most likely things to need to be not-constant. Better yet, they take only a little time to parameterize (or at least define as actual constants). But another thing to be wary of is constant behavior. The System.out.println for example. That sort of assumption about use tends to be something that changes in the future and tends to be very costly to fix. Not only that, but IO like this makes the function impure (along with the timezone fetching somewhat). Parameterizing that behavior can make the function more pure leading to increased flexibility and testability. Big benefits with minimal cost (especially if you make an overload that uses System.out by default).






        share|improve this answer

















        • 1




          It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
          – Telastyn
          Nov 27 at 4:50






        • 5




          @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
          – Doc Brown
          Nov 27 at 4:55








        • 7




          @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
          – Jörg W Mittag
          Nov 27 at 7:21






        • 3




          As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
          – Flater
          Nov 27 at 7:52








        • 4




          @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
          – Flater
          Nov 27 at 8:12













        up vote
        39
        down vote










        up vote
        39
        down vote









        Practice



        This is Software Engineering SE, but crafting software is a lot more art than engineering. There's no universal algorithm to follow or measurement to take to figure out how much reusability is enough. Like with anything, the more practice you get designing programs the better you will get at it. You'll get a better feel for what is "enough" because you'll see what goes wrong and how it goes wrong when you parameterize too much or too little.



        That's not very helpful now though, so how about some guidelines?



        Look back at your question. There's a lot of "she might say" and "I could". A lot of statements theorizing about some future need. Humans are shite at predicting the future. And you (most likely) are a human. The overwhelming problem of software design is trying to account for a future you don't know.



        Guideline 1: You Ain't Gonna Need It



        Seriously. Just stop. More often than not, that imagined future problem doesn't show up - and it certainly won't show up just like you imagined it.



        Guideline 2: Cost/Benefit



        Cool, that little program took you a few hours to write maybe? So what if your wife does come back and ask for those things? Worst case, you spend a few more hours tossing together another program to do it. For this case, it's not too much time to make this program more flexible. And it's not going to add much to the runtime speed or memory usage. But non-trivial programs have different answers. Different scenarios have different answers. At some point, the costs are clearly not worth the benefit even with imperfect future telling skills.



        Guideline 3: Focus on constants



        Look back at the question. In your original code, there's a lot of constant ints. 2018, 1. Constant ints, constant strings... They're the most likely things to need to be not-constant. Better yet, they take only a little time to parameterize (or at least define as actual constants). But another thing to be wary of is constant behavior. The System.out.println for example. That sort of assumption about use tends to be something that changes in the future and tends to be very costly to fix. Not only that, but IO like this makes the function impure (along with the timezone fetching somewhat). Parameterizing that behavior can make the function more pure leading to increased flexibility and testability. Big benefits with minimal cost (especially if you make an overload that uses System.out by default).






        share|improve this answer












        Practice



        This is Software Engineering SE, but crafting software is a lot more art than engineering. There's no universal algorithm to follow or measurement to take to figure out how much reusability is enough. Like with anything, the more practice you get designing programs the better you will get at it. You'll get a better feel for what is "enough" because you'll see what goes wrong and how it goes wrong when you parameterize too much or too little.



        That's not very helpful now though, so how about some guidelines?



        Look back at your question. There's a lot of "she might say" and "I could". A lot of statements theorizing about some future need. Humans are shite at predicting the future. And you (most likely) are a human. The overwhelming problem of software design is trying to account for a future you don't know.



        Guideline 1: You Ain't Gonna Need It



        Seriously. Just stop. More often than not, that imagined future problem doesn't show up - and it certainly won't show up just like you imagined it.



        Guideline 2: Cost/Benefit



        Cool, that little program took you a few hours to write maybe? So what if your wife does come back and ask for those things? Worst case, you spend a few more hours tossing together another program to do it. For this case, it's not too much time to make this program more flexible. And it's not going to add much to the runtime speed or memory usage. But non-trivial programs have different answers. Different scenarios have different answers. At some point, the costs are clearly not worth the benefit even with imperfect future telling skills.



        Guideline 3: Focus on constants



        Look back at the question. In your original code, there's a lot of constant ints. 2018, 1. Constant ints, constant strings... They're the most likely things to need to be not-constant. Better yet, they take only a little time to parameterize (or at least define as actual constants). But another thing to be wary of is constant behavior. The System.out.println for example. That sort of assumption about use tends to be something that changes in the future and tends to be very costly to fix. Not only that, but IO like this makes the function impure (along with the timezone fetching somewhat). Parameterizing that behavior can make the function more pure leading to increased flexibility and testability. Big benefits with minimal cost (especially if you make an overload that uses System.out by default).







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 27 at 4:13









        Telastyn

        91.6k24206315




        91.6k24206315








        • 1




          It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
          – Telastyn
          Nov 27 at 4:50






        • 5




          @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
          – Doc Brown
          Nov 27 at 4:55








        • 7




          @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
          – Jörg W Mittag
          Nov 27 at 7:21






        • 3




          As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
          – Flater
          Nov 27 at 7:52








        • 4




          @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
          – Flater
          Nov 27 at 8:12














        • 1




          It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
          – Telastyn
          Nov 27 at 4:50






        • 5




          @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
          – Doc Brown
          Nov 27 at 4:55








        • 7




          @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
          – Jörg W Mittag
          Nov 27 at 7:21






        • 3




          As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
          – Flater
          Nov 27 at 7:52








        • 4




          @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
          – Flater
          Nov 27 at 8:12








        1




        1




        It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
        – Telastyn
        Nov 27 at 4:50




        It’s just a guideline, the 1s are fine but you look at them and go “will this ever change?” Nah. And the println could be parameterized with a higher order function - though Java is not great at those.
        – Telastyn
        Nov 27 at 4:50




        5




        5




        @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
        – Doc Brown
        Nov 27 at 4:55






        @KorayTugay: if the program was really for your wife coming at home, YAGNI would tell you that your initial version is perfect, and you should not invest any more time to introduce constants or parameters. YAGNI needs context - is your program a throw-away solution, or a migration program run only for a few months, or is it part of a huge ERP system, intended to be used and maintained over several decades?
        – Doc Brown
        Nov 27 at 4:55






        7




        7




        @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
        – Jörg W Mittag
        Nov 27 at 7:21




        @KorayTugay: Separating I/O from computation is a fundamental program structuring technique. Separate generation of data from filtering of data from transformation of data from consumption of data from presentation of data. You should study some functional programs, then you will see this more clearly. In functional programming, it is quite common to generate an infinite amount of data, filter out only the data you are interested in, transform the data into the format you need, construct a string from it, and print this string in 5 different functions, one for each step.
        – Jörg W Mittag
        Nov 27 at 7:21




        3




        3




        As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
        – Flater
        Nov 27 at 7:52






        As a sidenote, strongly following YAGNI leads to needing to continuously refactor: "Used without continuous refactoring, it could lead to disorganized code and massive rework, known as technical debt." So while YAGNI is a good thing in general, it comes with a great responsibility of revisiting and reevaluating code, which is not something every developer/company is willing to do.
        – Flater
        Nov 27 at 7:52






        4




        4




        @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
        – Flater
        Nov 27 at 8:12




        @Telastyn: I suggest expanding the question to “will this never change and is the intention of the code trivially readable without naming the constant?” Even for values that never change, it may be relevant to name them just to keep things readable.
        – Flater
        Nov 27 at 8:12










        up vote
        24
        down vote













        Firstly: No security minded software developer would write a DestroyCity method without passing an Authorisation Token for any reason.



        I too can write anything as an imperative which has evident wisdom without it being applicable in another context. Why is it necessary to authorise a string concatenation?



        Secondly: All code when executed must be fully specified.



        It does not matter whether the decision was hard coded in place, or deferred to another layer. At some point there is a piece of code in some language that knows both what is to be destroyed and how to instruct it.



        That could be in the same object file destroyCity(xyz), and it could be in a configuration file: destroy {"city": "XYZ"}", or it might be a series of clicks and keypresses in a UI.



        Thirdly:




        Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




        is a very different set of requirements to:




        she wants to pass a custom string formatter, ... interested in only certain month periods, ... [and] interested in certain hour periods...




        Now the second set of requirements obviously makes for a more flexible tool. It has a broader target audience, and a broader realm of application. The danger here is that the most flexible application in the world is in fact a compiler for machine code. It is literally a program so generic it can build anything to make the computer whatever you need it to be (within the constraints of its hardware).



        Generally speaking people who need software do not want something generic; they want something specific. By giving more options you are in fact making their lives more complicated. If they wanted that complexity, they would instead be using a compiler, not asking you.



        Your wife was asking for functionality, and under-specified her requirements to you. In this case it was seemingly on purpose, and in general it's because they don't know any better. Otherwise they would have just used the compiler themselves. So the first problem is you didn't ask for more details about exactly what she wanted to do. Did she want to run this for several different years? Did she want it in a CSV file? You didn't find out what decisions she wanted to make herself, and what she was asking you to figure out and decide for her. Once you've figured out what decisions need to be deferred you can figure out how to communicate those decisions through parameters (and other configurable means).



        That being said, most clients miss-communicate, presume, or are ignorant of certain details (aka. decisions) that they really would like to make themselves, or that they really didn't want to make (but it sounds awesome). This is why work methods like PDSA (plan-develop-study-act) are important. You've planned the work in line with the requirements, and then you developed a set of decisions (code). Now it's time to study it, either by yourself or with your client and learn new things, and these inform your thinking going forward. Finally act on your new insights - update the requirements, refine the process, get new tools, etc... Then start planning again. This would have revealed any hidden requirements over time, and proves progress to many clients.



        Finally. Your time is important; it is very real and very finite. Every decision you make entails many other hidden decisions, and this is what developing software is about. Delaying a decision as an argument may make the current function simpler, but it does make somewhere else more complex. Is that decision relevant in that other location? Is it more relevant here? Whose decision is it really to make? You are deciding this; this is coding. If you repeat sets of decision frequently, there is a very real benefit in codifying them inside some abstraction. XKCD has a useful perspective here. And this is relevant at the level of a system be it a function, module, program, etc.



        The advice at the start implies that decisions your function has no right to make should be passed in as an argument. The problem is that a DestroyBaghdad function might actually be the function that has that right.






        share|improve this answer























        • +1 love the part about the compiler!
          – Lee
          Nov 28 at 9:10















        up vote
        24
        down vote













        Firstly: No security minded software developer would write a DestroyCity method without passing an Authorisation Token for any reason.



        I too can write anything as an imperative which has evident wisdom without it being applicable in another context. Why is it necessary to authorise a string concatenation?



        Secondly: All code when executed must be fully specified.



        It does not matter whether the decision was hard coded in place, or deferred to another layer. At some point there is a piece of code in some language that knows both what is to be destroyed and how to instruct it.



        That could be in the same object file destroyCity(xyz), and it could be in a configuration file: destroy {"city": "XYZ"}", or it might be a series of clicks and keypresses in a UI.



        Thirdly:




        Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




        is a very different set of requirements to:




        she wants to pass a custom string formatter, ... interested in only certain month periods, ... [and] interested in certain hour periods...




        Now the second set of requirements obviously makes for a more flexible tool. It has a broader target audience, and a broader realm of application. The danger here is that the most flexible application in the world is in fact a compiler for machine code. It is literally a program so generic it can build anything to make the computer whatever you need it to be (within the constraints of its hardware).



        Generally speaking people who need software do not want something generic; they want something specific. By giving more options you are in fact making their lives more complicated. If they wanted that complexity, they would instead be using a compiler, not asking you.



        Your wife was asking for functionality, and under-specified her requirements to you. In this case it was seemingly on purpose, and in general it's because they don't know any better. Otherwise they would have just used the compiler themselves. So the first problem is you didn't ask for more details about exactly what she wanted to do. Did she want to run this for several different years? Did she want it in a CSV file? You didn't find out what decisions she wanted to make herself, and what she was asking you to figure out and decide for her. Once you've figured out what decisions need to be deferred you can figure out how to communicate those decisions through parameters (and other configurable means).



        That being said, most clients miss-communicate, presume, or are ignorant of certain details (aka. decisions) that they really would like to make themselves, or that they really didn't want to make (but it sounds awesome). This is why work methods like PDSA (plan-develop-study-act) are important. You've planned the work in line with the requirements, and then you developed a set of decisions (code). Now it's time to study it, either by yourself or with your client and learn new things, and these inform your thinking going forward. Finally act on your new insights - update the requirements, refine the process, get new tools, etc... Then start planning again. This would have revealed any hidden requirements over time, and proves progress to many clients.



        Finally. Your time is important; it is very real and very finite. Every decision you make entails many other hidden decisions, and this is what developing software is about. Delaying a decision as an argument may make the current function simpler, but it does make somewhere else more complex. Is that decision relevant in that other location? Is it more relevant here? Whose decision is it really to make? You are deciding this; this is coding. If you repeat sets of decision frequently, there is a very real benefit in codifying them inside some abstraction. XKCD has a useful perspective here. And this is relevant at the level of a system be it a function, module, program, etc.



        The advice at the start implies that decisions your function has no right to make should be passed in as an argument. The problem is that a DestroyBaghdad function might actually be the function that has that right.






        share|improve this answer























        • +1 love the part about the compiler!
          – Lee
          Nov 28 at 9:10













        up vote
        24
        down vote










        up vote
        24
        down vote









        Firstly: No security minded software developer would write a DestroyCity method without passing an Authorisation Token for any reason.



        I too can write anything as an imperative which has evident wisdom without it being applicable in another context. Why is it necessary to authorise a string concatenation?



        Secondly: All code when executed must be fully specified.



        It does not matter whether the decision was hard coded in place, or deferred to another layer. At some point there is a piece of code in some language that knows both what is to be destroyed and how to instruct it.



        That could be in the same object file destroyCity(xyz), and it could be in a configuration file: destroy {"city": "XYZ"}", or it might be a series of clicks and keypresses in a UI.



        Thirdly:




        Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




        is a very different set of requirements to:




        she wants to pass a custom string formatter, ... interested in only certain month periods, ... [and] interested in certain hour periods...




        Now the second set of requirements obviously makes for a more flexible tool. It has a broader target audience, and a broader realm of application. The danger here is that the most flexible application in the world is in fact a compiler for machine code. It is literally a program so generic it can build anything to make the computer whatever you need it to be (within the constraints of its hardware).



        Generally speaking people who need software do not want something generic; they want something specific. By giving more options you are in fact making their lives more complicated. If they wanted that complexity, they would instead be using a compiler, not asking you.



        Your wife was asking for functionality, and under-specified her requirements to you. In this case it was seemingly on purpose, and in general it's because they don't know any better. Otherwise they would have just used the compiler themselves. So the first problem is you didn't ask for more details about exactly what she wanted to do. Did she want to run this for several different years? Did she want it in a CSV file? You didn't find out what decisions she wanted to make herself, and what she was asking you to figure out and decide for her. Once you've figured out what decisions need to be deferred you can figure out how to communicate those decisions through parameters (and other configurable means).



        That being said, most clients miss-communicate, presume, or are ignorant of certain details (aka. decisions) that they really would like to make themselves, or that they really didn't want to make (but it sounds awesome). This is why work methods like PDSA (plan-develop-study-act) are important. You've planned the work in line with the requirements, and then you developed a set of decisions (code). Now it's time to study it, either by yourself or with your client and learn new things, and these inform your thinking going forward. Finally act on your new insights - update the requirements, refine the process, get new tools, etc... Then start planning again. This would have revealed any hidden requirements over time, and proves progress to many clients.



        Finally. Your time is important; it is very real and very finite. Every decision you make entails many other hidden decisions, and this is what developing software is about. Delaying a decision as an argument may make the current function simpler, but it does make somewhere else more complex. Is that decision relevant in that other location? Is it more relevant here? Whose decision is it really to make? You are deciding this; this is coding. If you repeat sets of decision frequently, there is a very real benefit in codifying them inside some abstraction. XKCD has a useful perspective here. And this is relevant at the level of a system be it a function, module, program, etc.



        The advice at the start implies that decisions your function has no right to make should be passed in as an argument. The problem is that a DestroyBaghdad function might actually be the function that has that right.






        share|improve this answer














        Firstly: No security minded software developer would write a DestroyCity method without passing an Authorisation Token for any reason.



        I too can write anything as an imperative which has evident wisdom without it being applicable in another context. Why is it necessary to authorise a string concatenation?



        Secondly: All code when executed must be fully specified.



        It does not matter whether the decision was hard coded in place, or deferred to another layer. At some point there is a piece of code in some language that knows both what is to be destroyed and how to instruct it.



        That could be in the same object file destroyCity(xyz), and it could be in a configuration file: destroy {"city": "XYZ"}", or it might be a series of clicks and keypresses in a UI.



        Thirdly:




        Honey.. Can you print all the Day Light Savings around the world for 2018 in the console? I need to check something.




        is a very different set of requirements to:




        she wants to pass a custom string formatter, ... interested in only certain month periods, ... [and] interested in certain hour periods...




        Now the second set of requirements obviously makes for a more flexible tool. It has a broader target audience, and a broader realm of application. The danger here is that the most flexible application in the world is in fact a compiler for machine code. It is literally a program so generic it can build anything to make the computer whatever you need it to be (within the constraints of its hardware).



        Generally speaking people who need software do not want something generic; they want something specific. By giving more options you are in fact making their lives more complicated. If they wanted that complexity, they would instead be using a compiler, not asking you.



        Your wife was asking for functionality, and under-specified her requirements to you. In this case it was seemingly on purpose, and in general it's because they don't know any better. Otherwise they would have just used the compiler themselves. So the first problem is you didn't ask for more details about exactly what she wanted to do. Did she want to run this for several different years? Did she want it in a CSV file? You didn't find out what decisions she wanted to make herself, and what she was asking you to figure out and decide for her. Once you've figured out what decisions need to be deferred you can figure out how to communicate those decisions through parameters (and other configurable means).



        That being said, most clients miss-communicate, presume, or are ignorant of certain details (aka. decisions) that they really would like to make themselves, or that they really didn't want to make (but it sounds awesome). This is why work methods like PDSA (plan-develop-study-act) are important. You've planned the work in line with the requirements, and then you developed a set of decisions (code). Now it's time to study it, either by yourself or with your client and learn new things, and these inform your thinking going forward. Finally act on your new insights - update the requirements, refine the process, get new tools, etc... Then start planning again. This would have revealed any hidden requirements over time, and proves progress to many clients.



        Finally. Your time is important; it is very real and very finite. Every decision you make entails many other hidden decisions, and this is what developing software is about. Delaying a decision as an argument may make the current function simpler, but it does make somewhere else more complex. Is that decision relevant in that other location? Is it more relevant here? Whose decision is it really to make? You are deciding this; this is coding. If you repeat sets of decision frequently, there is a very real benefit in codifying them inside some abstraction. XKCD has a useful perspective here. And this is relevant at the level of a system be it a function, module, program, etc.



        The advice at the start implies that decisions your function has no right to make should be passed in as an argument. The problem is that a DestroyBaghdad function might actually be the function that has that right.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 28 at 8:35









        Peter Mortensen

        1,11621114




        1,11621114










        answered Nov 27 at 4:57









        Kain0_0

        9078




        9078












        • +1 love the part about the compiler!
          – Lee
          Nov 28 at 9:10


















        • +1 love the part about the compiler!
          – Lee
          Nov 28 at 9:10
















        +1 love the part about the compiler!
        – Lee
        Nov 28 at 9:10




        +1 love the part about the compiler!
        – Lee
        Nov 28 at 9:10










        up vote
        4
        down vote













        There's a lot of long winded answers here, but honestly I think it's super simple




        Any hard coded information you have in your function that isn't part
        of the function name should be a parameter.




        so in your function



        class App {
        void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
        LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
        ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
        while (2018 == now.getYear()) {
        int hour = now.getHour();
        now = now.plusHours(1);
        if (now.getHour() == hour) {
        System.out.println(now);
        }
        }
        });
        }
        }


        You have:



        The zoneIds
        2018, 1, 1
        System.out


        So I would move all these to parameters in one form or another. You could argue that the zoneIds are implicit in the function name, maybe you would want to make that even more so by changing it to "DaylightSavingsAroundTheWorld" or something



        You don't have a format string, so adding one is a feature request and you should refer your wife to your family Jira instance. It can be put on the backlog and prioritised at the appropriate project management committee meeting.






        share|improve this answer

















        • 1




          You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
          – David Conrad
          2 days ago










        • the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
          – Ewan
          2 days ago






        • 1




          Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
          – David Conrad
          2 days ago












        • an ourput stream is a consumer
          – Ewan
          2 days ago










        • No, an OutputStream is not a Consumer.
          – David Conrad
          2 days ago















        up vote
        4
        down vote













        There's a lot of long winded answers here, but honestly I think it's super simple




        Any hard coded information you have in your function that isn't part
        of the function name should be a parameter.




        so in your function



        class App {
        void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
        LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
        ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
        while (2018 == now.getYear()) {
        int hour = now.getHour();
        now = now.plusHours(1);
        if (now.getHour() == hour) {
        System.out.println(now);
        }
        }
        });
        }
        }


        You have:



        The zoneIds
        2018, 1, 1
        System.out


        So I would move all these to parameters in one form or another. You could argue that the zoneIds are implicit in the function name, maybe you would want to make that even more so by changing it to "DaylightSavingsAroundTheWorld" or something



        You don't have a format string, so adding one is a feature request and you should refer your wife to your family Jira instance. It can be put on the backlog and prioritised at the appropriate project management committee meeting.






        share|improve this answer

















        • 1




          You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
          – David Conrad
          2 days ago










        • the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
          – Ewan
          2 days ago






        • 1




          Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
          – David Conrad
          2 days ago












        • an ourput stream is a consumer
          – Ewan
          2 days ago










        • No, an OutputStream is not a Consumer.
          – David Conrad
          2 days ago













        up vote
        4
        down vote










        up vote
        4
        down vote









        There's a lot of long winded answers here, but honestly I think it's super simple




        Any hard coded information you have in your function that isn't part
        of the function name should be a parameter.




        so in your function



        class App {
        void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
        LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
        ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
        while (2018 == now.getYear()) {
        int hour = now.getHour();
        now = now.plusHours(1);
        if (now.getHour() == hour) {
        System.out.println(now);
        }
        }
        });
        }
        }


        You have:



        The zoneIds
        2018, 1, 1
        System.out


        So I would move all these to parameters in one form or another. You could argue that the zoneIds are implicit in the function name, maybe you would want to make that even more so by changing it to "DaylightSavingsAroundTheWorld" or something



        You don't have a format string, so adding one is a feature request and you should refer your wife to your family Jira instance. It can be put on the backlog and prioritised at the appropriate project management committee meeting.






        share|improve this answer












        There's a lot of long winded answers here, but honestly I think it's super simple




        Any hard coded information you have in your function that isn't part
        of the function name should be a parameter.




        so in your function



        class App {
        void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
        LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
        ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
        while (2018 == now.getYear()) {
        int hour = now.getHour();
        now = now.plusHours(1);
        if (now.getHour() == hour) {
        System.out.println(now);
        }
        }
        });
        }
        }


        You have:



        The zoneIds
        2018, 1, 1
        System.out


        So I would move all these to parameters in one form or another. You could argue that the zoneIds are implicit in the function name, maybe you would want to make that even more so by changing it to "DaylightSavingsAroundTheWorld" or something



        You don't have a format string, so adding one is a feature request and you should refer your wife to your family Jira instance. It can be put on the backlog and prioritised at the appropriate project management committee meeting.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 27 at 10:39









        Ewan

        36.9k32981




        36.9k32981








        • 1




          You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
          – David Conrad
          2 days ago










        • the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
          – Ewan
          2 days ago






        • 1




          Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
          – David Conrad
          2 days ago












        • an ourput stream is a consumer
          – Ewan
          2 days ago










        • No, an OutputStream is not a Consumer.
          – David Conrad
          2 days ago














        • 1




          You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
          – David Conrad
          2 days ago










        • the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
          – Ewan
          2 days ago






        • 1




          Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
          – David Conrad
          2 days ago












        • an ourput stream is a consumer
          – Ewan
          2 days ago










        • No, an OutputStream is not a Consumer.
          – David Conrad
          2 days ago








        1




        1




        You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
        – David Conrad
        2 days ago




        You (addressing myself to OP) certainly should not add a format string, since you shouldn't be printing anything. The one thing about this code that absolutely prevents its reuse is that it prints. It should return the zones, or a map of the zones to when they go off DST. (Although why it only identifies when the go off DST, and not on DST, I don't understand. It doesn't seem to match the problem statement.)
        – David Conrad
        2 days ago












        the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
        – Ewan
        2 days ago




        the requirement is to print to console. you can mitgate the tight couplung by passing in the output stream as a parameter as i suggest
        – Ewan
        2 days ago




        1




        1




        Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
        – David Conrad
        2 days ago






        Even so, if you want the code to be reusable, you shouldn't print to the console. Write a method that returns the results, and then write a caller that gets them and prints. That also makes it testable. If you do want to have it produce output, I wouldn't pass in an output stream, I would pass in a Consumer.
        – David Conrad
        2 days ago














        an ourput stream is a consumer
        – Ewan
        2 days ago




        an ourput stream is a consumer
        – Ewan
        2 days ago












        No, an OutputStream is not a Consumer.
        – David Conrad
        2 days ago




        No, an OutputStream is not a Consumer.
        – David Conrad
        2 days ago










        up vote
        3
        down vote













        Experience, Domain Knowledge, and Code Reviews.



        And, regardless of how much or how little experience, domain knowledge, or team you have, you cannot avoid the need to refactor as-needed.





        With Experience, you'll start to recognize patterns in the domain-nonspecific methods (and classes) you write. And, if you're at all interested in DRY code, you'll feel bad feelings when you're about a write a method that you instinctively know you'll write variations of in the future. So, you'll intuitively write a parametrized least common denominator instead.



        (This experience may transfer over instinctively into some your domain objects and methods too.)



        With Domain Knowledge, you'll have a sense for which business concepts are closely related, which concepts have variables, which are fairly static, etc..



        With Code Reviews, under- and over-parametrization will more likely be caught before it becomes production code, because your peers will (hopefully) have unique experiences and perspectives, both on the domain and coding in general.





        That said, new developers won't generally have these Spidey Senses or an experienced group of peers to lean on right away. And, even experienced developers benefit from a basic discipline to guide them through new requirements — or through brain-foggy days. So, here's what I'd suggest as a start:




        • Start with the naive implementation, with minimal parametrization.
          (Include any parameters you already know you'll need, obviously ...)

        • Remove magic numbers and strings, moving them to configs and/or parameters

        • Factor "large" methods down into smaller, well-named methods

        • Refactor highly redundant methods (if convenient) into a common denominator, parametrizing the differences.


        These steps don't necessarily occur in the stated order. If you sit down to write a method you already know to be highly redundant with an existing method, jump straight into refactoring if it's convenient. (If it's not going to take significantly more time to refactor than it would be to just write, test, and maintain two methods.)



        But, apart from just having lots of experience and so forth, I advise pretty minimalistic code DRY-ing. It's not hard to refactor obvious violations. And, if you're too zealous, you can end up with "over-DRY" code that's even harder to read, understand, and maintain than the "WET" equivalent.






        share|improve this answer

















        • 2




          So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
          – Koray Tugay
          Nov 27 at 17:45










        • Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
          – svidgen
          Nov 27 at 20:28












        • I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
          – svidgen
          Nov 27 at 20:31








        • 1




          So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
          – Koray Tugay
          Nov 27 at 20:52










        • Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
          – svidgen
          Nov 27 at 21:44















        up vote
        3
        down vote













        Experience, Domain Knowledge, and Code Reviews.



        And, regardless of how much or how little experience, domain knowledge, or team you have, you cannot avoid the need to refactor as-needed.





        With Experience, you'll start to recognize patterns in the domain-nonspecific methods (and classes) you write. And, if you're at all interested in DRY code, you'll feel bad feelings when you're about a write a method that you instinctively know you'll write variations of in the future. So, you'll intuitively write a parametrized least common denominator instead.



        (This experience may transfer over instinctively into some your domain objects and methods too.)



        With Domain Knowledge, you'll have a sense for which business concepts are closely related, which concepts have variables, which are fairly static, etc..



        With Code Reviews, under- and over-parametrization will more likely be caught before it becomes production code, because your peers will (hopefully) have unique experiences and perspectives, both on the domain and coding in general.





        That said, new developers won't generally have these Spidey Senses or an experienced group of peers to lean on right away. And, even experienced developers benefit from a basic discipline to guide them through new requirements — or through brain-foggy days. So, here's what I'd suggest as a start:




        • Start with the naive implementation, with minimal parametrization.
          (Include any parameters you already know you'll need, obviously ...)

        • Remove magic numbers and strings, moving them to configs and/or parameters

        • Factor "large" methods down into smaller, well-named methods

        • Refactor highly redundant methods (if convenient) into a common denominator, parametrizing the differences.


        These steps don't necessarily occur in the stated order. If you sit down to write a method you already know to be highly redundant with an existing method, jump straight into refactoring if it's convenient. (If it's not going to take significantly more time to refactor than it would be to just write, test, and maintain two methods.)



        But, apart from just having lots of experience and so forth, I advise pretty minimalistic code DRY-ing. It's not hard to refactor obvious violations. And, if you're too zealous, you can end up with "over-DRY" code that's even harder to read, understand, and maintain than the "WET" equivalent.






        share|improve this answer

















        • 2




          So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
          – Koray Tugay
          Nov 27 at 17:45










        • Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
          – svidgen
          Nov 27 at 20:28












        • I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
          – svidgen
          Nov 27 at 20:31








        • 1




          So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
          – Koray Tugay
          Nov 27 at 20:52










        • Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
          – svidgen
          Nov 27 at 21:44













        up vote
        3
        down vote










        up vote
        3
        down vote









        Experience, Domain Knowledge, and Code Reviews.



        And, regardless of how much or how little experience, domain knowledge, or team you have, you cannot avoid the need to refactor as-needed.





        With Experience, you'll start to recognize patterns in the domain-nonspecific methods (and classes) you write. And, if you're at all interested in DRY code, you'll feel bad feelings when you're about a write a method that you instinctively know you'll write variations of in the future. So, you'll intuitively write a parametrized least common denominator instead.



        (This experience may transfer over instinctively into some your domain objects and methods too.)



        With Domain Knowledge, you'll have a sense for which business concepts are closely related, which concepts have variables, which are fairly static, etc..



        With Code Reviews, under- and over-parametrization will more likely be caught before it becomes production code, because your peers will (hopefully) have unique experiences and perspectives, both on the domain and coding in general.





        That said, new developers won't generally have these Spidey Senses or an experienced group of peers to lean on right away. And, even experienced developers benefit from a basic discipline to guide them through new requirements — or through brain-foggy days. So, here's what I'd suggest as a start:




        • Start with the naive implementation, with minimal parametrization.
          (Include any parameters you already know you'll need, obviously ...)

        • Remove magic numbers and strings, moving them to configs and/or parameters

        • Factor "large" methods down into smaller, well-named methods

        • Refactor highly redundant methods (if convenient) into a common denominator, parametrizing the differences.


        These steps don't necessarily occur in the stated order. If you sit down to write a method you already know to be highly redundant with an existing method, jump straight into refactoring if it's convenient. (If it's not going to take significantly more time to refactor than it would be to just write, test, and maintain two methods.)



        But, apart from just having lots of experience and so forth, I advise pretty minimalistic code DRY-ing. It's not hard to refactor obvious violations. And, if you're too zealous, you can end up with "over-DRY" code that's even harder to read, understand, and maintain than the "WET" equivalent.






        share|improve this answer












        Experience, Domain Knowledge, and Code Reviews.



        And, regardless of how much or how little experience, domain knowledge, or team you have, you cannot avoid the need to refactor as-needed.





        With Experience, you'll start to recognize patterns in the domain-nonspecific methods (and classes) you write. And, if you're at all interested in DRY code, you'll feel bad feelings when you're about a write a method that you instinctively know you'll write variations of in the future. So, you'll intuitively write a parametrized least common denominator instead.



        (This experience may transfer over instinctively into some your domain objects and methods too.)



        With Domain Knowledge, you'll have a sense for which business concepts are closely related, which concepts have variables, which are fairly static, etc..



        With Code Reviews, under- and over-parametrization will more likely be caught before it becomes production code, because your peers will (hopefully) have unique experiences and perspectives, both on the domain and coding in general.





        That said, new developers won't generally have these Spidey Senses or an experienced group of peers to lean on right away. And, even experienced developers benefit from a basic discipline to guide them through new requirements — or through brain-foggy days. So, here's what I'd suggest as a start:




        • Start with the naive implementation, with minimal parametrization.
          (Include any parameters you already know you'll need, obviously ...)

        • Remove magic numbers and strings, moving them to configs and/or parameters

        • Factor "large" methods down into smaller, well-named methods

        • Refactor highly redundant methods (if convenient) into a common denominator, parametrizing the differences.


        These steps don't necessarily occur in the stated order. If you sit down to write a method you already know to be highly redundant with an existing method, jump straight into refactoring if it's convenient. (If it's not going to take significantly more time to refactor than it would be to just write, test, and maintain two methods.)



        But, apart from just having lots of experience and so forth, I advise pretty minimalistic code DRY-ing. It's not hard to refactor obvious violations. And, if you're too zealous, you can end up with "over-DRY" code that's even harder to read, understand, and maintain than the "WET" equivalent.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 27 at 17:21









        svidgen

        11.2k22754




        11.2k22754








        • 2




          So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
          – Koray Tugay
          Nov 27 at 17:45










        • Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
          – svidgen
          Nov 27 at 20:28












        • I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
          – svidgen
          Nov 27 at 20:31








        • 1




          So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
          – Koray Tugay
          Nov 27 at 20:52










        • Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
          – svidgen
          Nov 27 at 21:44














        • 2




          So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
          – Koray Tugay
          Nov 27 at 17:45










        • Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
          – svidgen
          Nov 27 at 20:28












        • I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
          – svidgen
          Nov 27 at 20:31








        • 1




          So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
          – Koray Tugay
          Nov 27 at 20:52










        • Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
          – svidgen
          Nov 27 at 21:44








        2




        2




        So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
        – Koray Tugay
        Nov 27 at 17:45




        So there is no right answer to If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better? ? It is a yes / no question, but it does not have an answer, right? Or the initial assumption is wrong, destroyCity(City) might not necessarly be better and it actually depends? So it does not mean that I am not a not ethically-trained software engineer because I directly implemented without any parameters the first place? I mean what is the answer to the concrete question I am asking?
        – Koray Tugay
        Nov 27 at 17:45












        Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
        – svidgen
        Nov 27 at 20:28






        Your question sort of asks a few questions. The answer to the title question is, "experience, domain knowledge, code reviews ... and ... don't be afraid to refactor." The answer to any concrete "are these the right parameters for method ABC" question is ... "I don't know. Why are you asking? Is something wrong with the number of parameters it currently has??? If so, articulate it. Fix it." ... I might refer you to "the POAP" for further guidance: You need to understand why you're doing what you're doing!
        – svidgen
        Nov 27 at 20:28














        I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
        – svidgen
        Nov 27 at 20:31






        I mean ... let's even take a step back from the destroyBaghdad() method. What's the context? Is it a video game where the end of the game results in Baghdad being destroyed??? If so ... destroyBaghdad() might be a perfectly reasonable method name/signature ...
        – svidgen
        Nov 27 at 20:31






        1




        1




        So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
        – Koray Tugay
        Nov 27 at 20:52




        So you do not agree with the cited quote in my question, right? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. If you were in the room with Nathaniel Borenstein, you would argue him that it actually depends and his statement is not correct? I mean it is beautiful that many people are answering, spending their times and energy, but I do not see a single concrete answer anywhere. Spidey-senses, code reviews.. But what is the answer to is takeActionOnCity(Action action, City city) better? null?
        – Koray Tugay
        Nov 27 at 20:52












        Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
        – svidgen
        Nov 27 at 21:44




        Heh. Sure, I guess I might argue the point with Nathaniel Borenstein if I were in a room with him and he was spouting useless dogma... takeActionOnCity(Action a ...) looks like an abstraction you'd end up with if you needed the command pattern -- if you needed strong "implicit" auditing or the ability to perform rollbacks and retries. Otherwise, it looks to me like it just makes harder-to-read code than is necessary.
        – svidgen
        Nov 27 at 21:44










        up vote
        3
        down vote













        In short, don't engineer your software for reusability because no end user cares if your functions can be reused. Instead, engineer for design comprehensibility -- is my code easy for someone else or my future forgetful self to understand? -- and design flexibility -- when I inevitably have to fix bugs, add features, or otherwise modify functionality, how much will my code resist the changes? The only thing your customer cares about is how quickly you can respond when she reports a bug or asks for a change. Asking these questions about your design incidentally tends to result in code that is reusable, but this approach keeps you focused on avoiding the real problems you will face over the life of that code so you can better serve the end user rather than pursuing lofty, impractical "engineering" ideals to please the neck-beards.



        For something as simple as the example you provided, your initial implementation is fine because of how small it is, but this straightforward design will become hard to understand and brittle if you try to jam too much functional flexibility (as opposed to design flexibility) into one procedure. Below is my explanation of my preferred approach to designing complex systems for comprehensibility and flexibility which I hope will demonstrate what I mean by them. I would not employ this strategy for something that could be written in fewer than 20 lines in a single procedure because something so small already meets my criteria for comprehensibility and flexibility as it is.





        Objects, not Procedures



        Rather than using classes like old-school modules with a bunch of routines you call to execute the things your software should do, consider modeling the domain as objects which interact and cooperate to accomplish the task at hand. Methods in an Object-Oriented paradigm were originally created to be signals between objects so that Object1 could tell Object2 to do its thing, whatever that is, and possibly receive a return signal. This is because the Object-Oriented paradigm is inherently about modeling your domain objects and their interactions rather than a fancy way to organize the same old functions and procedures of the Imperative paradigm. In the case of the void destroyBaghdad example, instead of trying to write a context-less generic method to handle the destruction of Baghdad or any other thing (which could quickly grow complex, hard to understand, and brittle), every thing that can be destroyed should be responsible for understanding how to destroy itself. For example, you have an interface that describes the behavior of things that can be destroyed:



        interface Destroyable {
        void destroy();
        }


        Then you have a city which implements this interface:



        class City implements Destroyable {
        @Override
        public void destroy() {
        ...code that destroys the city
        }
        }


        Nothing that calls for the destruction of an instance of City will ever care how that happens, so there is no reason for that code to exist anywhere outside of City::destroy, and indeed, intimate knowledge of the inner workings of City outside of itself would be tight coupling which reduces felxibility since you have to consider those outside elements should you ever need to modify the behavior of City. This is the true purpose behind encapsulation. Think of it like every object has its own API which should enable you to do anything you need to with it so you can let it worry about fulfilling your requests.



        Delegation, not "Control"



        Now, whether your implementing class is City or Baghdad depends on how generic the process of destroying the city turns out to be. In all probability, a City will be composed of smaller pieces that will need to be destroyed individually to accomplish the total destruction of the city, so in that case, each of those pieces would also implement Destroyable, and they would each be instructed by the City to destroy themselves in the same way someone from outside requested the City to destroy itself.



        interface Part extends Destroyable {
        ...part-specific methods
        }

        class Building implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class Street implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class City implements Destroyable {
        public List<Part> parts() {...}

        @Override
        public void destroy() {
        parts().forEach(Destroyable::destroy);
        }
        }


        If you want to get really crazy and implement the idea of a Bomb that is dropped on a location and destroys everything within a certain radius, it might look something like this:



        class Bomb {
        private final Integer radius;

        public Bomb(final Integer radius) {
        this.radius = radius;
        }

        public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
        grid,
        target,
        this.radius
        ).forEach(Destroyable::destroy);
        }
        }


        ObjectsByRadius represents a set of objects that is calculated for the Bomb from the inputs because the Bomb does not care how that calculation is made so long as it can work with the objects. This is reusable incidentally, but the main goal is to isolate the calculation from the processes of dropping the Bomb and destroying the objects so you can comprehend each piece and how they fit together and change the behavior of an individual piece without having to reshape the entire algorithm.



        Interactions, not Algorithms



        Instead of trying to guess at the right number of parameters for a complex algorithm, it makes more sense to model the process as a set of interacting objects, each with extremely narrow roles, since it will give you the ability to model the complexity of your process through the interactions between these well-defined, easy to comprehend, and nearly unchanging objects. When done correctly, this makes even some of the most complex modifications as trivial as implementing an interface or two and reworking which objects are instantiated in your main() method.



        I'd give you something to your original example, but I honestly can't figure out what it means to "print... Day Light Savings." What I can say about that category of problem is that any time you are performing a calculation, the result of which could be formatted a number of ways, my preferred way to break that down is like this:



        interface Result {
        String print();
        }

        class Caclulation {
        private final Parameter paramater1;

        private final Parameter parameter2;

        public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
        }

        public Result calculate() {
        ...calculate the result
        }
        }

        class FormattedResult {
        private final Result result;

        public FormattedResult(final Result result) {
        this.result = result;
        }

        @Override
        public String print() {
        ...interact with this.result to format it and return the formatted String
        }
        }


        Since your example uses classes from the Java library which don't support this design, you could just use the API of ZonedDateTime directly. The idea here is that each calculation is encapsulated within its own object. It makes no assumptions about how many times it should run or how it should format the result. It is exclusively concerned with performing the simplest form of the calculation. This makes it both easy to understand and flexible to change. Likewise, the Result is exclusively concerned with encapsulating the result of the calculation, and the FormattedResult is exclusively concerned with interacting with the Result to format it according to the rules we define. In this way, we can find the perfect number of arguments for each of our methods since they each have a well-defined task. It's also much simpler to modify moving forward so long as the interfaces don't change (which they aren't as likely to do if you've properly minimized the responsibilities of your objects). Our main() method might look like this:



        class App {
        public static void main(String args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
        System.out.println(
        new FormattedResult(
        new Calculation(
        set.get(0),
        set.get(1)
        ).calculate()
        ).print()
        );
        });
        }
        }


        As a matter of fact, Object-Oriented Programming was invented specifically as a solution to the complexity/flexibility problem of the Imperative paradigm because there is just no good answer (that everyone can agree on or arrive at independently, anyhow) to how to optimally specify Imperative functions and procedures within the idiom.






        share|improve this answer























        • This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
          – maple_shaft
          2 days ago










        • @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
          – Stuporman
          2 days ago










        • @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
          – Stuporman
          2 days ago










        • @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
          – Stuporman
          2 days ago






        • 1




          Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
          – Stephen J
          yesterday















        up vote
        3
        down vote













        In short, don't engineer your software for reusability because no end user cares if your functions can be reused. Instead, engineer for design comprehensibility -- is my code easy for someone else or my future forgetful self to understand? -- and design flexibility -- when I inevitably have to fix bugs, add features, or otherwise modify functionality, how much will my code resist the changes? The only thing your customer cares about is how quickly you can respond when she reports a bug or asks for a change. Asking these questions about your design incidentally tends to result in code that is reusable, but this approach keeps you focused on avoiding the real problems you will face over the life of that code so you can better serve the end user rather than pursuing lofty, impractical "engineering" ideals to please the neck-beards.



        For something as simple as the example you provided, your initial implementation is fine because of how small it is, but this straightforward design will become hard to understand and brittle if you try to jam too much functional flexibility (as opposed to design flexibility) into one procedure. Below is my explanation of my preferred approach to designing complex systems for comprehensibility and flexibility which I hope will demonstrate what I mean by them. I would not employ this strategy for something that could be written in fewer than 20 lines in a single procedure because something so small already meets my criteria for comprehensibility and flexibility as it is.





        Objects, not Procedures



        Rather than using classes like old-school modules with a bunch of routines you call to execute the things your software should do, consider modeling the domain as objects which interact and cooperate to accomplish the task at hand. Methods in an Object-Oriented paradigm were originally created to be signals between objects so that Object1 could tell Object2 to do its thing, whatever that is, and possibly receive a return signal. This is because the Object-Oriented paradigm is inherently about modeling your domain objects and their interactions rather than a fancy way to organize the same old functions and procedures of the Imperative paradigm. In the case of the void destroyBaghdad example, instead of trying to write a context-less generic method to handle the destruction of Baghdad or any other thing (which could quickly grow complex, hard to understand, and brittle), every thing that can be destroyed should be responsible for understanding how to destroy itself. For example, you have an interface that describes the behavior of things that can be destroyed:



        interface Destroyable {
        void destroy();
        }


        Then you have a city which implements this interface:



        class City implements Destroyable {
        @Override
        public void destroy() {
        ...code that destroys the city
        }
        }


        Nothing that calls for the destruction of an instance of City will ever care how that happens, so there is no reason for that code to exist anywhere outside of City::destroy, and indeed, intimate knowledge of the inner workings of City outside of itself would be tight coupling which reduces felxibility since you have to consider those outside elements should you ever need to modify the behavior of City. This is the true purpose behind encapsulation. Think of it like every object has its own API which should enable you to do anything you need to with it so you can let it worry about fulfilling your requests.



        Delegation, not "Control"



        Now, whether your implementing class is City or Baghdad depends on how generic the process of destroying the city turns out to be. In all probability, a City will be composed of smaller pieces that will need to be destroyed individually to accomplish the total destruction of the city, so in that case, each of those pieces would also implement Destroyable, and they would each be instructed by the City to destroy themselves in the same way someone from outside requested the City to destroy itself.



        interface Part extends Destroyable {
        ...part-specific methods
        }

        class Building implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class Street implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class City implements Destroyable {
        public List<Part> parts() {...}

        @Override
        public void destroy() {
        parts().forEach(Destroyable::destroy);
        }
        }


        If you want to get really crazy and implement the idea of a Bomb that is dropped on a location and destroys everything within a certain radius, it might look something like this:



        class Bomb {
        private final Integer radius;

        public Bomb(final Integer radius) {
        this.radius = radius;
        }

        public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
        grid,
        target,
        this.radius
        ).forEach(Destroyable::destroy);
        }
        }


        ObjectsByRadius represents a set of objects that is calculated for the Bomb from the inputs because the Bomb does not care how that calculation is made so long as it can work with the objects. This is reusable incidentally, but the main goal is to isolate the calculation from the processes of dropping the Bomb and destroying the objects so you can comprehend each piece and how they fit together and change the behavior of an individual piece without having to reshape the entire algorithm.



        Interactions, not Algorithms



        Instead of trying to guess at the right number of parameters for a complex algorithm, it makes more sense to model the process as a set of interacting objects, each with extremely narrow roles, since it will give you the ability to model the complexity of your process through the interactions between these well-defined, easy to comprehend, and nearly unchanging objects. When done correctly, this makes even some of the most complex modifications as trivial as implementing an interface or two and reworking which objects are instantiated in your main() method.



        I'd give you something to your original example, but I honestly can't figure out what it means to "print... Day Light Savings." What I can say about that category of problem is that any time you are performing a calculation, the result of which could be formatted a number of ways, my preferred way to break that down is like this:



        interface Result {
        String print();
        }

        class Caclulation {
        private final Parameter paramater1;

        private final Parameter parameter2;

        public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
        }

        public Result calculate() {
        ...calculate the result
        }
        }

        class FormattedResult {
        private final Result result;

        public FormattedResult(final Result result) {
        this.result = result;
        }

        @Override
        public String print() {
        ...interact with this.result to format it and return the formatted String
        }
        }


        Since your example uses classes from the Java library which don't support this design, you could just use the API of ZonedDateTime directly. The idea here is that each calculation is encapsulated within its own object. It makes no assumptions about how many times it should run or how it should format the result. It is exclusively concerned with performing the simplest form of the calculation. This makes it both easy to understand and flexible to change. Likewise, the Result is exclusively concerned with encapsulating the result of the calculation, and the FormattedResult is exclusively concerned with interacting with the Result to format it according to the rules we define. In this way, we can find the perfect number of arguments for each of our methods since they each have a well-defined task. It's also much simpler to modify moving forward so long as the interfaces don't change (which they aren't as likely to do if you've properly minimized the responsibilities of your objects). Our main() method might look like this:



        class App {
        public static void main(String args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
        System.out.println(
        new FormattedResult(
        new Calculation(
        set.get(0),
        set.get(1)
        ).calculate()
        ).print()
        );
        });
        }
        }


        As a matter of fact, Object-Oriented Programming was invented specifically as a solution to the complexity/flexibility problem of the Imperative paradigm because there is just no good answer (that everyone can agree on or arrive at independently, anyhow) to how to optimally specify Imperative functions and procedures within the idiom.






        share|improve this answer























        • This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
          – maple_shaft
          2 days ago










        • @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
          – Stuporman
          2 days ago










        • @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
          – Stuporman
          2 days ago










        • @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
          – Stuporman
          2 days ago






        • 1




          Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
          – Stephen J
          yesterday













        up vote
        3
        down vote










        up vote
        3
        down vote









        In short, don't engineer your software for reusability because no end user cares if your functions can be reused. Instead, engineer for design comprehensibility -- is my code easy for someone else or my future forgetful self to understand? -- and design flexibility -- when I inevitably have to fix bugs, add features, or otherwise modify functionality, how much will my code resist the changes? The only thing your customer cares about is how quickly you can respond when she reports a bug or asks for a change. Asking these questions about your design incidentally tends to result in code that is reusable, but this approach keeps you focused on avoiding the real problems you will face over the life of that code so you can better serve the end user rather than pursuing lofty, impractical "engineering" ideals to please the neck-beards.



        For something as simple as the example you provided, your initial implementation is fine because of how small it is, but this straightforward design will become hard to understand and brittle if you try to jam too much functional flexibility (as opposed to design flexibility) into one procedure. Below is my explanation of my preferred approach to designing complex systems for comprehensibility and flexibility which I hope will demonstrate what I mean by them. I would not employ this strategy for something that could be written in fewer than 20 lines in a single procedure because something so small already meets my criteria for comprehensibility and flexibility as it is.





        Objects, not Procedures



        Rather than using classes like old-school modules with a bunch of routines you call to execute the things your software should do, consider modeling the domain as objects which interact and cooperate to accomplish the task at hand. Methods in an Object-Oriented paradigm were originally created to be signals between objects so that Object1 could tell Object2 to do its thing, whatever that is, and possibly receive a return signal. This is because the Object-Oriented paradigm is inherently about modeling your domain objects and their interactions rather than a fancy way to organize the same old functions and procedures of the Imperative paradigm. In the case of the void destroyBaghdad example, instead of trying to write a context-less generic method to handle the destruction of Baghdad or any other thing (which could quickly grow complex, hard to understand, and brittle), every thing that can be destroyed should be responsible for understanding how to destroy itself. For example, you have an interface that describes the behavior of things that can be destroyed:



        interface Destroyable {
        void destroy();
        }


        Then you have a city which implements this interface:



        class City implements Destroyable {
        @Override
        public void destroy() {
        ...code that destroys the city
        }
        }


        Nothing that calls for the destruction of an instance of City will ever care how that happens, so there is no reason for that code to exist anywhere outside of City::destroy, and indeed, intimate knowledge of the inner workings of City outside of itself would be tight coupling which reduces felxibility since you have to consider those outside elements should you ever need to modify the behavior of City. This is the true purpose behind encapsulation. Think of it like every object has its own API which should enable you to do anything you need to with it so you can let it worry about fulfilling your requests.



        Delegation, not "Control"



        Now, whether your implementing class is City or Baghdad depends on how generic the process of destroying the city turns out to be. In all probability, a City will be composed of smaller pieces that will need to be destroyed individually to accomplish the total destruction of the city, so in that case, each of those pieces would also implement Destroyable, and they would each be instructed by the City to destroy themselves in the same way someone from outside requested the City to destroy itself.



        interface Part extends Destroyable {
        ...part-specific methods
        }

        class Building implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class Street implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class City implements Destroyable {
        public List<Part> parts() {...}

        @Override
        public void destroy() {
        parts().forEach(Destroyable::destroy);
        }
        }


        If you want to get really crazy and implement the idea of a Bomb that is dropped on a location and destroys everything within a certain radius, it might look something like this:



        class Bomb {
        private final Integer radius;

        public Bomb(final Integer radius) {
        this.radius = radius;
        }

        public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
        grid,
        target,
        this.radius
        ).forEach(Destroyable::destroy);
        }
        }


        ObjectsByRadius represents a set of objects that is calculated for the Bomb from the inputs because the Bomb does not care how that calculation is made so long as it can work with the objects. This is reusable incidentally, but the main goal is to isolate the calculation from the processes of dropping the Bomb and destroying the objects so you can comprehend each piece and how they fit together and change the behavior of an individual piece without having to reshape the entire algorithm.



        Interactions, not Algorithms



        Instead of trying to guess at the right number of parameters for a complex algorithm, it makes more sense to model the process as a set of interacting objects, each with extremely narrow roles, since it will give you the ability to model the complexity of your process through the interactions between these well-defined, easy to comprehend, and nearly unchanging objects. When done correctly, this makes even some of the most complex modifications as trivial as implementing an interface or two and reworking which objects are instantiated in your main() method.



        I'd give you something to your original example, but I honestly can't figure out what it means to "print... Day Light Savings." What I can say about that category of problem is that any time you are performing a calculation, the result of which could be formatted a number of ways, my preferred way to break that down is like this:



        interface Result {
        String print();
        }

        class Caclulation {
        private final Parameter paramater1;

        private final Parameter parameter2;

        public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
        }

        public Result calculate() {
        ...calculate the result
        }
        }

        class FormattedResult {
        private final Result result;

        public FormattedResult(final Result result) {
        this.result = result;
        }

        @Override
        public String print() {
        ...interact with this.result to format it and return the formatted String
        }
        }


        Since your example uses classes from the Java library which don't support this design, you could just use the API of ZonedDateTime directly. The idea here is that each calculation is encapsulated within its own object. It makes no assumptions about how many times it should run or how it should format the result. It is exclusively concerned with performing the simplest form of the calculation. This makes it both easy to understand and flexible to change. Likewise, the Result is exclusively concerned with encapsulating the result of the calculation, and the FormattedResult is exclusively concerned with interacting with the Result to format it according to the rules we define. In this way, we can find the perfect number of arguments for each of our methods since they each have a well-defined task. It's also much simpler to modify moving forward so long as the interfaces don't change (which they aren't as likely to do if you've properly minimized the responsibilities of your objects). Our main() method might look like this:



        class App {
        public static void main(String args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
        System.out.println(
        new FormattedResult(
        new Calculation(
        set.get(0),
        set.get(1)
        ).calculate()
        ).print()
        );
        });
        }
        }


        As a matter of fact, Object-Oriented Programming was invented specifically as a solution to the complexity/flexibility problem of the Imperative paradigm because there is just no good answer (that everyone can agree on or arrive at independently, anyhow) to how to optimally specify Imperative functions and procedures within the idiom.






        share|improve this answer














        In short, don't engineer your software for reusability because no end user cares if your functions can be reused. Instead, engineer for design comprehensibility -- is my code easy for someone else or my future forgetful self to understand? -- and design flexibility -- when I inevitably have to fix bugs, add features, or otherwise modify functionality, how much will my code resist the changes? The only thing your customer cares about is how quickly you can respond when she reports a bug or asks for a change. Asking these questions about your design incidentally tends to result in code that is reusable, but this approach keeps you focused on avoiding the real problems you will face over the life of that code so you can better serve the end user rather than pursuing lofty, impractical "engineering" ideals to please the neck-beards.



        For something as simple as the example you provided, your initial implementation is fine because of how small it is, but this straightforward design will become hard to understand and brittle if you try to jam too much functional flexibility (as opposed to design flexibility) into one procedure. Below is my explanation of my preferred approach to designing complex systems for comprehensibility and flexibility which I hope will demonstrate what I mean by them. I would not employ this strategy for something that could be written in fewer than 20 lines in a single procedure because something so small already meets my criteria for comprehensibility and flexibility as it is.





        Objects, not Procedures



        Rather than using classes like old-school modules with a bunch of routines you call to execute the things your software should do, consider modeling the domain as objects which interact and cooperate to accomplish the task at hand. Methods in an Object-Oriented paradigm were originally created to be signals between objects so that Object1 could tell Object2 to do its thing, whatever that is, and possibly receive a return signal. This is because the Object-Oriented paradigm is inherently about modeling your domain objects and their interactions rather than a fancy way to organize the same old functions and procedures of the Imperative paradigm. In the case of the void destroyBaghdad example, instead of trying to write a context-less generic method to handle the destruction of Baghdad or any other thing (which could quickly grow complex, hard to understand, and brittle), every thing that can be destroyed should be responsible for understanding how to destroy itself. For example, you have an interface that describes the behavior of things that can be destroyed:



        interface Destroyable {
        void destroy();
        }


        Then you have a city which implements this interface:



        class City implements Destroyable {
        @Override
        public void destroy() {
        ...code that destroys the city
        }
        }


        Nothing that calls for the destruction of an instance of City will ever care how that happens, so there is no reason for that code to exist anywhere outside of City::destroy, and indeed, intimate knowledge of the inner workings of City outside of itself would be tight coupling which reduces felxibility since you have to consider those outside elements should you ever need to modify the behavior of City. This is the true purpose behind encapsulation. Think of it like every object has its own API which should enable you to do anything you need to with it so you can let it worry about fulfilling your requests.



        Delegation, not "Control"



        Now, whether your implementing class is City or Baghdad depends on how generic the process of destroying the city turns out to be. In all probability, a City will be composed of smaller pieces that will need to be destroyed individually to accomplish the total destruction of the city, so in that case, each of those pieces would also implement Destroyable, and they would each be instructed by the City to destroy themselves in the same way someone from outside requested the City to destroy itself.



        interface Part extends Destroyable {
        ...part-specific methods
        }

        class Building implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class Street implements Part {
        ...part-specific methods
        @Override
        public void destroy() {
        ...code to destroy a building
        }
        }

        class City implements Destroyable {
        public List<Part> parts() {...}

        @Override
        public void destroy() {
        parts().forEach(Destroyable::destroy);
        }
        }


        If you want to get really crazy and implement the idea of a Bomb that is dropped on a location and destroys everything within a certain radius, it might look something like this:



        class Bomb {
        private final Integer radius;

        public Bomb(final Integer radius) {
        this.radius = radius;
        }

        public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
        grid,
        target,
        this.radius
        ).forEach(Destroyable::destroy);
        }
        }


        ObjectsByRadius represents a set of objects that is calculated for the Bomb from the inputs because the Bomb does not care how that calculation is made so long as it can work with the objects. This is reusable incidentally, but the main goal is to isolate the calculation from the processes of dropping the Bomb and destroying the objects so you can comprehend each piece and how they fit together and change the behavior of an individual piece without having to reshape the entire algorithm.



        Interactions, not Algorithms



        Instead of trying to guess at the right number of parameters for a complex algorithm, it makes more sense to model the process as a set of interacting objects, each with extremely narrow roles, since it will give you the ability to model the complexity of your process through the interactions between these well-defined, easy to comprehend, and nearly unchanging objects. When done correctly, this makes even some of the most complex modifications as trivial as implementing an interface or two and reworking which objects are instantiated in your main() method.



        I'd give you something to your original example, but I honestly can't figure out what it means to "print... Day Light Savings." What I can say about that category of problem is that any time you are performing a calculation, the result of which could be formatted a number of ways, my preferred way to break that down is like this:



        interface Result {
        String print();
        }

        class Caclulation {
        private final Parameter paramater1;

        private final Parameter parameter2;

        public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
        }

        public Result calculate() {
        ...calculate the result
        }
        }

        class FormattedResult {
        private final Result result;

        public FormattedResult(final Result result) {
        this.result = result;
        }

        @Override
        public String print() {
        ...interact with this.result to format it and return the formatted String
        }
        }


        Since your example uses classes from the Java library which don't support this design, you could just use the API of ZonedDateTime directly. The idea here is that each calculation is encapsulated within its own object. It makes no assumptions about how many times it should run or how it should format the result. It is exclusively concerned with performing the simplest form of the calculation. This makes it both easy to understand and flexible to change. Likewise, the Result is exclusively concerned with encapsulating the result of the calculation, and the FormattedResult is exclusively concerned with interacting with the Result to format it according to the rules we define. In this way, we can find the perfect number of arguments for each of our methods since they each have a well-defined task. It's also much simpler to modify moving forward so long as the interfaces don't change (which they aren't as likely to do if you've properly minimized the responsibilities of your objects). Our main() method might look like this:



        class App {
        public static void main(String args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
        System.out.println(
        new FormattedResult(
        new Calculation(
        set.get(0),
        set.get(1)
        ).calculate()
        ).print()
        );
        });
        }
        }


        As a matter of fact, Object-Oriented Programming was invented specifically as a solution to the complexity/flexibility problem of the Imperative paradigm because there is just no good answer (that everyone can agree on or arrive at independently, anyhow) to how to optimally specify Imperative functions and procedures within the idiom.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 14 hours ago

























        answered Nov 28 at 4:42









        Stuporman

        1393




        1393












        • This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
          – maple_shaft
          2 days ago










        • @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
          – Stuporman
          2 days ago










        • @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
          – Stuporman
          2 days ago










        • @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
          – Stuporman
          2 days ago






        • 1




          Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
          – Stephen J
          yesterday


















        • This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
          – maple_shaft
          2 days ago










        • @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
          – Stuporman
          2 days ago










        • @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
          – Stuporman
          2 days ago










        • @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
          – Stuporman
          2 days ago






        • 1




          Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
          – Stephen J
          yesterday
















        This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
        – maple_shaft
        2 days ago




        This is a very detailed and thought out answer, but unfortunately I think it misses the mark on what the OP was really asking for. He wasn't asking for a lesson on good OOP practices to solve his specious example, he was asking about the criteria for where we decide investment of time in a solution vs generalization.
        – maple_shaft
        2 days ago












        @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
        – Stuporman
        2 days ago




        @maple_shaft Maybe I missed the mark, but I think you have, too. The OP doesn't ask about the investment of time vs. generalization. He asks "How do I know how reusable my methods should be?" He goes on to ask in the body of his question, "If destroyCity(City city) is better than destroyBaghdad(), is takeActionOnCity(Action action, City city) even better? Why / why not?" I made the case for an alternative approach to engineering solutions that I believe solves the problem of figuring out how generic to make methods and provided examples to support my claim. I'm sorry you didn't like it.
        – Stuporman
        2 days ago












        @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
        – Stuporman
        2 days ago




        @maple_shaft Frankly, only the OP can make the determination if my answer was relevant to his question since the rest of us could fight wars defending our interpretations of his intentions, all of which could be equally wrong.
        – Stuporman
        2 days ago












        @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
        – Stuporman
        2 days ago




        @maple_shaft I added an intro to try to clarify how it relates to the question and provided a clear delineation between the answer and the example implementation. Is that better?
        – Stuporman
        2 days ago




        1




        1




        Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
        – Stephen J
        yesterday




        Honestly, if you apply all of these principles, the answer will come naturally, be smooth, and massively readable. Plus, changeable without much fuss. I don't know who you are, but I wish there was more of you! I keep ripping out Reactive code for decent OO, and it's ALWAYS half the size, more readable, more controllable, and still has threading/splitting/mapping. I think React is for people who don't understand the "basic" concepts you just listed.
        – Stephen J
        yesterday










        up vote
        1
        down vote













        There's a clear process you can follow:




        • Write a failing test for a single feature which is in itself a "thing" (i.e., not some arbitrary split of a feature where neither half really makes sense).

        • Write the absolute minimum code to make it pass green, not a line more.

        • Rinse and repeat.

        • (Refactor relentlessly if necessary, which should be easy due to the great test coverage.)


        This turns up with - at least in the opinion of some people - pretty much optimal code, since it is as small as possible, each finished feature takes as little time as possible (which might or might not be true if you look at the finished product after refactoring), and it has very good test coverage. It also noticeably avoids over-engineered too-generic methods or classes.



        This also gives you very clear instructions when to make things generic and when to specialize.



        I find your city example weird; I would very likely never ever hardcode a city name. It is so obvious that additional cities will be included later, whatever it is you're doing. But another example would be colors. In some circumstances, hardcoding "red" or "green" would be a possibility. For example, traffic lights are such an ubiquitous color that you can just get away with it (and you can always refactor). The difference is that "red" and "green" have universal, "hardcoded" meaning in our world, it is incredibly unlikely that it will ever change, and there is not really an alternative either.



        Your first daylight savings method is simply broken. While it conforms to the specifications, the hardcoded 2018 is particularly bad because a) it is not mentioned in the technical "contract" (in the method name, in this case), and b) it will be out of date soon, so breakage is included from the get-go. For things that are time/date related, it would very seldomly make sense to hardcode a specific value since, well, time moves on. But apart from that, everything else is up for discussion. If you give it a simple year and then always calculate the complete year, go ahead. Most of the things you listed (formatting, choice of a smaller range, etc.) screams that your method is doing too much, and it should instead probably return a list/array of values so the caller can do the formatting/filtering themselves.



        But at the end of the day, most of this is opinion, taste, experience and personal bias, so don't fret too much about it.






        share|improve this answer























        • Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
          – dwizum
          2 days ago










        • @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
          – AnoE
          yesterday















        up vote
        1
        down vote













        There's a clear process you can follow:




        • Write a failing test for a single feature which is in itself a "thing" (i.e., not some arbitrary split of a feature where neither half really makes sense).

        • Write the absolute minimum code to make it pass green, not a line more.

        • Rinse and repeat.

        • (Refactor relentlessly if necessary, which should be easy due to the great test coverage.)


        This turns up with - at least in the opinion of some people - pretty much optimal code, since it is as small as possible, each finished feature takes as little time as possible (which might or might not be true if you look at the finished product after refactoring), and it has very good test coverage. It also noticeably avoids over-engineered too-generic methods or classes.



        This also gives you very clear instructions when to make things generic and when to specialize.



        I find your city example weird; I would very likely never ever hardcode a city name. It is so obvious that additional cities will be included later, whatever it is you're doing. But another example would be colors. In some circumstances, hardcoding "red" or "green" would be a possibility. For example, traffic lights are such an ubiquitous color that you can just get away with it (and you can always refactor). The difference is that "red" and "green" have universal, "hardcoded" meaning in our world, it is incredibly unlikely that it will ever change, and there is not really an alternative either.



        Your first daylight savings method is simply broken. While it conforms to the specifications, the hardcoded 2018 is particularly bad because a) it is not mentioned in the technical "contract" (in the method name, in this case), and b) it will be out of date soon, so breakage is included from the get-go. For things that are time/date related, it would very seldomly make sense to hardcode a specific value since, well, time moves on. But apart from that, everything else is up for discussion. If you give it a simple year and then always calculate the complete year, go ahead. Most of the things you listed (formatting, choice of a smaller range, etc.) screams that your method is doing too much, and it should instead probably return a list/array of values so the caller can do the formatting/filtering themselves.



        But at the end of the day, most of this is opinion, taste, experience and personal bias, so don't fret too much about it.






        share|improve this answer























        • Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
          – dwizum
          2 days ago










        • @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
          – AnoE
          yesterday













        up vote
        1
        down vote










        up vote
        1
        down vote









        There's a clear process you can follow:




        • Write a failing test for a single feature which is in itself a "thing" (i.e., not some arbitrary split of a feature where neither half really makes sense).

        • Write the absolute minimum code to make it pass green, not a line more.

        • Rinse and repeat.

        • (Refactor relentlessly if necessary, which should be easy due to the great test coverage.)


        This turns up with - at least in the opinion of some people - pretty much optimal code, since it is as small as possible, each finished feature takes as little time as possible (which might or might not be true if you look at the finished product after refactoring), and it has very good test coverage. It also noticeably avoids over-engineered too-generic methods or classes.



        This also gives you very clear instructions when to make things generic and when to specialize.



        I find your city example weird; I would very likely never ever hardcode a city name. It is so obvious that additional cities will be included later, whatever it is you're doing. But another example would be colors. In some circumstances, hardcoding "red" or "green" would be a possibility. For example, traffic lights are such an ubiquitous color that you can just get away with it (and you can always refactor). The difference is that "red" and "green" have universal, "hardcoded" meaning in our world, it is incredibly unlikely that it will ever change, and there is not really an alternative either.



        Your first daylight savings method is simply broken. While it conforms to the specifications, the hardcoded 2018 is particularly bad because a) it is not mentioned in the technical "contract" (in the method name, in this case), and b) it will be out of date soon, so breakage is included from the get-go. For things that are time/date related, it would very seldomly make sense to hardcode a specific value since, well, time moves on. But apart from that, everything else is up for discussion. If you give it a simple year and then always calculate the complete year, go ahead. Most of the things you listed (formatting, choice of a smaller range, etc.) screams that your method is doing too much, and it should instead probably return a list/array of values so the caller can do the formatting/filtering themselves.



        But at the end of the day, most of this is opinion, taste, experience and personal bias, so don't fret too much about it.






        share|improve this answer














        There's a clear process you can follow:




        • Write a failing test for a single feature which is in itself a "thing" (i.e., not some arbitrary split of a feature where neither half really makes sense).

        • Write the absolute minimum code to make it pass green, not a line more.

        • Rinse and repeat.

        • (Refactor relentlessly if necessary, which should be easy due to the great test coverage.)


        This turns up with - at least in the opinion of some people - pretty much optimal code, since it is as small as possible, each finished feature takes as little time as possible (which might or might not be true if you look at the finished product after refactoring), and it has very good test coverage. It also noticeably avoids over-engineered too-generic methods or classes.



        This also gives you very clear instructions when to make things generic and when to specialize.



        I find your city example weird; I would very likely never ever hardcode a city name. It is so obvious that additional cities will be included later, whatever it is you're doing. But another example would be colors. In some circumstances, hardcoding "red" or "green" would be a possibility. For example, traffic lights are such an ubiquitous color that you can just get away with it (and you can always refactor). The difference is that "red" and "green" have universal, "hardcoded" meaning in our world, it is incredibly unlikely that it will ever change, and there is not really an alternative either.



        Your first daylight savings method is simply broken. While it conforms to the specifications, the hardcoded 2018 is particularly bad because a) it is not mentioned in the technical "contract" (in the method name, in this case), and b) it will be out of date soon, so breakage is included from the get-go. For things that are time/date related, it would very seldomly make sense to hardcode a specific value since, well, time moves on. But apart from that, everything else is up for discussion. If you give it a simple year and then always calculate the complete year, go ahead. Most of the things you listed (formatting, choice of a smaller range, etc.) screams that your method is doing too much, and it should instead probably return a list/array of values so the caller can do the formatting/filtering themselves.



        But at the end of the day, most of this is opinion, taste, experience and personal bias, so don't fret too much about it.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited yesterday

























        answered Nov 28 at 0:08









        AnoE

        3,883717




        3,883717












        • Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
          – dwizum
          2 days ago










        • @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
          – AnoE
          yesterday


















        • Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
          – dwizum
          2 days ago










        • @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
          – AnoE
          yesterday
















        Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
        – dwizum
        2 days ago




        Regarding your second to last paragraph - look at the "requirements" initially given, ie those that the first method was based on. It specifies 2018, so the code is technically correct (and would probably match your feature-driven approach).
        – dwizum
        2 days ago












        @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
        – AnoE
        yesterday




        @dwizum, it is correct regarding the requirements, but there's the method name is misleading. In 2019, any programmer just looking at the method name would assume that it's doing whatever (maybe return the values for the current year), not 2018... I'll add a sentence to the answer to make more clear what I meant.
        – AnoE
        yesterday










        up vote
        0
        down vote













        A good rule of thumb is: your method should be as reusable as… reusable.



        If you expect that you will call your method only in one place, it should have only parameters that are known to the call site and that are not available to this method.



        If you have more callers, you can introduce new parameters as long as other callers may pass those parameters; otherwise you need new method.



        As the number of callers may grow in time, you need to be prepared for refactoring or overloading. In many cases it means that you should feel safe to select expression and run “extract parameter” action of your IDE.






        share|improve this answer

























          up vote
          0
          down vote













          A good rule of thumb is: your method should be as reusable as… reusable.



          If you expect that you will call your method only in one place, it should have only parameters that are known to the call site and that are not available to this method.



          If you have more callers, you can introduce new parameters as long as other callers may pass those parameters; otherwise you need new method.



          As the number of callers may grow in time, you need to be prepared for refactoring or overloading. In many cases it means that you should feel safe to select expression and run “extract parameter” action of your IDE.






          share|improve this answer























            up vote
            0
            down vote










            up vote
            0
            down vote









            A good rule of thumb is: your method should be as reusable as… reusable.



            If you expect that you will call your method only in one place, it should have only parameters that are known to the call site and that are not available to this method.



            If you have more callers, you can introduce new parameters as long as other callers may pass those parameters; otherwise you need new method.



            As the number of callers may grow in time, you need to be prepared for refactoring or overloading. In many cases it means that you should feel safe to select expression and run “extract parameter” action of your IDE.






            share|improve this answer












            A good rule of thumb is: your method should be as reusable as… reusable.



            If you expect that you will call your method only in one place, it should have only parameters that are known to the call site and that are not available to this method.



            If you have more callers, you can introduce new parameters as long as other callers may pass those parameters; otherwise you need new method.



            As the number of callers may grow in time, you need to be prepared for refactoring or overloading. In many cases it means that you should feel safe to select expression and run “extract parameter” action of your IDE.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 27 at 21:33









            Karol

            142




            142






















                up vote
                0
                down vote













                Ultra short answer: The less coupling or dependency to other code your generic module has the more reusable it can be.



                Your example only depends on



                import java.time.*;
                import java.util.Set;


                so in theory it can be highly reusable.



                In practise i donot think that you will ever have a second usecase that needs this code so following yagni principle i would not make it reusable if there are not more than 3 different projets that need this code.



                Other aspects of reusability are ease of use and doocumentation which corelate with Test Driven Development: It is helpfull if you have a simple unit-test that demonstrates/documents an easy use of your generic module as a coding example for users of your lib.






                share|improve this answer



























                  up vote
                  0
                  down vote













                  Ultra short answer: The less coupling or dependency to other code your generic module has the more reusable it can be.



                  Your example only depends on



                  import java.time.*;
                  import java.util.Set;


                  so in theory it can be highly reusable.



                  In practise i donot think that you will ever have a second usecase that needs this code so following yagni principle i would not make it reusable if there are not more than 3 different projets that need this code.



                  Other aspects of reusability are ease of use and doocumentation which corelate with Test Driven Development: It is helpfull if you have a simple unit-test that demonstrates/documents an easy use of your generic module as a coding example for users of your lib.






                  share|improve this answer

























                    up vote
                    0
                    down vote










                    up vote
                    0
                    down vote









                    Ultra short answer: The less coupling or dependency to other code your generic module has the more reusable it can be.



                    Your example only depends on



                    import java.time.*;
                    import java.util.Set;


                    so in theory it can be highly reusable.



                    In practise i donot think that you will ever have a second usecase that needs this code so following yagni principle i would not make it reusable if there are not more than 3 different projets that need this code.



                    Other aspects of reusability are ease of use and doocumentation which corelate with Test Driven Development: It is helpfull if you have a simple unit-test that demonstrates/documents an easy use of your generic module as a coding example for users of your lib.






                    share|improve this answer














                    Ultra short answer: The less coupling or dependency to other code your generic module has the more reusable it can be.



                    Your example only depends on



                    import java.time.*;
                    import java.util.Set;


                    so in theory it can be highly reusable.



                    In practise i donot think that you will ever have a second usecase that needs this code so following yagni principle i would not make it reusable if there are not more than 3 different projets that need this code.



                    Other aspects of reusability are ease of use and doocumentation which corelate with Test Driven Development: It is helpfull if you have a simple unit-test that demonstrates/documents an easy use of your generic module as a coding example for users of your lib.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited 2 days ago

























                    answered 2 days ago









                    k3b

                    6,65011127




                    6,65011127






















                        up vote
                        0
                        down vote













                        I've come to the opinion that there are two sorts of reusable code:




                        • Code which is reusable because it's such a fundamental, basic thing.

                        • Code which is reusable because it has parameters, overrides and hooks for everywhere.


                        The first sort of reusability is often a good idea. It applies to things like lists, hashmaps, key/value stores, string matchers (e.g. regex, glob, ...), tuples, unification, search trees (depth-first, breadth-first, iterative-deepening, ...), parser combinators, caches/memoisers, data format readers/writers (s-expressions, XML, JSON, protobuf, ...), task queues, etc.



                        These things are so general, in a very abstract way, that they're re-used all over the place in day to day programming. If you find yourself writing special-purpose code that would be simpler if it were made more abstract/general (e.g. if we have "a list of customer orders", we could throw away the "customer order" stuff to get "a list") then it might be a good idea to pull that out. Even if it doesn't get re-used, it lets us decouple unrelated functionality.



                        The second sort is where we have some concrete code, which solves a real issue, but does so by making a whole bunch of decisions. We can make it more general/reusable by "soft-coding" those decisions, e.g. turning them into parameters, complicating the implementation and baking in even more concrete details (i.e. knowledge of which hooks we might want for overrides). Your example seems to be of this sort. The problem with this sort of reusability is that we may end up trying to guess at the use-cases of other people, or our future selves. Eventually we might end up having so many parameters that our code isn't usable, let alone reusable! In other words, when calling it takes more effort than just writing our own version. This is where YAGNI (You Ain't Gonna Need It) is important. Many times, such attempts at "reusable" code end up not being reused, since it may be incompatable with those use-cases more fundamentally than parameters can account for, or those potential users would rather roll their own (heck, look at all the standards and libraries out there whose authors prefixed with the word "Simple", to distinguish them from the predecessors!).



                        This second form of "reusability" should basically be done on an as-needed basis. Sure, you can stick some "obvious" parameters in there, but don't start trying to predict the future. YAGNI.






                        share|improve this answer





















                        • Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                          – Koray Tugay
                          yesterday










                        • Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                          – Warbo
                          yesterday










                        • We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                          – Warbo
                          yesterday










                        • I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                          – Koray Tugay
                          14 hours ago















                        up vote
                        0
                        down vote













                        I've come to the opinion that there are two sorts of reusable code:




                        • Code which is reusable because it's such a fundamental, basic thing.

                        • Code which is reusable because it has parameters, overrides and hooks for everywhere.


                        The first sort of reusability is often a good idea. It applies to things like lists, hashmaps, key/value stores, string matchers (e.g. regex, glob, ...), tuples, unification, search trees (depth-first, breadth-first, iterative-deepening, ...), parser combinators, caches/memoisers, data format readers/writers (s-expressions, XML, JSON, protobuf, ...), task queues, etc.



                        These things are so general, in a very abstract way, that they're re-used all over the place in day to day programming. If you find yourself writing special-purpose code that would be simpler if it were made more abstract/general (e.g. if we have "a list of customer orders", we could throw away the "customer order" stuff to get "a list") then it might be a good idea to pull that out. Even if it doesn't get re-used, it lets us decouple unrelated functionality.



                        The second sort is where we have some concrete code, which solves a real issue, but does so by making a whole bunch of decisions. We can make it more general/reusable by "soft-coding" those decisions, e.g. turning them into parameters, complicating the implementation and baking in even more concrete details (i.e. knowledge of which hooks we might want for overrides). Your example seems to be of this sort. The problem with this sort of reusability is that we may end up trying to guess at the use-cases of other people, or our future selves. Eventually we might end up having so many parameters that our code isn't usable, let alone reusable! In other words, when calling it takes more effort than just writing our own version. This is where YAGNI (You Ain't Gonna Need It) is important. Many times, such attempts at "reusable" code end up not being reused, since it may be incompatable with those use-cases more fundamentally than parameters can account for, or those potential users would rather roll their own (heck, look at all the standards and libraries out there whose authors prefixed with the word "Simple", to distinguish them from the predecessors!).



                        This second form of "reusability" should basically be done on an as-needed basis. Sure, you can stick some "obvious" parameters in there, but don't start trying to predict the future. YAGNI.






                        share|improve this answer





















                        • Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                          – Koray Tugay
                          yesterday










                        • Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                          – Warbo
                          yesterday










                        • We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                          – Warbo
                          yesterday










                        • I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                          – Koray Tugay
                          14 hours ago













                        up vote
                        0
                        down vote










                        up vote
                        0
                        down vote









                        I've come to the opinion that there are two sorts of reusable code:




                        • Code which is reusable because it's such a fundamental, basic thing.

                        • Code which is reusable because it has parameters, overrides and hooks for everywhere.


                        The first sort of reusability is often a good idea. It applies to things like lists, hashmaps, key/value stores, string matchers (e.g. regex, glob, ...), tuples, unification, search trees (depth-first, breadth-first, iterative-deepening, ...), parser combinators, caches/memoisers, data format readers/writers (s-expressions, XML, JSON, protobuf, ...), task queues, etc.



                        These things are so general, in a very abstract way, that they're re-used all over the place in day to day programming. If you find yourself writing special-purpose code that would be simpler if it were made more abstract/general (e.g. if we have "a list of customer orders", we could throw away the "customer order" stuff to get "a list") then it might be a good idea to pull that out. Even if it doesn't get re-used, it lets us decouple unrelated functionality.



                        The second sort is where we have some concrete code, which solves a real issue, but does so by making a whole bunch of decisions. We can make it more general/reusable by "soft-coding" those decisions, e.g. turning them into parameters, complicating the implementation and baking in even more concrete details (i.e. knowledge of which hooks we might want for overrides). Your example seems to be of this sort. The problem with this sort of reusability is that we may end up trying to guess at the use-cases of other people, or our future selves. Eventually we might end up having so many parameters that our code isn't usable, let alone reusable! In other words, when calling it takes more effort than just writing our own version. This is where YAGNI (You Ain't Gonna Need It) is important. Many times, such attempts at "reusable" code end up not being reused, since it may be incompatable with those use-cases more fundamentally than parameters can account for, or those potential users would rather roll their own (heck, look at all the standards and libraries out there whose authors prefixed with the word "Simple", to distinguish them from the predecessors!).



                        This second form of "reusability" should basically be done on an as-needed basis. Sure, you can stick some "obvious" parameters in there, but don't start trying to predict the future. YAGNI.






                        share|improve this answer












                        I've come to the opinion that there are two sorts of reusable code:




                        • Code which is reusable because it's such a fundamental, basic thing.

                        • Code which is reusable because it has parameters, overrides and hooks for everywhere.


                        The first sort of reusability is often a good idea. It applies to things like lists, hashmaps, key/value stores, string matchers (e.g. regex, glob, ...), tuples, unification, search trees (depth-first, breadth-first, iterative-deepening, ...), parser combinators, caches/memoisers, data format readers/writers (s-expressions, XML, JSON, protobuf, ...), task queues, etc.



                        These things are so general, in a very abstract way, that they're re-used all over the place in day to day programming. If you find yourself writing special-purpose code that would be simpler if it were made more abstract/general (e.g. if we have "a list of customer orders", we could throw away the "customer order" stuff to get "a list") then it might be a good idea to pull that out. Even if it doesn't get re-used, it lets us decouple unrelated functionality.



                        The second sort is where we have some concrete code, which solves a real issue, but does so by making a whole bunch of decisions. We can make it more general/reusable by "soft-coding" those decisions, e.g. turning them into parameters, complicating the implementation and baking in even more concrete details (i.e. knowledge of which hooks we might want for overrides). Your example seems to be of this sort. The problem with this sort of reusability is that we may end up trying to guess at the use-cases of other people, or our future selves. Eventually we might end up having so many parameters that our code isn't usable, let alone reusable! In other words, when calling it takes more effort than just writing our own version. This is where YAGNI (You Ain't Gonna Need It) is important. Many times, such attempts at "reusable" code end up not being reused, since it may be incompatable with those use-cases more fundamentally than parameters can account for, or those potential users would rather roll their own (heck, look at all the standards and libraries out there whose authors prefixed with the word "Simple", to distinguish them from the predecessors!).



                        This second form of "reusability" should basically be done on an as-needed basis. Sure, you can stick some "obvious" parameters in there, but don't start trying to predict the future. YAGNI.







                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered yesterday









                        Warbo

                        97759




                        97759












                        • Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                          – Koray Tugay
                          yesterday










                        • Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                          – Warbo
                          yesterday










                        • We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                          – Warbo
                          yesterday










                        • I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                          – Koray Tugay
                          14 hours ago


















                        • Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                          – Koray Tugay
                          yesterday










                        • Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                          – Warbo
                          yesterday










                        • We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                          – Warbo
                          yesterday










                        • I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                          – Koray Tugay
                          14 hours ago
















                        Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                        – Koray Tugay
                        yesterday




                        Can we say that you agree that my first take was fine, where even the year was hardcoded? Or if you were initially implementing that requirement, would you make the year a parameter in your first take?
                        – Koray Tugay
                        yesterday












                        Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                        – Warbo
                        yesterday




                        Your first take was fine, since the requirement was a one-off script to 'check something'. It fails her 'ethical' test, but she fails the 'no dogma' test. "She might say..." is inventing requirements You Ain't Gonna Need.
                        – Warbo
                        yesterday












                        We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                        – Warbo
                        yesterday




                        We can't say which 'destroy city' is "better" without more info: destroyBaghdad is a one-off script (or at least, it's idempotent). Maybe destroying any city would be an improvement, but what if destroyBaghdad works by flooding the Tigris? That may be reusable for Mosul and Basra, but not for Mecca or Atlanta.
                        – Warbo
                        yesterday












                        I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                        – Koray Tugay
                        14 hours ago




                        I see so you disagree with the Nathaniel Borenstein, the owner of the quote. I am tryting to understand slowly I think by reading all these responses and the discussions.
                        – Koray Tugay
                        14 hours ago










                        up vote
                        0
                        down vote













                        This is a good opportunity to state a rule I coined recently:



                        Being a good programmer means being able to predict the future.



                        Of course, this is strictly impossible!  After all, you never know for sure what generalisations you'll find useful later on, which related tasks you'll want to perform, what new features your users will want, &c.  But experience sometimes gives you a rough idea of what might come in handy.



                        The other factors you have to balance that against are how much extra time and effort would be involved, and how much more complex would it make your code.  Sometimes you're lucky, and solving the more general problem is actually simpler!  (At least conceptually, if not in the amount of code.)  But more often, there's a complexity cost as well as one of time and effort.



                        So if you think a generalisation is very likely to be needed, it's often worth doing (unless it adds a lot of work or complexity); but if it seems much less likely, then it's probably not (unless it's very easy and/or simplifies the code).



                        (For a recent example: last week I was given a spec. for actions a system should take exactly 2 days after something expired.  So of course I made the 2-day period a parameter.  This week the business folks were delighted, as they were about to ask for that enhancement!  I was lucky: it was an easy change, and I guessed it was quite likely to be wanted.  Often it's harder to judge.  But it's still worth trying to predict, and experience is often a good guide.)






                        share|improve this answer

























                          up vote
                          0
                          down vote













                          This is a good opportunity to state a rule I coined recently:



                          Being a good programmer means being able to predict the future.



                          Of course, this is strictly impossible!  After all, you never know for sure what generalisations you'll find useful later on, which related tasks you'll want to perform, what new features your users will want, &c.  But experience sometimes gives you a rough idea of what might come in handy.



                          The other factors you have to balance that against are how much extra time and effort would be involved, and how much more complex would it make your code.  Sometimes you're lucky, and solving the more general problem is actually simpler!  (At least conceptually, if not in the amount of code.)  But more often, there's a complexity cost as well as one of time and effort.



                          So if you think a generalisation is very likely to be needed, it's often worth doing (unless it adds a lot of work or complexity); but if it seems much less likely, then it's probably not (unless it's very easy and/or simplifies the code).



                          (For a recent example: last week I was given a spec. for actions a system should take exactly 2 days after something expired.  So of course I made the 2-day period a parameter.  This week the business folks were delighted, as they were about to ask for that enhancement!  I was lucky: it was an easy change, and I guessed it was quite likely to be wanted.  Often it's harder to judge.  But it's still worth trying to predict, and experience is often a good guide.)






                          share|improve this answer























                            up vote
                            0
                            down vote










                            up vote
                            0
                            down vote









                            This is a good opportunity to state a rule I coined recently:



                            Being a good programmer means being able to predict the future.



                            Of course, this is strictly impossible!  After all, you never know for sure what generalisations you'll find useful later on, which related tasks you'll want to perform, what new features your users will want, &c.  But experience sometimes gives you a rough idea of what might come in handy.



                            The other factors you have to balance that against are how much extra time and effort would be involved, and how much more complex would it make your code.  Sometimes you're lucky, and solving the more general problem is actually simpler!  (At least conceptually, if not in the amount of code.)  But more often, there's a complexity cost as well as one of time and effort.



                            So if you think a generalisation is very likely to be needed, it's often worth doing (unless it adds a lot of work or complexity); but if it seems much less likely, then it's probably not (unless it's very easy and/or simplifies the code).



                            (For a recent example: last week I was given a spec. for actions a system should take exactly 2 days after something expired.  So of course I made the 2-day period a parameter.  This week the business folks were delighted, as they were about to ask for that enhancement!  I was lucky: it was an easy change, and I guessed it was quite likely to be wanted.  Often it's harder to judge.  But it's still worth trying to predict, and experience is often a good guide.)






                            share|improve this answer












                            This is a good opportunity to state a rule I coined recently:



                            Being a good programmer means being able to predict the future.



                            Of course, this is strictly impossible!  After all, you never know for sure what generalisations you'll find useful later on, which related tasks you'll want to perform, what new features your users will want, &c.  But experience sometimes gives you a rough idea of what might come in handy.



                            The other factors you have to balance that against are how much extra time and effort would be involved, and how much more complex would it make your code.  Sometimes you're lucky, and solving the more general problem is actually simpler!  (At least conceptually, if not in the amount of code.)  But more often, there's a complexity cost as well as one of time and effort.



                            So if you think a generalisation is very likely to be needed, it's often worth doing (unless it adds a lot of work or complexity); but if it seems much less likely, then it's probably not (unless it's very easy and/or simplifies the code).



                            (For a recent example: last week I was given a spec. for actions a system should take exactly 2 days after something expired.  So of course I made the 2-day period a parameter.  This week the business folks were delighted, as they were about to ask for that enhancement!  I was lucky: it was an easy change, and I guessed it was quite likely to be wanted.  Often it's harder to judge.  But it's still worth trying to predict, and experience is often a good guide.)







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 14 hours ago









                            gidds

                            1552




                            1552






















                                up vote
                                0
                                down vote













                                Firstly, the best answer to 'How do I know how reusable my methods should be?" is "experience." Do this a few thousand times, and you typically get the right answer. But as a teaser, I can give you the last line of this answer: Your customer will tell you how much flexibility and how many layers of generalization you should seek.



                                Many of these answers have specific pieces of advice. I wanted to give something more generic... because the irony is too much fun to pass up!



                                As some of the answers have noted, generality is expensive. However, it really isn't. Not always. Understanding the expense is essential to playing the reusability game.



                                I focus on putting things on a scale from "irreversable" to "reversable." It's a smooth scale. The only truly irreversable thing is "time spent on the project." You'll never get those resources back. Slightly less reversable might be "golden handcuffs" situations such as the Windows API. Deprecated features remain in that API for decades because Microsoft's business model calls for it. If you have customers whose relationship would be permanently damaged by undoing some API feature, then that should be treated as irreversable. Looking at the other end of the scale, you have things like prototype code. If you don't like where it goes, you can simply throw it away. Slightly less reversable might be internal use APIs. They can be refactored without bothering a customer, but they may cost more time (the most irreversable resource of all!)



                                So put these on a scale. Now you can apply a heuristic: the more reversable something is, the more you can use it for future looking activities. If something is irreversable, only use it for concrete customer-driven tasks. This is why you see principles like those from extreme programming which suggest only doing what the customer asks for and nothing more. These principles are good at making sure you don't do something you regret.



                                Things like the DRY principle suggest a way to move that balance. If you find yourself repeating yourself, that's an opportunity to create what's basically an internal API. No customer sees it, so you can always change it. Once you have this internal API, now you can start playing with forward looking things. How many different timezone based tasks do you think your wife is going to give you? Do you have any other customers who might want timezone based tasks? Your flexibility here is bought by the concrete demands of your current customers, and it supports the potential future demands of future customers.



                                This layered thinking approach, which comes naturally from DRY naturally provides the generalization you want without waste. But is there a limit? Of course there is. But to see it, you have to see the forest for the trees.



                                If you have many layers of flexiblity, they often lead to a lack of direct control of the layers that face your customers. I've had software where I've had the brutal task of explaining to a customer why they can't have what they want due to flexibility built in 10 layers down which they were never supposed to see. We wrote ourselves into a corner. We tied ourselves in a knot with all the flexibility we thought we needed.



                                So when you're doing this generalization/DRY trick, always keep a pulse on your customer. What do you think your wife is going to ask for next? Are you putting yourself in a position to fulfil those needs? If you have the knack, the customer will effectively tell you their future needs. If you don't have the knack, well, most of us just rely on guesswork! (especially with spouses!) Some customers will want great flexibility, and be willing to accept the extra cost of you developing with all these layers because they directly benefit from those layers' flexibility. Other customers have rather fixed unwavering requirements, and they prefer development be more direct. Your customer will tell you how much flexibility and how many layers of generalization you should seek.






                                share|improve this answer





















                                • Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                  – Cort Ammon
                                  13 hours ago










                                • In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                  – Cort Ammon
                                  13 hours ago












                                • So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                  – Cort Ammon
                                  11 hours ago















                                up vote
                                0
                                down vote













                                Firstly, the best answer to 'How do I know how reusable my methods should be?" is "experience." Do this a few thousand times, and you typically get the right answer. But as a teaser, I can give you the last line of this answer: Your customer will tell you how much flexibility and how many layers of generalization you should seek.



                                Many of these answers have specific pieces of advice. I wanted to give something more generic... because the irony is too much fun to pass up!



                                As some of the answers have noted, generality is expensive. However, it really isn't. Not always. Understanding the expense is essential to playing the reusability game.



                                I focus on putting things on a scale from "irreversable" to "reversable." It's a smooth scale. The only truly irreversable thing is "time spent on the project." You'll never get those resources back. Slightly less reversable might be "golden handcuffs" situations such as the Windows API. Deprecated features remain in that API for decades because Microsoft's business model calls for it. If you have customers whose relationship would be permanently damaged by undoing some API feature, then that should be treated as irreversable. Looking at the other end of the scale, you have things like prototype code. If you don't like where it goes, you can simply throw it away. Slightly less reversable might be internal use APIs. They can be refactored without bothering a customer, but they may cost more time (the most irreversable resource of all!)



                                So put these on a scale. Now you can apply a heuristic: the more reversable something is, the more you can use it for future looking activities. If something is irreversable, only use it for concrete customer-driven tasks. This is why you see principles like those from extreme programming which suggest only doing what the customer asks for and nothing more. These principles are good at making sure you don't do something you regret.



                                Things like the DRY principle suggest a way to move that balance. If you find yourself repeating yourself, that's an opportunity to create what's basically an internal API. No customer sees it, so you can always change it. Once you have this internal API, now you can start playing with forward looking things. How many different timezone based tasks do you think your wife is going to give you? Do you have any other customers who might want timezone based tasks? Your flexibility here is bought by the concrete demands of your current customers, and it supports the potential future demands of future customers.



                                This layered thinking approach, which comes naturally from DRY naturally provides the generalization you want without waste. But is there a limit? Of course there is. But to see it, you have to see the forest for the trees.



                                If you have many layers of flexiblity, they often lead to a lack of direct control of the layers that face your customers. I've had software where I've had the brutal task of explaining to a customer why they can't have what they want due to flexibility built in 10 layers down which they were never supposed to see. We wrote ourselves into a corner. We tied ourselves in a knot with all the flexibility we thought we needed.



                                So when you're doing this generalization/DRY trick, always keep a pulse on your customer. What do you think your wife is going to ask for next? Are you putting yourself in a position to fulfil those needs? If you have the knack, the customer will effectively tell you their future needs. If you don't have the knack, well, most of us just rely on guesswork! (especially with spouses!) Some customers will want great flexibility, and be willing to accept the extra cost of you developing with all these layers because they directly benefit from those layers' flexibility. Other customers have rather fixed unwavering requirements, and they prefer development be more direct. Your customer will tell you how much flexibility and how many layers of generalization you should seek.






                                share|improve this answer





















                                • Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                  – Cort Ammon
                                  13 hours ago










                                • In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                  – Cort Ammon
                                  13 hours ago












                                • So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                  – Cort Ammon
                                  11 hours ago













                                up vote
                                0
                                down vote










                                up vote
                                0
                                down vote









                                Firstly, the best answer to 'How do I know how reusable my methods should be?" is "experience." Do this a few thousand times, and you typically get the right answer. But as a teaser, I can give you the last line of this answer: Your customer will tell you how much flexibility and how many layers of generalization you should seek.



                                Many of these answers have specific pieces of advice. I wanted to give something more generic... because the irony is too much fun to pass up!



                                As some of the answers have noted, generality is expensive. However, it really isn't. Not always. Understanding the expense is essential to playing the reusability game.



                                I focus on putting things on a scale from "irreversable" to "reversable." It's a smooth scale. The only truly irreversable thing is "time spent on the project." You'll never get those resources back. Slightly less reversable might be "golden handcuffs" situations such as the Windows API. Deprecated features remain in that API for decades because Microsoft's business model calls for it. If you have customers whose relationship would be permanently damaged by undoing some API feature, then that should be treated as irreversable. Looking at the other end of the scale, you have things like prototype code. If you don't like where it goes, you can simply throw it away. Slightly less reversable might be internal use APIs. They can be refactored without bothering a customer, but they may cost more time (the most irreversable resource of all!)



                                So put these on a scale. Now you can apply a heuristic: the more reversable something is, the more you can use it for future looking activities. If something is irreversable, only use it for concrete customer-driven tasks. This is why you see principles like those from extreme programming which suggest only doing what the customer asks for and nothing more. These principles are good at making sure you don't do something you regret.



                                Things like the DRY principle suggest a way to move that balance. If you find yourself repeating yourself, that's an opportunity to create what's basically an internal API. No customer sees it, so you can always change it. Once you have this internal API, now you can start playing with forward looking things. How many different timezone based tasks do you think your wife is going to give you? Do you have any other customers who might want timezone based tasks? Your flexibility here is bought by the concrete demands of your current customers, and it supports the potential future demands of future customers.



                                This layered thinking approach, which comes naturally from DRY naturally provides the generalization you want without waste. But is there a limit? Of course there is. But to see it, you have to see the forest for the trees.



                                If you have many layers of flexiblity, they often lead to a lack of direct control of the layers that face your customers. I've had software where I've had the brutal task of explaining to a customer why they can't have what they want due to flexibility built in 10 layers down which they were never supposed to see. We wrote ourselves into a corner. We tied ourselves in a knot with all the flexibility we thought we needed.



                                So when you're doing this generalization/DRY trick, always keep a pulse on your customer. What do you think your wife is going to ask for next? Are you putting yourself in a position to fulfil those needs? If you have the knack, the customer will effectively tell you their future needs. If you don't have the knack, well, most of us just rely on guesswork! (especially with spouses!) Some customers will want great flexibility, and be willing to accept the extra cost of you developing with all these layers because they directly benefit from those layers' flexibility. Other customers have rather fixed unwavering requirements, and they prefer development be more direct. Your customer will tell you how much flexibility and how many layers of generalization you should seek.






                                share|improve this answer












                                Firstly, the best answer to 'How do I know how reusable my methods should be?" is "experience." Do this a few thousand times, and you typically get the right answer. But as a teaser, I can give you the last line of this answer: Your customer will tell you how much flexibility and how many layers of generalization you should seek.



                                Many of these answers have specific pieces of advice. I wanted to give something more generic... because the irony is too much fun to pass up!



                                As some of the answers have noted, generality is expensive. However, it really isn't. Not always. Understanding the expense is essential to playing the reusability game.



                                I focus on putting things on a scale from "irreversable" to "reversable." It's a smooth scale. The only truly irreversable thing is "time spent on the project." You'll never get those resources back. Slightly less reversable might be "golden handcuffs" situations such as the Windows API. Deprecated features remain in that API for decades because Microsoft's business model calls for it. If you have customers whose relationship would be permanently damaged by undoing some API feature, then that should be treated as irreversable. Looking at the other end of the scale, you have things like prototype code. If you don't like where it goes, you can simply throw it away. Slightly less reversable might be internal use APIs. They can be refactored without bothering a customer, but they may cost more time (the most irreversable resource of all!)



                                So put these on a scale. Now you can apply a heuristic: the more reversable something is, the more you can use it for future looking activities. If something is irreversable, only use it for concrete customer-driven tasks. This is why you see principles like those from extreme programming which suggest only doing what the customer asks for and nothing more. These principles are good at making sure you don't do something you regret.



                                Things like the DRY principle suggest a way to move that balance. If you find yourself repeating yourself, that's an opportunity to create what's basically an internal API. No customer sees it, so you can always change it. Once you have this internal API, now you can start playing with forward looking things. How many different timezone based tasks do you think your wife is going to give you? Do you have any other customers who might want timezone based tasks? Your flexibility here is bought by the concrete demands of your current customers, and it supports the potential future demands of future customers.



                                This layered thinking approach, which comes naturally from DRY naturally provides the generalization you want without waste. But is there a limit? Of course there is. But to see it, you have to see the forest for the trees.



                                If you have many layers of flexiblity, they often lead to a lack of direct control of the layers that face your customers. I've had software where I've had the brutal task of explaining to a customer why they can't have what they want due to flexibility built in 10 layers down which they were never supposed to see. We wrote ourselves into a corner. We tied ourselves in a knot with all the flexibility we thought we needed.



                                So when you're doing this generalization/DRY trick, always keep a pulse on your customer. What do you think your wife is going to ask for next? Are you putting yourself in a position to fulfil those needs? If you have the knack, the customer will effectively tell you their future needs. If you don't have the knack, well, most of us just rely on guesswork! (especially with spouses!) Some customers will want great flexibility, and be willing to accept the extra cost of you developing with all these layers because they directly benefit from those layers' flexibility. Other customers have rather fixed unwavering requirements, and they prefer development be more direct. Your customer will tell you how much flexibility and how many layers of generalization you should seek.







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered 14 hours ago









                                Cort Ammon

                                9,54331730




                                9,54331730












                                • Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                  – Cort Ammon
                                  13 hours ago










                                • In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                  – Cort Ammon
                                  13 hours ago












                                • So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                  – Cort Ammon
                                  11 hours ago


















                                • Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                  – Cort Ammon
                                  13 hours ago










                                • In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                  – Cort Ammon
                                  13 hours ago












                                • So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                  – Koray Tugay
                                  13 hours ago










                                • @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                  – Cort Ammon
                                  11 hours ago
















                                Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                – Koray Tugay
                                13 hours ago




                                Well there should be other people who done this 10000 times, so why should I do it 10000 times and gain experience when I can learn from others? Because the answer will be different for each individual so answers from experienced are not applicable to me? Also Your customer will tell you how much flexibility and how many layers of generalization you should seek. what world is this?
                                – Koray Tugay
                                13 hours ago












                                @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                – Cort Ammon
                                13 hours ago




                                @KorayTugay It's a business world. If your customers aren't telling you what to do, then you aren't listening hard enough. Of course, they wont always tell you in words, but they tell you in other ways. Experience helps you listen to their more subtle messages. If you don't have the skill yet, find someone in your company who does have the skill of listening to those subtle customer hints, and ply them for direction. Someone will have that skill, even if they're the CEO or in marketing.
                                – Cort Ammon
                                13 hours ago












                                In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                – Cort Ammon
                                13 hours ago






                                In your specific case, if you had failed to take the trash out because you were too busy coding up a generalized version of this timezone problem instead of hacking together the specific solution, how would your customer feel?
                                – Cort Ammon
                                13 hours ago














                                So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                – Koray Tugay
                                13 hours ago




                                So you agree that my first approach was the right one, hardcoding 2018 in the first take instead of parameterizing the year? (btw, That is not really listening to my customer I think, the garbage example. That is knowing your customer.. Even if get support from The Oracle, there are no subtle messages to listen to when she says I need a list of daylight changes for 2018.) Thanks for your time and answer btw.
                                – Koray Tugay
                                13 hours ago












                                @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                – Cort Ammon
                                11 hours ago




                                @KorayTugay Without knowing any additional details, I'd say hardcoding was the right approach. You had no way of knowing if you were going to need future DLS code, nor any idea what sort of request she might give next. And if your customer is trying to test you, they get what they get =D
                                – Cort Ammon
                                11 hours ago










                                up vote
                                -3
                                down vote













                                If you are using at least java 8, you would write the WorldTimeZones class to provide what in essence appears to be a collection of time zones.



                                Then add a filter(Predicate filter) method to the WorldTimeZones class. This provides the ability for the caller to filter on anything they want by passing a lambda expression as the parameter.



                                In essence, the single filter method supports filtering on anything contained in the value passed to the predicate.



                                Alternately, add a stream() method to your WorldTimeZones class that produces a stream of time zones when called. Then the caller can filter, map and reduce as desired without you writing any specializations at all.






                                share|improve this answer

















                                • 3




                                  These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                  – maple_shaft
                                  2 days ago










                                • So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                  – Rodney P. Barbati
                                  yesterday















                                up vote
                                -3
                                down vote













                                If you are using at least java 8, you would write the WorldTimeZones class to provide what in essence appears to be a collection of time zones.



                                Then add a filter(Predicate filter) method to the WorldTimeZones class. This provides the ability for the caller to filter on anything they want by passing a lambda expression as the parameter.



                                In essence, the single filter method supports filtering on anything contained in the value passed to the predicate.



                                Alternately, add a stream() method to your WorldTimeZones class that produces a stream of time zones when called. Then the caller can filter, map and reduce as desired without you writing any specializations at all.






                                share|improve this answer

















                                • 3




                                  These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                  – maple_shaft
                                  2 days ago










                                • So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                  – Rodney P. Barbati
                                  yesterday













                                up vote
                                -3
                                down vote










                                up vote
                                -3
                                down vote









                                If you are using at least java 8, you would write the WorldTimeZones class to provide what in essence appears to be a collection of time zones.



                                Then add a filter(Predicate filter) method to the WorldTimeZones class. This provides the ability for the caller to filter on anything they want by passing a lambda expression as the parameter.



                                In essence, the single filter method supports filtering on anything contained in the value passed to the predicate.



                                Alternately, add a stream() method to your WorldTimeZones class that produces a stream of time zones when called. Then the caller can filter, map and reduce as desired without you writing any specializations at all.






                                share|improve this answer












                                If you are using at least java 8, you would write the WorldTimeZones class to provide what in essence appears to be a collection of time zones.



                                Then add a filter(Predicate filter) method to the WorldTimeZones class. This provides the ability for the caller to filter on anything they want by passing a lambda expression as the parameter.



                                In essence, the single filter method supports filtering on anything contained in the value passed to the predicate.



                                Alternately, add a stream() method to your WorldTimeZones class that produces a stream of time zones when called. Then the caller can filter, map and reduce as desired without you writing any specializations at all.







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered 2 days ago









                                Rodney P. Barbati

                                1172




                                1172








                                • 3




                                  These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                  – maple_shaft
                                  2 days ago










                                • So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                  – Rodney P. Barbati
                                  yesterday














                                • 3




                                  These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                  – maple_shaft
                                  2 days ago










                                • So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                  – Rodney P. Barbati
                                  yesterday








                                3




                                3




                                These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                – maple_shaft
                                2 days ago




                                These are good generalization ideas however this answer completely misses the mark on what is being asked in the question. The question is not about how to best generalize the solution but where we draw the line at generalizations and how we weigh these considerations ethically.
                                – maple_shaft
                                2 days ago












                                So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                – Rodney P. Barbati
                                yesterday




                                So I am saying that you should weigh them by the number of use cases they support modified by the complexity of creation. A method that supports one use case is not as valuable as a method that supports 20 use cases. On the other hand, if all you know of is one use case, and it takes 5 minutes to code for it - go for it. Often coding methods that support specific uses informs you of the way to generalize.
                                – Rodney P. Barbati
                                yesterday





                                protected by gnat Nov 28 at 5:18



                                Thank you for your interest in this question.
                                Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



                                Would you like to answer one of these unanswered questions instead?



                                Popular posts from this blog

                                AnyDesk - Fatal Program Failure

                                How to calibrate 16:9 built-in touch-screen to a 4:3 resolution?

                                QoS: MAC-Priority for clients behind a repeater