Enter: legacy code
Transcript of Enter: legacy code
Боль №3: Legacy Code
The only valid measurement of code quality: WTF/minute
Почему не проще?
• Код, в принципе, решает свои задачи
• На него потрачено немало человеко-часов
• Ресурсы ограничены и много текущих задач
• Есть другой подход - evolution
Как будем улучшать?• Будем использовать composer
• Придерживаться стандартов PSR
• Поэтапно рефакторить
• Отлаживать только с Xdebug
• Профилировать код на бою
Наследство• проект живет с 17 июля 2011
• построен на Zend 1 (Zend 2 уже больше двух лет*)
• My_Super_Awesome_Class (пространству имен уже два года*)
• отсутствие единого стандарта (до PSR еще долгих два года*)
• отсутствие цельной архитектуры (толстые контроллеры; мешанина из php и sql; массивы адской структуры)
• сторонние библиотеки в library (composer только на подходе*)
• код опирается на PHP 5.2 (PHP 5.3 уже два года*)
* на момент старта разработки
Используйте Composer
• Проще управлять зависимостями
• Проще обновлять сторонние библиотеки
• Очень гибкий инструмент:
• version: ~x.y, > x.z; x.y.z; x.*
• private: Satis или Toran Proxy
• non-composer: директива repositories
• Автозагрузка PSR-0, PSR-4 и не только
• Разработчики не лезут в исходный код сторонних библиотек!
Какие преимущества?
однажды добавил zend
однажды его обновил
и криво удалил
Как было: жизненный цикл Zend в проекте
Как сейчас{ "name": "enter/core", "require": { "php": ">=5.5", …, "misterion/pinba-phpdoc": "1.0.*@dev", "zendframework/zendframework1": "~1.12" }, "require-dev": { "phpunit/phpunit": "~4", "squizlabs/php_codesniffer": "~1.0" }, "autoload": { "classmap": ["library/"], "psr-0": { … }, "psr-4": { "Enter\\": "src/Enter" } }, "config": { "bin-dir": "bin" } }
смело используем новые возможности языка -
Enter: code style -
а новый положим рядом -старый код нужно поддерживать -
остались правки в исходном коде сторонних библиотек -
Составьте roadmap по рефакторингу
• Придерживайтесь стандарту PSR-4 для улучшения структуры проекта
• Пишите unit-тесты
• Покрывайте критичный функционал приемочными тестами
• Придерживайтесь принципов SOLID, DRY, KISS и YAGNI
• Анализируйте логи
Анализ логов для рефакторинга
• Настроили rsyslog и logrotate для сбора логов со всех боевых машин
• cat %project%-pool-ro.log | grep -Eo "PHP.*[0-9]" | sort | uniq -c
• Количество вхождений - вес; в каждый следующий релиз обязательно включаем парочку ошибок с наибольшим весом
52 PHP Fatal error: Call to a member function getId() on a non-object in %file% on line 1233
1871 PHP Fatal error: Call to a member function setPrice() on a non-object in %file% on line 125
217392 PHP Warning: array_key_exists() expects parameter 2 to be array, null given in %file% on line 826
29676 PHP Strict Standards: Only variables should be passed by reference in %file% on line 1269
217392 PHP Warning: array_key_exists() expects parameter 2 to be array, null given in %file% on line 826
147344 PHP Warning: Invalid argument supplied for foreach() in %file% on line 2522
и еще сотни строк
* количество записей в день
Что получили в начале
7 PHP Warning: array_key_exists() expects parameter 2 to be array, null given in %file% on line 333
75 PHP Warning: array_key_exists() expects parameter 2 to be array, null given in %file% on line 336
60 PHP Warning: Creating default object from empty value in %file% on line 60
1 PHP Warning: Invalid argument supplied for foreach() in %file% on line 215
86 PHP Warning: Invalid argument supplied for foreach() in %file% on line 2198
64 PHP Warning: Invalid argument supplied for foreach() in %file% on line 407
75 PHP Warning: Invalid argument supplied for foreach() in %file% on line 55
Что имеем сейчас
Что дальшеСобираем slowlog с помощью fabric
@task(default=True)def alalyze_slow_logs(): execute(get_slow_log_ro) execute(agregate_slow_log_ro) execute(alalyze_slow_log_ro)
@task@parallel@roles('dbro')def get_slow_log_ro(): get( env.log_remote_path + 'slow.log', env.log_local_path + 'slow_ro.%s.log' % (env.host) )
@taskdef agregate_slow_log_ro(): local('cat %s/slow_ro.*.log > %s/slow_ro.log' % ( env.log_local_path, env.log_local_path ))
@taskdef alalyze_slow_log_ro(): local('mysqldumpslow %s/slow_ro.log > %s/total_slow_ro.log' % ( env.log_local_path, env.log_local_path ))
- просто `$ fab` для старта
- параллельно собираем slow.log со всех тачек
- собираем в один файл для обработки
- прогоняем его через mysqldumpslow
Улучшайте процесс отладки кода
Logger::getLogger(…)->debug(‘Загружаем информацию о дереве категорий');
зачем это повсюду в коде?
Почему сразу не использовать Xdebug?!
• Сложно настроить удаленную отладку
• ssh -R 9191:127.0.0.1:9000 sandbox
• Сложно настроить отладку через шлюз
• ProxyCommand или ssh -L 9191:sandbox:9191 -R 9191:127.0.0.1:9000 gateway
• var_dump(…); die(); быстрее
• на самом деле это наиболее затратный по времени способ отладки
Xdebug - это просто!
ssh -R 9191:127.0.0.1:9000 sandbox
zend_extension = xdebug.so xdebug.remote_enable = 1 xdebug.remote_autostart = 1 xdebug.remote_host = 127.0.0.1 xdebug.remote_port = 9191 xdebug.idekey = PHPSTORM
1
2
3
4
Пользуйтесь профилировщиком для поиска узких мест
• В качестве профилировщика взяли Pinba
• В качестве «клиента» взяли Intaro Pinboard
• Для включения профилировщика используем подход Progressive Enhancement (подробно тут http://kamil.samigullin.info/github/GracefulDegradation)
Полезные ссылки• https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
• https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
• https://en.wikipedia.org/wiki/KISS_principle
• https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it
• http://kamil.samigullin.info/github/GracefulDegradation
• http://pinba.org
• http://intaro.github.io/pinboard/
• http://xdebug.org
эволюционируй
Спасибо за внимание!
Есть вопросы?
Камиль Самигуллин какой-то разработчик
[email protected] @ikamilsk github.com/kamilsk
Присматриваюсь к Blackfire, подробности в следующих докладах
• https://blackfire.io/