Reduce, Reuse, Refactor
improving the design of existing code
GOAL != “Perfection”
GOAL == “Improvement”
What is “Legacy Code”
Is there a coding standard for your project?
Is code using OOP?
Is Composer used in your project?
Are you unit testing?
Does your project avoid NIH?
What is “refactoring”?
“…process of changing a computer program’s source code without
modifying its external functional behavior…”
No functionality added
Code quality
Two Hats
adding functionality hat
refactoring hat
we add functionality, then refactor, then add more functionality
Then optimize
Do not optimize while refactoring
Separate step
Refactoring is NOT optimizing
Why refactor?
Prevent decay
Preserve or fix design
Reduce duplication
Improve maintainability
Helps us code faster
Locate bugs
Code smells
What are code “smells”?
Indications of spoiled code nearby
Not conclusive
The “smell” is not bad
“Potential” problems
Code “smells” hints to refactor
Duplicate Code (rule 3)
Long Methods
Large Class
Long Parameter (argument) List
Divergent Change - cascade change to accommodate another
Shotgun Surgery - change ripples as bugs
Feature Envy - methods uses parts from other class
Switch Statements - sacrifice polymorphism
Code “smells” hints to refactor: cont’d
Lazy Class - class not doing much
Speculative Generality - something built for possible future
Temporary Field/Variable
Message Chains - object asking object asking object
Middle Man - directors in place but serve no real purpose
Inappropriate Intimacy - classes share price parts
Data Class - getters and setters, but nothing else
Comments - where comments cover bad code
Tools to highlight smells
PHPqatools.org
PHPUnit
PHPLoc
PHP_Codesniffer
PHP_Depend
PHP Copy/Paste Detector
PHP Mess Detector
PHP Dead Code Detector
Zend Z-Ray: realtime profiling
Paying off technical debt
a lot like paying off financial debt
got the stuff first, but have to pay for it eventually
must pay off technical debt not of our own choosing
suffering as things are, or suffering through change
Declare Bankruptcy
rewrite from scratch!
expend effort while not earning
revenue
old devs on new project? new devs
on new project
takes longer than you think
end up with different bad architecture
When to rewrite
Want a new app
Not just better coded current
app
Business logic change
Target market change
Framework integration or change
Incremental approach
pay off smallest debt first (build
inertia and raise spirits)
small changes across codebase
build on previous small changes
improve quality over time
Embrace incremental change
When to refactor
No “special” time
Short bursts
Refactor to gain something
Prior to adding functionality
When fixing a bug
During code review
ninjagrl.com
Before You start
Get it working!
Use source control (Git, SVN, etc.)
Use an IDE
Follow a Style Guide
Have Tests in place
Source Control
Refactor in branch
Allows rollback
Editor/IDE
Makes searching easier
Search within project
Style Guide
Framework Interop Group
http://php-fig.org
PSR
Faster reading
United team
Testing
Consistent results
Prevents breaks
Speeds up development
Refactor, Test, Repeat
Ensure tests pass
Plan and implement refactor
Ensure tests still pass
Updating test if needed
Add more tests to cover newly discovered items
Repeat!
Modernizing Steps
Autoloading
Consolidate Classes
Cleanup Globals
Replace “new” (instantiation)
Create Tests
Extract SQL
Extract Domain Logic
Extract Presentation Logic
Replace Remaining “Includes”
Autoloading Approaches
PSR-0 or PSR-4
Approaches
Global function
Closure
Static or Instance Method (preferred, if possible)
__autoload() - PHP v 5.0
Need a central place for classes
Test, Repeat
Consolidate Classes
Step 1
Search for include statements (include, include_once, require, require_once)
Step 2
Move into central location (“includes”, “classes”, “src”, “lib”, etc.)
Step 3
Implement autoloading (no more include statements for classes)
Test, Repeat
Cleanup “Global” Dependencies Step
1. Search for global reference
2. Move global calls to constructor
3. Convert global call to a constructor parameter
4. Update global call to a class
5. Instantiate new class and pass as parameter (DI)
Test, Repeat
Steps to Replace “new”
1. Search for “new”
2. Extract instantiation to constructor parameter. (if one time)
Or extract block of creation code to new Factory class. (if repeated)
3. Update instantiation calls
Test, Repeat
Write Tests
Code is fairly clean
Write tests for entire application
If not testable, refactor
Extract method
Replace temp with query
Etc.
Test, Repeat
Extract SQL
1. Search for SQL
2. Move Statement and relevant logic to Gateway class
3. Create test for new class
4. Alter code to use new method
Test, Repeat
Extract Domain Logic
Search for uses of Gateway class outside of Transaction classes
Extract logic to Transaction classes
Test, Repeat
Extract Presentation Logic
Setup your Views
Should have MINIMAL logic
May use templating such as Twig
Allows UI designers to own that code
Test, Repeat
Replace leftover “includes”
If in current class
Copy contents into file directly
Refactor for: no globals, no ‘new’, DI, return instead of output, no includes
More often
Copy contents of includes as-is to new class method
Replace with in-line instantiation
Search for other uses of same, and update them as well
Delete original include file, regression test
Test, Repeat
Initial Goals Completed
Consolidated into classes with autoloading
Remove globals in favor of dependency injection
Kept it running the whole time
Paid off some technical debt
Organizational structure for future work
Started writing unit tests
Additional Possibilities
Can now implement framework
Leverage services
Leverage events
Use Composer
Concluding Thoughts
Do not refactor a broken application
Have tests in place prior to refactoring
Unit Tests
Functional test
Manual tests
Do things in small steps
Leave the code better than you left it
Resources
Alena Holligan
• Wife, and Mother of 3 young children
• PHP Teacher at Treehouse
• Portland PHP User Group Leader
• Cascadia PHP Conference (cascadiaphp.com)
@alenaholligan alena@holligan.us https://joind.in/talk/efa39

