SlideShare a Scribd company logo
Tackling
Tech Debt
with Rector
Michele Orselli
Eng Manager@Spreaker
_orso_
micheleorselli
michele.orselli@spreaker.com
● Spreaker is a podcast platform
● Within Spreaker, podcasts can be
hosted, distributed, monetized,
discovered, and listened to
● Part of iHeartMedia family since
2020
🤓 Expert teams can tame it, choosing willingly
to accumulate it
→ trade off cleaner solution with a good
enough one
Tech Debt
🐥 Green teams accumulate it unconsciously
→ let's try this and that
Never saw a project without it 🙄
The mere fact that
we are writing code
exposes us to tech debt
Tech Debt
-> Language Evolves
-> Libraries evolve
-> Frameworks evolve
-> Things are (re)discovered
How can we deal with it?
Invest 🕒 and ⚡
to repay the debt
😫 Sometimes it is hard
to justify the investment
🤔 Is there a way to get away
with it?
Tech Debt
Performs automated refactorings on a codebase
It provides a list of predefined rectors, aka rules, refactorings you can choose
to apply on your codebase, via a rector.php config file. E.g.
- Change a property access with a method call
from $obj->property to $obj->method()
- Substitute class invocation with another one
from Banana::method() to Apple::anotherMethod()
Rector
Provided a ton of rules out of the box
- Code style/quality
- Manipulate properties/methods
- Upgrade language version
- Upgrade framework version
Rector
https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md
Why do I need it?
-> Regexp are not enough?
-> Search and replace is not enough?
Rectors parses the code, so it can perform complex manipulation. For this
reason it is also unaffected by code formatting (spaces, newlines, …)
Rector
Installation/configuration
$ composer require rector/rector --dev
If you create custom rules, configure composer.json to enable autoload
"autoload-dev": {
"psr-4": {
"UtilsRector": "tools/php/rector/src",
"UtilsRectorTests": "tools/php/rector/tests"
}
}
Installation/configuration
Create an empty rector.php file in the project root directory
$ vendor/bin/rector init
Change rector.php to specify which directories should be scanned and which
rules should be applied
rector.php
<?php
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/apps',
__DIR__ . '/lib',
__DIR__ . '/test',
]);
$rectorConfig->rule(AddContainerAwareIfNotPresent::class);
$rectorConfig->rule(MigrateSpUtilStaticCallsRector::class);
};
Run
In the project root directory run
$ vendor/bin/rector process
If you want to see a preview of the changes without touching the files
$ vendor/bin/rector process --dry-run
Rules
RectorPhp74RectorPropertyTypedPropertyRector
TypedPropertyRector
RectorPhp80RectorClass_StringableForToStringRector
StringableForToStringRector
RectorPhp82RectorFuncCallUtf8DecodeEncodeToMbConvertEncodingRector
Utf8DecodeEncodeToMbConvertEncodingRector
Configurable Rules
Rule accepting configuration params
RectorTransformRectorClass_AddAllowDynamicPropertiesAttributeRector
AddAllowDynamicPropertiesAttributeRector
SetLists
Define a group of rules to avoid specify them one by one
rector.php
<?php
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/apps',
__DIR__ . '/lib',
__DIR__ . '/test',
]);
$rectorConfig->sets([SetList::DEAD_CODE, SetList::PHP_82]);
};
LevelSetLists
Applies all the rules up to a certain PHP version
rector.php
<?php
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/apps',
__DIR__ . '/lib',
__DIR__ . '/test',
]);
$rectorConfig->sets([LevelSetList::UP_TO_PHP_82]);
};
1. Loads configuration from rector.php
2. Iterates all files
3. For each file
a. Preparation phase: parse file, creates AST (array of nodes), add some
metadata
b. Rectify Phase
i. Traverse AST
ii. Check if rectors can be applied to that node
iii. If yse, apply
c. Save/Diff phase
How it works
A rule is a PHP class that extends AbstractRector
Implement 3 method
Writing Custom Rules
public function getRuleDefinition(): RuleDefinition
public function getNodeTypes(): array
public function refactor(Node $node): ?Node
Life of a PHP file 🐘
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
c
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
c
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
<?php
namespace AppTime;
c
class Clock
{
public function now()
{
return date('Y-m-d');
}
}
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Tokenizer
token_get_all
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Parser
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Parser
T_OPEN_TAG ('<?php')
T_NAMESPACE ('namespace')
T_WHITESPACE (' ')
T_STRING ('App')
T_NS_SEPARATOR ('')
T_STRING ('Time')
T_WHITESPACE (' ')
T_CLASS ('class')
T_WHITESPACE (' ')
T_STRING ('Clock')
T_WHITESPACE (' ')
T_WHITESPACE (' ')
T_PUBLIC ('public')
T_WHITESPACE (' ')
T_FUNCTION ('function')
T_WHITESPACE (' ')
T_STRING ('now')
T_WHITESPACE (' ')
T_WHITESPACE ('')
T_RETURN ('return')
T_WHITESPACE (' ')
T_STRING ('date')
T_CONSTANT_ENCAPSED_STRING
(''Y-m-d'')
T_WHITESPACE ('')
T_WHITESPACE ('')
T_WHITESPACE ('')
Parser
AST:
Abstract
Syntax
Tree
PHP-Parser
PHP Vm
AST:
Abstract
Syntax
Tree
Compiler
PHP Vm
AST:
Abstract
Syntax
Tree
Compiler
PHP Vm
Opcodes
AST:
Abstract
Syntax
Tree
Compiler
PHP Vm
Opcodes
AST:
Abstract
Syntax
Tree
AST - Abstract Syntax Tree
Intermediate representation of the source code
Machine friendly
Tree structure
Traversing the AST means
traversing the original source code
https://phpast.com/
https://github.com/rectorphp/php-parser-nodes-docs
PHP-Parser nodes docs
A rule is a PHP class that extends AbstractRector
Implement 3 method
Writing Custom Rules
public function getRuleDefinition(): RuleDefinition
public function getNodeTypes(): array
public function refactor(Node $node): ?Node
Look for existing rules that perform similar tasks. Use it as a starting point.
Look for helper classes: classes that perform some heavy lifting for you. E.g
pull up a property
There are plenty:
- ClassDependencyManipulator
- ClassInsertManipulator
- NodeFactory
- ParamAnalyzer
Create a small snippet of code to test it with --dry-run
Start with an existing rule
Let's 👀 at the code…
Look for a rule that does something similar to what you need and use it as a
starting point
This part is a bit time consuming, there are multiple ways to achieve the same
result
Don't try to do too much in a single rule, combine multiple rules
Writing Custom Rules: lesson learned
Get all the corner cases is hard, Pareto rulez!
NodeDumper is your friend
Create a Dummy snippet for quick testing
Writing Custom Rules: lesson learned
Extensions: composer installable set of rules for specific framework/libraries
These are provided out of the box
https://github.com/rectorphp/rector-symfony
https://github.com/rectorphp/rector-doctrine
https://github.com/rectorphp/rector-phpunit
Extensions
That's All Folks!
https://leanpub.com/rector-the-power-of-automated-refactoring
Questions?

