My Secret Life as a Spaghetti Coder
home | about | contact | privacy statement
We could all stand to be better at what we do - especially those of us who write software. Although many of these ideas were not news to me, and may not be for you either, you'd be surprised at how you start to slack off and what a memory refresh will do for you.

Here are (briefly) 10 ways to improve your code from the NFJS session I attended with Neal Ford. Which do you follow?
  1. Know the fundamentals
    It was no surprise that Neal led off with the DRY principle. Being one from the school of thought that "code reuse" meant "copy-and-pastable with few changes," I feel this is the most important principle to follow when coding.

    Neal uses it in the original sense: Refuse to repeat yourself not just in your code, but all throughout the system. To quote (If I remember correctly, The Pragmatic Programmer): "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." I can't think of anything that wouldn't be improved by following that advice.

    Continuous integration and version control are important fundamentals as well. Continuous integration means you know the code base at least compiles, and you can have it run your unit tests as well. The codebase is verified as always working (at least to the specs as you wrote them - you might have gotten them wrong, of course).

    Using version control means you don't have to be afraid of deleting that commented out code that you'll probably never use again but you keep around anyway "just in case." That keeps your code clean.

    Finally, Neal suggests we should use static analysis tools such as FindBugs and PMD that inspect our code, look for bad patterns, and suggest fixes and places to trim. Those two are for Java. Do you know of similar applications for other platforms? (Leave a comment please!)

  2. Analyze code odors and remove them if they stink
    Inheritance is a smell. It doesn't always have to smell bad, but at least you should take the time to determine if what you really need is composition.

    Likewise, the existence of helper or utility classes often indicates a "poor design." As Neal notes, "if you've gotten the abstraction correct, why do you need 'helpers'?" Try to put them in "intelligent domain object[s]" instead.

    Static methods are an indication you are thinking procedurally, and they are "virtually impossible to test." Seeing the procedural mindset is easy, I think, but why are they hard to test?

    Finally, avoid singletons. I know Singleton is everyone's favorite design pattern because it's so easy to conceptualize, but as Neal mentions:
    • They're really just a poor excuse for global variables.
    • They violate SRP (link is to a PDF file) by mixing business logic with policing you from using too many
    • They make themselves hard to test
    • It's very hard to build a real singleton, as you have to ensure you are using a unified class loader.
    • More that I won't get into here...

  3. Kill Sacred Cows
    Neal started off saying that "sacred cows make the tastiest hamburger" and told the story of the angry monkeys:

    A few monkeys were put in a room with a stepladder and a banana that could only be reached if the monkeys used the stepladder. However, when a monkey would climb upon the stepladder, all the monkeys would be doused with water. Then new monkeys were introduced and when they tried to use the stepladder, the other monkeys would get angry and beat him up. Eventually, the room contained none who had been doused with water, but the monkeys who had been beat up for using the stepladder were still refusing to allow new monkeys to get the banana.

    That is the light in which you may often view sacred cows in software development. In particular, Neal points out that
    • StickingToCamelCaseForSentenceLongTestNamesIsRidiculous so_it_is_ok_to_use_underscores in languages_that_standardized_on_camel_case for your test names.
    • Using getters and setters does not equate to encapsulation. One of my favorite sayings, and a reference to why they are evil is appropriate.
    • Avoidance of multiple returns in a method is antiquated because it comes from a time when we weren't using tiny, cohesive methods. I still like to avoid them, but will use them when it makes the code more readable.
    • Polluting interface names with "I", as in ISomeInterface should be avoided. It is contrary to what interfaces are about. Instead, you should decorate the concrete class name.

  4. Your objects should be Good Citizens. Never let them exist in an invalid state.

  5. Use Test Driven Development
    TDD provides "explicit dependency management" because you must think about dependencies as you code. It provides instant feedback and encourages you to implement the simplest thing that works.

    You can take it further by analyzing your dependencies with JDepend.

    And don't forget about YAGNI. Quoting Neal,
    Speculative development saves time if
    • You have nothing better to work on right now
    • You guarantee that it won't take longer to fix later
    Is that ever going to be true?

  6. Use reflection when you can (and when it makes sense)
    It's not slow, and it can be extremely useful!

  7. Colour Your World
    Nick Drew, a ThoughtWorker in the UK came up with a system of coloring the code you write by classifying it as to who will use the feature: only a specific business customer, only a particular market, many markets, or unchangeable infrastructure. Based on the color and amount of it, you can decide what value or cost your code has.

    It's a very interesting system, so I recommend seeing a more detailed overview in Neal's handout (PDF). You can find it on pages 21-22.

  8. Use a DSL style of coding
    It lets you "[Build] better abstraction layers, using language instead of trees" while "utilizing" current building blocks. As he pointed out, "every complicated human endeavor has its own DSL. [If you can] make the code closer to the DSL of the business [then] it is better abstracted."

    I'll add that it's easier to spot logical errors as well.

    A funny quote from Neal: "It's almost as if in Java we're talking to a severely retarded person." Regarding that, he recommended looking at EasyMock as a good example of fluent interfaces, and that having setters return void in a waste. Instead, we should return this so that calls can be chained together (and if you are using fluent-interface type names, you could construct sentences in your DSL that way).

    Neal also noted a distinction between an API and DSL: API has an explicit context that must be repeated with each call. However, a DSL uses an implicit context.

  9. SLAP tells us to keep all lines of code in a method at the same level of abstraction. Steve McConnell's Code Complete 2 tells us about this as well, but I don't recall if it had the clever acronym.

  10. And finally, Think about Antiobjects.
    Quoting Neal, "Antiobjects are the inverse of what we perceive to be the computational objects." So instead of solving the really hard "foreground" problem, have a look at the "background" problem (the inverse) to see if it is easier to solve.

    As an example, he used PacMan: Rather than constructing a solution for the "shortest route around the maze," the game has a "notion of a PacMan 'scent'" for each tile." That way, the ghosts follow the strongest scent.

