Slide 1

Slide 1 text

Tombstones, Vampires & Discovering Dead Code

Slide 2

Slide 2 text

Presented by Chris Gmyr @cmgmyr

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

What are we going to learn? → What is technical debt? → Efficiently find dead code → Remove code with confidence

Slide 6

Slide 6 text

What is technical debt? The implied cost of additional rework caused by choosing an easy solution now instead of using a better approach. If technical debt is not repaid, it accumulates 'interest', making it harder to implement changes later on...

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

What is technical debt? ...Technical debt is not necessarily a bad thing, and sometimes is required to move projects forward. → https://en.wikipedia.org/wiki/Technical_debt

Slide 9

Slide 9 text

What does technical debt look like?

Slide 10

Slide 10 text

Crowdpac Codebase → Laravel 4.2 → ~300 Classes → ~50,000 Logical lines of code → 667.13 Average system complexity score → (excludes: migrations, tests, views)

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

How do we start paying back our technical debt?

Slide 13

Slide 13 text

Pay back technical debt by → remove commented out blocks of code → improve your test suite (or start one) → refactor code you're currently working on → removing dead code

Slide 14

Slide 14 text

What is "dead code"? → not referenced → referenced, not executed → no longer provides business value

Slide 15

Slide 15 text

Great, let's remove some code!

Slide 16

Slide 16 text

PHP "gotchas" → Dynamic language → Magic methods → Easy to write "bad" code

Slide 17

Slide 17 text

Variable Functions function foo() { echo "In foo()
\n"; } $func = 'foo'; $func(); We can't search for references of foo()

Slide 18

Slide 18 text

Variable Functions function displayName($name) { echo $name; } $func = 'display' . ucwords($_GET['property']); $func($_GET['property_value']); How are we easily going to search for displayName() usages?

Slide 19

Slide 19 text

Magic Methods __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() and __debugInfo() http://php.net/manual/en/language.oop5.magic.php

Slide 20

Slide 20 text

Magic Methods class User { public function __toString() { return $this->fullName(); } } echo new User('Chris Gmyr'); echo new User(['full_name' => 'Chris Gmyr']); $person = new User([...]); // User::make([...]); echo $person;

Slide 21

Slide 21 text

Framework "Magic" class User { public function setPasswordAttribute($value) { $this->attributes['password'] = Hash::make($value); } public function getPasswordAttribute($value) { return 'HIDDEN'; } }

Slide 22

Slide 22 text

Framework "Magic" User::create([ 'password' => $_POST['password'] // setPasswordAttribute() ]); $user->password = $_POST['password']; // setPasswordAttribute() echo $user->password; // getPasswordAttribute()

Slide 23

Slide 23 text

Framework "Magic" class Post { public function scopePopular($query) { return $query->where('votes', '>', 100); } } $posts = Post::popular()->get(); // scopePopular()

Slide 24

Slide 24 text

Searching for usages is inefficient and error-prone

Slide 25

Slide 25 text

Let's try this again...

Slide 26

Slide 26 text

Using tombstones to find dead code

Slide 27

Slide 27 text

What is a tombstone? A marker (function) placed inside code that is assumed dead. When a tombstone is executed, tracking information is written to a log. public function reallyOldMethod() { tombstone('2017-10-31', 'cmgmyr'); //... }

Slide 28

Slide 28 text

What is a vampire? Suspected dead code, marked with a tombstone, that is alive echo $user->fullName(); class User { public function fullName() // vampire! { tombstone('2017-10-31', 'cmgmyr'); // remove this return $this->first_name . ' ' . $this->last_name; } }

Slide 29

Slide 29 text

PHP Implementation https://github.com/scheb/tombstone function tombstone($date, $author, $label = null) { try { $trace = TraceProvider::getTraceHere(); $streamHandler = new StreamHandler(dirname(__DIR__)."/logs/tombstones.log"); GraveyardProvider::getGraveyard()->addHandler($streamHandler); GraveyardProvider::getGraveyard()->tombstone($date, $author, $label, $trace); } catch (\Exception $e) { // } }

Slide 30

Slide 30 text

PHP Implementation https://github.com/scheb/tombstone-analyzer

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

Other Tombstone Implementations JavaScript https://blog.bugsnag.com/javascript-refactoring-with- bugsnag-and-tombstones/ Perl http://devblog.nestoria.com/post/115930183873/ tombstones-for-dead-code

Slide 33

Slide 33 text

Let's set some tombstones!

Slide 34

Slide 34 text

class Post { public function publishToMySpace() { tombstone('2017-10-31', 'cmgmyr'); //... } }

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

class User { public function canPublishPost() { if ($this->isAwesome()) { if($this->type == 'zombie') { tombstone('2017-10-31', 'cmgmyr'); //... } //... } //... } }

Slide 37

Slide 37 text

Add tombstones to suspected dead code

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

...or ALL code...

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

...and wait...

Slide 42

Slide 42 text

Check the logs 2017-11-01 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-02 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-03 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ...

Slide 43

Slide 43 text

class User { public function canPublishPost() { if ($this->isAwesome()) { if($this->type == 'zombie') { tombstone('2017-10-31', 'cmgmyr'); //... } //... } //... } }

Slide 44

Slide 44 text

Check the logs 2017-11-01 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-02 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-03 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-04 ... 2017-11-05 ... 2017-11-06 ... No more canPublishPost() vampires!!!

Slide 45

Slide 45 text

class Post { public function publishToMySpace() { tombstone('2017-10-31', 'cmgmyr'); //... } }

Slide 46

Slide 46 text

class TombstoneWorkflow { public function __construct() { $this->makePlan(); $this->cleanCode(); } public function cleanCode() { $this->setTombstones(); while($this->codebaseHasVampires()) { $this->checkLogs(); $this->removeVampires(); } $this->reviewRemainingTombstones(); $this->removeDeadCode(); } }

Slide 47

Slide 47 text

Crowdpac 2,400 initial tombstones (class methods) -750 locally -925 staging/production ~700 Remaining (30%)

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Who should utilize tombstones? → legacy code → complex domain → inherited codebase → upgrading a codebase

Slide 50

Slide 50 text

So, why tombstones? → Low impact solution → Use with dynamic languages → High confidence in removing code

Slide 51

Slide 51 text

Thank you!

Slide 52

Slide 52 text

Say "Hi"! twitter.com/cmgmyr github.com/cmgmyr chrisgmyr.com