More Related Content

PDF
Comunicare, condividere e mantenere decisioni architetturali nei team di svil...
PPTX
OOP Is More Than Cars and Dogs
KEY
Workshop quality assurance for php projects tek12
ODP
Best practices tekx
PDF
Quality Assurance for PHP projects - ZendCon 2012
PPTX
Speed up your developments with Symfony2
ODP
Practical catalyst
PPTX
CiklumJavaSat_15112011:Alex Kruk VMForce
Comunicare, condividere e mantenere decisioni architetturali nei team di svil...
OOP Is More Than Cars and Dogs
Workshop quality assurance for php projects tek12
Best practices tekx
Quality Assurance for PHP projects - ZendCon 2012
Speed up your developments with Symfony2
Practical catalyst
CiklumJavaSat_15112011:Alex Kruk VMForce

Similar to Tackling Tech Debt with Rector (20)

PDF
Workshop quality assurance for php projects - ZendCon 2013
ODP
Bring the fun back to java
PDF
Living With Legacy Code
PDF
Dependency Injection
PPTX
Spstc2011 managed metadata real world
PPTX
Spstc2011 managed metadata real world
PDF
Workshop quality assurance for php projects - phpbelfast
PDF
TYPO3 Extension development using new Extbase framework
PPTX
How to perform debounce in react
PDF
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
PPTX
Symfony 1, mi viejo amigo
PDF
PDF
What's New In Laravel 5
PDF
[xp2013] Narrow Down What to Test
PPTX
Introducing PHP Latest Updates
PPTX
Event Sourcing with php
PDF
What's new in Doctrine
PDF
Reactive programming with RxJS - Taiwan
PDF
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
PPT
course slides -- powerpoint
Workshop quality assurance for php projects - ZendCon 2013
Bring the fun back to java
Living With Legacy Code
Dependency Injection
Spstc2011 managed metadata real world
Spstc2011 managed metadata real world
Workshop quality assurance for php projects - phpbelfast
TYPO3 Extension development using new Extbase framework
How to perform debounce in react
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Symfony 1, mi viejo amigo
What's New In Laravel 5
[xp2013] Narrow Down What to Test
Introducing PHP Latest Updates
Event Sourcing with php
What's new in Doctrine
Reactive programming with RxJS - Taiwan
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
course slides -- powerpoint
Ad

