Ads
Comments 48
+3
В рамках предложения: 1.5. Написал, собрался рефакторить — сначала напиши тесты. Да, я все еще встречаю людей, которые рефакторят код без тестов.
+6
Всегда так делал и буду делать.
Когда у вас есть статические типы, тесты при рефакторинге вам по большому счету нафиг не нужны.
+7
Простите, не понял. Если вы рефакторите логику, разве нельзя ее сломать, при этом не поломав типов?
0
Если вы рефакторите логику, разве нельзя ее сломать, при этом не поломав типов?

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

Ну и именно потому, что всё можно сломать — рефакторинг и делает программист, а не нейронка. Вне зависимости от того, есть ли у вас тесты или нет. Тесты — не гарантия того, что вы ничего не сломали. Как максимум это гарантия того, что вы не сломали тесты (да и то не всегда).
+2

Например я использую Java и Idea. Она поддерживает очень много рефакторингов, которые практически гарантированно не меняют семантику кода. А более сложные уже привыкаешь делать составляя их из простых. В итоге рефакторинг получается достаточно безопасный и без тестов. Конечно если это тупо переписывание куска кода, это другой вопрос.

+1
Согласен про Idea, в моем случае PhpStorm, и тоже использую эти фичи постоянно. Я имел в виду именно переписывание кусков кода, когда нужно, к примеру, переписать логику, запросы к ORM/БД, и подобные вещи.
+1

А если вам на ревью пришёл PR с рефакторингом, как вы со спокойной совестью поставите Approve? В этом случае сильно помогает автоматизация, в том числе тестирования.

+1

Это не рефакторинг, а переименования и перемещения. Рефакторинг — это когда значительно меняется подход к реализации какого-либо слоя логики без изменения ее функциональности.

+1

Это у вас своё определение уже. Слово Refactoring популяризировал Мартин Фаулер и в его одноименной книге переименование это один из видов рефакторинга. Про "значительно" там ничего нет. Рефакторинг это изменение кода без изменения его функционала. Значительное оно или не значительное — не важно. https://refactoring.com/catalog/ тут даже целый каталог есть.

-2

Обожаю диванных экспертов. Расскажите, как в библиотеке, которая парсит маркдаун, хоть в чем-то помогут типы.

+2
Сразу после того, как вы нам расскажете, как вы покроете библиотеку, парсящую маркдаун, такими тестами, чтоб можно было обосновать, что она корректно парсит любой ввод.
+1

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


Типы там вообще не помогут буквально ничем.

0
Существуют наборы тестов для маркдауна

Конечно существуют.

которые покрывают ожидаемый ввод

Что будем делать с не ожидаемым?

Но на самом деле наверное даже не стоило влезать в общение с человеком, который ради пачки комментариев намеренно проигнорировал то, что заглавный комментарий ветки не содержит в себе повального обобщения.
0
Что будем делать с не ожидаемым?

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

Более того, давно уже известно, что написание тестов полезно не только и не столько постоянной проверкой результата исполнения кода, сколько качеством получающейся структуры кода за счёт более глубокого продумывания при написании тестов.
0
TDD требует отказа от тестирования тех сценариев, которые использоваться не будут, и покрытия тестами только тех, которые будут.

А, ну теперь-то всё понятно. Нужно просто использовать ПО только так, как это было задумано; а как не было задумано — не использовать. Мы-то, тупые, тут чё-то сокрушаемся, что код порой не работает, потому что разработчики не всё предусмотрели. А умные парни всё уже давно решили.

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

А знаете, что еще повышает качество структуры кода, помимо продумывания при написании тестов?
Продумывание собственно самой структуры кода перед её написанием.
0
TDD требует отказа от тестирования тех сценариев, которые использоваться не будут, и покрытия тестами только тех, которые будут.

Офигенно подходит для библиотек, которые потребляют хоть какой-то пользовательский ввод.


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

Исключительно по личному опыту: написание типов перед написанием реализаций помогает продумать структуру кода куда лучше.


Хотя, наверное, мне стоило написать «давно известно», а не «по личному опыту».

+1

Это не вариант.


Что там от кого требует TDD — такое же точно идолопоклонничество, как «типы нас спасут от ядерного взрыва».


Неожиданный ввод может перекладываться в аккумулятор, как есть. Может выбрасывать исключение. Может сразу возвращать пустую строку. Может возвращать разметку до позиции, в которой мы нашли что-то неожиданное.


Зависит от варианта использования.

+1

