Curly's Law: Do One Thing

In Outliving the Great Variable Shortage, Tim Ottinger invokes Curly's Law:


This is a companion discussion topic for the original blog entry at: http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html

So what exactly do you propose to your little example with 3 options

See Uncle Bob’s solution here-- basically, multiple classes. In .NET, partial classes.

http://butunclebob.com/ArticleS.UncleBob.SrpInRuby

The goal is to make localized changes in one area of the code–avoiding “shotgun surgery” changes to multiple areas.

http://www.codinghorror.com/blog/archives/000589.html

I’m not familiar with Ruby, but splitting the employee class into 3 partial classes doesn’t seem like the right solution to me. I would much rather use IoC (Inversion of control) and define an interface for Save (ISave), calculate (ICalculatePay) and report (IReportHours) respectively and have their implementations injected into the Employee class. That way when any of the implementations change the change will be done in the respective class and not in the Employee class. The employee is totally unaware of how these actions are carried out. Another advantage would be that these classes might be used by other parts of the system since they are not necessarily specific to the Employee class.

OK, once again: the RDMBS mantra – one fact, one place, one time. From that, one generates the interface code. Change the schema, regenerate the interface code, start the build, go home to the wife and kids. What more do you want?

Oh, yeah. A framework that actually does that. A few exist, some java, some not.

LOL. Maybe the Embedded Linux community should adopt this paradigm. In the Atmel world, they initialize board peripherals 3 times. Once in the data flash boot loader, once in the u-boot boot loader, and once in Linux. LOL. I laugh because it’s true.

On the other hand, I have a customer who’s RS-485 doesn’t work because those pins are initialized in 3 different places, and they only need to be written to once after reset. The issue isn’t which place should initialize them, but how to set it up so it’s fast and doesn’t DRY and is easy to maintain going forward.

I’ve definitely been bitten by DRY more than once:
-Still doesn’t work? But I did fix it! Look when you do it in X it works. What, They do it from Y? Oh gee i forgot about that…
-I’d swear i’ve already done that before.

Even though I know duplicating is bad I still do it without even realizing!
Examples :
-I perform some operation in the application when the user changes data (for example computing some kind of sum of that data). And I do the same operation (the sum) when I fetch data from the database.

-Performing an operation on different data : DoXToA(), DoXToB(), DoXToC() failing to realize that it really is the same operation for all three and could do like this instead : DoX(A/B/C)

-Three databases (development/test/production) and modifications are done manually. Everytime something is done we musn’t forget that I have/will have to reflect the changes on the others databases.

Sometimes it’s easy to find duplication in some code… but not before you actually code it.

Hilarious, how many ways can we say ‘Don’t repeat yourself’? :slight_smile:

When explaining these concepts to junior developers or to non-tech staff I make an analogy to noting down a decision. Once we have come to a decision about how we want something to be we write it down in one spot. If new information causes us to change that decision we have one place we can update that decision. Our code and data should represent all the decision-making about how our tool or product should work. When we change our minds about a particular decision we shouldn’t need to be changing it in multiple places. Oh… I am starting to repeat myself…

Ah yes, the “almost-eternal” question, “What do I break if I change this here?”

Does that mean you have to not re-use i for loops? :slight_smile:

I used to program in a text editor. I liked it better than all those pop-up windows.

I used header files extensively, it helped make the code more self-documented.

Also I prefixed all my variables with lowercase type reminders.
aVar for array, nVar, cVar, sVar, bVar, etc.

“Does that mean you have to not re-use i for loops? :)”

No. It means that you should only use i for loops!

So what exactly do you propose to your little example with 3 options??? Even if you make the Employee essentially a container and have multiple classes handle Employee actions, or more likely system generic actions, you may have single point of failure per class, put you maybe have multiple classes that are affected! How is that for a long sentence!

Yes, we all know what code should look like. Unfortunately everybody have their own ideals and our ideals also change over time. At least mine do :-). But the big question is: what do we do when we are faced with reality (i.e. deadlines)? I often have to comprise to quick n’ dirty because I simply do not have the time to refactor properly. :-/

