What Is Simplest

In DoTheSimplestThingThatCouldPossiblyWork, MichaelFeathers asks, "WhatIsSimplest?"

I suggest that an absolute definition doesn't matter. The point is to apply your own educated definition of simplicity until you're out of ideas, then do the simplest. Please consider code volume as a rough estimate of simplicity.

We don't get in trouble because we don't know what simple is, we get in trouble because we are too bloody clever and like to do things that are fancy in our own estimation. We enjoy running our minds and doing cool things. Just cut it out.

I think it extremely unlikely that any of us would find it simpler to implement a shell sort than a bubble sort. If you think sort by selection is simpler than bubble, go for it. Do you like insertion sort? Fine. Get it done in ten minutes and move on. Save the big sort implementation for when the GoldOwners decide the system is too slow. Encapsulation will save you. -- RonJeffries


Here's some discussion on WhatIsSimplest. Help me out. Or remove it.

Context: expectedAge of a Person has been found to depend on gender.

A simple if statement is simpler than creating a subclass:

 Person>>expectedAge
	^male
	ifTrue: [65]
	ifFalse: [72]
is simpler than creating MalePerson and FemalePerson classes to allow

 MalePerson>>expectedAge
	^65

FemalePerson>>expectedAge ^72
But if the subclasses already exist, the latter is simpler. With only one check of gender, I'd not yet create the subclass.

If average weight also depends on gender:

 Person>>averageWeight
	^male
	ifTrue: [170]
	ifFalse: [120]
there is now a violation of OnceAndOnlyOnce (male ifTrue:[] ifFalse: []) which pushes us a little more in the direction of refactoring to male and female subclasses. As yet, we are probably not convinced.

Suppose that for other reasons, the male and female subclasses are not appropriate. List here some other possible implementations of expectedAge or averageWeight. How do they rank in simplicity? How about generality and other measures?

 Person>>expectedAge
	^self class expectedAge: male

Person class>>expectedAge: aBoolean ^aBoolean ifTrue: [65] ifFalse: [72]

Person>>expectedAge ^self class expectedAge: self

Person class>>expectedAge: aPerson ^aPerson male ifTrue: [65] ifFalse: [72]
What about building an AgeClassifier??

 Person>>expectedAge
	^AgeClassifier? expectedAge: self

AgeClassifier?>>expectedAge: aPerson ^aPerson male ifTrue: [65] ifFalse: [72]

What about building a general Classifier? Maybe it takes a message selector (e.g. male), and a dictionary of values that it can look up (in this case, true->65, false->72) but it could be any values that could come back from the selector.

What about a Classifier that accepted ranges of returns from the selector? We're going to need to produce expected age as a function of current age: 0-20 -> 85, 21-35 -> 80, and so on.

What about Classifier combinations, such as multiple keys, and AND and OR? We're going to need to do logic, after all, average weight depends on race and gender, and probably age.

Stop me before I generalize again ...

-- RonJeffries

I think this is an example of question that can be answered in isolation. If you don't care for any other purposes that the person is male or female, I would suggest the simplest thing is to initialize the object with the expected age instead of with the sex. If you have other gender based differentiations, it is probably simpler to create a male class and a female class. It is very hard to extrapolate much more because this example seems to be describing a simple data structure, not a class.


Gack, statistics. These statistics can get arbitrarily convoluted - expected age (you mean life expectancy??) might include not only sex but race, place of residence, occupation, number of cats, etc.

Therefore, we go to Ron's extreme programming page and see the words "DoItInAnObject?". Ah, we now know we need some sort of Statistics object. It knows what properties of a person are important in determining a person's life expectancy (and average height and number of divorces), and the method becomes

	Person>>lifeExpectancy
		Statistics instance lifeExpectancy: self

and possibly calling

	USInsuranceStatistics>>lifeExpectancy: aPerson
		self table at: (aPerson sex @ aPerson race @ aPerson cats size)

-- BillTrost

Ah, but is it SIMPLER that way? Should we do these things, and if so, when?


Ron, I was wondering when in all of the above you would pull out CrcCards. How do people go back and forth between cards and plain old object discovery through working out a problem? -- MichaelFeathers

Basically we go to cards whenever the solution isn't obvious from a few moments discussion, or when there is a need to communicate it to more people, such as customers. We should go to cards more often than we do, IMO. -- RonJeffries


"Which is simpler" depends on what you want to do with it, and on what prior expectations you have. I don't think the question can be answered without such contextual information.

