Usability кода

Первый раз когда я заговорил о юзабилити кода на меня посмотрели косо, когда же я начал объяснять, то встретил понимание у коллег по цеху. Теперь попробую и вам донести до чего я тут дошел с жизнью такой.

«Usability кода» звучит хорошо, название притягивает, еще мне нравится «эргономика кода», тоже отлично

Ну что же, в понятие юзабилити кода я вкладываю несколько вещей:

  • Соблюдение стандартов кодирования
  • Отсутствие избыточности в коде и комментариях
  • Предсказуемость кода
  • Поддержка читаемости кода на уровне «понятно и чайнику»
  • Время необходимое для изучение кода до уровня «понимаю где баг глядя на скриншот»
  • Скорость разработки при повторном использовании кода и соблюдении принятых норм и стандартов

Думаю, стоит по чуть-чуть остановиться на каждом.

Стандарты кодирования

Тут обсуждать нечего, ты либо их соблюдаешь (хотя бы свои, но лучше принятые в своей среде), либо ты тут временно, и никому не будешь нужен.

Избыточность кода

Иногда, гоняясь за красотой ООП мы приходим к очень громоздким решениям:

$file = Application::getInstance()->getRequest()->getFiles()->get($filename);

По сути — думаю каждому понятно что делает данная строчка кода, но почему же не заменить ее на простую функцию? Чего боитесь?

$file = getRequestFile($filename);
// процедурное программирование отстой, бла-бла-бла
$file = Request::getFile($filename);
// выучи еще патерны
// ...

Баланс на грани

Бытует мнение, что комментировать хорошо читаемый код не стоит, и так всем всё понятно, но мне в это слабо верится, так что я тут несколько тезисов написал:

  • Отсутствие комментариев в коде — плохо
  • Правильное именование переменных в коде — хорошо
  • Много комментариев с прогнозом погоды — плохо

Что же хорошо? Вот тут вроде бы баланс удалось соблюсти:

/**
 * Check-in user location to DB
 *
 * <code>Location::checkinUser($User, 2.123654, 0.456321)</code>
 *
 * @param User $User
 * @param float $lat latitude
 * @param float $long longitude
 * @return bool
 */
function checkinUser(User $User, $lat, $long) {
    return Db::insert("checkin", array(
        'userId' => $User->id,
        'lat' => $lat,
        'long' => $long
    ));
}

В данном примере постарался уместить все, что нужно: однозначные имена методов и переменных, минимум комментариев, которых хватает для создания документации по проекту, да подсказок в IDE (что немаловажно!).

Быть провидцем

Если вам приходится работать с незнакомым кодом и мысли «я бы на месте предыдущего разработчика сделал это так-то» совпадают с реальность — поздравляю, у вас совместимость с предыдущим автором, и вы сможете легко работать с текущим кодом. Так что пишите код «предсказуемо», и это не только соблюдение правил использования глаголов в именах методов, но и простая, читаемая структуру кода.

Читаемость кода

Чего хочу я от читаемого кода? Это чтобы любой разработчик «со словариком» мог прочитать любой участок вашего кода. Я могу не знать JAVA, Python, или еще с десяток языков, но у меня есть знание о PHP и JavaScript, я и должен взяв на вооружение что-то типа «RoR для чайников» в качестве словарика и начать работать в проекте спустя день (ну уж лучше «Rails for PHP Developers», но это придирки).

Не должно быть магии по шпаргалкам — «достать объект не знаю где, вызвать метод не знаю как»:

// привет ZF
public function errorAction()
{
    // магия тут
    $errors = $this->_getParam('error_handler');
 
    // а тут правдоподобно
    $this->getResponse()->setHttpResponseCode(404);

    // ни шатко, ни валко, но все в курсе
    $this->view->message = 'Page not found';
}

В идеале, читаемость кода должна быть на уровне псевдокода.

Еще есть такая болезнь — когда из шаблонов проектирования знаешь лишь singleton, конечно же это плохо, но вот юзабельность кода, если оный применять с умом, очень даже ничего:

// тут без комментариев понятно что происходит
$id = Application::getRequest()->getParam('id');
// и тут
$User = UserManager::getById($id);

Где баг?

MVC очень правильный патерн для web приложения, он удобен и всё такое прочее — имхо, вполне юзабельный. Но вот беда, в большом приложении контроллеров много, и по этой причине появилась тенденция объединять их в модули (что вполне логично), а сами контроллеры бить на экшены (что возможно избыточно). Таким образом контроллер стал составным, и знаком нам по многим фреймворкам — модуль/контроллер/экшен. Всё хорошо, и URL к такому контроллеру выглядит адекватно и читается легко, но пришли требования, и нам приходится писать правила-роуты, и теперь URL запрошенной страницы стал совсем неоднозначным для нас. Следовательно надо просмотреть все эти роуты, которые, возможно, расфасованы по модулям, иль еще куда, да елки ж палки…

