Transcript

Arjan van Leeuwen

The Evolution of Good Code

ACCU 2014

@avl7771

Take one of each

! !

"The past is a foreign country: they do things differently there."— L.P. Hartley

Good Solid Readable

Maintainable Elegant

Bug-free reliableTestable

Debuggable CLEAN

The bookshelf“A man may as well expect to grow stronger by always eating as wiser by always reading.”!— Jeremy Collier

Writing Solid Code

❖ Steve Maguire!

❖ Microsoft Programming!

❖ 1993!

❖ “Microsoft’s Techniques for Developing Bug-Free C Programs”

The Pragmatic Programmer

❖ Andrew Hunt & David Thomas!

❖ Addison Wesley!

❖ 2000!

❖ “examines the core process - taking a requirement and producing working, maintainable code that delights its users”

Code Complete

❖ Steve McConnell!

❖ Microsoft Programming!

❖ 2004 (1st Edition 1993)!

❖ “A Practical Handbook of Software Construction” - “Helps you build the highest quality code”

Clean Code

❖ Robert C. Martin (“Uncle Bob”)!

❖ Prentice Hall!

❖ 2009!

❖ “A handbook for Agile Software Craftsmanship” — “How to write good code and how to transform bad code into good code”

97 Things Every Programmer Should Know

❖ Various Artists, edited by Kevlin Henney!

❖ O’Reilly!

❖ 2010!

❖ “Collective Wisdom from the Experts” — “you’ll expand your skills by … learning appropriate best practices”

Growing Object-Oriented Software, Guided by Tests

❖ Steve Freeman & Nat Pryce!

❖ Addison Wesley!

❖ 2010!

❖ “Two TDD pioneers show how to let tests guide your development and ‘grow’ software that is coherent, reliable, and maintainable”

The Art of Readable Code

❖ Dustin Boswell & Trevor Foucher!

❖ O’Reilly!

❖ 2011!

❖ “Simple and Practical Techniques for Writing Better Code”

Guess the book!“If we encounter a man of rare intellect, we should ask him what books he reads.”!— Ralph Waldo Emerson

2011

2009

! ! 1993

Use Hungarian notation for variables

❖ “Although names like pch look funny and are hard to pronounce, they are filled with information.”!

❖ “Conveying information is far more important in naming your variables than being able to stand up and read your code aloud during a program review.”

/* pointer to character pointer */char **ppch;

! ! 1993

Use Hungarian notation for variables

❖ “Although names like pch look funny and are hard to pronounce, they are filled with information.”!

❖ “Conveying information is far more important in naming your variables than being able to stand up and read your code aloud during a program review.”

/* pointer to character pointer */char **ppch;

Don’t use Hungarian notation for variables

❖ “In modern languages we have much richer type systems, and the compilers remember and enforce the types.”!

❖ “Nowadays HN and other forms of type encoding are simply impediments. They make it harder to read the code.”

/* pointer to character pointer */char **ppch;

2011

2009

! ! 1993

Don’t use Hungarian notation for variables

❖ “In modern languages we have much richer type systems, and the compilers remember and enforce the types.”!

❖ “Nowadays HN and other forms of type encoding are simply impediments. They make it harder to read the code.”

/* pointer to character pointer */char **ppch;

2009

2004

Don’t use Yoda Conditions

❖ “Putting constants and expressions on the left-hand side of comparisons works only when one of the operands is a constant or an expression.”!

❖ “If instead you use a compiler switch, the compiler would alert you to every possible assignment bug.”

2011

! ! 1993 if (10 == x) // if 10, x is!

Don’t use Yoda Conditions

❖ “Putting constants and expressions on the left-hand side of comparisons works only when one of the operands is a constant or an expression.”!

❖ “If instead you use a compiler switch, the compiler would alert you to every possible assignment bug.”

if (10 == x) // if 10, x is! ! ! 1993

2010

Enforce singletons

❖ “If you want to define a class that allows only one object to be instantiated, enforce this by hiding all the constructors of the class and providing a static routine to access the class’s single instance.”

public class MaxId { private MaxId() {} public static MaxId GetInstance() { /*..*/ }}

2004

! ! 1993

Enforce singletons

❖ “If you want to define a class that allows only one object to be instantiated, enforce this by hiding all the constructors of the class and providing a static routine to access the class’s single instance.”

public class MaxId { private MaxId() {} public static MaxId GetInstance() { /*..*/ }}

2004

2011

Prefer Write-Once Variables❖ “The more places a variable is

manipulated, the harder it is to reason about its current value”!

