SlideShare a Scribd company logo
TWO TRAINSAND OTHER
REFACTORING
ANALOGIES
by Kevin London
@kevin_london
ChowNow
Legacy Code
Formula:
1. No tests
2. Hard to read.
3. Hard to change.
“Any fool can write code that a
computer can understand.
Good programmers write code
that humans can understand.”
Martin Fowler
It’s
easy to create
legacy code.
How can we
prevent it?
1. Add tests.
2. Refactor.
Refactoring:
Improving design
without changing
behavior
My analogies
for refactoring:
The Trash Pile
“Who will notice if I
add this piece of
trash?”
Better to clean
as we go.
An example.
def get_tax(order):
if order.state == 'CA':
return order.subtotal * .0875
elif order.state == 'PA':
return order.subtotal * .08
else:
raise NotImplementedError()
Need to add
in Texas.
def get_tax(order):
if order.state == 'CA':
return order.subtotal * .0875
elif order.state == 'PA':
return order.subtotal * .08
elif order.state == 'TX':
return order.subtotal * .0825
else:
raise NotImplementedError()
def get_tax(order):
if order.state == 'CA':
return order.subtotal * .0875
elif order.state == 'PA':
return order.subtotal * .08
else:
raise NotImplementedError()
Add tests.
import pytest
from taxes import get_tax, Order
def test_get_tax_for_ca():
order = Order(subtotal=10, state='CA')
assert get_tax(order) == 10 * .0875
def test_get_tax_for_pa():
order = Order(subtotal=10, state='PA')
assert get_tax(order) == 10 * 0.08
def test_it_raises_error_for_others():
with pytest.raises(NotImplementedError):
order = Order(subtotal=1, state='EQ')
assert get_tax(order) == 0
Refactor.
TAXES = {
'CA': .0875,
'PA': .08,
}
def get_tax(order):
try:
return order.subtotal * TAXES[order.state]
except KeyError:
raise NotImplementedError()
============ test session starts =================
test.py ...
============ 3 passed in 0.01 seconds ============
def tax_equals_percent(state, percent):
subtotal = 10
order = Order(subtotal, state)
return get_tax(order) == subtotal *
percent
. . .
def test_get_tax_for_texas():
assert tax_equals_percent(‘TX', .0825)
. . .
============ test session starts ============
test.py ..F.
===== 1 failed, 3 passed in 0.02 seconds ====
TAXES = {
'CA': .0875,
'PA': .08,
'TX': .0825,
}
def get_tax(order):
try:
return order.subtotal * TAXES[order.state]
except KeyError:
raise NotImplementedError()
============ test session starts =================
test.py ....
============ 4 passed in 0.01 seconds ============
Make it
better
over time.
Tube of
Toothpaste
Easy to get toothpaste
from a new tube.
At the end,
need to roll it to get
toothpaste
With Code:
Make it easy to
change.
“For each desired change:
make the change easy
(warning: this may be hard),
then make the easy change.”
Kent Beck
class User:
def __init__(self, data):
self.data = data
def validate(self):
if '@' not in self.data['email']:
raise ValueError('Invalid email provided.')
if len(self.data['first_name']) > 50:
raise ValueError('First name too long.')
if len(self.data['last_name']) > 100:
raise ValueError('Last name too long.')
Change:
Validate phone number
import pytest
def assert_raises_error(data, expected_message):
user = User(data=data)
with pytest.raises(ValueError) as excinfo:
user.validate()
assert expected_message in str(excinfo.value)
class TestValidateUser:
def test_invalid_email_raises_error(self):
data = {
'first_name': 'foo',
'email': 'bademail',
'last_name': 'lee',
}
assert_raises_error(data, 'Invalid email provided')
. . .
=========== test session starts =======
test.py ...
======== 3 passed in 0.01 seconds=====
Make the
change easy.
class User:
def __init__(self, data):
self.data = data
def validate(self):
self.validate_email()
self.validate_first_name()
self.validate_last_name()
def validate_email(self):
if '@' not in self.data['email']:
raise ValueError('Invalid email provided.')
def validate_first_name(self):
if len(self.data['first_name']) > 50:
raise ValueError('First name too long.')
def validate_last_name(self):
if len(self.data['last_name']) > 100:
raise ValueError('Last name too long.')
=========== test session starts =======
test.py ...
======== 3 passed in 0.01 seconds=====
class User:
_fields = [
'email',
'first_name',
'last_name'
]
def __init__(self, data):
self.data = data
def validate(self):
for field in self._fields:
method_name = 'validate_{}'.format(field)
validate_field = getattr(self, method_name)
validate_field()
def validate_email(self):
. . .
=========== test session starts =======
test.py ...
======== 3 passed in 0.01 seconds=====
Make the
easy change.
class TestValidateUser:
. . .
def test_invalid_phone_number_raises_error(self):
data = {'phone_number': 'no_phone'}
user = User(data)
with pytest.raises(ValueError):
user.validate_phone_number()
=========== test session starts =======
test.py ...F
== 1 failed, 3 passed in 0.01 seconds==
class User:
_fields = [
'email',
'first_name',
'last_name',
'phone_number',
]
PHONE_LENGTH = 10
. . .
def validate_phone_number(self):
if len(self.data['phone_number']) != User.PHONE_LENGTH:
raise ValueError('Phone number is wrong length.')
=========== test session starts =======
test.py ....
======== 4 passed in 0.01 seconds======
By refactoring,
we make it easy to
change the code.
The Two
Trains
Need to
jump.
The nuclear
option:
rewrite
With code:
1. Reach Parity
2. Switch
3. Improve
A story about a
failed rewrite.
Let’s say we’re
switching
languages.
PHP -> Python
<?php
if (isset($_GET['fahrenheit'])) {
$fahrenheit = $_GET['fahrenheit'];
} else {
die('No degrees in Fahrenheit provided.');
}
$celsius = ($fahrenheit - 32) * 5 / 9;
echo floor($celsius);
1. Add tests.
2. Refactor.
import requests
API_URL = 'http://localhost:8888/celsius'
def test_api_converts_fahrenheit():
degrees_f = 32
degrees_c = 0
path = '{}?fahrenheit={}'.format(API_URL, degrees_f)
response = requests.get(path)
assert int(response.content) == degrees_c
def test_api_rounds_down():
degrees_f = 100
degrees_c = 37 # rounded down
path = '{}?fahrenheit={}'.format(API_URL, degrees_f)
response = requests.get(path)
assert int(response.content) == degrees_c
def test_api_returns_error_if_no_degrees():
response = requests.get(API_URL)
assert 'No degrees' in response.content
=========== test session starts =======
test_php_api.py ...
======== 3 passed in 0.01 seconds=====
Rewrite the
code.
import requests
API_URL = 'http://localhost:5000/celsius'
. . .
=========== test session starts =======
test_py_api.py FFF
======== 3 failed in 0.01 seconds=====
Get the tests
passing again
from math import floor
from flask import Flask, request
app = Flask(__name__)
@app.route('/celsius')
def celsius():
try:
fahrenheit = int(request.args.get('fahrenheit'))
except TypeError:
return 'No degrees in Fahrenheit provided.'
celsius = (fahrenheit - 32) * 5 / 9;
return str(floor(celsius))
=========== test session starts =======
test_py_api.py ...
======== 3 passed in 0.01 seconds=====
Reaching parity
reduces risk.
the smaller
the switch,
the better.
IN REVIEW
Don’t treat code like
a pile of trash.
Clean it up as you go.
Remember the tube of
toothpaste:
Make the change easy,
make the easy change.
If you have to jump
across two trains,
Get them to the same
speed & switch.
We can change
how we think
about legacy
code.
Technical
debt
Technical
wealth
Improving
code pays
dividends.
References:
Refactoring - Martin Fowler & Kent Beck
https://www.amazon.com/Refactoring-Improving-Design-Existing-
Code/dp/B007WTFWJ6/ref=sr_1_1?
s=books&ie=UTF8&qid=1472533198&sr=1-1&keywords=refactori
ng+martin+fowler#nav-subnav
Working with Legacy Code - Michael Feathers
https://www.amazon.com/Working-Effectively-Legacy-Michael-
Feathers/dp/0131177052
Forget Technical Debt — Here's How to Build
Technical Wealth
http://firstround.com/review/forget-technical-debt-heres-how-to-
build-technical-wealth/
Credits:
IMG_5163 - Justin Henry, https://flic.kr/p/ebaYR
The sky is the limit #2 - Mark Dries, https://flic.kr/p/eeq2eQ
The way it should be! - Patrick Dirden, https://flic.kr/p/
8Yp4mf
0001_zoriah_photojournalist_war_photographer_20120719
_0631 - Zoriah, https://flic.kr/p/dkScaB
Freaktography - Ravaged, https://flic.kr/p/nh72Tp
Aachen - Aachener Dom Pala d’oro Antependium - Daniel
Mennerich, https://flic.kr/p/pBmHyw
Thank you!
Slides:
http://bit.ly/twotrains
www.kevinlondon.com
@kevin_london

More Related Content

PPTX
Firebase ng2 zurich
PDF
RSpock Testing Framework for Ruby
PPTX
Typescript barcelona
KEY
How To Test Everything
PPTX
Introduction to nsubstitute
PPTX
Java script – basic auroskills (2)
PPTX
Refactoring domain driven design way
KEY
Tdd for BT E2E test community
Firebase ng2 zurich
RSpock Testing Framework for Ruby
Typescript barcelona
How To Test Everything
Introduction to nsubstitute
Java script – basic auroskills (2)
Refactoring domain driven design way
Tdd for BT E2E test community

What's hot (20)

PDF
Advanced Developer Testing
PDF
Effective Java with Groovy - How Language Influences Adoption of Good Practices
PDF
Nativescript angular
PDF
The Ring programming language version 1.4 book - Part 11 of 30
PDF
Refactoring
PDF
Why Your Test Suite Sucks - PHPCon PL 2015
PDF
Refactoring
PDF
Integrating React.js with PHP projects
PDF
ngMess: AngularJS Dependency Injection
PDF
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
PDF
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
PDF
4 mishchevskii - testing stage18-
PDF
focuslight-validator validate sinatra application - validation night at LINE ...
PDF
pytest로 파이썬 코드 테스트하기
PDF
Finding the Right Testing Tool for the Job
PPTX
Angular mix chrisnoring
PDF
Антипаттерны модульного тестирования
PDF
React lecture
PDF
Currying and Partial Function Application (PFA)
PDF
Next Level Testing Revisited
Advanced Developer Testing
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Nativescript angular
The Ring programming language version 1.4 book - Part 11 of 30
Refactoring
Why Your Test Suite Sucks - PHPCon PL 2015
Refactoring
Integrating React.js with PHP projects
ngMess: AngularJS Dependency Injection
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
4 mishchevskii - testing stage18-
focuslight-validator validate sinatra application - validation night at LINE ...
pytest로 파이썬 코드 테스트하기
Finding the Right Testing Tool for the Job
Angular mix chrisnoring
Антипаттерны модульного тестирования
React lecture
Currying and Partial Function Application (PFA)
Next Level Testing Revisited
Ad

Viewers also liked (15)

DOCX
Juego de alimentación para niños de 6 años
PDF
Supervisor variance in psychotherapy outcome in routine practice (psychothera...
PDF
у нашій школі є шкільне самоврядування яке складається з
PPTX
Black jack ppt
PPSX
CMaxwell-Miller_Research Proposal_041716_pptx
DOC
Rajeev_R_CV Updated
PPTX
Pricing Team
DOCX
Formato de evaluación sesion 4
RTF
aviation_resume_-_Copy_-_Copy_converted
PPTX
Noción de estructura socioeconómica
PDF
Afgang Eksp
PPTX
Luz miriam jimenez garcia
PDF
Reach slides handout day I (2 slides per pg miller & chow, 16)
PDF
EikenTALENT2016 azalpena
PDF
Dokumen satu
Juego de alimentación para niños de 6 años
Supervisor variance in psychotherapy outcome in routine practice (psychothera...
у нашій школі є шкільне самоврядування яке складається з
Black jack ppt
CMaxwell-Miller_Research Proposal_041716_pptx
Rajeev_R_CV Updated
Pricing Team
Formato de evaluación sesion 4
aviation_resume_-_Copy_-_Copy_converted
Noción de estructura socioeconómica
Afgang Eksp
Luz miriam jimenez garcia
Reach slides handout day I (2 slides per pg miller & chow, 16)
EikenTALENT2016 azalpena
Dokumen satu
Ad

Similar to Two Trains and Other Refactoring Analogies (20)

PDF
Why Our Code Smells
KEY
Unit testing zend framework apps
PDF
Unit testing with zend framework tek11
KEY
Unit testing with zend framework PHPBenelux
PPT
Test driven development_for_php
PDF
Ch ch-changes cake php2
ODP
Pruebas unitarias con django
PDF
TDD, BDD and mocks
PDF
Bdd for-dso-1227123516572504-8
PDF
Quality Assurance for PHP projects - ZendCon 2012
KEY
Workshop quality assurance for php projects tek12
PDF
Mock Hell PyCon DE and PyData Berlin 2019
PDF
Tips and tricks for building api heavy ruby on rails applications
PPT
Behaviour-Driven Development
PDF
Django (Web Konferencia 2009)
PDF
PHPSpec BDD Framework
PDF
Testing most things in JavaScript - LeedsJS 31/05/2017
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
PDF
Leveraging Symfony2 Forms
PPTX
Designing REST API automation tests in Kotlin
Why Our Code Smells
Unit testing zend framework apps
Unit testing with zend framework tek11
Unit testing with zend framework PHPBenelux
Test driven development_for_php
Ch ch-changes cake php2
Pruebas unitarias con django
TDD, BDD and mocks
Bdd for-dso-1227123516572504-8
Quality Assurance for PHP projects - ZendCon 2012
Workshop quality assurance for php projects tek12
Mock Hell PyCon DE and PyData Berlin 2019
Tips and tricks for building api heavy ruby on rails applications
Behaviour-Driven Development
Django (Web Konferencia 2009)
PHPSpec BDD Framework
Testing most things in JavaScript - LeedsJS 31/05/2017
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Leveraging Symfony2 Forms
Designing REST API automation tests in Kotlin

Recently uploaded (20)

PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Empathic Computing: Creating Shared Understanding
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Machine learning based COVID-19 study performance prediction
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPT
Teaching material agriculture food technology
PDF
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
A comparative study of natural language inference in Swahili using monolingua...
PDF
Getting Started with Data Integration: FME Form 101
PDF
Approach and Philosophy of On baking technology
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Heart disease approach using modified random forest and particle swarm optimi...
PPTX
cloud_computing_Infrastucture_as_cloud_p
Diabetes mellitus diagnosis method based random forest with bat algorithm
Empathic Computing: Creating Shared Understanding
MIND Revenue Release Quarter 2 2025 Press Release
Machine learning based COVID-19 study performance prediction
Per capita expenditure prediction using model stacking based on satellite ima...
Teaching material agriculture food technology
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
NewMind AI Weekly Chronicles - August'25-Week II
Unlocking AI with Model Context Protocol (MCP)
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
A comparative study of natural language inference in Swahili using monolingua...
Getting Started with Data Integration: FME Form 101
Approach and Philosophy of On baking technology
Univ-Connecticut-ChatGPT-Presentaion.pdf
Assigned Numbers - 2025 - Bluetooth® Document
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Heart disease approach using modified random forest and particle swarm optimi...
cloud_computing_Infrastucture_as_cloud_p

Two Trains and Other Refactoring Analogies