As with my write-ups on Scott Davis's Groovy: Greasing the Wheels of Java, and Stuart Halloway's JavaScript for Ajax Programmers, Neal gets all the credit for the good stuff here. I'm just reporting from my notes, his mindmap, and my memory, so any mistakes you find are probably mine. If they aren't, they probably should have been.

So which strategies have you used? Will you start using any of the above?

Hey! Why don't you make your life easier and subscribe to the full post or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!


Comments
Leave a comment

OK, you've pushed me over the edge. I've snapped. I can take it no more. My outbound tact filter has overloaded.

Inheritance is a smell? You've got to be kidding. "Inheritance is overused" I can stomach. "Inheritance is poorly understood" I can hear and yet maintain my gentlemanly poise. But a smell? Jeez Louise! Dan Ingalls and Bertrand Meyer must be wishing they were dead so they had graves to turn in!

Posted by Jaime Metcher on Jul 28, 2007 at 03:16 AM UTC - 5 hrs

Jaime - there are 2 things to consider here:

The first is that smells needn't be noxious. As the wiki at c2.com puts it:

"A CodeSmell is a hint that something might be wrong, not a certainty. A perfectly good idiom may be considered a CodeSmell because it's often misused, or because there's a simpler alternative that works in most cases. Calling something a CodeSmell is not an attack; it's simply a sign that a closer look is warranted." (http://c2.com/xp/CodeSmell.html)

The next is the question, "is inheritance a smell?" Neal Ford certainly thinks so, and I agree. He didn't provide his reasoning (that I recall), but I can provide one from the Gang of Four Design Patterns book (among many other places): Inheritance tightly couples base classes to derived ones, is more rigid than composition, and breaks encapsulation. Further, as you mention (and they do too), it is overused as a code-reuse mechanism while "designs are often made more reusable (and simpler) by depending more on object composition" (this is all from pages 18-20).

So knowing these things, I think it is worthwhile to look at inheritance in your system as it crops up and ask yourself, "is inheritance what I really want, or would my design be better if I used composition?"

By attaching the "smell" label to a particular coding practice like this, you make it easier to remember to think about those things, and inheritance fits the bill in my opinion.

Does that ease the frustration with classifying inheritance as a smell, or do you still have misgivings?

Posted by Sam on Jul 28, 2007 at 02:16 PM UTC - 5 hrs

Hi Sam,

One of the quotes:

"API has an explicit context that must be repeated with each call. However, a DSL uses an implicit context."

Any idea what he means by that?

Posted by Peter Bell on Jul 28, 2007 at 03:17 PM UTC - 5 hrs

Sure Peter. The explicit context that you repeat with each function call of the API would be like (graphics is on my mind at the moment, so here's a contrived API):

Point pointStart = new Point(0,0);
Point pointEnd = new Point(10,20);
Line line = new Line(pointStart, pointEnd);
Drawer d = new Drawer();
d.setColor(Colors.Green);
d.draw(Line);

//and so on.

But the implicit context of a DSL might read like:

Drawer d = new Drawer();
d.draw(ShapeOf.Line).from(0,0).to(10,20).with(TheColor.Green);

It's a bad example of OO, since you'd want shapes to draw themselves, but I think it gets the point across.

As always, Martin Fowler has a good example (I think more illustrative than mine) of what Neal was talking about here: http://www.martinfowler.com/bliki/FluentInterface....

His first example shows the repetitive explicit context, while the second example only defines the context ones and reuses it.

Posted by Sam on Jul 28, 2007 at 06:19 PM UTC - 5 hrs

Sam,

Yeah, I pretty much agree with everything you say about inheritance. I could also provide a list of problems with using composition where inheritance would be simpler. Does that make composition a smell too? That wiki definition stinks - according to it, what's *not* a smell? I would say Java, Cold Fusion, Excel macros and coding in general all fit the definition.

Just trying to get equal time for inheritance. It's not anything particular you've said, just lately the press has been all bad...must be time to release a new single...maybe a video clip...

Jaime

Posted by Jaime Metcher on Jul 28, 2007 at 06:57 PM UTC - 5 hrs

I had not heard of the colouring approach for features.. What I have done in the past, is a priority numbering approach. Features that have a higher benefit get a higher number. That's pretty straight forward. The more interesting one is a second score which is the cost of not doing the feature. For example, losing a customer, or a sale, etc. The combination of the scores reflects cost and benefit.

Posted by Fred Medlin on Jul 29, 2007 at 08:28 AM UTC - 5 hrs

@Jaime - I think attaching the label of code smell to particular items is a useful way to categorize things that may be harmful more often than the rest of the pitfalls in programming. It certainly isn't a license to indiscriminately pay non-attention to other areas.

As for architectures and platforms - you raise an interesting idea. I'm not aware of any catalog of smells relating to having chosen the wrong platform, for example, but it may be worthwhile to create a such a catalog. Do you have any ideas for specific smells in those areas?

Finally, I always like to learn and I think it would be useful for other readers as well, so if you were inclined to provide a list of smells relating to composition and places where inheritance is a better choice (I know there are several!), please feel free to do so. It can only benefit everyone. =)

@Fred - I too just heard about the coloring system. We haven't generally classified features like this, as most of the work we do is custom (though I realize there have been features we could have reused across different types of applications).

However, lately we've been trying to develop resellable solutions and have been discussing features in terms of generally useful or client-specific, and have tried to avoid client-specific features where possible, because any customization on that end makes the entire product harder to sell to anyone else. The next time we start discussing that, I'm going to bring up the color scale and try it out.

Finally, you said you rate the features on a benefit (including negative benefits, or cost) scale. From whose perspective do you do the rating, your own or the customer? (I'm assuming your own, but wanted to ask to be sure).

Posted by Sam on Jul 29, 2007 at 01:46 PM UTC - 5 hrs

Yes, our perspective. On this scale, a highly valued feature would have the same score as a less valued feature that would cause much pain if it were not implemented.

It can be tricky, based on the definition of pain. If you're not careful, then your sales force will say that every competitive feature that not implemented causes pain. You have to be faithful to the product's niche.

I guess roughly it's opportunity cost; this is just a simple way to help quantify it.

Posted by Fred Medlin on Jul 31, 2007 at 09:39 AM UTC - 5 hrs

"your sales force will say that every competitive feature that not implemented causes pain."

I like that. It happens a lot for me recently, and I know what you mean!

Posted by Sam on Jul 31, 2007 at 10:57 AM UTC - 5 hrs

Leave a comment

Leave this field empty
Your Name
Email (not displayed, more info?)
Website

Comment:

Subcribe to this comment thread
Remember my details
Google
Web CodeOdor.com

Me
Picture of me

Topics
.NET (19)
AI/Machine Learning (14)
Answers To 100 Interview Questions (10)
Bioinformatics (2)
Business (1)
C and C++ (6)
cfrails (22)
ColdFusion (78)
Customer Relations (15)
Databases (3)
DRY (18)
DSLs (11)
Future Tech (5)
Games (5)
Groovy/Grails (8)
Hardware (1)
IDEs (9)
Java (38)
JavaScript (4)
Linux (2)
Lisp (1)
Mac OS (4)
Management (15)
MediaServerX (1)
Miscellany (76)
OOAD (37)
Productivity (11)
Programming (168)
Programming Quotables (9)
Rails (31)
Ruby (67)
Save Your Job (58)
scriptaGulous (4)
Software Development Process (23)
TDD (41)
TDDing xorblog (6)
Tools (5)
Web Development (8)
Windows (1)
With (1)
YAGNI (10)

Resources
Agile Manifesto & Principles
Principles Of OOD
ColdFusion
CFUnit
Ruby
Ruby on Rails
JUnit



RSS 2.0: Full Post | Short Blurb
Subscribe by email:

Delivered by FeedBurner