К чему я тут это всё рассказываю? Да к тому, что чем больше действий мне надо выполнить для локализации ошибки в приложении, тем меньше мне такой код нравится. Вижу баг, знаю до файла и даже строчки где искать — отличный результат.

Время — деньги

Время потраченное непосредственно на сам кодинг оценивают в 30%, но это при проектировании «до запятой», но как-то усидчивости у нас не хватает, не наш метод. По этой причине стоит писать удобный код:

// можем же, мляяяять
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('db');
$dbAdapter = $bootstrap->getResource('db');
$dbAdapter->getConnection()->exec($dataSql);

// но как-то сподручнее
$application->getDb()->exec($dataSql);

Если расширение функционала приводит к копи-пасту — WTF man?

Вместо выводов

Если бы разработчики фреймворков думали не о красоте ООП, а о эргономике кода, то таких популярных и удобных фреймворков как jQuery было бы больше :)

P.S. В обязательном порядке, продумать юзабилити кода необходимо всем, кто собирается разрабатывать иль модифицировать очередной фреймворк, ведь удобный инструмент всегда найдет своего мастера.

P.P.S. По теме нашел лишь одну статью — Introducing Code Usability, возможно кто подскажет еще?

P.P.P.S. Поругал ZF, похвалил jQuery — OK

20 thoughts on “Usability кода”

  1. спасибо за статью.
    function checkinUser(User $User, $lat, $long)
    почему не соблюдается code style? или это такой code style?

  2. По этому поводу целая библия написана – Code Complete МакКоннелла. У него белой нитью идёт идея об управлении сложностью.

      1. Хм, я в бумажном виде читал – “Совершенный код” в русском варианте.

  3. Если бы разработчики фреймворков думали не о красоте ООП, а о эргономике кода

    А примеры по эргономике ZF в вашей практике можно увидеть?)

    P.S. Незнаю по чему, но MooTools мне больше нравится, как раз из-за более ООП стиля в программировании.

  4. Кстати, в ZF 2.0 вообще очень жестокое раздереление на модули, если в ZF 1.* общие модели еще можно выделить в application/models, то в ZF 2.0 все на namespace, разделение на модули и да же js и css призывают хранить в папке с конкретным модулем и собирать воедино сборщиком кода.

    Хотя остается вариант выделять общее в library, но ни счетаю что это очень красиво.

  5. Присмотрись к Yii – они какраз не стали мудрить ООП ради ООП, а использовали его столько, сколько надо. В итоге всё довольно просто, логично и достаточно не многословно :)

  6. было бы отлично прочитать как вы видите фреймворк который максимально возможно подходит вам для работы. не сферический в вакууме, а именно такой который удобно пользовать постоянно
    какие требования к структуре, чего там обязательно должно быть, а чего нет и т. п.

  7. Рекомендую по теме книгу издательства O’Reilly авторов Dustin Boswell и Trevor Foucher “The Art of Readable Code”. Можно найти в сети в pdf

  8. Є досить хороша книжка – “Clean Code”, рекомендую почитати.

  9. Не сочти за холивар. Но добиться читаемости кода до того уровня чтобы его не нужно было комментировать php не позволит, а вообще такое вполне возможно для примера

    $this->view->user = $userModel->getAll()

    ruby
    @users = User.all

    Db::insert("checkin", array(
            'userId' => $User->id,
            'lat' => $lat,
            'long' => $long
        ));

    ruby
    Checkin.new(params[:checkin])

    if (Zend_Auth::getInstance()->hasIdentity()) {
    }
    

    ruby
    user_signed_in?

    class User {
     function getName() ...
     function setName($name) ...
     function getSurname() ...
     function setSurname($surname) ...
    }

    ruby
    class User
    attr_accessor :name, :surname
    end

    $router->addRoute(
        'user',
        new Zend_Controller_Router_Route('user/:username',
                                         array('controller' => 'user',
                                               'action' => 'info'))
    );

    ruby
    match ‘user/:username’ => “user#info”

    1. Не сочту, но и привычные конструкции для RoR тоже неахти читаются разработчиками других языков ;)
      Хотя местами Ruby бесспорно хорош.

    2. ruby & ROR – это для гомиков! и что это за хрень?:
      ruby
      user_signed_in?
      – в php:
      if ($user_signed_in){}

      1. это хелпер devise пакета, который генерит код контроллеров, вьюшки, хелперы и роуты для регистрации, авторизации, конфирмации на мыло, восстановление пароля, запомнить меня 2умя коммандами с консоли :)

  10. Pingback: PHP Framework Bluz

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.