30 июня 2013 в 23:48

Обратная совместимость для неудачников перевод

Вы верно прочли. Если целью вашего проекта является сохранение обратной совместимости — вы неудачник. Множество популярных проектов от PHP до Microsoft Windows заявляют об обратной совместимости между версиями. И да, я хочу сказать, что это не правильно.

Реальные перспективы


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

Изъян в рассуждениях


Проблема в поддержке обратной совместимости в том, что каждый релиз добавляет большое количество хлама. Со временем это создаст остановку в развитии проекта и станет сложнее поддерживать чистоту кода при реализации новых идей. Это создает якорь, который удерживает проект в каменном веке.
Это вопрос нарастающего технического долга. Подумайте об этом. Если вы сделаете ошибку в дизайне API в первой версии проекта, то это ошибка будет преследовать вас на протяжении МНОГИХ версий. Вы не можете просто так взят и погасить этот долг. И это не сферический конь в вакууме*. Посмотрите на функции для строк и массивов в PHP. Почему параметры для них указываются именно в таком порядке? Потому что если их изменить — это обрушит обратную совместимость!

Дальновидность


Основная проблема обратной совместимости состоит в ее идеологии — считается, что все написанное будет корректно изначально. Если все идеально изначально, то и поддержка совместимости проста. В теории. Но на практике все не так. Как и другие вещи, мы никогда не сделаем все правильно с первого раза.
Вот почему я хочу представить новую концепцию. Почему бы вместо заботы об обратной совместимости не предусмотреть «поступательную совместимость»(Forward Compatibility).

Поступательная совместимость


Основы концепции:
Стараемся предвидеть будущие потребности кода, который мы пишем сейчас, и написать его достаточно гибко, для того чтобы не беспокоиться об обратной совместимости потом.

Вот это благородная цель… Нецелесообразно?
Ну, не совсем. Нам не нужно, чтобы код работал идеально — нам нужно, чтобы он выполнял свою задачу. Лучше мы ошибемся и сделаем неправильно сначала, но нам будет проще в будущем, чем изначально вообразим, что этот код решит все необходимые задачи и удовлетворит потребности в будущем.

В действии


Я придерживаюсь этой идеологии на протяжении года. Когда я разрабатывал API для пароля, использовался именно этот подход. Вот почему там есть параметр $options, вместо $cost и $salt. Я постарался предусмотреть будущие изменения и адаптировал под это API. Хорошо ли я это сделал? На этот вопрос мы сможем ответить в будущем. Но я думаю, что получилось намного лучше, чем если бы я следовал идеологии обратной совместимости(в соответствии с которой я мог делать все что хочу и как считаю нужным).
function password_hash($password, $algo, array $options = array())


TL/DR


В следующий раз, предлагая изменения, подумайте о том, как это можно реализовать под будущие потребности, а не оглядываясь на обратную совместимость. Лучший способ предотвратить отказы от обратной совместимости — планировать их изначально. Я не говорю игнорировать обратную совместимость, но лучше сфокусироваться на будущем и позволить прошлому стать на свое место.
Будущее — то на что мы можем влиять. Ошибки уже сделаны, они в прошлом. Давайте не будем тянуть их за собой вечно…

*Оригинал: And this isn't even a theoretical problem

Этот перевод явно содержит множество недочетов, я только учу язык, буду благодарен за указание на ошибки в личке, спасибо.
А как считаете вы?

Проголосовало 409 человек. Воздержалось 114 человек.

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