Reduce Reuse Refactor

  • 1.
    Reduce, Reuse, Refactor improvingthe design of existing code
  • 2.
    GOAL != “Perfection” GOAL== “Improvement”
  • 3.
    What is “LegacyCode” Is there a coding standard for your project? Is code using OOP? Is Composer used in your project? Are you unit testing? Does your project avoid NIH?
  • 4.
    What is “refactoring”? “…processof changing a computer program’s source code without modifying its external functional behavior…” No functionality added Code quality
  • 5.
    Two Hats adding functionalityhat refactoring hat we add functionality, then refactor, then add more functionality
  • 6.
    Then optimize Do notoptimize while refactoring Separate step Refactoring is NOT optimizing
  • 7.
    Why refactor? Prevent decay Preserveor fix design Reduce duplication Improve maintainability Helps us code faster Locate bugs Code smells
  • 8.
    What are code“smells”? Indications of spoiled code nearby Not conclusive The “smell” is not bad “Potential” problems
  • 9.
    Code “smells” hintsto refactor Duplicate Code (rule 3) Long Methods Large Class Long Parameter (argument) List Divergent Change - cascade change to accommodate another Shotgun Surgery - change ripples as bugs Feature Envy - methods uses parts from other class Switch Statements - sacrifice polymorphism
  • 10.
    Code “smells” hintsto refactor: cont’d Lazy Class - class not doing much Speculative Generality - something built for possible future Temporary Field/Variable Message Chains - object asking object asking object Middle Man - directors in place but serve no real purpose Inappropriate Intimacy - classes share price parts Data Class - getters and setters, but nothing else Comments - where comments cover bad code
  • 11.
    Tools to highlightsmells PHPqatools.org PHPUnit PHPLoc PHP_Codesniffer PHP_Depend PHP Copy/Paste Detector PHP Mess Detector PHP Dead Code Detector Zend Z-Ray: realtime profiling
  • 13.
    Paying off technicaldebt a lot like paying off financial debt got the stuff first, but have to pay for it eventually must pay off technical debt not of our own choosing suffering as things are, or suffering through change
  • 14.
    Declare Bankruptcy rewrite fromscratch! expend effort while not earning revenue old devs on new project? new devs on new project takes longer than you think end up with different bad architecture
  • 15.
    When to rewrite Wanta new app Not just better coded current app Business logic change Target market change Framework integration or change
  • 16.
    Incremental approach pay offsmallest debt first (build inertia and raise spirits) small changes across codebase build on previous small changes improve quality over time Embrace incremental change
  • 17.
    When to refactor No“special” time Short bursts Refactor to gain something Prior to adding functionality When fixing a bug During code review ninjagrl.com
  • 18.
    Before You start Getit working! Use source control (Git, SVN, etc.) Use an IDE Follow a Style Guide Have Tests in place
  • 19.
    Source Control Refactor inbranch Allows rollback
  • 20.
  • 21.
    Style Guide Framework InteropGroup http://php-fig.org PSR Faster reading United team
  • 22.
  • 23.
    Refactor, Test, Repeat Ensuretests pass Plan and implement refactor Ensure tests still pass Updating test if needed Add more tests to cover newly discovered items Repeat!
  • 24.
    Modernizing Steps Autoloading Consolidate Classes CleanupGlobals Replace “new” (instantiation) Create Tests Extract SQL Extract Domain Logic Extract Presentation Logic Replace Remaining “Includes”
  • 25.
    Autoloading Approaches PSR-0 orPSR-4 Approaches Global function Closure Static or Instance Method (preferred, if possible) __autoload() - PHP v 5.0 Need a central place for classes
  • 26.
  • 27.
    Consolidate Classes Step 1 Searchfor include statements (include, include_once, require, require_once) Step 2 Move into central location (“includes”, “classes”, “src”, “lib”, etc.) Step 3 Implement autoloading (no more include statements for classes)
  • 28.
  • 29.
    Cleanup “Global” DependenciesStep 1. Search for global reference 2. Move global calls to constructor 3. Convert global call to a constructor parameter 4. Update global call to a class 5. Instantiate new class and pass as parameter (DI)
  • 30.
  • 31.
    Steps to Replace“new” 1. Search for “new” 2. Extract instantiation to constructor parameter. (if one time) Or extract block of creation code to new Factory class. (if repeated) 3. Update instantiation calls
  • 32.
  • 33.
    Write Tests Code isfairly clean Write tests for entire application If not testable, refactor Extract method Replace temp with query Etc.
  • 34.
  • 35.
    Extract SQL 1. Searchfor SQL 2. Move Statement and relevant logic to Gateway class 3. Create test for new class 4. Alter code to use new method
  • 36.
  • 37.
    Extract Domain Logic Searchfor uses of Gateway class outside of Transaction classes Extract logic to Transaction classes
  • 38.
  • 39.
    Extract Presentation Logic Setupyour Views Should have MINIMAL logic May use templating such as Twig Allows UI designers to own that code
  • 40.
  • 41.
    Replace leftover “includes” Ifin current class Copy contents into file directly Refactor for: no globals, no ‘new’, DI, return instead of output, no includes More often Copy contents of includes as-is to new class method Replace with in-line instantiation Search for other uses of same, and update them as well Delete original include file, regression test
  • 42.
  • 43.
    Initial Goals Completed Consolidatedinto classes with autoloading Remove globals in favor of dependency injection Kept it running the whole time Paid off some technical debt Organizational structure for future work Started writing unit tests
  • 44.
    Additional Possibilities Can nowimplement framework Leverage services Leverage events Use Composer
  • 45.
    Concluding Thoughts Do notrefactor a broken application Have tests in place prior to refactoring Unit Tests Functional test Manual tests Do things in small steps Leave the code better than you left it
  • 47.
  • 48.
    Alena Holligan • Wife,and Mother of 3 young children • PHP Teacher at Treehouse • Portland PHP User Group Leader • Cascadia PHP Conference (cascadiaphp.com) @alenaholligan alena@holligan.us https://joind.in/talk/efa39