improving the quality of existing software
TRANSCRIPT
Improving the Quality of Existing Software
Steve SmithFounder, DevIQ
@ardalis | ardalis.com
Please Share
• Live Tweeting and Photos are encouraged• Questions and Feedback via Social Media are welcome• Use #StirTrek and/or #ImproveSoftware
Pluralsight – Free Tokens
I have some 1-month free passes; see me after if you’d like one
Software Rots
Technical Debt
• Low quality code and shortcuts in our applications
• Technical debt, like real debt, has direct cost to pay off as well as interest
http://www.jimhighsmith.com/
Preventive Maintenance
• Refactoring• Eliminate Duplication• Simplify Design
• Automated Tests• Verify correctness• Avoid regressions• Increase Confidence
When should you refactor?
• While delivering value
“”
You don’t need permission to practice basic hygiene when you write software.
http://ardalis.com/when-should-you-refactor/
Make cleaning up your code something you do as part of writing code.
Refactoring Should Not Change System Behavior
The Refactoring Process
• Verify existing behavior• Write Characterization Tests if none exist• Find test points• Break dependencies
• Apply Refactoring• Confirm existing behavior is preserved
Characterization Tests
Process1. Write a test you know will fail2. Use the output of the failing test to determine the existing
behavior to assert3. Update the test with the new value/behavior4. Run the test again – it should now pass
S O L I D
Principleshttp://flickr.com/photos/kevinkemmerer/
2772526725/
Principles of OO Design
0. Don’t Repeat Yourself (DRY)
1.Single Responsibility2.Open/Closed3.Liskov Substitution4.Interface Segregation5.Dependency Inversion
Don’t RepeatRepeat Yourself
• Duplication in logic calls for abstraction
• Duplication in process calls for automation
Common Refactorings
• Replace Magic Number/String• Parameterize Method• Pull Up Field• Pull Up Method• Replace Conditional With Polymorphism• Introduce Method
Common Source of Repetition: Role Checks
if(user.IsInRole(“Admins”){ // allow access to resource}
// favor privileges over role checks// ardalis.com/Favor-Privileges-over-Role-Checks
var priv = new ContentPrivilege(user, article);if(priv.CanEdit()){ // allow access}
Visual Studio Code Clones
• Find similar blocks of code in your projects/solution
• Can detect matches that are similar but vary in small ways (like variable names)
• Available in VS2015 Premium and Ultimate
Single Responsibility Principle
The Single Responsibility Principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
Wikipedia
There should never be more than one reason for a class to change.Robert C. “Uncle Bob” Martin
What is a responsibility?
“My CustomerManager class is only responsible for anything to do with a Customer. That follows SRP, right?”
Examples of Responsibilities• Persistence• Validation• Notification• Error Handling• Logging• Class Selection / Construction• Formatting• Parsing• Mapping
Dependency and Coupling
• Excessive coupling makes changing legacy software difficult
• Breaking apart responsibilities and dependencies is a large part of working with existing code
Common Refactorings
• Extract Class• Extract Method• Move Method
Heuristics and Code Smells
• Visual Studio Metrics
Cyclomatic Complexity
https://en.wikipedia.org/wiki/Cyclomatic_complexity
Keep Cyclomatic Complexity
under 10 for every method
Code Smell: Regions
?More on Regions: http://ardalis.com/regional-differences
Open / Closed Principle
The Open / Closed Principle states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Wikipedia
Open / Closed Principle
Open to ExtensionNew behavior can be added in the future
Closed to ModificationChanges to source or binary code are not required
Dr. Bertrand Meyer originated the OCP term in his 1988 book, Object Oriented Software Construction
Common Refactorings
• Extract Interface / Apply Strategy Pattern• Parameterize Method• Form Template Method
OCP Fail
OCP OK
OCP Failpublic bool IsSpecialCustomer(Customer c){ if(c.Country == “US” && c.Balance < 50) return false; if(c.Country == “DE” && c.Balance < 25) return false; if(c.Country == “UK” && c.Balance < 35) return false; if(c.Country == “FR” && c.Balance < 27) return false; if(c.Country == “BG” && c.Balance < 29) return false;
if(c.Age < 18 || c.Age > 65) return false; if(c.Income < 50000 && c.Age < 30) return false; return true;}
OCP OK
private IEnumerable<ICustomerRule> _rules;
public bool IsSpecialCustomer(Customer customer){ foreach(var rule in _rules) { if(rule.Evaluate(customer) == false) return false; } return true;}
Liskov Substitution Principle
The Liskov Substitution Principle states that Subtypes must be substitutable for their base types.
Agile Principles, Patterns, and Practices in C#
Named for Barbara Liskov, who first described the principle in 1988.
Common Refactorings
• Collapse Hierarchy• Pull Up / Push Down Field• Pull Up / Push Down Method
Liskov Substitution Fail
foreach(var employee in employees){ if(employee is Manager) { Helpers.PrintManager(employee as Manager); break; } Helpers.PrintEmployee(employee);}
Liskov Substitution OK
foreach(var employee in employees){ employee.Print(); // or Helpers.PrintEmployee(employee);}
Nulls Break Polymorphism
foreach(var employee in employees){ if(employee == null) { // print not found message break; } Helpers.PrintEmployee(employee);} http://ardalis.com/nulls-break-
polymorphism
Interface Segregation Principle
The Interface Segregation Principle states that Clients should not be forced to depend on methods they do not use.
Agile Principles, Patterns, and Practices in C#
Corollary:Prefer small, cohesive interfaces to “fat” interfaces
Common Refactorings
• Extract Interface
Keep Interfaces Small and Focused
Membership Provider
ISP Fail (sometimes)
public IRepository<T>{ T GetById(int id); IEnumerable<T> List(); void Create(T item); void Update(T item); void Delete(T item);}
ISP OK (i.e. to support CQRS)public IRepository<T> : IReadRepository<T>, IWriteRepository<T>{ }public IReadRepository<T>{ T GetById(int id); IEnumerable<T> List();}public IWriteRepository<T> void Create(T item); void Update(T item); void Delete(T item);}
Existing implementations of IRepository<T> are unaffected by pulling out smaller interfaces!No existing code breaks!
Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
Agile Principles, Patterns, and Practices in C#
Dependency Inversion Principle
• Depend on Abstractions• Interfaces, not concrete types
• Inject Dependencies into Classes
• Structure Solution so Dependencies Flow Toward Core• Onion Architecture (a.k.a. Ports and Adapters, a.k.a. Hexagonal Architecture)
Application Layers
Data Access Evolution
No separation of concerns:
Data access logic baked directly into UIASP.NET Data Source ControlsClassic ASP scripts
Data access logic in UI layer via codebehindASP.NET Page_Load eventASP.NET Button_Click event
User Interface
Database
Compile Time
Runtime
Data Access : Helper Classes
Calls to data made through a utility
Example: Data Access Application Block (SqlHelper)
Logic may still live in UI layer
Or a Business Logic Layer may make calls to a Data Access Layer which might then call the helper
User Interface
Database
Compile Time
Runtime
Helper Class
What’s Missing? Abstraction!
No way to abstract away data access
Tight coupling
Leads to Big Ball of Mud system
Solution:Depend on interfaces, not
concrete implementationsWhat should we call such
interfaces? Repositories!
User Interface
Database
Compile Time
Runtime
CoreIFooRepository
InfrastructureSqlFooRepository
DIP Architecture (aka Ports and Adapters)
Common Dependencies• Framework• Third Party Libraries• Database• File System• Email• Web Services• System Resources (Clock)• Configuration• The new Keyword• Static methods• Thread.Sleep• Random
See also responsibilities:• Persistence• Validation• Notification• Error Handling• Logging• Class Selection /
Construction• Formatting• Parsing• Mapping
Common Refactorings
• Extract Class• Extract Interface / Apply Strategy Pattern• Extract Method• Introduce Service Locator / Container
DIP Fail
Hidden Dependencies
• Checkout Depends on an available SMTP server, but the class doesn’t reflect this
• Follow the Explicit Dependencies Principle• http://deviq.com/explicit-dependencies-principle/
Some Improvement (Façade)
DIP OK (Strategy Pattern / DI)
DIP OK (Strategy Pattern / DI)
Improving Quality Across the Industry
Self-Improvement and Quality
• How fast can you produce:• Code you believe to be of high quality• Code that maybe gets the job done, but you believe to be of low
quality
• Which one can you produce more quickly?• Why?• How can we develop our skills and our tools so that
building quality is natural and easier than not doing so?
Week 1 Week 2 Week 3 Week 4 Week 5 Week 6 Week 7 Week 8 Week 90
2
4
6
8
10
12
14
16
User Stories Completed
High Quality Low Quality
Week 1 Week 2 Week 3 Week 4 Week 5 Week 6 Week 7 Week 8 Week 90
2
4
6
8
10
12
14
16
18
20
User Stories Completed
High Quality Low Quality
Summary•Maintain / Improve Application Code•Follow DRY/SOLID Principles•Use Characterization Tests to “fix” behavior•Apply Common Refactorings•Re-run Tests After (and during) Refactorings•Be Explicit About Class Dependencies•Train and Practice to Write Better Code Faster
Learn More• DevIQ.com• Ardalis.com• Twitter: @ardalis• Pluralsight:• SOLID Principles of OO Design• N-Tier Architecture in C#• Refactoring Fundamentals• Domain-Driven Design Fundamentals• Design Pattern Library• Pair Programming
Books
Refactoring http://amzn.to/110tscA
Refactoring to Patterns http://amzn.to/Vq5Rj2
Working Effectively with Legacy Code http://amzn.to/VFFYbn
Code Complete http://amzn.to/Vq5YLv
Clean Code http://amzn.to/YjUDI0