Recall that we urge you to use your own definition of simplest, subject to the Simplest and YAGNI rules. Recall that when we DoTheSimplestThingThatCouldPossiblyWork, the code we put in has to support the UnitTests. The UnitTests define what you want to do with it. The rule YouArentGonnaNeedIt says that we discount all future assumptions: if there isn't a test for it, the code doesn't have to do it.

In the context of supporting all the requirements, as reflected in the tests, is it still going to be unclear which implementation is simplest?


The answer is all of the above are simple. A Wiki Wiki page can not hold an example that demonstrates the issue. -- DonWells


With respect to the code above, the answer (it seems to me) is quite obvious: When you're typing male ifTrue: [] ... for the nth time (you define n), and it gives you pause, and makes you stop to think that there might be a better way, then it's probably not the-simplest-thing-that-could-possibly-work anymore.

This, of course, leads me to a question which I shall preface with a real-world example:

A coworker recently showed me 10 VisualWorks controller subclasses which swap the red and yellow button activites of their superclasses (you know, juggleButtons). After pointing out a one-method fix which replaces the 10-subclasses fix, I was struck dumb wondering how someone makes that many subclasses doing the same thing without wondering if there isn't a better way.

So my question is this: How do you fail to notice this sort of thing? Is it possible to teach someone to notice this sort of thing? How?

My gut tells me that it can't be taught. Good programming requires good taste, and you can't teach someone good taste. Am I wrong?

<RANT>Aside: I was significantly more dumbstruck by the "well, this already works and it will take too long to change it" response that I got. Sigh...</RANT>

-- AnthonyLander

It is possible to teach people. I suspect this particular programmer still doesn't grasp whole system structures. Experience has taught him that local modifications are safer.

Always believe honest programmer estimates. When he says "it will take too long", it probably means he has a few days of learning to do before he would be able to create the simpler solution.

It's a business decision to let people go or take the time to teach them to program. Most courses fail to teach programming.

-- EricUlevik

RohitKhare said...
"Bad design should make you physically ill."

I have a question about the stated context and the answer. I've been taught never to hard code the values into my programs, both by others and experience. The first sample answer suggests that the actual question context is: Context: expectedAge of a Male person is 65 and a female person is 72. The simplest answer doesn't seem to answer the stated context? -- MichaelChean

So, instead of putting it in the class you would do...
	#define EXPECTED_AGE_OF_A_MALE 65
or...
	const int EXPECTED_AGE_OF_A_MALE = 65;

Well, that's nice. But look at it this way: You've still hard-coded the value in your program. Yes, you did it in some "slightly more visible data area" rather than in the middle of the code, but it's still "hard coded."

Really, the big danger of "hard coding" values in programs is that the numbers, and slight variations of them will be hard-coded in many places in a given program.

This is the problem that the "don't hard code values" rule is trying to solve:
	char name[100]
	for (i=0;i<=99;++i)
	dostuff(name[i]);
	if (strlen(newname) < 101) {
	// Then it'll fit in name[], so we'll copy it...
	...
	}
Bad. Very bad: Not only does the size of the array (100) appear in several places, "size + 1" and "size - 1" are used too. Mix this with some other array sizes or magic numbers at or around 100, and you've got real problems.

So, maybe OnceAndOnlyOnce is the rule you were really looking for. ;-> -- JeffGrigg

See also: DimensionsOfSimplicity, AnticlimacticSimplicity.

Also, YAGNI would say stick to hardcoded values until you can prove you actually really do need it now. -- HubertMatthews


What is simple here?

You can create a good ole Java logger a couple of ways:

  logger = LoggerFactory().getLogger(ThisHereClass.class);

This then uses the ThisHereClass? class name in the log statement, it's a common pattern we've all used.

But if we have our own factory we could get clever, and create a factory signature in the form:

  logger = LoggerFactory().getLogger();

and then obtain the class name to use with something like:

  return new Throwable().getStackTrace()[2].getClassName();

So the code getting the logger is a bit simpler (not much though) but the logger factory is more complex (not that much though). To me DoTheSimplestThingThatCouldPossiblyWork dictates that we should _not_ add the extra complexity to the Factory...There may be more code but it is simpler and more explicit.

Anyone agree, or I am obssessing and in obvious need of a holiday? Chris


CategorySimplification CategoryDefinition

EditText of this page (last edited August 14, 2010) or FindPage with title or text search