Is it important to write good code?

The last three weeks I have visit several companies and talked about writing good code. It's amazing to see how different developer thinks about writing good code. Here are some comments when I asked if it's important to write good code:

- I don't care as long as it works it's fine.

- I don't have the time to write good code.

- The customer don't see the code, so as long as the application works, I'm satisfied.

- Most customers want to pay as little as possible for as much as possible, and to deliver it in time, I make sure the code only works.

- I know that we can use refactoring to make the code more readable, but we don't have the time to do it.

When I asked about reading other peoples code and also maintain it, most people answer:

- It's not easy all the time

- It's horrible and hard to understand what the code does.


That are some of the results when developers only write code to make stuff works and write code for them self and don't care to write code for other humans to understand.

Here is an example which I use during my presentation, it's a method which will calculate the price for renting a movie, the price differs between different type of customers and also different types of movies.

public class MovieRenter
{
    public double CalculatePrice(string customerType, string movieType)
    {
        if (customerType == "VIC" && movieType == "Transfer")
            return 20;
        else if (customerType == "Regular" && movieType == "Transfer")
            return 30;
        else if (customerType == "VIC" && movieType == "Normal")
            return 10;
        else if (customerType == "Regular" && movieType == "Normal")
            return 20;
        else
            return 50;
    }
}
 
MovieRenter movieRenter = new MovieRenter();
double price = movieRenter.CalculatePrice("VIC", "Transfer");


I asked the attendance if the method looks ok, I got surprised when the answer was "Yes". Some one told me, well I should have used enumeration and switch statement instead of strings and if statements. I did some refactoring and use an enumeration and switch statement instead with the help of the attendance, the result was the following code:

public class MovieRenter
{
       public double CalculatePrice(CustomerType customerType, MovieType movieType)
       {
           switch (customerType)
           {
               case CustomerType.VIC:
                   switch (movieType)
                   {
                       case MovieType.Transfer:
                           return 20;
                       case MovieType.Normal:
                           return 10;
                       default:
                           return 20;
                   }
                   break;
               case CustomerType.Regular:
                   switch (movieType)
                   {
                       case MovieType.Transfer:
                           return 30;
                       case MovieType.Normal:
                           return 20;
                       default:
                           return 30;
                   }
                   break;
               default:
                   return 30;
           }
}

public enum MovieType
{
    Transfer,
    Normal
}

public enum CustomerType
{
    VIC,
    Regular
}
MovieRenter movieRenter = new MovieRenter();
double price = movieRenter.CalculatePrice(CustomerType.VIC, MovieType.Transfer);

When I asked if we can do it even better, someone wanted to split the switch statement inside of the case CustomerType.VIC into a separate method, the same with the switch statement in the case CustomerType.Regular. I did that changes to the code and everyone was happy. The problem with conditional statement in code, is that they can be hard to maintain, if I need to add a new kind of movie and customer, I need to add more conditions to my code. Sooner or later it will be difficult to maintain. So one thing we can do, is to replace a conditional statement with polymorphism.

If we look at the code, we can see that we have a customer and a movie to which a customer wants to rent. So we create two entities, one Customer entity and one Movie entity. We can then add a Rebate property to the Customer class and a Price property to the Move class. A movie have the price, and Customer have a rebate. Now when we have a Customer and a Movie, we need to make sure we also have the different type of Customers and Movie represented as classes, we don't want to add a CustomerType or MovieType property to the Customer and Movie class, if we do so, we will en up with a new conditional statement. So we create a new class called CustomerVIC which will inherit from Customer, and we create a MovieTransfer class which inherits from the Movie class. We let the Customer class represents a regular customer and the Movie class as normal movie. Because a VIC Customer should have it's own Rebate and a Transfer Movie should have it's own price, we make sure the base classes properties are virtual so we can override them in our sub classes.

public class Customer
{
    public virtual double Rebate
    {
       get { return 0; }
    }
}

public class CustomerVIC : Customer
{
    public override double Rebate
    {
       get { return 10;  }
    }
}

public class Movie
{
    public virtual double Price
    {
       get { return 20; }
    }
}

public class MovieTransfer : Movie
{
   public virtual double Price
   {
      get { return 30; }
   }
}

If we compare a normal customer with a VIC, we can see that a VIC will have 10 in rebate. So we make sure the CustomerVIC class Rebate property returns the price a VIC Customer should have in Rebate over a regular customer. The price of a Transfer movie is 30 and the price of a normal movie is 20.

Now when we have created our Customers and Movies, we can change the MovieRenter's CalculatePrice method, which in this case should take a Customer as an argument and a Movie.

public class MovieRenter
{
   public double CalculatePrice(Customer customer, Movie movie)
   {
      return movie.Price - customer.Rebate;
   }
}

CustomerVIC vicCustomer = new CustomerVIC();
MovieTransfer transferMovie = new MovieTransfer();
          
MovieRenter movieRenter = new MovieRenter();
double price = movieRenter.CalculatePrice(vicCustomer, transferMovie);


So by using polymorphism and more Object Oriented Programming over a normal functional programming, we can remove conditional statements. If I need a new type of Customer or Movie, I just create a new class and inherits from its base class and override the Rebate and Price property. Anyone have another suggestion of making the first code example better, and do you think it's important to make it better even if it works?

What do you think is most important when it comes to writing code?

What is the most common thing you think a developers are doing wrong when they write code?

23 Comments