Что именно выразить-то? Чувствую, в воздухе явственно запахло монадами.


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

0

Все выразить.


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

+1

А почему это нельзя прямо кодом выразить? Буквально, кинуть эксепшн?

0

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

+1

Ах вон оно что!


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


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


Ну вот, например, библиотека параллельного вычисления — даже не корка языка. У меня весь мой код такой же, и в OSS, и под NDA.


Автоматически генерируется вот в такое.

+1

Ну да, конечно, документация, которая может устареть и не проверяется компилятором — это куда лучше, чем типы!

+1

Конечно. И да, доктесты придумали как раз для проверок документации компилятором.

0

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


И она не может устареть; эта страшилка из девяностых — сама устарела, с введением современных правил разработки. CR проверяет и документацию тоже. С тем же упехом можно заявить, что код может устареть: ведь у нас теперь расширился список обрабатываемых вариантов для параметра x, а в коде это не поправили (и типы тут не помогут тоже).


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

0

Ну, для начала они могут помочь доказать, что парсер всегда завершается и никогда не входит в бесконечный цикл. Жду проверки этого свойства тестами.

+1

Во-первых, мне это неочевидно. Во-вторых John Hughes для такого типа задач придумал property testing. В-третьих, один последний clause для парсящей ввод из стрима функции, который откусывает один символ если ничего не заматчилось, и перекладывает в аккумулятор, делает утверждение очевидным. В-пятых, потенциальная возможность обработки бесконечного стрима делает доказательство невозможным.

0
Во-первых, мне это неочевидно.

Ну, не знаю, что тут сказать. Посмотрите статью «Total parser combinators» для примера — с мобильника трудно дать ссылку.


Во-вторых John Hughes для такого типа задач придумал property testing.

Ну сформулируйте свойство «всегда завершается» для начала (это можно сделать, но не очень тривиально).


А, во-вторых, вот те свойства, которые вы пишете в property-based testing, вполне могут быть выражены в типах.


В-третьих, один последний clause для парсящей ввод из стрима функции, который откусывает один символ если ничего не заматчилось, и перекладывает в аккумулятор, делает утверждение очевидным.

Про левую рекурсию не слышали?


В-пятых, потенциальная возможность обработки бесконечного стрима делает доказательство невозможным.

Тем интереснее!


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

+1
Total parser combinators

Пункт «во-первых» там присутствовал ради последних двух пунктов.


сформулируйте свойство «всегда завершается»

Не могу придумать, зачем бы это мне (кроме академического интереса похвастаться коллегам).


те свойства, которые вы пишете в property-based testing, вполне могут быть выражены в типах

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


Про левую рекурсию не слышали?

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


В языках, где бесконечные стримы выразимы

Бесконечные стримы выразимы во всех без исключения полных по Тюрингу языках, даже если они не знают, что называются красивым словом «выразимы». Любой язык умеет читать из сокета.


понятие завершимости заменяется более общим понятием продуктивности

А вот это уже вредно для конечного продукта. Например, работа с XMPP протоколом в общем случае не удовлетворяет этому условию: ejabberd в прямом смысле запустит поток и будет висеть бесконечно, ожидая следующей станзы, пока клиент не отключится.


Ему не надо никого оповещать о таймаутах и каноничекую продуктивность такого процесса не доказать (потому что ее нет). И ничего, мир не рухнул.

0
Не могу придумать, зачем бы это мне (кроме академического интереса похвастаться коллегам).

Чтобы ваш парсер, например, не зависал на каком-то входе, триггерящем редкий кусок грамматики.


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

Чтобы проверять их компилятором формально на всём множестве входов, а не тестами на десятке-сотне примеров.


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

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


Бесконечные стримы выразимы во всех без исключения полных по Тюрингу языках, даже если они не знают, что называются красивым словом «выразимы».

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


А вот это уже вредно для конечного продукта. Например, работа с XMPP протоколом в общем случае не удовлетворяет этому условию: ejabberd в прямом смысле запустит поток и будет висеть бесконечно, ожидая следующей станзы, пока клиент не отключится.

Ему не надо никого оповещать о таймаутах и каноничекую продуктивность такого процесса не доказать (потому что ее нет). И ничего, мир не рухнул.

Вы неправильно понимаете продуктивность (или я не до конца формально её выражаю). Если вкратце и не ударятся в десятки страниц матана, то нет никаких проблем с выражением серверов или repl'ов или тому подобных вещей в терминах продуктивности.

+1