❖ “Variables that are a ‘permanent fixture’ are easier to think about”!

❖ “Immutables tend to more often be trouble-free”

2004

! ! 2000

2011

Prefer Write-Once Variables❖ “The more places a variable is

manipulated, the harder it is to reason about its current value”!

❖ “Variables that are a ‘permanent fixture’ are easier to think about”!

❖ “Immutables tend to more often be trouble-free”

2011

Design for Unit Tests❖ “Most important, is it possible

to automatically and thoroughly validate the design using a unit test? If not, you should consider using an alternative design that can be tested.” 2004

! ! 1993

Design for Unit Tests❖ “Most important, is it possible

to automatically and thoroughly validate the design using a unit test? If not, you should consider using an alternative design that can be tested.”

! ! 1993

Names and aesthetics Small changes that help you understand code

Use Hungarian Notation for Variables

❖ Prefix indicates type or intended use!

❖ Widely used after use in Microsoft Windows C libraries!

❖ “Systems Hungarian” vs. “Apps Hungarian”

char ch; /* a plain old character */ bool f; /* flags that are always TRUE or FALSE */ char *pch; /* a character pointer */ char **ppch; /* pointer to a character pointer */ char *szName; /* a zero-terminated string */

Why?

❖ Makes wrong code ‘look wrong’ [Maguire93], [McConnell04]!

❖ Easy to decipher pointer expressions [McConnell04]!

❖ Semantic prefixes add information that compiler doesn’t know about [McConnell04]!

❖ Standardized prefixes encourage consistent naming [McConnell04]

Why not?

❖ Encoding adds burden of deciphering [Martin09]!

❖ Not necessary for types anymore [Martin09]!

❖ Functions and classes are shorter now [Martin09]!

❖ Inappropriate in object-oriented systems [Hunt00]!

❖ Code is read more than it’s written [Hunt00]

Use Hungarian Notation for Variables

1993 2000 2004 2009 2011

Specify interface or implementation in class name

❖ Denote in the class name when a class is an interface or when it implements an interface!

❖ Typically using prefix ‘I’ for Interface and/or postfix ‘Impl’ for Implementation

class IBloober { virtual void blob() = 0; }; !class BlooberImpl : public IBloober { virtual void blob(); };

Why?

❖ See whether you are using an interface just from the name

Why not?

❖ Users shouldn’t (have to) know they’re dealing with an interface, better to encode the Impl [Martin09]!

❖ Names ending in Impl duplicate information [Freeman10]!

❖ Can indicate poorly named interface or design [Freeman10]

Specify interface or implementation in class name

1993 2004 2009 2010

? ?

Align similar statements

❖ Can be used wherever similar statements are done on multiple lines!

❖ Purely aesthetic enhancement

customerPurchases = customerPurchases + CustomerSales(customerID); customerBill = customerBill + customerPurchases; totalCustomerBill = customerBill + PreviousBalance(customerID) + LateCharge(customerID); customerRating = Rating(customerID, totalCustomerBill);

Why?

❖ Alignment scheme shows statements belong together [McConnell93]!

❖ Neater listing and quicker scanning [McConnell93]!

❖ Column edges provide “visual handrails” [Boswell11]!

❖ Doesn’t take much work [Boswell11]

Why not?

❖ A headache to maintain as names change and lines are moved amongst different indentations [McConnell04]!

❖ Emphasizes wrong things [Martin09]!

❖ Keep lists short [Martin09]!

❖ Eliminated by automatic reformatting tools [Martin09]

Align similar statements

1993 2004 2009 2011

Keep functions short

❖ Functions should be as small as possible

std::string renderPageWithSetupsAndTeardowns( PageData pageData, bool isSuite) { if (isTestPage(pageData)) includeSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml(); }

Why?

❖ Easier to read and understand [Martin09]!

❖ Documentary value (descriptive name) [Martin09]!

❖ Easier to improve function when the code is by itself [Boswell11]

Why not?

❖ Routines of longer length are no more error prone than shorter routines, shorter may have more errors [McConnell93], [McConnell04]!

❖ Routines of longer length are cheaper to develop [McConnell93], [McConnell04]!

❖ Tiny functions can hurt readability [Boswell11]

Keep functions short

1993 2004 2009 2011

Simplifying Logic Or: how to write less

Don’t Repeat Yourself (DRY)

❖ Every piece of code must have a single, unambiguous, authoritative representation within a system [Hunt00]

Why?❖ Change in one will make two implementations diverge

[Hunt00]!❖ Similar code in two routines implies an error in

decomposition [McConnell93]!

