Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Rewriting 15-Year-Old Code
Anna Filina
PRO
November 15, 2017
Programming
4
300
Rewriting 15-Year-Old Code
Anna Filina
PRO
November 15, 2017
Tweet
Share
More Decks by Anna Filina
See All by Anna Filina
Writing Testable Symfony Apps
afilina
PRO
0
21
Upgrading Legacy to the Latest PHP Version
afilina
PRO
0
69
Avoid Costly Framework Upgrades
afilina
PRO
2
1.2k
Fantastic Bugs and How to Avoid Them
afilina
PRO
1
710
Adding Tests to Untestable Legacy Code
afilina
PRO
4
760
You Don't Need More Developers
afilina
PRO
5
1.1k
Fantastic Bugs and How to Avoid Them
afilina
PRO
0
140
Ship 10 Times Faster With These Designs
afilina
PRO
0
350
Effortless Software Development
afilina
PRO
1
340
Other Decks in Programming
See All in Programming
Go1.19で採用された Pattern-defeating Quicksort の紹介
po3rin
7
1.5k
回帰分析ではlm()ではなくestimatr::lm_robust()を使おう / TokyoR100
dropout009
0
4.6k
FutureCon 2022 FlutterアプリのPerformance測定
harukafujita
0
140
10歳の minne から、これから長く続くプロダクトを作るすべての人へ
tsumichan
9
3.7k
Computer Vision Seminar 1/コンピュータビジョンセミナーvol.1 OpenCV活用
fixstars
0
170
Git Rebase
bkuhlmann
7
1.1k
読みやすいコード クラスメソッド 2022 年度新卒研修
januswel
0
2.9k
How to Test Your Compose UI (Droidcon Berlin 2022)
stewemetal
1
130
Amazon Lookout for Visionで 筆跡鑑定してみた
cmnakamurashogo
0
180
AWS Config Custom Rule、ノーコードでできるかな?
watany
0
250
閱讀原始碼 - 再戰十年的 jQuery
eddie
1
310
Pluggable Storage in PostgreSQL
sira
1
190
Featured
See All Featured
Writing Fast Ruby
sferik
612
57k
Thoughts on Productivity
jonyablonski
44
2.4k
Web development in the modern age
philhawksworth
197
9.3k
Rails Girls Zürich Keynote
gr2m
87
12k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
655
120k
Making the Leap to Tech Lead
cromwellryan
113
7.4k
WebSockets: Embracing the real-time Web
robhawkes
57
5.6k
What's in a price? How to price your products and services
michaelherold
229
9.4k
Keith and Marios Guide to Fast Websites
keithpitt
404
21k
Learning to Love Humans: Emotional Interface Design
aarron
261
37k
Designing the Hi-DPI Web
ddemaree
272
32k
Designing for humans not robots
tammielis
242
24k
Transcript
@afilina Rewriting 15-Year-Old Code BuildStuff, Vilnius - November 15, 2017
Anna Filina • Project rescue • Legacy code • Development
• Conferences • Private workshops • Canada
You inherited a 17-year-old codebase Code doesn't age like wine.
The Web Was 600px Wide
Feeling Old Yet? • Y2K bug. • Internet Explorer 5.
• ICQ was starting to be cool. • Rounded corners = status symbol.
Code smells
Mixed Concerns <?php mysql_query('...'); $total = 0; foreach ($products as
$p) { $total += $p['qty'] * $p['price']; } ?> <p>Total: <?= $total ?></p>
Poorly Named Variables $a = //... $array = //... $item3
= //... $tempItem = //...
Global Functions & Constants include("functions.php"); MyClass::myMethod(); // namespaced function? myMethod(DEV_MODE);
Inexplicable Conditions if ($order_id > 20117) { // use this
sql } else { // use that sql } // weakness = time to refactor
Long Methods public function importCsv($path, $googleApiKey, $databaseDsn, $databaseUser, $databasePassword) {
// Convert CSV to array of conferences $lines = file($path); $csv = array_map('str_getcsv', $lines); $conferences = []; foreach ($csv as $line) { $conference = new Conference(); $conference->name = $line[0]; $conference->location = $line[1]; $conferences[] = $conference; // Get coordinates for location $location = urlencode($conference->location); $url = 'https://maps.googleapis.com/maps/api/geocode/json?address='.$location.'&key='.$googleApiKey; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($curl); curl_close($curl); $json = json_decode($response); if (count($json->results) == 0) { continue; } $latitude = $json->results[0]->geometry->location->lat; $longitude = $json->results[0]->geometry->location->lng; $coordinates = $latitude.','.$longitude; $conference->coordinates = $coordinates; // Save conference to database $pdo = new PDO($databaseDsn, $databaseUser, $databasePassword); $statement = $pdo->prepare('REPLACE INTO conference (name, location, coordinates) VALUES (?, ?, ?)'); $statement->execute([ $conference->name, $conference->location, $conference->coordinates, ]);
Average Legacy Code File • 3000-6000 lines of code. •
Half of it is commented "in case we need it later". • Method length of 800 lines. • Abuse of helper classes. • Sometimes no classes at all.
Codebase • 5,000 classes. • 20,000 methods. • 1,500,000 lines
of code.
Before you code
Strategy • Make a strategy based on constraints. • Full
rewrite vs progressive: ◦By class. ◦By feature/module. ◦By HTTP call. • How to run code side-by-side: ◦DB/Session sharing. ◦mod_rewrite.
Data Can Be Lost, Stuff Can Break • Backup: test
restore. • Nullify sensitive data. • Staging: simulate deployments/ upgrades/batch processes. • Automate tests before code changes. • Make a risk assessment: ◦ Don't be too optimistic. ◦ Account for side-effects.
Build With Real Legacy Data
(Failed) rewrite example
PHP 3 to PHP 5.6 • HTML + PHP +
SQL in same file. • Includes all over the place. • IFs that concatenate SQL. • Previous rewrite attempt: ◦ Failed, made things worse. ◦ Folders of dead code. ◦ Classes with static functions (no instances).
Solution • Rewrite complex forms in Symfony: ◦ mod_rewrite for
concerned pages. • Rewrite biggest module as OOP: ◦ Design extraction. ◦ Flexible architecture. ◦ Automated tests.
New feature should not take longer than a sprint.
Design extraction
Avoid Code Bias • Old code → design docs. •
Validate design docs: ◦Clarify business rules. • Improve design: ◦Reduce technical debt. ◦More flexible. • Design docs → new code.
Fixing bugs
Code Duplication • Sometimes, the bug is repeated 80+ times.
• Remove duplications ASAP.
Fix Long Methods • Extract broken part into its own
method. • Write unit tests for it. • Fix it. • Call it from the mega-method.
Spot Logical Groups Code block Comment Code block Comment Convert
CSV to array of conferences. Get coordinates for conference location. Save conference to database. Code block Comment
Extract & Test Code block Comment Code block Method Code
block Comment Call method
Name Code block Method Code block Method Call method Call
method Code block Method Call method getConferencesFromCsv getLocationCoordinates saveConference
Rewrite example #2
ASP Classic to PHP 5.6 • 15+ spaghetti and hacks.
• Language no longer supported. • Huge ERP with lots of code.
Solution • Rewrite page by page to Symfony. • mod_rewrite
for concerned pages. • DB session adapter in both apps. • Page in any language = HTTP request. ◦ Guzzle tests FTW!
Guzzle Tests
On Testing Before you code You better test So that
on weekends You may rest. -- Anna Filina
Testing Static Methods $mock = Mockery::mock('alias:SomeClass'); $mock->shouldReceive('staticMethod')->andReturn(42);
Stuck?
Try Something New • Bounce ideas. ◦ New people to
avoid tunnel vision. • Has this been done before? • Can I try another approach?
Takeaways • Make a strategy. • You touch it, you
refactor it. • Use known tools & methodologies. • Get inspiration from others. • Refactoring gets easier. • Every problem has a solution.
Refactoring: Improving the Design of Existing Code Martin Fowler
@afilina afilina.com