Deprecations vs. backwards-incompatible changes for tightening behavior that hides probable developer error

94 views
Skip to first unread message

Tim Graham

unread,
Dec 21, 2015, 10:09:31 AM12/21/15
to Django developers (Contributions to Django itself)
I'd like to ask for opinions about whether or not deprecations are more useful than making small backwards incompatible changes when they move Django in a direction toward unhiding probable developer error.

An example from a past release is the validation of fields in select_related() [1]. This was done as a backwards-incompatible change in 1.8: invalid fields immediately raised an error. Would you rather a change like that go through the normal deprecation cycle (pending deprecation, deprecation, error)? My reasoning against it is that it continues to hide probable errors in new and old code (especially in the first release since PendingDeprecationWarning isn't loud by default) and the cost for users of fixing existing code is likely low compared to the overhead of a deprecation cycle in Django. Did anyone find this was not the case in their own projects?

The question came up again with the behavior of the default error views silencing TemplateDoesNotExist when a custom template name is specified [2].

[1] https://docs.djangoproject.com/en/dev/releases/1.8/#select-related-now-checks-given-fields
[2] https://code.djangoproject.com/ticket/25697

Carl Meyer

unread,
Dec 21, 2015, 1:45:51 PM12/21/15
to django-d...@googlegroups.com
Hi Tim,

On 12/21/2015 08:09 AM, Tim Graham wrote:
> I'd like to ask for opinions about whether or not deprecations are more
> useful than making small backwards incompatible changes when they move
> Django in a direction toward unhiding probable developer error.
>
> An example from a past release is the validation of fields in
> select_related() [1]. This was done as a backwards-incompatible change
> in 1.8: invalid fields immediately raised an error. Would you rather a
> change like that go through the normal deprecation cycle (pending
> deprecation, deprecation, error)? My reasoning against it is that it
> continues to hide probable errors in new and old code (especially in the
> first release since PendingDeprecationWarning isn't loud by default) and
> the cost for users of fixing existing code is likely low compared to the
> overhead of a deprecation cycle in Django. Did anyone find this was not
> the case in their own projects?
>
> The question came up again with the behavior of the default error views
> silencing TemplateDoesNotExist when a custom template name is specified [2].

Obviously the first question is whether the behavior in question is
documented. If it is, then a deprecation path is definitely required. On
the other hand, if the current behavior contradicts the documentation,
then it's a bug and can just be fixed.

But the two examples you cite have an edge case with behavior that is
unspecified in the documentation. The next question I would ask in those
cases is "Can I imagine a scenario in which someone is intentionally
taking advantage of the current behavior - it's not just hiding an error
in their code? How likely is that scenario? How reasonable is their use
case? And if we conclude that there may be people out there with a
reasonable use case for the current behavior, how hard would it be for
them to change their code to continue working as-is, if Django's
behavior is changed?"

So to take the TemplateDoesNotExist case with the built-in error views:

The one scenario I can imagine is that someone has written their own set
of reusable error views wrapping the default ones with a different
default template naming convention, but they still want to use their
custom reusable error views in projects that may or may not choose to
actually provide the custom error templates.

I think this use case is reasonable and the scenario is possible, but
I'll concede that it may be unlikely. But if someone were doing this, I
would consider it a perfectly reasonable use of the existing API
contract ("nonexistent templates fall back to the default error view
content"), not abuse of a bug.

To adapt their code to the new behavior, they would need to add a
try/except for TemplateDoesNotExist in their wrappers. Not totally
trivial (and might require them to write some new tests), but not
unreasonable.

So for me, this case falls in a murky area. I tend to think it's better
for us to err on the side of being more backwards-compatible when the
current behavior isn't clearly a bug, and someone might be reasonably
relying on it. But I concede that it's in a grey area, and wouldn't
stand in the way of a decision to just treat it as a bugfix.

In cases where we really can't construct any realistic scenario for
intentional use of the current behavior, then I think we just make the
change right away and call it a bug fix. I don't think it's a bad thing
to cause code that was previously silently broken to start breaking loudly.

I think `select_related` with invalid field names is closer to this end
of the spectrum. I _can_ construct a scenario for intentional use of
that, but it's very unlikely and feels much more like abuse of a buggy
API. So if I did earlier suggest that the select_related change should
have gone through a deprecation path, I hereby retract that suggestion :-)

Carl

signature.asc

Shai Berger

unread,
Dec 21, 2015, 6:58:36 PM12/21/15
to django-d...@googlegroups.com
On Monday 21 December 2015 20:45:26 Carl Meyer wrote:
>
> Obviously the first question is whether the behavior in question is
> documented. If it is, then a deprecation path is definitely required. On
> the other hand, if the current behavior contradicts the documentation,
> then it's a bug and can just be fixed.
>

+1

> But the two examples you cite have an edge case with behavior that is
> unspecified in the documentation. The next question I would ask in those
> cases is "Can I imagine a scenario in which someone is intentionally
> taking advantage of the current behavior - it's not just hiding an error
> in their code? How likely is that scenario? How reasonable is their use
> case? And if we conclude that there may be people out there with a
> reasonable use case for the current behavior, how hard would it be for
> them to change their code to continue working as-is, if Django's
> behavior is changed?"
>

Again, +1.

Another example where we changed immediately was when we disallowed
select_related in automatic transaction mode, in 1.6.3. In that case, we had a
stronger argument: 1.6 changed automatic mode, so that code which had been
correct suddenly gained a potential for data-corruption.

Shai.

Cristiano Coelho

unread,
Dec 21, 2015, 10:03:29 PM12/21/15
to Django developers (Contributions to Django itself)
Hello, the select_related change was a really good one, after updating I found around 3 or 4 queries that had a typo in select_related which was obviously never noticed before. In this project finding those errors was not complicated at all, but I believe that on a big project that also has poor testing, that change might have given the team a few days/weeks of bug hunting.
Now the question is, even if it goes through a deprecation cycle, what if the team isn't even looking at debug warnings? The issues might still go undetected until it is too late and start crashing everywhere, so I'm not really sure how big of a difference it would have made.
Reply all
Reply to author
Forward
0 new messages