49
It Was Like That When I Got Here: Steps Toward Modernizing A Legacy Codebase Lone Star PHP Conference 30 Jun 2012 joind.in/6700 paul-m-jones.com @pmjones

It Was Like That When I Got Here

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: It Was Like That When I Got Here

It Was Like That When I Got Here:Steps Toward Modernizing A Legacy Codebase

Lone Star PHP Conference30 Jun 2012

joind.in/6700

paul-m-jones.com@pmjones

Page 2: It Was Like That When I Got Here

Read These

Page 3: It Was Like That When I Got Here

About Me• 8 years USAF Intelligence

• PHP since 1999

• Developer, Senior Developer,Team Lead, Architect, VP Engineering

• Aura project, benchmarking series, Solar framework, Savant template system, Zend_DB, Zend_View

• PEAR group member, ZCE advisor, PHP-FIG voting member

Page 4: It Was Like That When I Got Here

Overview

• It was like that when I got here

• Paying off technical debt

• Life is easier (but not perfect)

Page 5: It Was Like That When I Got Here

It Was Like ThatWhen I Got Here

Page 6: It Was Like That When I Got Here

Messy Codebase

• Spaghetti include logic

• Few or no classes

• Global variables

• No unit tests -- QA working overtime

Page 7: It Was Like That When I Got Here

No Time To Remedy

• Bugs to fix, right now

• Features to implement, right now

• Making your own life easier?Not a priority.

• Dig in and try to make do

• How did it get this bad? “It was like that when I got here.”

Page 8: It Was Like That When I Got Here

The Great Thing About PHP ...

• ... is that anyone can use it.

• Have an idea? Implement it!

• It works! Great success!

• ... it “works.”

Page 9: It Was Like That When I Got Here

The Awful Thing About PHP ...

• ... is that anyone can use it.

• The codebase is like a “dancing bear”

• Architecture? Maintenance? Testing?

• Move on to the next idea ...

• ... but *you* are stuck with it now.

Page 10: It Was Like That When I Got Here

Project Evolution Tracks

Standalone App

Library Collection Modular App

Framework CMS

One-Off Heap

?

Page 11: It Was Like That When I Got Here

One-Off Heap

• No discernible architecture

• Browse directly to scripts

• Add to it piece by piece

• Little to no separation of concerns

• Global variables

Page 12: It Was Like That When I Got Here

Standalone Application

• One-off heap ++

• Page scripts and common includes

• Installed in web root

• Responsible for global execution environment

• Script variables still global

Page 13: It Was Like That When I Got Here

Typical Page Script

see editor for example

Page 14: It Was Like That When I Got Here

Why Is It Like This?

• Original developer probably didn’t know better

• Subsequent developers worked with what was there

• “We can fix that later; we need this done now.”

• ... until later becomes now.

Page 15: It Was Like That When I Got Here

Technical Debt

• A metaphor referring to the eventual consequences of poor or evolving software architecture and software development within a codebase.

• As a change is started on a codebase, there is often the need to make other coordinated changes at the same time in other parts of the codebase.

• http://en.wikipedia.org/wiki/Technical_debt

Page 16: It Was Like That When I Got Here

Paying Off Technical Debt

Page 17: It Was Like That When I Got Here

Paying Off Technical Debt

• A lot like paying off financial debt

• Got the stuff first, but have to pay for it eventually

• No easy way out. Suffer as things are, or suffer through change.

Page 18: It Was Like That When I Got Here

Big-Bang Approach

• Rewrite from scratch! (mmm, sexy)

• “It’s the only way to be sure.”

• Expend effort while not earning revenue

• End up with different bad architecture

Page 19: It Was Like That When I Got Here

Incremental Approach

• Small changes across entire codebase

• Build on previous small changes

• Keeps running the whole time

Page 20: It Was Like That When I Got Here

Incremental Goals

• Stop digging!

• Keep the application running

• Consolidate classes for autoloading (PSR-0)

• Convert globals to injected dependencies

Page 21: It Was Like That When I Got Here

Consolidate Classes For Autoloading

Page 22: It Was Like That When I Got Here

// without autoloading, must include file firstinclude_once "/path/to/classes/Example/Class/Name.php";$obj = new Example_Name();

// with autoloading, gets included automatically$obj = new Example_Name();

What Is Autoloading?

Page 23: It Was Like That When I Got Here

PSR-0

• Class name maps directly to file name

• Namespace separators map to directory separators

• Class underscores map to directory separators

• Vendor\Package_Name\Example_Name=> Vendor/Package_Name/Example/Name.php

Page 24: It Was Like That When I Got Here