More from Michele Orselli (20)

PDF
A dive into Symfony 4
PDF
A recommendation engine for your applications codemotion ams
PDF
A recommendation engine for your applications phpday
PDF
Hopping in clouds - phpuk 17
PDF
A recommendation engine for your php application
PDF
Symfony e micro (non così tanto) services
PDF
Hopping in clouds: a tale of migration from one cloud provider to another
PDF
Vagrant for real (codemotion rome 2016)
PDF
Vagrant for real codemotion (moar tips! ;-))
PDF
Migrare a Symfony 3
PDF
Vagrant for real
PDF
Implementing data sync apis for mibile apps @cloudconf
PDF
Server side data sync for mobile apps with silex
PDF
Continuous, continuous, continuous
PDF
Deploy a PHP App on Google App Engine
PDF
Implementing Server Side Data Synchronization for Mobile Apps
PDF
Deploy a php app on Google App Engine
PDF
PDF
Manage a project portfolio
PDF
Developing sustainable php projects
A dive into Symfony 4
A recommendation engine for your applications codemotion ams
A recommendation engine for your applications phpday
Hopping in clouds - phpuk 17
A recommendation engine for your php application
Symfony e micro (non così tanto) services
Hopping in clouds: a tale of migration from one cloud provider to another
Vagrant for real (codemotion rome 2016)
Vagrant for real codemotion (moar tips! ;-))
Migrare a Symfony 3
Vagrant for real
Implementing data sync apis for mibile apps @cloudconf
Server side data sync for mobile apps with silex
Continuous, continuous, continuous
Deploy a PHP App on Google App Engine
Implementing Server Side Data Synchronization for Mobile Apps
Deploy a php app on Google App Engine
Manage a project portfolio
Developing sustainable php projects
Ad

Recently uploaded (20)

PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
ISO 45001 Occupational Health and Safety Management System
PPTX
Odoo POS Development Services by CandidRoot Solutions
PPT
Introduction Database Management System for Course Database
PPTX
Online Work Permit System for Fast Permit Processing
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
AI in Product Development-omnex systems
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
Transform Your Business with a Software ERP System
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
System and Network Administration Chapter 2
Operating system designcfffgfgggggggvggggggggg
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Softaken Excel to vCard Converter Software.pdf
ISO 45001 Occupational Health and Safety Management System
Odoo POS Development Services by CandidRoot Solutions
Introduction Database Management System for Course Database
Online Work Permit System for Fast Permit Processing
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Design an Analysis of Algorithms II-SECS-1021-03
Design an Analysis of Algorithms I-SECS-1021-03
ManageIQ - Sprint 268 Review - Slide Deck
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
AI in Product Development-omnex systems
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Upgrade and Innovation Strategies for SAP ERP Customers
Wondershare Filmora 15 Crack With Activation Key [2025
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Transform Your Business with a Software ERP System
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
System and Network Administration Chapter 2

Tackling Tech Debt with Rector

  • 3. ● Spreaker is a podcast platform ● Within Spreaker, podcasts can be hosted, distributed, monetized, discovered, and listened to ● Part of iHeartMedia family since 2020
  • 4. 🤓 Expert teams can tame it, choosing willingly to accumulate it → trade off cleaner solution with a good enough one Tech Debt 🐥 Green teams accumulate it unconsciously → let's try this and that Never saw a project without it 🙄
  • 5. The mere fact that we are writing code exposes us to tech debt Tech Debt -> Language Evolves -> Libraries evolve -> Frameworks evolve -> Things are (re)discovered
  • 6. How can we deal with it? Invest 🕒 and ⚡ to repay the debt 😫 Sometimes it is hard to justify the investment 🤔 Is there a way to get away with it? Tech Debt
  • 7. Performs automated refactorings on a codebase It provides a list of predefined rectors, aka rules, refactorings you can choose to apply on your codebase, via a rector.php config file. E.g. - Change a property access with a method call from $obj->property to $obj->method() - Substitute class invocation with another one from Banana::method() to Apple::anotherMethod() Rector
  • 8. Provided a ton of rules out of the box - Code style/quality - Manipulate properties/methods - Upgrade language version - Upgrade framework version Rector https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md
  • 9. Why do I need it? -> Regexp are not enough? -> Search and replace is not enough? Rectors parses the code, so it can perform complex manipulation. For this reason it is also unaffected by code formatting (spaces, newlines, …) Rector
  • 10. Installation/configuration $ composer require rector/rector --dev If you create custom rules, configure composer.json to enable autoload "autoload-dev": { "psr-4": { "UtilsRector": "tools/php/rector/src", "UtilsRectorTests": "tools/php/rector/tests" } }
  • 11. Installation/configuration Create an empty rector.php file in the project root directory $ vendor/bin/rector init Change rector.php to specify which directories should be scanned and which rules should be applied
  • 12. rector.php <?php return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/apps', __DIR__ . '/lib', __DIR__ . '/test', ]); $rectorConfig->rule(AddContainerAwareIfNotPresent::class); $rectorConfig->rule(MigrateSpUtilStaticCallsRector::class); };
  • 13. Run In the project root directory run $ vendor/bin/rector process If you want to see a preview of the changes without touching the files $ vendor/bin/rector process --dry-run
  • 14. Rules
  • 18. Configurable Rules Rule accepting configuration params
  • 20. SetLists Define a group of rules to avoid specify them one by one
  • 21. rector.php <?php return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/apps', __DIR__ . '/lib', __DIR__ . '/test', ]); $rectorConfig->sets([SetList::DEAD_CODE, SetList::PHP_82]); };
  • 22. LevelSetLists Applies all the rules up to a certain PHP version
  • 23. rector.php <?php return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([ __DIR__ . '/apps', __DIR__ . '/lib', __DIR__ . '/test', ]); $rectorConfig->sets([LevelSetList::UP_TO_PHP_82]); };
  • 24. 1. Loads configuration from rector.php 2. Iterates all files 3. For each file a. Preparation phase: parse file, creates AST (array of nodes), add some metadata b. Rectify Phase i. Traverse AST ii. Check if rectors can be applied to that node iii. If yse, apply c. Save/Diff phase How it works
  • 25. A rule is a PHP class that extends AbstractRector Implement 3 method Writing Custom Rules public function getRuleDefinition(): RuleDefinition public function getNodeTypes(): array public function refactor(Node $node): ?Node
  • 26. Life of a PHP file 🐘
  • 27. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } }
  • 28. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 29. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 30. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 31. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 32. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 33. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 34. <?php namespace AppTime; class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 35. <?php namespace AppTime; c class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 36. <?php namespace AppTime; c class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer
  • 37. <?php namespace AppTime; c class Clock { public function now() { return date('Y-m-d'); } } T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Tokenizer token_get_all
  • 38. T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Parser
  • 39. T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Parser
  • 40. T_OPEN_TAG ('<?php') T_NAMESPACE ('namespace') T_WHITESPACE (' ') T_STRING ('App') T_NS_SEPARATOR ('') T_STRING ('Time') T_WHITESPACE (' ') T_CLASS ('class') T_WHITESPACE (' ') T_STRING ('Clock') T_WHITESPACE (' ') T_WHITESPACE (' ') T_PUBLIC ('public') T_WHITESPACE (' ') T_FUNCTION ('function') T_WHITESPACE (' ') T_STRING ('now') T_WHITESPACE (' ') T_WHITESPACE ('') T_RETURN ('return') T_WHITESPACE (' ') T_STRING ('date') T_CONSTANT_ENCAPSED_STRING (''Y-m-d'') T_WHITESPACE ('') T_WHITESPACE ('') T_WHITESPACE ('') Parser AST: Abstract Syntax Tree PHP-Parser
  • 45. AST - Abstract Syntax Tree Intermediate representation of the source code Machine friendly Tree structure Traversing the AST means traversing the original source code
  • 48. A rule is a PHP class that extends AbstractRector Implement 3 method Writing Custom Rules public function getRuleDefinition(): RuleDefinition public function getNodeTypes(): array public function refactor(Node $node): ?Node
  • 49. Look for existing rules that perform similar tasks. Use it as a starting point. Look for helper classes: classes that perform some heavy lifting for you. E.g pull up a property There are plenty: - ClassDependencyManipulator - ClassInsertManipulator - NodeFactory - ParamAnalyzer Create a small snippet of code to test it with --dry-run Start with an existing rule
  • 50. Let's 👀 at the code…
  • 51. Look for a rule that does something similar to what you need and use it as a starting point This part is a bit time consuming, there are multiple ways to achieve the same result Don't try to do too much in a single rule, combine multiple rules Writing Custom Rules: lesson learned
  • 52. Get all the corner cases is hard, Pareto rulez! NodeDumper is your friend Create a Dummy snippet for quick testing Writing Custom Rules: lesson learned
  • 53. Extensions: composer installable set of rules for specific framework/libraries These are provided out of the box https://github.com/rectorphp/rector-symfony https://github.com/rectorphp/rector-doctrine https://github.com/rectorphp/rector-phpunit Extensions