I really don’t see a whole lot of advantage of scattering the code across multiple files. This would seem to lend itself to a file-per-method result, no?

The sin would be to insert business logic into the Employee class itself (not that I think this example is necessarily presenting that).

My fear, especially given the number of times I’ve heard (wrongly) that partial classes are the solution to separating business logic from view/model, is that this would lead people to think that some partial class of calculatePay() justifies embedding the business logic in there (and then the Contractor class comes in and…)

DRY is definitely something to strive for. Agile development practices (such as those I’ve seen at length in books by Jeffries and the Pragmatic team) certainly aim for DRY, but not at the expense of clarity. As Jeff A. notes at the end of this article, we frequently can’t solve the problem until we have enough solvable problems, which is why Agile requires a blend of quick, make it work and pass a test code with an aggressive round of no-holds-barred refactoring. Read Jeffries’ Adventures in C# book for some great examples of how to make refactoring work for you.

As a web developer of some years, and having had to pick up others’ REALLY BAD code many, many times, I can honestly say that when I’ve violated DRY I’ve paid for it in unbillable warranty fixes. The client likes those kinds of surprises far less than a milestone slip, and the accountants are none too sympathetic either. CYA and DRY, every time, all the time. Have the courage to stand for the quality of your work.

This would seem to lend itself to a file-per-method result, no?

I’m not saying I agree with Uncle Bob’s proposed solution. Note that he used Ruby, so it’s a little more than seperate files-- he added methods dynamically to the class at runtime.

Mainly, I’m agreeing with his observation that the data layer, and the reporting layer, need to be editable seperately from the employee class. However we choose to achieve that goal is up to us. The “Do One Thing” of the employee class is the employee, not database details, or reporting details. This does fly in the face of traditional OOP-- the data and the logic “live together”-- which is why it’s counter-intuitive.

BuggyFunBunny is bang on. Straight from database basics: instead of one data item in one place only, it’s one piece of logic in one place only. The power of OO is that is does so much to enable This One Thing.

The problem with Jeff’s “Employee class” example is that it’s at the wrong level of granularity — and there’s no way to tell what the right level of granularity is.

Suppose we take “Do One Thing” at face value. Then clearly, each line of source code should Do One Thing. Clearly, each function or method should have a single purpose. Clearly, each object instance and each class should have a single responsibility. Clearly, each module, source file, or DLL should Do One Thing. Clearly, each program and each operating environment should Do One Thing…

Now, at some point these conclusions become obviously absurd; even in the Unix world, where Curly’s Law has been obeyed unconsciously since the dawn of time, we have “GNU hello” and OpenOffice.

So the question is, at what point do we admit that “Do One Thing” is not a fundamental law? Jeff seems to believe that the law applies all the way up to the “class” level, but not the “module” level. I’d personally set the bar at the “method” level, or maybe lower; and Jeff’s “Employee” class certainly fits in with Curly’s Law on those levels.

these conclusions become obviously absurd

Not necessarily.

It’s true that “Do One Thing” becomes larger and larger as you move up the chain, more nebulous, more difficult to define. The higher up you get (OS level?), the more the One Thing becomes a vision statement rather than a concrete definition you can point to. But I’d still say it’s true; I think we can somewhat reliably point to the “Do One Thing” of OS X, Windows Vista, Ubuntu.

I didn’t find it until after I wrote this post, but Tim Ottinger has an earlier post he calls Curly’s Guide To Software with some excellent examples:

http://blog.objectmentor.com/articles/2007/01/10/curlys-guide-to-software

As Tim points out, Do One Thing applied at the line level is one reason I don’t care for one-line conditional operators:

return x==1 ? Stuff() : OtherStuff();

I think DRY principle is only one tool for writing good code. Design is about compromises – very often, when DRY requires me to use some clever design pattern i find it more useful to compromise purity in favor of clarity.

If you pick purity as your goal then you’ll have less time for hobbies and family!