In Outliving the Great Variable Shortage, Tim Ottinger invokes Curly's Law:
A variable should mean one thing, and one thing only. It should not mean one thing in one circumstance, and carry a different value from a different domain some other time. It should not mean two things at once. It must not be both a floor polish and a dessert topping. It should mean One Thing, and should mean it all of the time.
The late, great Jack Palance played grizzled cowboy Curly Washburn in the 1991 comedy City Slickers. Curly's Law is defined in this bit of dialog from the movie:
Curly: Do you know what the secret of life is?
Curly: This. [holds up one finger]
Mitch: Your finger?
Curly: One thing. Just one thing. You stick to that and the rest don't mean shit.
Mitch: But what is the "one thing?"
Curly: [smiles] That's what you have to find out.
Curly's Law, Do One Thing, is reflected in several core principles of modern software development:
Although Curly's Law definitely applies to normalization and removing redundancies, Do One Thing is more nuanced than the various restatements of Do Each Thing Once outlined above. It runs deeper. Bob Martin refers to it as The Single Responsibility Principle:
The Single Responsibility Principle says that a class should have one, and only one, reason to change. As an example, imagine the following class:
class Employee { public Money calculatePay() public void save() public String reportHours() }This class violates the SRP because it has three reasons to change:
- The business rules having to do with calculating pay.
- The database schema.
- The format of the string that reports hours.
We don't want a single class to be impacted by these three completely different forces. We don't want to modify the Employee class every time the accounts decide to change the format of the hourly report, or every time the DBAs make a change to the database schema, as well as every time the managers change the payroll calculation. Rather, we want to separate these functions out into different classes so that they can change independently of each other.
Curly's Law is about choosing a single, clearly defined goal for any particular bit of code: Do One Thing. That much is clear. But in choosing one thing, you are ruling out an infinite universe of other possible things you could have done. Curly's Law also means consciously choosing what your code won't do. This is much more difficult than choosing what to do, because it runs counter to all the natural generalist tendencies of software developers. It could mean breaking code apart, violating traditional OOP rules, or introducing duplicate code. It's taking one step backward to go two steps forward.
Each variable, each line of code, each function, each class, each project should Do One Thing. Unfortunately, we usually don't find out what that one thing is until we've reached the end of it.
Posted by Jeff Atwood View blog reactions
« Choosing Anti-Anti-Virus Software Your Code: OOP or POO? »
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.
Monkeyget on March 3, 2007 06:22 AMHilarious, how many ways can we say 'Don't repeat yourself'? :)
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...
wioota on March 3, 2007 07:21 AMAh 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? :)
Greg on March 3, 2007 10:39 AMI 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!
RevMike on March 3, 2007 11:39 AMSo 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!
josh on March 3, 2007 12:42 PM> 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
Jeff Atwood on March 3, 2007 01:43 PMI'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.
Kim on March 3, 2007 03:00 PMOK, 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.
buggyfunbunny on March 3, 2007 03:07 PMLOL. 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.
woot on March 3, 2007 04:44 PMBuggyFunBunny 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.
John Pirie on March 4, 2007 07:36 AMIt's a wonderful rule, but one doesn't always have the luxury of following it.
Consider a horribly complex legacy application without any regression test suite, where all the original developers have long gone, and documentation is outdated. The application is retired, put into "maintenance mode" they say. But, inevitably, a new bug is reported, and the customer is important enough to dust off the source code and provide them with a fix. What is Junior Developer to do?
Modifying the behavior of the existing application is dangerous because Junior doesn't understand all the ramifications of what he is changing. So Junior hacks a solution on top of an already complex ball of spaghetti, making sure to alter the minimal amount of existing logic as possible. If this requires duplicating some code that already lies somewhere in the abyss, I argue that it is a necessary evil.
Hillbilly on March 4, 2007 10:22 AMYes, 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...)
John on March 4, 2007 03:10 PMDRY 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.
rickcabral on March 4, 2007 04:35 PM> 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.
Jeff Atwood on March 4, 2007 06:19 PMThe 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.
Anonymous Cowherd on March 4, 2007 08:11 PM> 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!
Aivar on March 4, 2007 10:07 PM"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."
Or Plan9. They took "Do One Thing" to a whole new level. I once read that adding features to the Plan9 code-base actually made it shrink.
I wonder how that relates to the 'defects in programming languages' discussion?
Mars on March 5, 2007 06:49 AMThe only problem with the proposed solution is that partial classes didn't show up until .Net 2.0, so if you're working in an older version of the runtime (because, say, you're working with a web app built in ASP.Net 1.1 and, say, upgrading it is a nightmare) you either stick all the methods in one class or you get gnarly with your class structure to compartmentalize it.
As far as editing the data and reporting layers of Employee separate from Employee's business logic, it depends. Is the data layer considered to be the database and the stored procedures you use to have your code talk to it or is it code to talk to the database? If it's code to talk to the database, why separate it from the Employee class? That sort of monotonic relationship doesn't earn you anything: the Employee class is worthless without its data layer and the data interface layer is worthless without the Employee class, so have Employee encapsulate the data interface layer.
Even with the GoF's patternalia on speed-dial, it's easy to mangle the implementation of something like MVC - lord knows I've done it enough in my time.
Ridiculous.
Jeff, do you actually know what you're doing?
Let's take a look at this:
class Employee
{
public Money calculatePay()
public void save()
public String reportHours()
}
_There's nothing wrong with this_. Absolutely nothing.
These are simply convenience methods. That's it. Put your functionality somewhere else, if need be.
These methods are simply an application of the facade pattern.
foobar on March 5, 2007 09:26 AM> Jeff, do you actually know what you're doing?
For the umpteenth time, I do not always agree with everything I quote. I didn't write the example; Uncle Bob did.
Jeff Atwood on March 5, 2007 09:54 AMJeff,
I'd be interested to hear your comments on Kim's suggestions to use dependency injection to solve the issue adequately. Since it was a Java problem, Uncle Bob's idea to use Ruby to solve it seems... out of place.
Garret on March 5, 2007 12:04 PM> Jeff, do you actually know what you're doing?
Of course not. If he knew what he was doing, he wouldn't be writing a blog. He'd be leaving snarky comments on other people's blogs.
A. Lloyd Flanagan on March 6, 2007 10:03 AM"Does that mean you have to not re-use i for loops? :)"
Clearly it means you should only code a for-loop once. I can only assume the prescribed code would look something like this:
public delegate void LoopProcessDelegate(int Ndx);
public void Loop(int startNdx,
int endNdx,
LoopProcessDelegate process)
{
for (int i = startNdx; i < endNdx; i++)
{
process(i);
}
}
public void TestLoop()
{
Loop(0, 9, new LoopProcessDelegate(
delegate(int ndx)
{
Console.WriteLine(
string.Format("{0} : Hello World", ndx));
}));
}
Isn't that much better?
-Andy
For the humor-impaired, yes, this is a joke.
Nice article Jeff...
Just gotta be careful when creating objects...
http://www.codinghorror.com/blog/archives/000801.html
So, finding the right place for things even happens in blog entries...
Both are EXCELLENT articles, and the point being made is quite well made. Thanks, Jeff!
John Baughman on March 6, 2007 05:56 PM"This class violates the SRP because it has three reasons to change:"
How can you possibly justify this statement without providing even a hint of implementation?
Each of these methods would be reasonable as long as they were implemented reasonably.
Tim on March 6, 2007 07:13 PMHow many ways?
Well, we started with OAOO, then DRY, then Single Point of Truth. Actually, Curly predates the DRY acronym.
Oh, wait... we started a long time before OAOO. We spoke in the negative about "multiple maintenance points" and before that...
We'll find a dozen ways to say it, but the Java people will decide that they need one more so that they can maintain their terminology ghetto, and we'll have at least a million and one.
Yeah, I'm mostly joking. The point is that we *do* keep restating the same thing for decades, and saying in more ways and more clever ways doesn't seem to cause people to actually do it. The interesting question is how can we help people do it? Some of us are insane (in the Einstein-ian mode) and keep trying the same thing by stating it over and over. Other than following the principle ourselves and teaching it to others, what can we do?
Tim on March 7, 2007 08:11 AMI've been perusing your blog for about an hour and have found some useful stuff and some not-so-useful stuff. Your "Curly's Law" post is extremely useful. Your "Branding" and "Office Tour" posting are not.
My friendly advice is to leave "branding" and "tours" to the marketing hacks and give us some more coding pointers.
praxedis on March 8, 2007 01:21 PMThere is nothing wrong with the employee class. Don't listen to this example or else you could end with a system with object bloat.
DRY works here if calculatepay only exists in the employee class and not in the front end, in the RDMS layer, or is repeated in another class.
Employee could be a base class with calculatepay being an overridable method. Other class inherit and may calculate pay differently by implementing there own version of calculatepay, or they use base employee method.
You can have an object that has multiple methods that update that objects state. Its a guideline not an absolute rule. If you folow this rule to the letter, you will have lots of little classes in your application and your code will be hard to follow.
I don't agree with the Single Responsibility Principle but that's just my opinion. I agree that all business logic for employee's should be located in one location of code and not spread throughout the codebase.
Good article. Nothing I didn't know before, but as I was reading through it I immediately thought about a current program I'm writing that I got sloppy on and just copy-pasted a loop (with minor modifications) rather than turning it into a proper function. Lo and behold, I go back and look at the code after two weeks and another guy has come through and fixed a bug in one of the loops but not the other (guess he just missed it). So I converted it into a proper function.
And all before finishing reading the blog post.
Cyde Weys on September 2, 2007 06:39 PMIn the '70's, I knew Black Curley in Austin. He owned a 'junk yard' where I scrounged VW parts.
Curley's Rule was "You gots to keep fixin' it 'tils you gets it fixed."
And the corallary: "If it woiks, it's fixed."
He could have been a programmer... .
Click my name for a different take on the whole Curly's Law thang ...
Seamus Anthony on August 5, 2008 06:42 AM| Content (c) 2008 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved. |