function autoload($class){ $class = ltrim($class, '\\'); $file = ''; $ns = ''; $pos = strripos($class, '\\') if ($pos) { $ns = substr($class, 0, $pos); $class = substr($class, $pos + 1); $file = str_replace('\\', DIRECTORY_SEPARATOR, $ns) . DIRECTORY_SEPARATOR; } $file .= str_replace('_', DIRECTORY_SEPARATOR, $class); $base = "/path/to/classes"; require "{$base}/{$file}.php";}

spl_autoload_register('autoload');

Page 25: It Was Like That When I Got Here

Move Class Files

• If you have class files in several paths, move to same base path

• If you have more than one class per file, split into separate files

• If you define classes as part of a script, extract to own file

• Remove include/require as you go (grep)

• If needed, change names as you go (grep)

Page 26: It Was Like That When I Got Here

Convert Function Files to Class Files

• Many projects have files of function definitions

• Wrap in a class as static or instance methods

• Move to classes directory

• Change calls to static or instance calls (grep)

• Remove include/require as you go (grep)

Page 27: It Was Like That When I Got Here

Original Function

function fetch_results(){ global $db; $results = $db->fetch('results'); return $results;}

$results = fetch_results();

Page 28: It Was Like That When I Got Here

Static Method

class Example{ public static function fetchResults() { global $db; $results = $db->fetch('results'); return $results; }}

$results = Example::fetchResults();

Page 29: It Was Like That When I Got Here

Instance Method

class Example{ public function fetchResults() { global $db; $results = $db->fetch('results'); return $results; }}

$example = new Example;$results = $example->fetchResults();

Page 30: It Was Like That When I Got Here

Convert Globalsto Injected Dependencies

Page 31: It Was Like That When I Got Here

Instantiating Dependencies In Methods

class Example{ public function fetchResults() { $db = new Database('username', 'password'); return $db->fetch('whatever'); }}

Page 32: It Was Like That When I Got Here

Drawbacks Of Method Instantiation

• New connection on each call

• Cannot reuse connection

• Parameter modification

Page 33: It Was Like That When I Got Here

Global Dependencies

// setup file$db = new Database('username', 'password');

// example class fileclass Example{ public function fetchResults() { global $db; return $db->fetch('whatever'); }}

Page 34: It Was Like That When I Got Here

Global Drawbacks

class Evil{ public function actionAtADistance() { global $db; unset($db); }}

Page 35: It Was Like That When I Got Here

Dependency Injection

• Instead of reaching out from inside the class to bring in dependencies ...

• ... inject the dependency into the class from the outside.

Page 36: It Was Like That When I Got Here

Starting Point: Global In Method

class Example{ public function fetchResults() { global $db; return $db->fetch('results'); }}

Page 37: It Was Like That When I Got Here

Interim: Global In Constructor

class Example{ public function __construct() { global $db; $this->db = $db; } function fetchResults() { return $this->db->fetch('results'); }}

Page 38: It Was Like That When I Got Here

Final: Dependency Injection

class Example{ public function __construct($db) { $this->db = $db; } function fetchResults() { return $this->db->fetch('results'); }}

Page 39: It Was Like That When I Got Here

• Must change all new instantiations to pass dependencies (grep)

• Class instantiation inside methods? Pass intermediary dependencies.

Change Instantiation Calls

Page 40: It Was Like That When I Got Here

Intermediary Dependencyclass Example{ public function fetchResults() { global $db; return $db->fetch('whatever'); }}

class Service{ public function action() { $example = new Example; return $example->fetchResults(); }}

Page 41: It Was Like That When I Got Here

class Example{ public function __construct($db) { $this->db = $db; } public function fetchResults() { return $this->db->fetch('whatever'); }}

class Service{ public function __construct($db) { $this->db = $db; } public function action() { $example = new Example($this->db); return $example->fetchResults(); }}

Page 42: It Was Like That When I Got Here

Eliminate Intermediary Dependency

class Service{ public function __construct($example) { $this->example = $example; } public function action() { return $this->example->fetchResults(); }}

Page 43: It Was Like That When I Got Here

Progression of Instantiation

// all globals$service = new Service;

// intermediary: Example uses DI,// but Service creates Example internally$db = new Database('username', 'password');$service = new Service($db);

// all DI all the time$db = new Database('username', 'password');$example = new Example($db);$service = new Service($example);

Page 44: It Was Like That When I Got Here

Life After Reorganizing

Page 45: It Was Like That When I Got Here

Initial Goals Completed ...

• Consolidated into classes with PSR-0 and autoloading

• Removed globals in favor of dependency injection

• Kept it running the whole time

Page 46: It Was Like That When I Got Here

... But Much Remains

• Things are still not perfect

• Individual page scripts still in place

• No tests yet ...

Page 47: It Was Like That When I Got Here

WE ALL TEST DOWN HERE(with apologies to Stephen King’s “It”)

Page 48: It Was Like That When I Got Here

It’s Better Than Before

• ... easier to test the classes you have

• Organizational structure for future work

• Paid off a lot of technical debt