Если коротко: проблема масштабирования - это когда при расширении фичи она начинает разбухать, потому что интуитивный подход к масштабированию - неправильный.
Чертовски верно определена причина. Поработайте над интуитивным подходом к масштабированнию и проблема решится. Почитайте, что ли, как теория рекомендует масштабировать системы в общем и фичи и код в частности? Ну, чтобы подход был научный, а не интуитивный.
Проблема разрыва логики
Тоже шикарнейшее название. Но вот над теорией тоже надо поработать... Это называется асинхронностью. Сей феномен уже давно изучен вдоль и поперек. Известны все проблемы во всех возможных и невозможных случаях. На каждую проблему асинхронности есть своя таблетка, которая зарекомендовала себя лет так за 50-60. В крайнем случае асинхронность можно не использовать, не будет никаких разрывов в логике – вот только пользователям Вашим это не понравится сильно.
Про проблему остатка специально ничего писать не буду, но как Вы понимаете, она тоже называется слегка по другому и про нее тоже все известно давно и с такой же степенью детализации, как Ньютоновская физика.
Вместо этого скажу, что архитектура не то, чтобы должна решать все перечисленные 3 проблемы. Для их решения есть свои собственные инструменты. А архитектура немножко для другого. (И, кстати, про цели архитектуры тоже неплохо бы подтянуть матчасть.) Вот например, из документации MVC имеет своей целью максимальную переиспользуемость кода. Цитата: "A goal of a well-designed MVC application should be to use as many objects as possible that are (theoretically, at least) reusable."
Но мы прям с нетерпением ждем, что же такое Вы нам покажете.
Разве в VIP ViewController знает о Presenter? В своей прошлой статье вы описали, что под «знает» подразумеваете «владение». Что-то порядок навести так и не вышло :)
В VIP не зря все стрелки направлены в одном лишь направлении. ViewController ничего не должен знать о Presenter. Наоборот, лишь Presenter знает о и говорит с ViewController на языке последнего.
Нет. Способ подходящий. Сам им активно пользуюсь, только не заморачиваюсь со списком :)
Просто мне кажется, что Вы ошибаетесь, когда говорите, что из-за одного неотвеченного хардварного вопроса Вам отказывают потому, что находится тот, кто отвечает и на этот вопрос.
Хардварные вопросы, кажется, уже давно не являются критерием приема во многих компаниях (ну, если не ищут супер-пупер-архитектора). Они, скорее, просто повод, чтобы о чем-то поговорить. Чтобы заполнить пустоту на собеседовании. С таким же успехом можно было бы поговорить и о Вашем хобби, но это бы у Вас еще большее недоумение вызвало и гневную статью о бренде на Хабре. Поэтому и принято говорить о том, что меньший стресс у человека вызывает. :)
А решение принимают все же по другим критериям. Иногда чисто по очень субъективным. Увы и ах, но да.
Поэтому и спросил про мягкие навыки. Без их упоминания и анализа делать глубокие выводы, которые делаете Вы, немножко некорректно. Нужно анализировать всю картину целиком. Возможно, Вы как раз проваливали мягкую часть и шутейки ^.^
В том-то и дело, что старая функция нужна. У нее есть ценность. Она покрыта тестами. Она работает. Она проверена временем. А вот для новой ситуации использования достаточно создать адаптер между этой функцией и новой ситуацией.
Да нет, я посмотрел. Вы считаете, что в обсуждаемом примере вместо того, чтобы добавить 1 функцию с новым контрактом, вызывающую старую, и покрыть ее 2мя тестами, лучше переписывать эту функцию под новый контракт и все тесты, которые на эту функцию завязаны?
Пример того, как любую идею можно довести до абсурда.
По моему скромному мнению, для того, чтобы следовать OCP, не нужно быть провидцем. Достаточно понимать, как вносить изменения, не ломая старого. Постфактум, без предвидения. Для этого ведь есть инструменты. Расширения во многих языках прям в стандарте заложены же. С определенными правилами. Надо просто брать их в чистом виде, принимать и использовать, как есть, не пытаясь применять не по назначению и искать различные хаки на их основе.
Я понял, у вас неприязнь к OCP и SOLID. Ок, специально для Вас: замените OCP в моей предыдущей реплике на следующую цитату из приведенной видюхи, смысл останется неизменным и справедливым:
"Не ломай публичный контракт без необходимости!"
Ну, или скажу другими словами. Вместо рефакторинга тестов в первых трех пунктах у@akaDualityнадо было просто не ломать публичные интерфейсы.
Кстати, я сам OCP понимаю исключительно, как постоянство публичных интерфейсов и отсутствие в них ломающих изменений в терминологии symver.
Желаете что-то возразить или поправить меня?
А за ссылку спасибо, полистаю, что там у Вас еще имеется.
В первых трех случаях вы нарушили Open/Close принцип в вашем коде. Вы изменили поведение, а должны были расширить его. Например, достаточно было завести вторую функцию, которая бы вызывала первую, но оборачивала бы ее в async/await. В таком случае Вам не нужно переписывать ни одного теста (и ни одной строчки кода!), достаточно написать пару новых тестов на новую функцию, которые будут очень примитивны: один проверит вызов функции, второй – возврат колбека. Тот самый SOLID, от которого все плюются, на практике :)
Четвертый случай еще как-то оправдывает рефакторинг тестов. Да, я такой случай не учел. Но даже в этом случае имеет смысл работать только с теми тестами, которые работают с кодом, который вы активно меняете. Остальные тесты можно тупо не запускать (по импакт-анализу) и получить тем самым даже больший выигрыш в скорости, чем от рефакторинга тестов.
Могу ли я по приведенной Вами выборке сделать в первом приближении вывод, что более 75% работы Вы делаете зря? При этом из-за Ваших же ошибок в коде?
И да, я тоже откачусь назад и слегка подправлю свой изначальный тезис: "Если Вам приходится рефакторить тесты, то Вы пишите неправильные тесты И неправильный код" :)
В общем, завязываю мешать Вам рефакторить тесты, пойду смотреть видюху! Спасибо еще раз Вам за нее.
Нет, Вы, кажется, не поняли. Рефакторите Вы код, но не тесты. Вы верно пишите, что поведение не меняется. Значит, не должны меняться и тесты. Значит, рефакториться они не должны :) Ну, смысла в этом тупо нет. Они есть, они проверяют поведение. Точка. Зачем их рефакторить? Ну, это понятно для кода: сделать его быстрее (в 99% случаев тоже сомнительно), сделать его читабельнее, масштабируемее, переиспользуемее... А тесты зачем рефакторить?
За видео спасибо. А не подскажете, таймфреймы по теме для экономии времени?
В итоге приходиться писать больше кода, сами тесты приходится обновлять и рефакторить, что удлиняет разработку, но мы отыгрываем это время на процессах за счет ранней обратной связи. Именно на связях между людьми кроются самые большие потери, поэтому суммарно мы выигрываем по времени. Чем дальше, тем больше накапливающийся эффект.
Все верно говорите. Но есть нюанс. Если Вам приходится рефакторить тесты, то Вы пишите неправильные тесты :)
Попробуйте подумать, что же не так с Вашими тестами.
Несколько подсказок.
Тесты – это ТЗ, переведенное в код. Если ТЗ не менялось, тест тоже меняться не должен. Если Вы вынуждены менять тест при рефакторинге кода, то Ваш тест связан с кодом. Это ошибка. Связность в коде стараются избегать, а про то, что тест тоже не должен зависеть от кода, обычно забывают (от публичных интерфейсов тесты могут и должны зависеть, от деталей реализации – не должны)
Вторая самая распространенная ошибка – это тесты, проверяющие все. Этакие God-тесты, когда в одном тесте проверяется сразу 100500 условий и поведений. В идеале тут тоже должен выполняться принцип единой ответственности – каждый тест должен падать строго от одной и только одной ошибки. Если у Вас при ошибке падает несколько тестов, Вы снова пишите их неверно. Снова связность. Тут, кажется, очень хорошо помогают мутационные тесты: вы сразу видите сколько тестов у вас сломалось от каждой мутации, и вынуждены думать, как же так их поменять, чтобы они конкретную мутацию проверили, но не зависели от остального кода.
Принципы FIRST. Много полезного. Особенно буковка I - которая снова про изоляцию и связность.
Итог. Тесты не должны обновляться и рефакториться, если не менялось ТЗ.
В следующий раз расскажу, как тесты повлияли на архитектуру проекта, как мы сэкономили на времени компиляции и поищем границы для интеграционных тестов.
А эта статья была? Что-то не нашел в Ваших статьях. С радостью бы почитал.
А есть где-то описание (желательно текстовое) как Вы этого добились? Просто я ни разу не видел, чтобы UI-тесты запускались быстрее, чем единицы или десятки секунд.
Плюс мне непонятно, у вас на видео открыт симулятор, при этом в нем нигде нет того компонента, скриншоты которого Вы проверяете. Это как это так работает?
Кажется, я слегка неправильно использовал скриншоты тесты. В моем понимании это выглядело так. Запускается симулятор, открывается нужный экран, делается нужный скриншот. Открывается следующий экран, делается скриншот для него. А это все жутко долго и нестабильно. То, что я увидел на ролике у Вас, мне совсем непонятно.
Можете чуть подробнее раскрыть первый пункт? Это как? Вы запускаете тяжелый скриншот-тестинг (который все равно запускает приложение) под все платформы вместо того, чтобы запускать приложение в Xcode? А в чем профит?
А за что минусовать? За факт? Или за отсутствие связи представленного факта с объемом работ? :)
С фактом не спорю. Качество разработчиков бывает разное. Особенно, когда их надо в моменте много. приходится брать всех, кто есть на рынке. Это ж не секрет :)
В маленьких конторках не делают и 10% того, что делают в банках. Отсюда и бюрократия. Хочешь или нет, но если работа увеличивается в 10 раз, то ее раз в 100 сложнее координировать. Вот и появляются регламенты, документация и прочая бюрократия.
Можно, конечно, спросить, а зачем делать эти 90% работы, если маленькие конторки получают тот же результат, выполняя 10%? Вот только результаты все же разные. Маленький финтех стартап и Сбер получают совершенно разные результаты. Отличающиеся на несколько порядков. Да, даже Тинькофф, работающий по модели CapitalOne и Сбер – это две большие разницы по результату. Кто не верит, может погуглить цифры. Еще недавно они были открытые :)
Чертовски верно определена причина. Поработайте над интуитивным подходом к масштабированнию и проблема решится. Почитайте, что ли, как теория рекомендует масштабировать системы в общем и фичи и код в частности? Ну, чтобы подход был научный, а не интуитивный.
Тоже шикарнейшее название. Но вот над теорией тоже надо поработать... Это называется асинхронностью. Сей феномен уже давно изучен вдоль и поперек. Известны все проблемы во всех возможных и невозможных случаях. На каждую проблему асинхронности есть своя таблетка, которая зарекомендовала себя лет так за 50-60. В крайнем случае асинхронность можно не использовать, не будет никаких разрывов в логике – вот только пользователям Вашим это не понравится сильно.
Про проблему остатка специально ничего писать не буду, но как Вы понимаете, она тоже называется слегка по другому и про нее тоже все известно давно и с такой же степенью детализации, как Ньютоновская физика.
Вместо этого скажу, что архитектура не то, чтобы должна решать все перечисленные 3 проблемы. Для их решения есть свои собственные инструменты. А архитектура немножко для другого. (И, кстати, про цели архитектуры тоже неплохо бы подтянуть матчасть.) Вот например, из документации MVC имеет своей целью максимальную переиспользуемость кода. Цитата: "A goal of a well-designed MVC application should be to use as many objects as possible that are (theoretically, at least) reusable."
Но мы прям с нетерпением ждем, что же такое Вы нам покажете.
В VIP не зря все стрелки направлены в одном лишь направлении. ViewController ничего не должен знать о Presenter. Наоборот, лишь Presenter знает о и говорит с ViewController на языке последнего.
Вот бы это все, да еще подробно и с примерами :)
А с Телеграмм? точно такой же конкурс объявляют раз в надцать месяцев.
Нет. Способ подходящий. Сам им активно пользуюсь, только не заморачиваюсь со списком :)
Просто мне кажется, что Вы ошибаетесь, когда говорите, что из-за одного неотвеченного хардварного вопроса Вам отказывают потому, что находится тот, кто отвечает и на этот вопрос.
Хардварные вопросы, кажется, уже давно не являются критерием приема во многих компаниях (ну, если не ищут супер-пупер-архитектора). Они, скорее, просто повод, чтобы о чем-то поговорить. Чтобы заполнить пустоту на собеседовании. С таким же успехом можно было бы поговорить и о Вашем хобби, но это бы у Вас еще большее недоумение вызвало и гневную статью о бренде на Хабре. Поэтому и принято говорить о том, что меньший стресс у человека вызывает. :)
А решение принимают все же по другим критериям. Иногда чисто по очень субъективным. Увы и ах, но да.
Поэтому и спросил про мягкие навыки. Без их упоминания и анализа делать глубокие выводы, которые делаете Вы, немножко некорректно. Нужно анализировать всю картину целиком. Возможно, Вы как раз проваливали мягкую часть и шутейки ^.^
А где же вопросы про мягкие навыки?
Какой ответ вызвал смех у вас обоих?
И устроились ли Вы в итоге, зазубрив ответы на самые популярные вопросы?
В том-то и дело, что старая функция нужна. У нее есть ценность. Она покрыта тестами. Она работает. Она проверена временем. А вот для новой ситуации использования достаточно создать адаптер между этой функцией и новой ситуацией.
Да нет, я посмотрел. Вы считаете, что в обсуждаемом примере вместо того, чтобы добавить 1 функцию с новым контрактом, вызывающую старую, и покрыть ее 2мя тестами, лучше переписывать эту функцию под новый контракт и все тесты, которые на эту функцию завязаны?
Вот прям необходимо?
Пример того, как любую идею можно довести до абсурда.
По моему скромному мнению, для того, чтобы следовать OCP, не нужно быть провидцем. Достаточно понимать, как вносить изменения, не ломая старого. Постфактум, без предвидения. Для этого ведь есть инструменты. Расширения во многих языках прям в стандарте заложены же. С определенными правилами. Надо просто брать их в чистом виде, принимать и использовать, как есть, не пытаясь применять не по назначению и искать различные хаки на их основе.
Я понял, у вас неприязнь к OCP и SOLID. Ок, специально для Вас: замените OCP в моей предыдущей реплике на следующую цитату из приведенной видюхи, смысл останется неизменным и справедливым:
"Не ломай публичный контракт без необходимости!"
Ну, или скажу другими словами. Вместо рефакторинга тестов в первых трех пунктах у@akaDualityнадо было просто не ломать публичные интерфейсы.
Кстати, я сам OCP понимаю исключительно, как постоянство публичных интерфейсов и отсутствие в них ломающих изменений в терминологии symver.
Желаете что-то возразить или поправить меня?
А за ссылку спасибо, полистаю, что там у Вас еще имеется.
В первых трех случаях вы нарушили Open/Close принцип в вашем коде. Вы изменили поведение, а должны были расширить его. Например, достаточно было завести вторую функцию, которая бы вызывала первую, но оборачивала бы ее в async/await. В таком случае Вам не нужно переписывать ни одного теста (и ни одной строчки кода!), достаточно написать пару новых тестов на новую функцию, которые будут очень примитивны: один проверит вызов функции, второй – возврат колбека. Тот самый SOLID, от которого все плюются, на практике :)
Четвертый случай еще как-то оправдывает рефакторинг тестов. Да, я такой случай не учел. Но даже в этом случае имеет смысл работать только с теми тестами, которые работают с кодом, который вы активно меняете. Остальные тесты можно тупо не запускать (по импакт-анализу) и получить тем самым даже больший выигрыш в скорости, чем от рефакторинга тестов.
Могу ли я по приведенной Вами выборке сделать в первом приближении вывод, что более 75% работы Вы делаете зря? При этом из-за Ваших же ошибок в коде?
И да, я тоже откачусь назад и слегка подправлю свой изначальный тезис: "Если Вам приходится рефакторить тесты, то Вы пишите неправильные тесты И неправильный код" :)
В общем, завязываю мешать Вам рефакторить тесты, пойду смотреть видюху! Спасибо еще раз Вам за нее.
Нет, Вы, кажется, не поняли. Рефакторите Вы код, но не тесты. Вы верно пишите, что поведение не меняется. Значит, не должны меняться и тесты. Значит, рефакториться они не должны :) Ну, смысла в этом тупо нет. Они есть, они проверяют поведение. Точка. Зачем их рефакторить? Ну, это понятно для кода: сделать его быстрее (в 99% случаев тоже сомнительно), сделать его читабельнее, масштабируемее, переиспользуемее... А тесты зачем рефакторить?
За видео спасибо. А не подскажете, таймфреймы по теме для экономии времени?
Все верно говорите. Но есть нюанс. Если Вам приходится рефакторить тесты, то Вы пишите неправильные тесты :)
Попробуйте подумать, что же не так с Вашими тестами.
Несколько подсказок.
Тесты – это ТЗ, переведенное в код. Если ТЗ не менялось, тест тоже меняться не должен. Если Вы вынуждены менять тест при рефакторинге кода, то Ваш тест связан с кодом. Это ошибка. Связность в коде стараются избегать, а про то, что тест тоже не должен зависеть от кода, обычно забывают (от публичных интерфейсов тесты могут и должны зависеть, от деталей реализации – не должны)
Вторая самая распространенная ошибка – это тесты, проверяющие все. Этакие God-тесты, когда в одном тесте проверяется сразу 100500 условий и поведений. В идеале тут тоже должен выполняться принцип единой ответственности – каждый тест должен падать строго от одной и только одной ошибки. Если у Вас при ошибке падает несколько тестов, Вы снова пишите их неверно. Снова связность. Тут, кажется, очень хорошо помогают мутационные тесты: вы сразу видите сколько тестов у вас сломалось от каждой мутации, и вынуждены думать, как же так их поменять, чтобы они конкретную мутацию проверили, но не зависели от остального кода.
Принципы FIRST. Много полезного. Особенно буковка I - которая снова про изоляцию и связность.
Итог. Тесты не должны обновляться и рефакториться, если не менялось ТЗ.
А эта статья была? Что-то не нашел в Ваших статьях. С радостью бы почитал.
Понял, спасибо. Из статьи это было не совсем очевидно. Ушел изучать инструмент.
А есть где-то описание (желательно текстовое) как Вы этого добились? Просто я ни разу не видел, чтобы UI-тесты запускались быстрее, чем единицы или десятки секунд.
Плюс мне непонятно, у вас на видео открыт симулятор, при этом в нем нигде нет того компонента, скриншоты которого Вы проверяете. Это как это так работает?
Кажется, я слегка неправильно использовал скриншоты тесты. В моем понимании это выглядело так. Запускается симулятор, открывается нужный экран, делается нужный скриншот. Открывается следующий экран, делается скриншот для него. А это все жутко долго и нестабильно. То, что я увидел на ролике у Вас, мне совсем непонятно.
Это как? Без запуска симулятора? 0_о
Можете чуть подробнее раскрыть первый пункт? Это как? Вы запускаете тяжелый скриншот-тестинг (который все равно запускает приложение) под все платформы вместо того, чтобы запускать приложение в Xcode? А в чем профит?
Как часто скриншот-тесты у вас показывают ошибки? Какие это ошибки?
А за что минусовать? За факт? Или за отсутствие связи представленного факта с объемом работ? :)
С фактом не спорю. Качество разработчиков бывает разное. Особенно, когда их надо в моменте много. приходится брать всех, кто есть на рынке. Это ж не секрет :)
В маленьких конторках не делают и 10% того, что делают в банках. Отсюда и бюрократия. Хочешь или нет, но если работа увеличивается в 10 раз, то ее раз в 100 сложнее координировать. Вот и появляются регламенты, документация и прочая бюрократия.
Можно, конечно, спросить, а зачем делать эти 90% работы, если маленькие конторки получают тот же результат, выполняя 10%? Вот только результаты все же разные. Маленький финтех стартап и Сбер получают совершенно разные результаты. Отличающиеся на несколько порядков. Да, даже Тинькофф, работающий по модели CapitalOne и Сбер – это две большие разницы по результату. Кто не верит, может погуглить цифры. Еще недавно они были открытые :)