escaping dependency hell
TRANSCRIPT
![Page 1: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/1.jpg)
ESCAPINGDEPENDENCY HELL
Michael Haeuslmann - IPC Munich 2016Source: Escape from Hell Constantine by Rommeu
![Page 2: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/2.jpg)
AGENDA1. Discuss
What is dependency hell?Why should we care?Are all dependencies created equal?
2. Analyse ( )DependenciesArchitecture
3. FixUntestable codeDependency (mis-)management
![Page 3: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/3.jpg)
MICHAEL HAEUSLMANNINDEPENDENT FREELANCER (PHPRAGMATIC.COM)
married, love to travel, board games, arch-nerd, ...developing in PHP for ~8 yearsmostly legacy applications or open sourceprofessional work in PHPprivate projects in Java, JavaScript, Swi, ...
![Page 4: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/4.jpg)
PART I: DISCUSS
![Page 5: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/5.jpg)
WHAT IS DEPENDENCY HELL?
![Page 6: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/6.jpg)
WHY SHOULD WE CARE?
» If you can’t understand it, you can’t change it. «
- Eric Evans
![Page 7: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/7.jpg)
WHY SHOULD WE CARE?COMPLEX VS. COMPLICATED
Big soware systems are complex (many moving parts)
Not all parts are complicated
BUT mismanaging complexity leads to morecomplicated problems
![Page 8: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/8.jpg)
WHAT ARE DEPENDENCIES?Cross-Team dependencies Team A requires results from Team B which requires results from Team C ... 3rd-party dependencies Composer packages etc. Soware dependencies Internal or external dependencies on class or package level
![Page 9: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/9.jpg)
WHY DEPENDENCY MANAGEMENT TOOLSMATTER?
Difficulty of any given task depends on its dependencies We don't want to do everything ourselves Managing dependencies in the right way helps with:
Understanding Maintenance
![Page 10: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/10.jpg)
PART II: ANALYSE
![Page 11: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/11.jpg)
WHAT SHOULD DEPENDENCY ANALYSIS TELLUS?
Where should we start refactoring? Why does [SomeClass] always break? What does our architecture actually look like? Is our architecture the way it should be?
![Page 12: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/12.jpg)
TOOLSPHP DEPEND BY MANUEL PICHLER
many metrics many metrics hard to maintain too vague
![Page 13: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/13.jpg)
TOOLSPHP DEPEND BY MANUEL PICHLER
![Page 14: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/14.jpg)
TOOLS
made with ♥ for OSS @ University of Rosenheim
generates UML and other dependency visualizations
detects even the sneakiest dependencies
hackable (grep, sed, awk, ...)
for the nerds: written using functional style
a lot more to come
![Page 15: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/15.jpg)
FEATURES Text For quick feedback, debugging, UNIX pipes etc. UML & DSM Detailed dependency & architectural analysis (Metrics)
![Page 16: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/16.jpg)
DEPHPEND - TEXT OUTPUT$> php dephpend.phar help text _ _____ _ _ _____ _ | | | __ \| | | | __ \ | | __| | ___| |__) | |__| | |__) |__ _ __ __| | / _` |/ _ \ ___/| __ | ___/ _ \ '_ \ / _` | | (_| | __/ | | | | | | | __/ | | | (_| | \__,_|\___|_| |_| |_|_| \___|_| |_|\__,_| version 0.1
Usage: text [options] [] ()...
$> php dephpend.phar text ~/workspace/dephpend/src
Mihaeu\PhpDependencies\Util\AbstractMap > Mihaeu\PhpDependencies\Util\CollectionMihaeu\PhpDependencies\Util\DI > Mihaeu\PhpDependencies\Analyser\Analyser...
(*) make sure XDebug is not enabled or use php -n
![Page 17: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/17.jpg)
DEPHPEND - TEXT OUTPUT$> php dephpend.phar text ~/workspace/dephpend/src noclasses | sort
Mihaeu\PhpDependencies\Analyser > Mihaeu\PhpDependencies\DependenciesMihaeu\PhpDependencies\Analyser > Mihaeu\PhpDependencies\OSMihaeu\PhpDependencies\Analyser > Mihaeu\PhpDependencies\UtilMihaeu\PhpDependencies\Analyser > PhpParserMihaeu\PhpDependencies\Analyser > PhpParser\NodeMihaeu\PhpDependencies\Analyser > PhpParser\Node\ExprMihaeu\PhpDependencies\Analyser > PhpParser\Node\NameMihaeu\PhpDependencies\Analyser > PhpParser\Node\StmtMihaeu\PhpDependencies\Analyser > PhpParser\NodeVisitor...
$> php dephpend.phar text ~/workspace/dephpend/src noclasses \ | grep e 'Analyser > .*OS'
Mihaeu\PhpDependencies\Analyser > Mihaeu\PhpDependencies\OS
![Page 18: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/18.jpg)
DEPHPEND - TEXT OUTPUTMake it yours!
#!/usr/bin/env sh
php build/dephpend.phar text ~/workspace/dephpend/src noclasses | grep \
e 'Analyser > .*OS' \
e 'OS > .*Analyser'
if [ "$?" eq 0 ]; then
echo 'Architecture violation!'
exit 1
fi
![Page 19: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/19.jpg)
DEPHPEND - TEXT OUTPUT
<?php
$output = shell_exec('php dephpend.phar text ' .'~/workspace/myMVCFramework/src noclasses');$constraints = [ 'Model.* > .*View', 'View.* > .*Model',];
if (preg_match('/('.implode(')|(', $constraints).')/x', $output)) echo 'Architecture violation'.PHP_EOL; exit(1);
![Page 20: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/20.jpg)
DEPHPEND - UML (SORT OF)dePHPend packages
$> php dephpend.phar uml ~/workspace/dephpend/src noclasses output=uml.png
![Page 21: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/21.jpg)
![Page 22: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/22.jpg)
DEPHPEND - UML (SORT OF)Symfony components
$> php d memory_limit=512M dephpend.phar uml \ ~/workspace/symfony/src/Symfony/Component \ noclasses \ depth 3 \ excluderegex='/Test/' \ output=uml.png
![Page 23: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/23.jpg)
DEPHPEND - UML (SORT OF)Symfony HTTP Kernel
![Page 24: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/24.jpg)
DEPENDENCY STRUCTURE MATRIX(DSM)
same data as graph diagrams (e.g. UML class diagram) quick overview for large apps
![Page 25: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/25.jpg)
NDEPEND EXAMPLE
![Page 26: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/26.jpg)
DEPHPEND DSM:
![Page 27: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/27.jpg)
PART III: FIX
![Page 28: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/28.jpg)
OBSCURE/NASTY DEPENDENCIES
Some dependencies cannot be detected by any tool (or developer):
Fake collections Overuse of scalar values (int, string, ...) Temporal dependencies ...
![Page 29: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/29.jpg)
WHY DO WE CARE?
We want code which is ... ... easier to understand ... easier to maintain ... easier to test
![Page 30: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/30.jpg)
BE EXPLICIT!
What is explicit/implicit?
Can your IDE provide assistance for it? (Ctrl + Le click or mouse over)Can you be sure it is what it says it is?
function sendNewsletter( array $customers, string $message ); function sendNewsletter( CustomerCollection $customers, Message $message );
![Page 31: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/31.jpg)
DON'T MAKE ME LOOK IT UP/** * @var mixed $email * @var string|Email|array $email */function addEmail($email) if (is_array($email)) // pray everything inside the array actually is an email foreach ($email as $singleEmail) addEmail($singleEmail); else if (is_string($email)) addEmail(new Email($email)); else if ($email instanceof Email) this>emails[] = email; else throw new InvalidArgumentException('Bad argument type');
![Page 32: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/32.jpg)
DON'T MAKE ME LOOK IT UPfunction addEmail(Email $email) $this>emails[] = $email;
function addEmailString(string $email) $this>addEmail(new Email($email));
function addEmailArray(array $emails) foreach ($emails as $email) /** @var Email $email */ if (is_string($email)) $this>addEmailstring($email); else if ($email instanceof Email) $this>addEmail($email); else throw new InvalidArgumentException('Bad argument type');
function addEmailCollection(EmailCollection $emails) $emails>each(function (Email $email) $this>addEmail($email); );
![Page 33: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/33.jpg)
PRINCIPLES OF OO: SOLID
Single responsibility principle Open/closed principle Liskov substitution principle Interface segregation principle Dependency inversion principle
![Page 34: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/34.jpg)
DEPENDENCY INVERSIONBAD:
class CustomerRepository public function __construct() $this>db = new MySQLDatabase(new DefaultConfig());
BETTER:class CustomerRepository public function __construct(MySQLDatabase $db) $this>db = $db;
GOOD:class CustomerRepository public function __construct(Database $db) $this>db = $db;
![Page 35: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/35.jpg)
Easier to understand and test, less likely to break
![Page 36: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/36.jpg)
DEPENDENCY INJECTIONCONTAINERS
$container = new Pimple\Container();
$container['cstmrrepo'] = function ($database) return new CustomerRepository($database);;
$cstmrRepo = $container['cstmrrepo'];
Too easy? obscure dependencies using YAMLadd another 3rd party libraryadd overhead by parsing meta format/sarcasm
![Page 37: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/37.jpg)
AVOID IMPLICIT DEPENDENCIES IN FAVOR OFEXPLICIT ONES
// why not do it yourselves?class DependencyInjectionContainer
// eager load public function getCustomerRepository() : CustomerRepository return new CustomerRepository($this>otherDeps);
// OR: lazy load public function getCustomerRepository() : CustomerRepository if (null === $this>customerRepository) $this>customerRepository = new CustomerRepository($this>otherDeps); return $this>customerRepository;
$dependencyInjectionContainer>getCustomerRepository();
![Page 38: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/38.jpg)
SERVICE LOCATORclass CustomerRepository public function __construct(ServiceLocator $serviceLocator) $this>db = $serviceLocator>getDb();
new CustomerRepository($serviceLocator);
![Page 39: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/39.jpg)
Same or similar implementation, but different usage:
CHOOSE DEPENDENCY INJECTIONCONTAINERS OVER SERVICE LOCATORS
Service Locator provides access to everythingMight as well use globals...(but not really)Target class knows more than it should= more reasons to change (=break)
Always choose REAL Dependency Injection
![Page 40: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/40.jpg)
WHERE TO GO FROM HERE?support more types of dependencies
improve visualizationcachingCI integration
php dephpend.phar testfeatures [] creating objects [] using traits ... [] known variable passed into method without type hints...
Contributions, ideas, feedback, bug reports are welcome!
![Page 41: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/41.jpg)
QUESTIONS
???LINKS
https://dephpend.comhttps://github.com/mihaeu/githubhttps://pdepend.org
![Page 42: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/42.jpg)
HOW DOES IT WORK?
STATIC ANALYSIS
Transform the code to make parsing easierInfer direct types (require, new, type hints, ...)Infer indirect types (DICs, ...)
DYNAMIC ANALYSIS
profile the applicationtrack function tracescollect all possible input values
![Page 43: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/43.jpg)
STATIC ANALYSISEasy right?
use SomeNamespace\SomeClass;
class MyClass extends MyParent implements MyInterface
/**
* @return AnotherClass
*/
public function someFunction(SomeClass $someClass) : AnotherClass
StaticClass::staticFunction();
return new AnotherClass();
![Page 44: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/44.jpg)
STATIC ANALYSISOr is it?
class MyClass
public function someMethod($dependency)
return call_user_func('globalFunction', $dependency);
![Page 45: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/45.jpg)
or:
DYNAMIC ANALYSISXDebug to the rescue!
; php.ini
zend_extension=/path/to/xdebug.so
[xdebug]xdebug.profiler_enable = 1xdebug.auto_trace=1xdebug.collect_params=1xdebug.collect_return=3xdebug.collect_assignments=1xdebug.trace_format=1xdebug.trace_options=1
# https://github.com/mihaeu/dephpend/blob/develop/bin/dephpendphptrace dynamic=/path/to/tracefile.xt S localhost:8080
![Page 46: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/46.jpg)
DYNAMIC ANALYSISTRACE START [20161019 16:59:03]1 0 0 0.000189 363984 main 1 /home/mike/workspace/dephpend/bin/dephpend 0 0
2 1 0 0.000209 363984 get_declared_classes 0 /home/mike/workspace/dephpend/bin/dephpend 80
...
11 211058 0 3.503452 4856528 strpos 0 /home/mike/workspace/dephpend/vendor/symfony/console/Formatter/OutputFormatter.php 177 2 string(15111) string(2)
...3 200813 R long 3.504303 238672TRACE END [20161019 16:59:07]
![Page 47: Escaping Dependency Hell](https://reader030.vdocuments.site/reader030/viewer/2022021507/58ed9c2d1a28ab94508b4661/html5/thumbnails/47.jpg)
DYNAMIC ANALYSIS
Parse the trace file and merge with static results
$> php dephpend.phar text src \ dynamic=/path/to/tracefile.xt \ filterfrom=YourNamespace \ excluderegex='(Test)|(Mock)'
There are no secrets at runtime!