  • Good post - this perfectly captures, between other things, one of the never-ending dilemmas developers face on a daily basis: "what's best\worst: adding an conditional statement or creating a new class?"

    I agree with the polymorphic approach adopted here; usually when you end up with too many IF statements or SWITCH CASEs you should ask yourself if it is possible to go either with polymorphism (as in this case) or inheritance.

    Back to the core of the discussion, It is certainly worth improving code quality even if it works.
    In my experience I had to jump in a number of so called "stable" projects where maintaining what was in place and adding new features was a nightmare.
    Real problem is that you find yourself constantly fighting fear of change from management on this kind of projects - and often developers are forced to be "consistent" even if it means writing BAD code.

  • Spot on - maintainability is the most important aspect of the code, in my opinion. Most software is going to be maintained (bugfixing, new/modified features etc) for much, much longer (years) than the initial investment made building v1 (weeks/months).

  • >>What is the most common thing you think a developers are doing wrong when they write code?

    IMO, the most common mistake is to be affraid of Exceptions.
    People associate exceptions with bugs, and if there are no exceptions, they think that there are no bugs.
    So they go to great extent to avoid exceptions and resort to methods returning error codes and other crazy stuff.

    Lacking refactoring is another common mistake imo.

  • Thanks Fredrik. Now I have a question for you:

    Somewhere you'll eventually need a conditional to determine which customer type ?


  • @Steve:
    Good question!
    If we assume we have our customers into a database we will probably have column in our Customers tables which indicate the type of the customer. So it will now be up to the Repository to return the correct type of the customer. So yes there will actually be a conditional statement. If we have used an Object Oriented Database, we would not need the conditional statement, so it’s based on how the customers are persisted.

  • In my opinion, this is not the best usage of polymorphism. Different value of a property (Rebate) is not reason good enough to create new subclass. What if you add new type of customer? Will you add another subclass just to return different value of Rebate property? And what will you do if you would like to promote ordinary customer into VIC customer?

    The same is true for Movie and MovieTransfer classes.

    I would split the Customer to two classes: Customer would keep data specific for one customer (like name, address etc.) and CustomerType would hold data common to a whole category of customers.

    public class CustomerType
    {
    public double Rebate {get; set;}
    }

    public class Customer
    {
    public CustomerType CustomerType {get; set;}

    double _rebate = -1;
    public double Rebate
    {
    get { return _rebate==-1 ? CustomerType.Rebate : _rebate; }
    }
    }