Вообще, мне кажется, что у вас легкая форма профессиональной деформации, вызванная прыжком из языка, где можно в WORD по указателю положить достаточно короткую строку, в язык со строгими типами (еще я удивлен, почему вы все-таки предпочитаете Хаскель — Идрису, тот вроде, идеологически еще концептуальнее).


Без обид, исключительно, как повод задуматься, если вдруг увидите в моих словах рациональное зерно.


Мир просто не черно-белый. В том же эрланге есть чудесный способ получить все плюшки, не связываясь с типами (продуманный pattern matching и guards). Кому не хватает — есть статический анализатор кода, который умеет выводить типы. И хотя сам Вирдинг утверждает, что типы — это круто, и их нет в эрланге только потому, что язык изначально проектировался для hot code reload, я не думаю, что они бы что-то всерьез улучшили.

0

Я все ещё пишу на плюсах :) Правда, теперь только на работе, а не как раньше.


И это не то что деформация, просто мне действительно нравится мир, где есть чуть более строгие гарантии, и где не нужно тратить непродуктивное время на тесты. А чем больше я про все это говорю и пропагандирую, тем больше людей про это знает, а чем больше людей про это знает, тем выше шансы жить в таком мире.


А предпочтения зависят. На хаскеле можно код в продакшен писать, ибо в идрисе даже вон пакетного менеджера и репозиториев не завезли толком (да и скоро Idris 2 будет с ещё более лучшей системой типов). На идрисе хорошо играться с завтипами и доказывать, что x + 0 = x. Но да, я очень жду завтипы в хаскеле и даже сам начал немножко это ковырять.

+1
чем больше я про все это говорю и пропагандирую, тем больше людей про это знает

Это-то мне понятно, и такая позиция мне крайне близка. Просто чем больше вы начинаете говорить о типах, как о панацее — тем дешевле становятся их преимущества (которых тоже, конечно, вагон).


IMSO, конечно.

0

«Дешевле» — в смысле «доказательная наука» превращается в «религию».


Я повторяю: я согласен, что есть ситуации, в которых типы помогают. Но когда мне начинают говорить, что типы лучше документации, типы делают ненужным тестирование и типы спасут мир — это уже начинает выглядеть как «на все — Воля Божья».

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

ИМХО одно другому не мешает. Представьте себе донельзя упрощенный вариант:

int sum(int a, int b) {
return a+b;
}

А потом кто-то впопыхах берет и меняет плюс на минус, и это проходит code review (лично видел не раз). Строгая типизация удовлетворена, компилятор счастлив, но самолет продакшн таки падает. YMMV, но у меня таких примеров в работе море.
0

Она просто недостаточно строгая. Вот если там рядом будут теоремки вроде ∀x. sum x 0 = x и ∀ x. sum 0 x = x...

0
Прошу прощения, мне не знаком данный синтаксис в контексте языков программирования (математически понятно). Подскажите, какой это язык? В любом случае, мой комментарий касался более классических ЯП, где подобное отсутствует. Ну и такой момент — если эти теоремки будут рядом, всегда есть искушение «поправить» и их заодно с рефакторингом. Понятно, что от полных идиотов не спасет ничто, но если тесты вообще в другом файле, то очумелые ручки доберутся до них с чуть меньшей вероятностью. Все ИМХО.
+1
но если тесты вообще в другом файле

А уж если их в блокчейн положить…

Я в принципе нисколько не против, чтоб люди упарывались, вы просто когда упарываетесь — то имейте в виду, что выраженные типами теоремы о том, как должен работать тот или иной код — это по сути почти то же самое, что и полное покрытие любого ввода через property-based тесты, только без тестов.

(Почти — потому что типы базируются на математике, а property-based тесты таки вынуждены выполнять ваш код и смотреть на результат)
+1
Подскажите, какой это язык?

Это гибрид из агды и идриса. На идрисе я что-то сделал, например, здесь или здесь или здесь.


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

0

Пф. Вы переписали код вычиления суммы с языка Foo на язык Bar. Теперь, по сути, сама функция sum не очень-то нужна, потому что вы выразили ее полностью в типах.


Смена аксиоматики не может сделать что-то понятнее, точнее, или правильнее.

0

Нет, я не выразил её в типах. Я в типах выразил её свойства, не более.

-2

В целом согласен, но с оговоркой что тесты должны быть интеграционными. От юнит тестов при более-менее большом рефакторинге толку ноль

Only those users with full accounts are able to leave comments.  , please.