When Sufficient Design Means Poor Design

Posted May 5, 2010 by Joshua Kerievsky

Sufficient Design is where Lean meets Craft.

Lean is quantitatively focused on increasing value while shortening cycle time.

Craft is qualitatively focused on increasing quality while removing kludges and cruft.

Lean is the Accountant in the family.

Craft is the Artist.

Endless sibling rivalry rages between the two.

When writing software, Sufficient Design asks you to decide whether Lean or Craft will win and by how much.

Let's consider the Lean/Craft rivalry in a real-world feature.

In September, 2008, Industrial Logic reached agreement with our largest client to deliver our Agile eLearning albums to hundreds of their programmers around the globe.

We'd roll out the software in January, 2009.

That gave us little over 3 months to complete some promised new features and content.

A month before the big roll out, our client asked about a way to give users alternative ways to navigate the content in our albums.

Our future plans already included support for Playlists, so I mentioned them and our client loved the idea.

I said I couldn't promise that we'd deliver that new feature in January (since we already had our hands full), but we could get to it soon after the big roll out.

By early January, we were a bit ahead of schedule, so we decided to see if we could squeeze in Playlists.

We had about two weeks to get the work done, which meant we'd have to design a minimal version of Playlists and program it quickly.

After some discussions, white board sketches and UI drawings, we had our basic idea for a simple Playlist feature.

We decided that we'd leverage our Album class to also act as a Playlist and we decided to produce a canned set of Playlists that users would see if they owned the albums referenced by a given Playlist.

This was a far cry from giving users the ability to create and manage their own playlists, yet our canned Playlists would provide our client with exactly what she had asked for.

As usual, we used Test-Driven Development to produce our solution, taking time to craft good tests but consciously doing quick and dirty design work to save time.

The UserLibrary class

Our UserLibrary class (pictured on the right), began to get bloated with responsibilities.

Lean was lording it over Craft.

By the end of week one, we had a barely functioning version of Playlists that was still invisible to end users.

Early the following week, we worked on fixing an unwanted behavioral limitation involving adding entire tracks to a playlist.

(We wanted the design to include new, future pages added to a track, even if they weren't there when the playlist was first created).

While we didn't completely avoid refactoring, the design was getting smellier with each passing day.

Our newest team member, Mike Bria, voiced a few concerns.

The thought crossed my mind that perhaps Bria (as we call him) worried that he'd joined a company of posers with exceedingly poor design skills.

I assured Bria that we were well aware of the poor design, yet releasing the Playlist feature quickly was more important to our product launch and our business.

Any good programmer could gaze at UserLibrary and wonder whether we needed remedial training in object-oriented design.

By the end of the second week, programming was done, graphics were done and the content for the new playlists was also done, just in time for our big release to the client.

A Playist Album Cover

Lean was the clear victor in this work.

Consequences of Dialing Down Design

Over a year has passed since we released the Playlist feature.

Since then, we've added a few more Playlists for different clients.

A Playist Album Cover

Based on a year of living with the Playlist feature and studying usage data, here's what we know:

  • Our users are definitely using Playlists.
  • No user has ever complained about Playlists.
  • Our users don't rave about Playlists the way they rave about other features in our product.
  • During sales, future clients are happy to hear that our Playlists provide time-efficient learning sequences for specific topics.

So are we paying the price for incurring high design debt on the Playlist feature?

No.

Here's why:

  • No New Behavior — we haven't added or changed any behavior of the original Playlist feature during the last year.
  • Unchanged Code — We've made only trivial changes to UserLibrary (main source of the Playlist design debt) during the last year and those changes had nothing to do with Playlists.
  • An Isolated Problem — The poor design we do have for Playlists is isolated: it hasn't gotten in the way of new feature development in the last year.

Playlists were an "accessory" thrown in to a deal to make our largest client happy.

The feature has been useful but isn't on the critical path of our product development.

The code smells in the Playlist code, such as Large Class and Conditional Complexity, have not slowed us down.

And here's an insight into how we manage quality:

If we decide to give users a more sophisticated Playlist feature, we will happily pay down design debt before making new progress.
 
So you took plenty of short cuts to get your feature out the door, yet when you want to add capabilities to that feature you will have to spend a lot of time cleaning up the mess, which will slow you down!
 

Yes and no.

First, because we test-drove the feature and we're skilled in TDD, we have good test coverage.

Second, we use powerful refactoring tools in our IDE that allow us to rapidly change designs while automated tests verify that we didn't break anything.

Third, our management (me) understands when to say no to high speed feature injection and when to focus on quality improvements instead.

Sometimes Craft needs to lord it over Lean.

We not only understand that, we also practice it.

Our entire code base is fairly clean. The messes are mostly contained and monitored.

So are we happy with the Playlist code from a year ago?

We are happy that we didn't invest more in Playlists than needed at the time.

We'd like to clean up the design but will wait until the time is right.

That is Sufficient Design.

What Others Are Saying

Brian Foote, an Industrial Logic author/coach/instructor and co-author of Big Ball of Mud, said (after reviewing this blog entry):

"Playlists occupy an area of our code that we could afford to let go to seed a little, to put on a few extra pounds, because the overall health of our codebase has been tended to."

"In a less healthy codebase, a compromise like this could have sent the system over the edge into squalor. A healthy balance sheet has room for debt that an overburdened one does not."

Bob Martin, in response to my first blog post about Sufficient Design, observed that my aborted cleanup of a software plumbing design flaw was proof that I am indeed a craftsman and that Sufficient Design is actually Damned Good Design.

As this post hopefully makes clear, I respectfully disagree. Sufficient Design may be anything from excellent to poor design based on the context at hand.

Ron Jeffries observed in Quality vs Speed? I Don't Think So! that while low quality code produced at high speed is common, we'd all do well to strive to produce high quality code at high speed.

I think this is an excellent goal, however, no matter how good I get, I often find that I need to reflect on early iterations of a design before I can find just the idea I'm looking for to evolve it into a great design.

Dave Smith pointed out a fitting quote from Gerald Weinberg in his classic, The Psychology of Computer Programming:

"[E]ach program has an appropriate level of care and sophistication dependent on the uses to which it will be put. Working above that level is, a way, even less professional than working below it."