Advertisement

More Related Content

Advertisement

Maintainable code

  1. Writing Maintainable Code Andy Palmer Strong opinions, weakly held
  2. Placeholders All of these talking points are placeholders for a conversation. Many of the things that I talk about will make more sense when we start to look at real code. I’ve included sample code where I think it helps explain the concepts, but the real code these lessons were learned from was proprietary, so the examples might seem a little over-simplistic.
  3. Fundamentally... The key to maintainable code is to write it so that it can be read and understood easily
  4. The long and winding road One of the biggest barriers to understandable code is when it goes on too long. As humans, we can only hold a certain amount of context in our minds at any one time. Methods that go on for tens of lines (or hundreds or thousands) are too complex for us to understand completely. The same is true of classes.
  5. Reinvent the wheel? It’s often easy to get into a problem solving mindset, and that’s when we start writing code that’s probably already been done If we’re working on something not directly related to our business domain, check for whether there’s already a library. For example, anything to do with the web, accessing the disk or network, interacting with other processes.
  6. Design Patterns Design patterns are very useful to talk about code. If we know the pattern name, we can discuss how this code is doing what it’s doing However Design patterns do not tell us why we are doing something, and so I don’t do design patterns. I make the code easier to read and understand, and then afterwards say “Oh, I used a Decorator there. Cool!”
  7. System Metaphor Often, when describing how a system works, we will use a metaphor. We rarely capture this in the code.
  8. System Metaphor Often, when describing how a system works, we will use a metaphor. We rarely capture this in the code. What a shame :-(
  9. System Metaphor Often, when describing how a system works, we will use a metaphor. We rarely capture this in the code. What a shame :-( A metaphor carries lots of information in a way that is easy to share.
  10. System Metaphor (cont.)
  11. System Metaphor (cont.) Metaphor Useful information conveyed Design Patterns Language Idiom Number of people who understand it
  12. System Metaphor (cont.) Metaphor Design Patterns Useful information conveyed Hard Difficulty to Learn Design Patterns Language Idiom Metaphor Language Idiom Easy Number of people who understand it Relevance to Domain
  13. Guard clauses if (weDontKnowWhichWireToCut()) { return new GetOutOfHereShesGonnaBlow(); }
  14. Guard clauses if (weDontKnowWhichWireToCut()) { return new GetOutOfHereShesGonnaBlow(); } if (someValue != null ) { doSomething(); doSomethingElse(); doSomeMoreSomething(); return result; } return null; // This null is a long way from where we decided to send it
  15. Guard clauses if (weDontKnowWhichWireToCut()) { return new GetOutOfHereShesGonnaBlow(); } if (someValue != null ) { doSomething(); doSomethingElse(); doSomeMoreSomething(); return result; } return null; // This null is a long way from where we decided to send it if (someValue == null ) { return null; } // Get out quickly doSomething(); doSomethingElse(); doSomeMoreSomething(); return result;
  16. Guard clauses (cont.) Often, we hear “Only one exit point per method” This makes sense in non-garbage-collected languages because the exit point is a good place to tidy up memory allocations. It’s not really relevant in Java, and I’d rather know quickly if there’s some short circuit decision I can make if (!carStarted) { return callAA(); } // duh! engageFirstGear(); if (carStarted) { engageFirstGear(); … 100s of lines … } return callAA(); // Why am I calling the AA here?
  17. Nested ifs Cyclomatic complexity for nested ifs follows a geometric progression. For a nest of 5 simple booleans, the number of possible paths is 32. If we have 5 guard clauses, the number of paths is only 10 Look for “mountains” in the code
  18. Cull Null “I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” - Tony Hoare, QCon 2009
  19. Cull Null “I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” - Tony Hoare, QCon 2009 If we never return or expect null then we: • no longer need null checks • can trust that what we get is valid • can trust that error handling is dealt with elsewhere
  20. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways:
  21. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { try { File.open(“some file”); } catch (IOException e) { e.printStackTrace(); } }
  22. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { try { File.open(“some file”); or } catch (IOException e) { e.printStackTrace(); } }
  23. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { public void someMethod() throws IOException { try { File.open(“some file”); or } File.open(“some file”); } catch (IOException e) { e.printStackTrace(); } }
  24. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { public void someMethod() throws IOException { try { File.open(“some file”); or } File.open(“some file”); } catch (IOException e) { e.printStackTrace(); } } Both of which essentially do nothing
  25. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { public void someMethod() throws IOException { try { File.open(“some file”); or } File.open(“some file”); } catch (IOException e) { e.printStackTrace(); } } Both of which essentially do nothing and worse... with the second option, details of the exception bubble up far beyond where it makes sense to the caller
  26. Throw away Checked Exceptions Typically checked exceptions are handled in one of two ways: public void someMethod() { public void someMethod() throws IOException { try { File.open(“some file”); or } File.open(“some file”); } catch (IOException e) { e.printStackTrace(); } } Both of which essentially do nothing and worse... with the second option, details of the exception bubble up far beyond where it makes sense to the caller If we want to do something, we can use and catch an unchecked exception without all the pollution
  27. Fail fast If we know that it doesn’t make sense to continue, then stop. NOW! Don’t return a null or some other non-useful result when we could let the caller know that something needs attention. If we’re constructing an object (such as an email address) and the email is not valid, we can throw an exception in the constructor instead of having to check validity everywhere the email is used. In this way, everyone who uses an EmailAddress knows that it’s a valid value.
  28. Code metrics Code metrics can give us good places to look for risky code. They help us discover areas of high complexity or areas of high dependency.
  29. Switch statements
  30. Switch statements Have you ever noticed that switch statements have the word break all over the place, and frequently that’s exactly what they do?
  31. Switch statements Have you ever noticed that switch statements have the word break all over the place, and frequently that’s exactly what they do? switch statements are often an indication that we have a Type that is trying to get out
  32. Static methods Static methods often appear in Utility classes.
  33. Static methods Static methods often appear in Utility classes. This makes sense for objects that we don’t control (such as any of the built-in libraries)
  34. Static methods Static methods often appear in Utility classes. This makes sense for objects that we don’t control (such as any of the built-in libraries) Well, possibly...
  35. Static methods Static methods often appear in Utility classes. This makes sense for objects that we don’t control (such as any of the built-in libraries) Well, possibly... But it almost never makes sense for objects that we own. Most static methods can be replaced with instance methods that are easier to test and understand
  36. Poor Man’s Dependency Injection Add an overloaded version of the method or constructor that takes the dependency, and new up the dependency with a sensible default in the simpler case.
  37. Poor Man’s Dependency Injection Add an overloaded version of the method or constructor that takes the dependency, and new up the dependency with a sensible default in the simpler case. public void someMethod() { return someMethod(new Dependency()); } public void someMethod(Dependency dependency) { return dependency.doSomething(); }
  38. I want to store £123.45 What data type should I use?
  39. I want to store £123.45 What data type should I use? float: The float data type is a single-precision 32-bit IEEE 754 floating point. Use a float (instead of double) if you need to save memory in large arrays of floating point numbers.
  40. I want to store £123.45 What data type should I use? float: The float data type is a single-precision 32-bit IEEE 754 floating point. Use a float (instead of double) if you need to save memory in large arrays of floating point numbers. double: The double data type is a double-precision 64-bit IEEE 754 floating point.
  41. I want to store £123.45 What data type should I use? float: The float data type is a single-precision 32-bit IEEE 754 floating point. Use a float (instead of double) if you need to save memory in large arrays of floating point numbers. double: The double data type is a double-precision 64-bit IEEE 754 floating point. These data types should never be used for precise values, such as currency. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
  42. I want to store £123.45 What data type should I use? float: The float data type is a single-precision 32-bit IEEE 754 floating point. Use a float (instead of double) if you need to save memory in large arrays of floating point numbers. double: The double data type is a double-precision 64-bit IEEE 754 floating point. These data types should never be used for precise values, such as currency. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html Use the java.math.BigDecimal class instead
  43. Avoid a void void methods restrict our ability to extract composed methods e.g., ServletFilter.doFilter for Authorisation If we’re authorised, we want to continue to the resource If we’re unauthenticated, then redirect us to the login page If we’re unauthorised, redirect us to the Access Denied page For the first case, we want to do a FilterChain.doFilter For the others we don’t
  44. Avoid a void (cont.) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { if (filterWorkflow() == Do.Filter) { chain.doFilter(…); } } public Do filterWorkflow() { if (userIsAuthorised()) { return Do.Filter; } if (userIsNotLoggedIn()) { return Do.Redirect(LoginPage); } if (userIsNotAuthorised()) { return Do.Redirect(AccessDenied); } }
  45. Extending Exceptions One of the things that I do in my code is to make an obvious separation between types of exception. I use Apology for something that the program should reasonably have been able to do, but failed (like fetching a User from a database) I use Complaint for something that the program could not possibly have achieved (like returning a single result when the criteria specify multiple results)
  46. Look outside of your area One of the best books on programming that I’ve read is called “Perl Medic”. It covers most of what we’ve covered here and is completely applicable to any language (it’s just that the examples are in Perl)
  47. Look outside of your area One of the best books on programming that I’ve read is called “Perl Medic”. It covers most of what we’ve covered here and is completely applicable to any language (it’s just that the examples are in Perl) Many .Netters who go to the Alt.Net events end up migrating away from .Net once they see how easy it is to do things in other languages.
  48. Continuous Learning I read blogs most days, across a wide variety of languages and topics. A problem that someone has solved in another domain might be transferrable to mine. We’ve never learned everything, there’s always something new. Go to a coding dojo, code retreat, or a language meetup (especially if it’s for a language other than your own) Hold “lunch and learns” or “after school clubs” in the office (get the company to buy pizza) Read books (I have lots of recommendations) Question conventional wisdom

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
Advertisement