Maintainable code

7,096 views

Published on

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,096
On SlideShare
0
From Embeds
0
Number of Embeds
4,638
Actions
Shares
0
Downloads
16
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Maintainable code

    1. 1. Writing Maintainable Code Andy Palmer Strong opinions, weakly held
    2. 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. 3. Fundamentally...The key to maintainable code is to write it so thatit can be read and understood easily
    4. 4. The long and winding roadOne 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. 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 doneIf 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. 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 HoweverDesign patterns do not tell us why we are doing something,and so I don’t do design patterns. I make the code easier toread and understand, and then afterwards say “Oh, I used a Decorator there. Cool!”
    7. 7. System MetaphorOften, when describing how a system works, we will use a metaphor. We rarely capture this in the code.
    8. 8. System MetaphorOften, when describing how a system works, we will use a metaphor. We rarely capture this in the code. What a shame :-(
    9. 9. System MetaphorOften, 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. 10. System Metaphor (cont.)
    11. 11. System Metaphor (cont.) MetaphorUseful information conveyed Design Patterns Language Idiom Number of people who understand it
    12. 12. System Metaphor (cont.) Metaphor Design PatternsUseful information conveyed Hard Difficulty to Learn Design Patterns Language Idiom Metaphor Language Idiom Easy Number of people who understand it Relevance to Domain
    13. 13. Guard clausesif (weDontKnowWhichWireToCut()) { return new GetOutOfHereShesGonnaBlow(); }
    14. 14. Guard clausesif (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. 15. Guard clausesif (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 itif (someValue == null ) { return null; } // Get out quicklydoSomething();doSomethingElse();doSomeMoreSomething();return result;
    16. 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 knowquickly 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. 17. Nested ifsCyclomatic 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. 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. 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. 20. Throw away Checked ExceptionsTypically checked exceptions are handled in one of two ways:
    21. 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. 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. 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. 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. 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 nothingand worse... with the second option, details of the exception bubble up far beyond where it makes sense to the caller
    26. 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 nothingand 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. 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 whenwe 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 theconstructor 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. 28. Code metricsCode metrics can give us good places to look for riskycode.They help us discover areas of high complexity or areasof high dependency.
    29. 29. Switch statements
    30. 30. Switch statementsHave you ever noticed that switch statements have the word break all over the place, and frequently that’s exactly what they do?
    31. 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. 32. Static methodsStatic methods often appear in Utility classes.
    33. 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. 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. 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. 36. Poor Man’s Dependency Injection Add an overloaded version of the method orconstructor that takes the dependency, and new up thedependency with a sensible default in the simpler case.
    37. 37. Poor Man’s Dependency Injection Add an overloaded version of the method orconstructor that takes the dependency, and new up thedependency with a sensible default in the simpler case.public void someMethod() { return someMethod(new Dependency());}public void someMethod(Dependency dependency) { return dependency.doSomething();}
    38. 38. I want to store £123.45 What data type should I use?
    39. 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 floatingpoint numbers.
    40. 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 floatingpoint numbers.double: The double data type is a double-precision 64-bit IEEE 754 floating point.
    41. 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. 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. 43. Avoid a voidvoid 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 pageIf 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. 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. 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 shouldreasonably have been able to do, but failed (like fetching a User from a database) I use Complaint for something that the program couldnot possibly have achieved (like returning a single result when the criteria specify multiple results)
    46. 46. Look outside of your areaOne 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. 47. Look outside of your areaOne 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 upmigrating away from .Net once they see how easy it is to do things in other languages.
    48. 48. Continuous LearningI read blogs most days, across a wide variety of languages and topics.A problem that someone has solved in another domain might betransferrable to mine.We’ve never learned everything, there’s always something new.Go to a coding dojo, code retreat, or a language meetup (especially ifit’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

    ×