10 ways to improve your code
Think outside the curly brackets
SD West 2008 Neil Ford's Software Development West presentation, 10 Ways to Improve Your Code, was aimed at Java programmers, but Ford's "advanced code hygiene" discussion had wisdom for coders of many stripes.
Ford is a senior application architect and "meme wrangler" at ThoughtWorks, an IT consultant that specializes in development and delivery of software, and that is home to object-oriented development, refactoring and agile authority Martin Fowler. Ford's talk covered a lot of territory, from test-driven development to advice on "good citizenship."
Ford advised attendees to:
1. Write the tests before writing the code. TDD stands for "test-driven development," but Ford believes it's more useful to think of it as "test driven design." "If you're rigorous about doing it, it has beneficial side effects on the design of your code that has nothing to do with testing," he said. Among those benefits: it discourages embedded object creation, forces mocking of dependent objects, and forces the earliest possible object interaction decisions.
2. Use static analysis tools. "If you're paying the static-typing tax in Java and C# anyway, you should take advantage of that in static analysis tools," Ford said. He cited two categories of these tools: byte-code analysis tools, such as the FindBugs open source tool; and source-analysis tools, such as PMD.
3. Practice "good citizenship" by paying attention to how well your objects interact with the outside world. "Never let them exist in an invalid state," Ford advised. "Mutations should always move from one known good state to another known good state." He also advised you should avoid the singletons, because they mix responsibilities by mixing static and state.
4. Avoid indulging in speculative software development. "The goal should be to build the simplest thing we need right now," he said. The practice increases software entropy, he added, which is a measure of code complexity.
5. Simplify essential complexity and kill accidental complexity. "It's the difference between, 'the problem we have is hard,' and 'we've made the problem we have hard,'" he said.
6. Challenge programming conventions, such as writing long, unreadable test names, and blindly following the JavaBean specification to the detriment of your code.
7. Embrace single level of abstraction principle (SLAP). The idea is based on advice from Kent Beck's book Smalltalk Best Practices and Patterns, Ford explained, which states that every public method should be as short as possible, and should consist of steps, each one of which is a private method. "Don't make abstraction leaps in your code," he said.
8. Leverage existing platforms with languages targeted at specific problems and applications. "This notion that there's this one true language that you should write everything in is melting away," Ford said. Coders should think in terms of "polyglot programming," because it takes advantage of "looming opportunities/ problems," such as massively parallel threading.
9. Learn every nuance of the languages you're using. If you're using Java as your everyday language, Ford said, there are a lot of "back alleys" worth investigating. For example: Java's reflection mechanism and regular expressions, which are widely misunderstood.
10. Change your perspective and consider "antiobjects." An antiobject is a kind of object that appears to do the opposite of what we think it should do, Ford explained. The object metaphor sometimes impedes the solution "by making us try to create objects that are too inspired by the real world."®
Maybe you had to be there
I watched this presentation at DevWeek 2008 last week (slightly modified for a .NET audience), and it was worthwhile. Neal talked for an hour and a half, so what you're reading in this article is necessarily a simple précis; he explains and justifies most of his bullet points very well.
Regarding 8 and 9, they're not mutually exclusive: you're going to have a primary programming language for constructing any given system, but it often makes sense to drop into another for certain purposes. I've always used C/C++ for better performance or Win32 API access. I'm going to learn F# because it does functional stuff better than C#, and probably Ruby because it looks right for some of the quick and dirty stuff. A lot of the time, learning other languages gives you some new ways of thinking about your primary language.
Regarding 4, the odds against you actually successfully predicting what you're going to need in 6 months are, as a rule, astronomically high. Obviously nobody would advocate writing code that you know you're going to have to completely rewrite in the next cycle, but it's perfectly sensible to write code that does only exactly what is currently needed, but in such a way that suspected requirements can be added easily by deriving, decorating or altering implementation.
@AC ("Maybe, maybe not")
I think the point is to avoid writing more code than is needed Right Now; the old Einstein-attributed "solutions should be as simple as possible but no simpler" applies just as much as common sense does. "Common sense", as AC indicated, is to have some sense of the entire life cycle of the code you're working on; if you have good reason to believe that Feature X will need to be added at some point but a) it's not needed now, b) taking what will be needed to support Feature X into consideration during the current design is cheaper than refactoring later, and incidentally c) thinking ahead doesn't introduce negatives like dead code or performance penalties into the current system, then go for it. If any of these preconditions doesn't apply, then keep your eyes on the ball that's being pitched at you now and refactor later.
As somebody else noted, 'wisdom of the ancients'. One thing I like about the whole TDD/XP way of doing things is that it reinforces a lot of the habits us OldTimers(TM)(R)(LSMFT) built up back in the day, that whippersnappers with their "Don't Worry, Be Crappy" attitude tried to throw under the wheels.
anti-objects = tools?
Never heard of anti-objects before. What I have found is that it is sometimes better to have classes which act as tools to do a job, manipulating other objects, rather than the more traditional the-object-knows-how-to-do-it-itself approach. But then a tool is a kind of real-world object.