    This way you can add new type of customers without changing your code, and set the rebate for each customer individually (if needed). Yes, it is more complicated to do this right (you must decide what should the Customer do if it's CustomerType is null), but that could be easily handled by small set of unit tests. I think it's no worse than deciding which class to instantiate (Customer or CustomerVIC). And there will be no problems with promoting ordinary customer into the VIC customer - you just change the CustomerType of the customer object.

  • I got confused regarding this comment:
    "- I don't have the time to write good code." hehe that was funny.

    The thing with good code is that it doesn't take time. I think people need to be more lazy :-) a lazy programmer is often a better programmer. The hardest part of being lazy is that you must stay unlazy to be better lazy :-) A lazy programmer doesn’t love to write 100 of lines methods. They don't have time or are so lazy so they don't write the same thing more than once. They are also so lazy so they write automatic test so they don't need to test more than once etc etc...

    The biggest problem regarding good code is often the lack of creative thinking. To write good code you need some creative skills regarding design of code. We must be able to think outside the box if we can't we will never be good at writing code...


  • @Dkl:
    Nice. In my case I don’t need to specify different rebate on individual customers, so I just keep it simple and thinking in the term of YAGNI and the demonstration is about how to remove conditional statements by using polymorphism. 

  • Or you could go data driven and declare a mapping between (CustomerType,MovieType) and the price. You would then end up with something along the lines of:

    Dictionary<Pair,double> _priceMap = new Dictionary<Pair,double>
    {new Pair(CustomerType.VIC,MovieType.Transfer),30};

    public double CalculatePrice(CustomerType customer,MovieType movie)
    {
    double price = 30;
    priceMap.TryGetValue(new Pair(customer,movie);
    return price;
    }

    This technique can get even more powerful if you use lambdas in the lookup.

  • Did we forget about [Flag]?

    I find that most developers define "Good Code" as code they can understand, and I'm realizing that developers are getting tired of learning new things. It's a shame, I thought that when we decided to get into this industry we realized that you never get to stop learning, and it's actually a difficult job. Remember, that's why you get paid so much.


  • Whatever you do, don't visit my workplace then.
    It is a disaster. I am trying to motivate my collages to look into TDD and so on.

    But no, 1 Bosses are idiots and wants us to work faster and not care about bugs (which they said straight into my face) 2 The coworkers keeps saying NO to my ideas or It can't be done.

    *shrug* I think i need a new job.

  • Why not just look up Rebates and Prices, instead of having to derive a new class for each type.

    Something like:

    public class MovieRental
    {
    protected Dictionary customerRebates = new Dictionary();
    protected Dictionary moviePrices = new Dictionary();

    public MovieRental()
    {
    Initialize();
    }

    protected virtual void Initialize()
    {
    // Probably really initialized from a database in the real world.
    customerRebates.Add("Regular", 0);
    customerRebates.Add("VIC", 10);

    moviePrices.Add("Normal", 20);
    moviePrices.Add("Transfer", 30);
    }

    public virtual double CalculatePrice(string customerType, string movieType)
    {
    double customerRebate = customerRebates[customerType];
    double moviePrice = moviePrices[movieType];

    return moviePrice - customerRebate;
    }
    }

  • To make this example even nicer, you may want to add a factory.

  • First, I am not a programmer. I do dabble in it a very little bit. But programming is writing formulas. I have used formulas in spreadsheets to handles cases like this. Question - Would it be possible to create in a database, a table called MovieRenter containing the columns customerType, movieType and rebate; with row 1 containing VIC, Transfer, 20, row 2 containing Regular, Transfer, 30, as so on; then write a single line of code that reads the input data; looks in MovieRenter; matches the correct customerType, movieType combination; then returns the rebate value. As more customerTypes and movieTypes are created, add a row to the table; the formula remains unchanged. Does this make sense?

  • In my opinion this is a continuous balancing act. Better code is usually easier to maintain and adapt to new business changes, but takes longer to deliver a product. At some point the company pays the price for bad code, either later to add changes or fix bugs. Another thing to consider is an experienced programmer will avoid a lot of design flaws even when building something too quickly.

  • Is your house spotless? Is your car cherry? Do you tell your wife and children you love them every day? Do you only eat "perfect" food? Do you exercise 60 minutes every day?

    It's an imperfect world. Perfect = obsessive/compulsive.

  • I agree with Joe M that it should be data driven.

    Prices should not be in code (except in unit tests) they should be in a some sort database (rel DB, config, whatever). Otherwise, it is not maintainable in the long run. Who expects prices to never change? In any case, the application using this code needs a database anyway since it depends on customers.

    In my opinion, for this code to be future proof, you need the type of customer and the price/rebate depending on the customer type to be in tables. This way you can add customer types, new pricing and edit prices. Load them once in static properties or though caching.


  • I was interesting to read all the comments I got. About the price being fixed to the Customer classes and Movies, well maybe I used a too easy and stupid example, but assume the Price is calculated from something, in that case the number 30 could have been an algorithm and they are different for different customers. &nbsp;In that case a data driven approached will not help us, and of course somewhere we have probably saved the “original” price. Most comments are based on your earlier experience and how you solved stuff. This post was more about using one of Martin Fowlers stated Refactoring methods, replacing conditional statements with Polymorphism.

  • I would write it as:

    Price=20
    if Trans then Price += 10
    if VIC then Price -= 10

  • I heartily disagree with almost all of your post, except that writing good code IS important.

    The grounds for your example are erroneous and misleading at best. We, as software engineers, are tasked with the problem of designing *systems* to process data, not writing "programs" full of "code" to handle explicit business logic like this example. This is the distinction we as software engineers need to be making in the first place. Your business customer should not care about this distinction. "Everything should be made as simple as possible, but not simpler."

    By refactoring this code, you're driving the decisions that need to be made by your program at a higher level down into the lower level of the runtime type system. This just makes things more wrong than they were before. In my experience, object-oriented design (and polymorphism thereof) is best suited to serve developers' interests in writing clean, elegant, reusable code in systems, almost never for mapping business problem domain models directly in to code form.


  • @James Dunne:
    "Your business customer should not care about this distinction. Everything should be made as simple as possible, but not simpler."
    "In my experience, object-oriented design (and polymorphism thereof) is best suited to serve developers' interests in writing clean, elegant, reusable code in systems"
    How about the people that should maintain it after you are done? Of course we should deliver something that solves a customer’s business need, but isn’t it also important to make sure our code is easy to understand and easy to maintain for our successor?
    "almost never for mapping business problem domain models directly in to code form."
    I totally disagree with you; a domain model is based on objects, like Customer, Order, Container, Ship etc. Take a look at Eric Evans book about Domain Driven Design if you haven’t done it before. Object thinking by David West is also an interesting book, as David West and David Parnas wrote: "Thinking like a computer is the prevailing mental habit of traditional developers. It’s a bad habit." Instead he is talking about thinking as an Object.

  • The #1 most important thing must be to write working code. There's no point writing readable or maintainable code if it doesn't work.

    However, I like the example.


  • @Stefan Bergfeldt:
    "The #1 most important thing must be to write working code. There's no point writing readable or maintainable code if it doesn't work"
    That is why TDD and Refactoring goes well in hand. Problem with some developers is that they are satisfied only if the code works and don't care about the rest. So the #1 rule must be: Most important thing must be to write working code which other people can understand, just a thought ;)

Comments have been disabled for this content.