❖ Duplication is a missed opportunity for abstraction [Martin09]!

❖ Duplication can indicate bad design [Hunt00]!

❖ Copy/Paste programming is bad, remember Y2K [Hunt00]

Don’t Repeat Yourself (DRY)

1993 2000 2009 2011

Early Return❖ Use guard clauses to return early for exceptional or

error conditions!

❖ Return early from e.g. linear search when a result is found

public boolean Contains(String str, String substr) { if (str == null || substr == null) return false; if (substr.equals("")) return true; ... }

Why?

❖ Implementing without guard clauses unnatural [Boswell11]!

❖ Avoids deep nesting [McConnell04], [Boswell11]!

❖ Single exit point less important with shorter functions [Martin09]!

❖ Single exit point less important with modern languages [Boswell11]

Why not?

❖ Breaks Structured Programming single-entry, single exit ‘law’ [Martin09]!

❖ Harder to understand a routine when unaware of all return points [McConnell93]

Early Return

1993 2000 2004 2009 2011

Prefer Exceptions to Returning Error Codes

❖ Use exceptions to clarify the control flow

try { socket.read(name); process(name); socket.read(address); processAddress(address); socket.read(telNo) // etc, etc... } catch (IOException e) { Logger.log("Error reading individual: " + e.getMessage()); }

Why?

❖ Clarifies normal flow of control [Hunt00]!

❖ Avoids violation of command query separation [Martin09]!

❖ Don’t force user to handle error immediately [Martin09]!

❖ Signal errors in a way that cannot be ignored [McConnell04]

Why not?

❖ High-level program flow obscured by bubbling up [Boswell11]!

❖ Alternative error handling often more appropriate [McConnell04]!

❖ Exceptions should only be used in exceptional situations (that should never happen) [Hunt00]

Prefer Exceptions to Returning Error Codes

2000 2004 2009 2011

Tell, Don’t Ask❖ Objects make their decisions based only on the

information they hold internally or that which came with the triggering message [Freeman10]

((EditSaveCustomizer) master.getModelisable() .getDockablePanel() .getCustomizer() .getSaveItem().setEnabled(Boolean.FALSE.booleanValue()); !// becomes... master.allowSavingOfCustomisations();

Why?

❖ Produces more flexible code, easier to swap objects [Freeman10]!

❖ Hides internal structure [Freeman10], [Martin09]!

❖ Avoids train wrecks [Freeman10], [Martin09]!

❖ Minimizes coupling [Hunt00]

Tell, Don’t Ask

2000 2004 2009 2010

Reliability, Debugging and Testing

Use Functional Programming Principles

❖ Use knowledge of functional programming in imperative languages!

❖ Work with immutable data and pure functions where possible

Why?

❖ Avoid side effects [Garson10]!

❖ Simpler to debug (source of change easy to find) [Garson10]!

❖ Avoid thread race conditions [Carmack12]!

❖ The more places a variable is manipulated, the harder it is to reason about its value [Boswell11]

Use Functional Programming Principles

1993 2004 2010 2011 2012

? ?

Use Functional Programming Principles

1993 2004 2010 2011 2012

? ?

Program in Terms of the Problem Domain

❖ Model types and behavior in terms of the programming problem rather than the computer science solution

if (portfolioIdsByTraderId.get(trader.getId()) .containsKey(portfolio.getId())) { ... } !// becomes ... if (trader.canView(portfolio)) { ... }

Why?

❖ Increases code readability and understanding [North10]!

❖ Can evolve code when domain model evolves [North10]!

❖ Work at a higher level of abstraction to allow focusing on solving domain problems [Hunt00]!

❖ Hides low-level details [McConnell93]!

❖ Separating solution and problem domain is part of the job of a good programmer [Martin09]

Why not?

❖ The people who read your code are programmers: they know CS terms [Martin09]

Program in Terms of the Problem Domain

1993 2000 2004 2009 2010

Prefer Message-Passing in Parallel Systems

❖ Use message passing instead of shared mutable variables!

❖ Use copies of data where necessary!

❖ If the language doesn’t provide it, use libraries or frameworks that do

Why?

❖ Shared mutable memory is at the root of problems related to concurrency: race conditions, deadlock, livelock [Winder10]!

❖ Proven way of handling concurrency [Winder10]!

❖ Avoiding synchronization makes up for overhead [Martin09]

Prefer Message-Passing in Parallel Systems

1993 2004 2009 2010

? ?

Step Through your Code in a Debugger

❖ Actively step through all new or modified code to watch it execute!

❖ As you step through code, focus on data flow!

❖ Step through every code path

Why?

❖ Allows you to get a ‘feeling’ for the code and gain confidence [Maguire93]!

❖ You could create tests, but debugging is much faster [Maguire93]

Why not?

❖ Problems can be found more quickly and more accurately by thinking [McConnell04]!

❖ A bug should be reproducible with a single command [Hunt00]

Step Through your Code in a Debugger

1993 2004 2009 2011

??

Test-Friendly Development

❖ Design your code so that it’s easy to test!

❖ Avoid use of global state!

❖ Avoid coupling

Why?

❖ Easier to test [Everyone]!

❖ Test-friendly code leads naturally to well-organized code [Boswell11]!

❖ Decoupling leads to easier refactoring [Boswell11]!

❖ Classes with less state are simpler and easier to understand [Boswell11]!

❖ Designing to test promotes reusability [Hunt00]

Why not?

❖ Could sacrifice readability for the sake of enabling tests [Boswell11]!

❖ Testing by itself does not improve software quality [McConnell93], [McConnell04]

Test-Friendly Development

1993 2000 2004 2009 2011

Test-Driven Development

❖ Write test cases before writing code!

❖ Follow Red-Green-Refactor cycle!

❖ Do not write code without having test cases

Why?

❖ Forces you to think about requirements and design of the code, finds problems earlier [McConnell04]!

❖ Tests will cover virtually all production code [Martin09]!

❖ Creates short feedback cycle in development [Freeman10]!

❖ Integrates refactoring in development [Freeman10]!

❖ Takes same amount of time as creating tests afterwards [McConnell04]

Why not?

❖ Can create false sense of security (should not be only form of testing) [McConnell04]!

❖ Just keeping testing in mind can help improve the code [Boswell11]!

❖ Testing can get in the way of product development [Boswell11]

Test-Driven Development

1993 2004 2009 2010 2011

?

Boy Scout Rule

❖ “Always leave the campground cleaner than you found it”!

❖ When you check in a module, make a small improvement, regardless of who the original author was

Why?

❖ The end of deterioration of software systems [Martin10]!

❖ Systems gradually get better [Martin10]!

❖ Promotes collective ownership [Martin10]!

❖ Don’t be afraid of the code [Lewis10]

Why not?

❖ What kind of motto is “If it ain’t broke, fix it anyway”? [Maguire93]!

❖ Programmers don’t treat improved code as if it were new code [Maguire93]!

❖ Your colleagues are not bozos, they might have had their reasons [Maguire93]!

❖ Change in itself is not a virtue [McConnell04]

Boy Scout Rule

1993 2004 2009 2010

Conclusion

“The word is about,!there's something evolving,!whatever may come,!the world keeps revolving!!They say the next big thing is here,!that the revolution's near,!but to me it seems quite clear!that it's all just a little bit of history repeating”!!—Alex Gifford by way of Shirley Bassey

Arjan van Leeuwen / @avl7771

Conclusion

❖ (Better) unit testing and test-driven development have an impact on other practices!

❖ Concurrency finally having an impact, comeback of functional language principles!

❖ Many practices that might seem recent were in fact known for a long time

References❖ [Boswell11] Dustin Boswell, Trevor Forcher, “The Art of Readable Code”. O’Reilly, 2011.!

❖ [Carmack12] John Carmack, “Functional Programming in C++”. http://www.altdevblogaday.com/2012/04/26/functional-programming-in-c/, 2012.!

❖ [Freeman10] Steve Freeman, Nat Pryce, “Growing Object-Oriented Software”. Addison-Wesley, 2010.!

❖ [Garson10] Edward Garson, “Apply Functional Programming Principles”, from “97 Things Every Programmer Should Know”. O’Reilly, 2010.!

❖ [Hunt00] Andrew Hunt, David Thomas, “The Pragmatic Programmer”. Addison-Wesley, 2000.!

❖ [Maguire93] Stephen A. Maguire, “Writing Solid Code”. Microsoft Press, 1993!

❖ [Martin09] Robert C. Martin, “Clean Code”. Prentice Hall, 2009.!

❖ [McConnell93] Steve McConnell, “Code Complete”. Microsoft Press, 1993!

❖ [McConnell04] Steve McConnell, “Code Complete 2”. Microsoft Press, 2004.!

❖ [North10] Dan North, “Code in the Language of the Domain”, from “97 Things Every Programmer Should Know”. O’Reilly, 2010.!

❖ [Winder10] Russel Winder, “Message Passing Leads to Better Scalability in Parallel Systems”, from “97 Things Every Programmer Should Know”. O’Reilly, 2010.


Top Related