Эмпирическое правило, к которому я стремлюсь, заключается в том, что если я смогу охватить новое поведение, добавив дополнительный параметр (или новое допустимое значение) в существующую функцию, оставив код более или менее «явно одинаковым» вв существующем случае, тогда нет большой опасности в изменении функции.
Например, старый код:
def utf8len(s):
return len(s.encode('utf8')) # or maybe something more memory-efficient
Новый вариант использования - я пишу некоторый код в стиле, который используетшаблон нулевого объекта, поэтому я хочу, чтобы utf8len(None)
возвращал None
вместо того, чтобы выдавать исключение.Я мог бы определить новую функцию utf8len_nullobjectpattern
, но это будет довольно быстро раздражать, поэтому:
def utf8len(s):
if s != None:
return len(s.encode('utf8')) # old code path is untouched
else:
return None # new code path introduced
Тогда, даже если модульные тесты для utf8len
были неполными, я могу поспорить, что у меня нетне изменил поведение для любого ввода, кроме None
.Я также должен проверить, что никто никогда не полагался на utf8len
, чтобы выдать исключение для None
ввода, что является вопросом (1) качества документации и / или тестов;и (2) обращают ли люди внимание на определенные интерфейсы или просто используют источник.Если последнее, мне нужно смотреть на вызывающие сайты, но если все сделано хорошо, то я в значительной степени не верю.
То, обрабатываются ли старые разрешенные входы по-прежнему "очевидно, то же самое", на самом деле не являетсяВопрос в том, какой процент кода модифицирован, это как он модифицирован.Я выбрал заведомо тривиальный пример, так как все тело старой функции все еще находится в новой функции, но я думаю, что это то, что вы знаете, когда видите это.Другой пример - сделать что-то, что было исправлено, настраиваемым (возможно, путем передачи значения или зависимости, используемой для получения значения) с параметром по умолчанию, который просто предоставляет старое фиксированное значение.Каждый экземпляр старой фиксированной вещи заменяется (вызовом) нового параметра, так что на разнице довольно легко увидеть, что означает это изменение.У вас есть (или вы пишете) по крайней мере несколько тестов, чтобы дать уверенность в том, что вы не сломали старые входные данные из-за какой-то глупой опечатки, так что вы можете идти вперед даже без полной уверенности в своем тестовом освещении.
Конечно, вам нужно всестороннее тестирование, но оно не обязательно у вас есть.Здесь также есть два конкурирующих императива обслуживания: 1 - не дублируйте код, так как если в нем есть ошибки или поведение, которое может потребоваться изменить в будущем, то вы дублируете ошибки / текущее поведение.2 - принцип открытого / закрытого типа, который немного высокофутулирован, но в основном гласит: «пиши вещи, которые работают, а потом не трогай их».1 говорит, что вам следует провести рефакторинг, чтобы разделить код между этими двумя похожими операциями, 2 говорит, что нет, вы отправили старую, либо она пригодна для этой новой вещи, либо нет, а если нет, то оставьте ее в покое..