Перевод: Anthony Ferrara
Никита Гусаков @hell0w0rd
карма
28,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (33)

  • +10
    Обратная совместимость дает возможность не ломать голову при каждом релизе «что же изменили разработчики», позволяя сконцентрироваться на нововведениях.

    К тому же, приведенный в качестве примера PHP имеет серьезные проблемы с обратной совмесимостью (особенно если мы возьмем 4 и 5 версии).

    А использование массивов для опций — адовый ад. Если что-то нужно обязательно сообщить для функции, она напомнит об этом в списке параметров. а в случае с опциями нужно вспоминать что за что отвечает. Если не верите мне, попробуйте поработать с php Mongodb — там опции передаются в массиве — поймете о чем я. Постоянно нужно смотреть в хелп, чтобы не забыть какую-нибудь очень важную опцию. И это бесит.
    • +6
      А на мой взгляд удобно необязательные параметры передавать в массиве. Это позволяет менять эти необязательные параметры, свободно тянуть их из конфигов, без всяческих преобразований.
      • 0
        А на мой взгляд удобно делать как это реализовано в python — там можно просто передавать по порядку, можно передавать именованные аргументы, а можно и совмещать оба способа сразу.
        • 0
          Это требует изменения языка, а пока речь только об интерфейсах библиотек и т. п.
    • +2
      Слова «опция» и «обязательно», имхо, несовместимы. Другое дело, что нет стандартизированного формата описаний массивов с опциями.

      А PHP, да, имеет с одной стороны проблемы с BC, но с другой многое тянет ещё из прошлого тысячелетия. На радикальный отказ от наследия ни разу не пошли.
      • 0
        Вот это да, youtrack.jetbrains.com/issue/WI-5223 тут идет обсуждение по api для «умных массивов», и arrayAccess классов. Туда добавили мое предложение, возможно можно это доработать и вполне получится api для указания ключей у массива с опциями.
        • 0
          Добавил коммент там про это :)
    • +3
      Скоро уже отметим юбилей мифа об обратной несовместимости 5 и 4 версий PHP :)
  • +7
    По-моему это плохая идея. Совместимость нужно сохранять, но не долго, объявлять старый синтаксис как deprecated, и в следующей версии убрать вовсе. Это поможет постепенно перевести старый проект на новую версию (например, фреймворка), и потом когда всё уже будет готово перейти на ещё следующую, а вот так категорично отрезать обратные пути я бы не стал. PHP делает верно, но затянуто, можно было бы немного быстрее делать изменения.
    $options — плохая идея. Если у вас есть набор отдельных аргументов — IDE подскажет вам их порядок, а в случае с $options нужно ещё и помнить названия этих параметров, формат входящих данных, и если засунуть всё это в PhpDoc секцию — удобнее не станет.
  • 0
    Очень не согласен. (может быть я и не прав) Если говорить в общем — Обратная совместимость — это тот конек на котором держатся проекты.

    Например Windows ее winapi. Он местами очень не логичный, но если только отказаться от него в сторону нового и лучшего — с windows уйдут сотни тысяч пользователей. Точно такая же картина будет и с другими проектами.

    Теперь возьмите JQuery. в своей «новой» версии разработчики отказались от поддержки старых браузеров. Да, это уменьшит код и все такое. Но НИКАКОЙ грамотный предприниматель не будет жертвовать миллисекундой загрузки за счет N рублей прибыли. Именно понимая этот факт параллельно новой ветки JQuery до определенного момента будет развиваться и поддерживаться обратная совместимость по предыдущей ветке.

    Итог: обратная совместимость должна быть. Но нужно понимать до какой поры. Если царь умер, пора перестать его слушаться, но если он еще дышит — не надо революций. Он ведь хороший этот царь. Нужный

    • 0
      Мне кажется автор не призывает никого отказаться наотрез от BC. Но призывает проектировать API так, чтобы введение новых фич не тормозилось предлогами об обратной совместимости.
      • 0
        Скорее чтобы изменение существующих не тормозилось. Чтобы их поведение можно было изменить в будущем.
        • 0
          Проблема «изменения в будущем» мне видится в том, что «сколько модулей будет написано на существующей архитектуре».
          Сменить все вызовы в этих модулях? Окей, ctrl+f — справились.
          Из-за нового поведения новых функций (пусть и выполняющих те же действия) может смениться поведение модулей… Сюда добавляем зависимости модулей друг от друга и получаем «стопор» нашего проекта на N дней в лучшем случае. + время на модульное и интеграционное тестирование…
          Вопросы совместимости и ее изменения, на мой взгляд, одни из самых сложных при разработке более-менее сложных продуктов.
          • 0
            Не уверен, что это хорошая практика, но например можно добавить в такие параметры ключ version, и в зависимости от его наличия/значения по разному трактовать эти параметры. В таком случае проекты могут безболезненно обновлять сторонние продукты и постепенно делать изменения
            • 0
              Мне кажется, что все же это небольшая статья не может описать ВСЕ в этом плане. Вопросы о совместимости, на данный момент времени для меня представляются слишком сложными, чтобы уместить в объем поста на хабре. Тут и книги, подчас, может быть мало.

              Ваш способ действенен (ИМХО) только для не очень большого числа версий. А представьте, если таких ключей 10… 15? да, можно все повесить на ifах и подобном… а теперь перейдите на часть — ушли одни разработчики, пришли другие. сколько им придется разбираться в такой версионности?
            • 0
              Не проще в таком случае нужную версию библиотеки подключить?
          • 0
            Так в том и дело, что нужно писать так, чтобы старые вызовы не менялись, а новая функциональность в те же вызовы добавлялась. Скажем пишем функцию экранирования для какого-то формата строки в известной кодировке. Вместо того чтобы написать format_escape(string $string), можно сразу написать format_ecape(string $string, string $encoding = null), где при null будет использоваться кодировка из конфига, а нормальный способ использования format_escape($string, 'ASCII'), пускай даже ничего кроме ASCII иы ещё не реализовали. А кто будет использовать format_escape($string) тот должен знать, что кодировка в любой момент может смениться, а если не учтет это, то ССЗБ.
      • 0
        Костыль он предлагает… старый добрый костыль — раньше этим занимались init-strings, он предлагает в этих же целях использовать массив… Но это ужасно! Это отличный способ увеличить проблемы с производительностью/надёжностью/etc. Потому как автоматизированно следить за этим сложно, качественно документировать такой подход сложно и т.д.
  • 0
    В разрезе функций хэширования (которые с нашей точки зрения просто прячут проблему неопытных разработчиков, так же как раньше ее прятали magic_quotes), нам разглагольствования про обратную совместимость не хочется слушать и вот почему
    PASSWORD_DEFAULT (integer)
    The default algorithm to use for hashing if no algorithm is provided. This may change in newer PHP releases when newer, stronger hashing algorithms are supported.

    То есть с одной стороны оградили неопытных разработчиков от неправильного использования функций хэширования, с другой стороны разрешили им не указывать алгоритм хэширования явно и предполагают ВНЕЗАПНОЕ изменение алгоритма по умолчанию в следующих версиях.
    Это как вообще совмещается?
    Заглянем в мрачное будущее: популярный плагин/скрипт/цмс сделанный неопытным разработчиком работающий с БД юзеров, соответственно алгоритм принудительно не задается (можно ведь). Хостер внезапно апгрейдит пхп (из самых добрых побуждений) и в БД начинается ад… новые юзеры идут с одними алгоритмами, старые юзеры не могут залогиниться ввиду устарелости их алгоритмов, все паникуют и ругают хостеров, пхп, бога, дьявола и слоников.
    • +1
      В Политбюро не дураки сидят — ночью полетят Там фишка в том, что результат password_hash содержит не только собственно хэш, но и все параметры ( кроме собственно пароля) и password_verify получает только хэш и пароль для сравнения, других параметров у неё нет, она извлечёт их из хэша. Так что в плане FC там всё очень продумано. И я бы даже сказал — красиво.
  • +5
    Добавьте в опрос промежуточный вариант.
    Да, сохранять, но не слишком долго.
  • +1
    Удобство пользователей важнее удобства создателей продукта.
    • 0
      В том-то и дело, что BC доставляет неудобство неконсервативным пользователям таких продуктов как ЯП, API, библиотеки, фреймворки и т. п. Из-за BC тормозится внедрение новых фич.
  • 0
    Обратная совместимость нужна тогда, когда вы отдаете результат своего труда третьему лицу. Если же вы переписываете лишь внутри своей команды, то вам в принципе не нужно соблюдать Open-Close
  • +1
    Моё мнение такое, что индустрии давно следует договориться и сохранять совместимость только внутри мажорных версий.
    Т.е., например, вся ветка 2.х обратно-совместима с ростом x, но вот ветка 3.х уже не отяжелена грузом совместимости. Однако поверх ветки 3 выпускается эмулятор апи для версии 2, чтобы пользователи не оказались в ситуации, когда нужная им софтина А уже требует «тройку» какой-то либы, а софтина В ещё сидит на «двойке» (или вообще брошена разработчиками, но еще используется; или она платная, а платить за новую версию просто так душит жаба).
    • 0
      С эмуляторами не всё так просто может быть. Название функции не измениться, а поведение измениться, а язык даже глобально переопределить функцию не даёт.
      • 0
        В чем проблема-то? Если речь про языки, то либо в первой строке указывать «myLang3» или там "#/bin/mylang3", или в расширении (.ml3 vs .ml2).
        С либами еще проще: use/import/include/wtf com.example.lib-api-v2
        • +1
          Это всё не прозрачно.
    • 0
      Насколько я знаю, переход в python с 2 до 3 существует, но до сих пор вызывает значительные проблемы, когда стоит 2.7, а программа пишется под 3 или наоборот (я с ней несколько раз сталкивался).
      В результате маленькая программа таскает с собой собственную версию python, веся при этом десятки мегабайт…
      Поэтому не все так просто с эмуляцией.

      Хотя я с вами полностью согласен. Изменение первой цифры и значительная потеря совместимости — изменение API. Для меня пример проекта, который этому следует — Yii 2.
  • +1
    Думаю, стоило бы пояснить, что статья это перевод блогозписи :) одного из разработчиков PHP? то есть в какой-то мере самокритика.
    • –3
      Всё же «блогозАписи»:)
      • +1
        Ваше уточнение совершенно справедливо.
  • 0
    Странно, что были противопоставлены оба подхода. У них разные цели и методы. Я думаю, что они могут быть успешно совмещены.

    Но при применении вперёдсмотрящей совместимости можно получить тот же эффект как от преждевременной оптимизации.

    При этом нет описания общих подходов и приёмов для опережающей совместимости. Что может повлечь за собой кучу ненужного кода, который (в отличии от обратной совместимости) вообще никогда не работал и не будет работать.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.