Боль №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 и не только
• Разработчики не лезут в исходный код сторонних библиотек!
Какие преимущества?
Как сейчас
{
"name": "enter/core",
"require": {
"php": ">=5.5",
…,
"zendframework/zendframework1": "~1.12"
},
"require-dev": {
"phpunit/phpunit": "~4",
"squizlabs/php_codesniffer": "~1.0"
},
"autoload": {
"classmap": ["library/"],
"psr-0": { … },
"psr-4": { "Enter": "src/Enter" }
}
}
Составьте 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)
   )
@task
def agregate_slow_log_ro():
   local('cat %s/slow_ro.*.log > %s/slow_ro.log' % (
       env.log_local_path,
       env.log_local_path
   ))
@task
def 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 (состояние
окружения)
Полезные ссылки
• 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
эволюционируй!
Спасибо за внимание!
Есть вопросы?
Камиль Самигуллин
какой-то разработчик
kamil@samigullin.info
@ikamilsk
github.com/kamilsk
linkedin.com/in/kamilsk

Enter: legacy code

  • 2.
  • 3.
    The only validmeasurement of code quality: WTF/minute
  • 7.
    Почему не проще? •Код, в принципе, решает свои задачи • На него потрачено немало человеко-часов • Ресурсы ограничены и много текущих задач • Есть другой подход - evolution
  • 8.
    Как будем улучшать? •Будем использовать composer • Придерживаться стандартов PSR • Поэтапно рефакторить • Отлаживать только с Xdebug • Профилировать код (в том числе и в бою)
  • 9.
    Наследство • проект живетс 17 июля 2011 • построен на Zend 1 (Zend 2 уже больше двух лет*) • My_Super_Awesome_Class (пространству имен уже два года*) • отсутствие единого стандарта (до PSR еще долгих два года*) • отсутствие цельной архитектуры (толстые контроллеры; мешанина из php и sql; массивы адской структуры) • сторонние библиотеки в library (composer только на подходе*) • код опирается на PHP 5.2 (PHP 5.3 уже два года*) * на момент старта разработки
  • 10.
  • 11.
    • Проще управлятьзависимостями • Проще обновлять сторонние библиотеки • Очень гибкий инструмент: • version: ~x.y, > x.z; x.y.z; x.* • private: Satis или Toran Proxy • non-composer: директива repositories • Автозагрузка PSR-0, PSR-4 и не только • Разработчики не лезут в исходный код сторонних библиотек! Какие преимущества?
  • 12.
    Как сейчас { "name": "enter/core", "require":{ "php": ">=5.5", …, "zendframework/zendframework1": "~1.12" }, "require-dev": { "phpunit/phpunit": "~4", "squizlabs/php_codesniffer": "~1.0" }, "autoload": { "classmap": ["library/"], "psr-0": { … }, "psr-4": { "Enter": "src/Enter" } } }
  • 13.
    Составьте roadmap по рефакторингу •Придерживайтесь стандарту PSR-4 для улучшения структуры проекта • Пишите unit-тесты • Покрывайте критичный функционал приемочными тестами • Придерживайтесь принципов SOLID, DRY, KISS и YAGNI • Анализируйте логи
  • 14.
    Анализ логов для рефакторинга •Настроили rsyslog и logrotate для сбора логов со всех боевых машин • cat %project%-pool-ro.log | grep -Eo "PHP.*[0-9]" | sort | uniq -c • Количество вхождений - вес; в каждый следующий релиз обязательно включаем парочку ошибок с наибольшим весом
  • 15.
    52 PHP Fatalerror: 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 и еще сотни строк * количество записей в день Что получили в начале
  • 16.
    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 Что имеем сейчас
  • 17.
    Что дальше Собираем 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)    ) @task def agregate_slow_log_ro():    local('cat %s/slow_ro.*.log > %s/slow_ro.log' % (        env.log_local_path,        env.log_local_path    )) @task def 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
  • 18.
  • 19.
    Logger::getLogger(…)->debug(‘Загружаем информацию одереве категорий'); зачем это повсюду в коде?
  • 21.
    Почему сразу неиспользовать 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(); быстрее • на самом деле это наиболее затратный по времени способ отладки
  • 22.
    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
  • 23.
    Пользуйтесь профилировщиком для поискаузких мест • В качестве профилировщика взяли Pinba • В качестве «клиента» взяли Intaro Pinboard • Для включения профилировщика используем подход Progressive Enhancement (состояние окружения)
  • 24.
    Полезные ссылки • 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
  • 27.
  • 28.
    Спасибо за внимание! Естьвопросы? Камиль Самигуллин какой-то разработчик kamil@samigullin.info @ikamilsk github.com/kamilsk linkedin.com/in/kamilsk