Marty Andrews

artful code

Tuesday, January 23, 2007

EAT Design Improvement

Steve and I ran our first EAT workshop last Saturday, and it went pretty well. As part of the feedback that we gathered from the course, we asked for people to indicate what other material they might be interested in seeing. One of the options we put forward was a design improvement course based on some material I've worked on recently with Simon Harris. As it turns out, that seems to be the most popular option.

Is anyone else interested in seeing a course like that? If demand is high enough, we'll put it together for next month.

Easy Acess Training. One Saturday. BYO laptop. Seats auctioned off. Bids start at $100.

Sunday, January 21, 2007

Why IntelliJ IDEA is a great IDE for Ruby on Rails

JetBrains have just released the first beta of their IntelliJ IDEA Ruby Plugin. I've been using builds from the source repository for a while, and its my favourite environment for building Ruby on Rails applications. The strange thing is, I don't use it because I think it makes IntelliJ a great Ruby environment. I use it because IntelliJ is already a great environment for building HTML and CSS. When I'm building Rails apps, the framework allows my code to be so lean that I actually find myself spending more time working on the presentation of the system.

IntelliJ has been a great environment for HTML and CSS for years now. Its certainly very familiar to me from my Java web app development experience. The release of this plugin makes it at least an adequate environment for Ruby development. If history is anything to go by, JetBrains may well turn it into something which makes me wish I was forced to write more Ruby code in a Rails app :-)

Friday, January 19, 2007

Pragmatic requirements duplication

Duplication in code is generally considered bad, because it violates the DRY principle. Probably the only commonly accepted exception to the rule is documented in Extreme Programming Explained by Kent Beck. He says that the one higher priority (than removing duplicate code) in simple design is to ensure that the system "must communicate everything you want to communicate". In other words, the readers of the code need to be able to understand what it is doing by reading it.

Requirements in the form of Use Cases are no different in this sense. The Use Cases should ideally have no duplication within them, but it is actually more important that the readers of the requirements understand what is being described within them. In other words, if putting some duplication in helps your readers better understand what is going on, that's a good thing.

The main difference between these cases is the skill of the reader. The people reading the code are (probably) trained in how to design and write it. The people reading the Use Cases however are probably not trained. In many projects, Use Cases have to be signed off by representatives of the business with no training or experience in them at all. In such cases, being pragmatic about duplicating some sections of requirements may be perfectly appropriate.

Oh - and by the way, Use Cases are not design. If you end up with duplicate code as a result of having duplicated requirements, beat your developers with a stick. They should know better.

Thursday, January 18, 2007

Giving your team an Elizabethan collar

If you have a pet dog or cat that has a nasty cut, you'll find that it will lick its wounds. The wound can actually take longer to heal, or can get infected, as a result. Of course, your pet doesn't know that, its just doing what its instincts tell it to. To solve the problem, you give them an Elizabethan collar. It lets them continue eating and drinking, but doesn't let them cause any more damage to the wound. In the meantime, you can apply some disinfectant and clean the wound right up so there's no more problem.

Sometimes you come across software teams in the same situation. They've got a wound in their software, and they're instinctively licking away at it in the hope that it will get better. It might change from a nasty cut into a puss-filled scab, but its often not improving.

You can solve the problem in exactly the same way. Give the team an Elizabethan collar that lets them keep going on with their daily lives, but keeps them away from that wound. You might need a layer of abstraction, a checkstyle rule, or a continuous build. Preferably something automated that keeps them from twisting around and gnawing on that nasty gash. In the meantime, step in and apply whatever disinfectant is required to clean up the problem. A spoonful of refactoring will often do the trick, but sometimes a full transplant is required.

Once the job is done, you've got a choice. Sometimes you will choose to keep the collar because the team have become accustomed to living with it, and it might come in handy one day. You might also be able to remove the collar to let the team see your efforts at first aid. In many cases, that patch of skin will now be boring and uninteresting, so they won't bother with it anymore. If you're lucky however, they might be interested in the process you went through, and will resist the urge to gnaw next time they get cut, coming to ask for help instead.

Leaving the asshole behind

Someone I know just quit their job because they refused to spend any more time working with an asshole manager. They had been at the company for many years, and would have been what I would consider a reasonably loyal employee. Ultimately, that turned out to be completely over powered by the pain felt in dealing with the asshole manager.

Working in an environment that has no assholes is a good goal to have, so I'm supportive of the decision. Loyalty to people is far more important in my mind than loyalty to organisations. Its nice to find a company that has beliefs in line with your own, but those beliefs only come about because of the people that created them. Find those people, and work with them, regardless of what company they happen to be in.

Sunday, January 7, 2007

Fixing a static method code smell

Static methods are a clue that a piece of code might not be conforming well to object oriented design principles. Its probably not as well designed as it could be, or as reusable as it could be. Most of all, its probably not as testable as it could be. The contents of the method often belong on one of the objects being worked on, and sometimes belong in a whole new object unto themselves.

The first clue for how to fix it is to look at the parameters being worked on in the method. If it is, then you can convert the method to an instance method on that object. IntelliJ IDEA has a refactoring option called "Convert To Instance Method...", which will ask you which parameter to move the method to. If you're not using IntelliJ IDEA, you can move the method to the appropriate Object, delete the parameter, and use this instead.

public class AccountFunctions {public static void withdraw(Long amountInCents, Account account) {// ...}}

becomes:

public class Account {public void withdraw(Long amountInCents) {// ...}}

The second clue for how to fix it is to look at the local variables being used in the method. It's possible that the contents of the method belongs on one of them instead of one of the parameters. Maybe a singleton object is being accessed in the method for example. In this case, the solution is similar to the first option, except that the parameters all still exist.

public class CartFunctions {public static void addItemToCart(Long itemId) {Cart cart = Cart.getInstance();List items = cart.getItems();items.add(itemId);}}

becomes:

public class Cart {public void addItem(Long itemId) {// ...}}

The third clue for how to fix it is to create a completely new class just for the static method. The parameters on the method become instance variables, and a constructor should be created that takes them as parameters. The method itself becomes an instance method on that class that uses the instance variables instead of having parameters to it.

public class DatabaseFunctions {public static String buildSelectStatement(String tableName, Collection columns,Collection conditions, Collection orderBys) {// ...}}

becomes:

public class Query {private String _tableName;private Collection _columns;private Collection _conditions;private Collection _orderBys;public Query(String tableName, Collection columns, Collection conditions, Collection orderBys) {}public String toSql() {// ...}}