Writing code that will still run years from now
Programming languages change. Libraries change. Some code from 5, 10, or even 20 years ago might still run and produce expected results, whereas some code from 2 years might fail with a syntax error. This is partly inevitable, since languages evolve (at least, most do). Developers have a responsibility to maintain their code. But sometimes, stability is an important requirement in production code, and code should simply run for 10 years without the need for someone going through the code every year to adapt it for language changes. Or I might have small scripts, for example for scientific data analysis, that I need to revisit after not touching them for years. For example, at meteorological offices there is a lot of operational Fortran code even for non-speed-essential parts, and code stability is one of the reasons. I've heard fear for instability is one of the objects they have against moving to Python (apart from language inertia of course; it's only possible for new code not dependent on old code). Of course, one strategy for stable code is to freeze the entire operating system. But that is not always feasible.
I'm using Python as on example, but the issue is not limited to Python in particular.
Documents on Python compatibility issues
In the case of Python, there are several documents outlining policy for backward-incompatible changes.
PEP-5
According to PEP 5:
There must be at least a one-year transition period between the release of the transitional version of Python and the release of the backwards incompatible version. Users will have at least a year to test their programs and migrate them from use of the deprecated construct to the alternative one.
Personally, I consider that one year is rather short. It means I might write some code, and 1½ years from now it won't run anymore.
PEP 291
PEP 291 contains an incomplete lists of guidelines of things that should be avoided in order to maintain backward compatibility. However, it relates only to Python 2.x. As Python 2.7 is the final release in the 2.x series and Python 2.7 is bugfix-only, this PEP is now only of historical interest.
PEP 387
There is also PEP 387 on backward-incompatible changes. PEP 387 is a draft and not official policy. In June 2009, this was discussed on the Python-ideas mailing-list. Part of the discussion focussed on how developers can write code that is robust against language changes. One post listed some advice on what not to do:
Along with this there are several rules you can infer that are probably true most of the time: don't call stuff starting with
"_"
, don't monkey- patch anything, don't use dynamic class replacement on objects from classes other than your own, don't depend on the depth of inheritance hierarchies (for example, no".__bases__[0].__bases__[0]"
), make sure your tests run without producing any DeprecationWarnings, be mindful of potential namespace conflicts when adding attributes to classes that inherit from other libraries. I don't think all these things are written down in one place though.
In addition, there were some points about "mine fields" (new features likely to change) and "frozen areas" (very sold APIs virtually guaranteed not to change). Quoting Antoine Pitrou:
I think the "frozen area" should be defined positively (explicit public APIs and explicitly guaranteed behaviour) rather than negatively (an explicit "mine field"). Otherwise, we will forget to put some important things in the minefield and get bitten later when we need to change those things in a backwards-incompatible way.
There doesn't seem to be any conclusion from this thread, but it gets pretty close to the core of what I'm looking for. The thread is almost four years old, so perhaps the situation has changed or improved. What kind of code is likely to survive, and what kind of code is more fragile?
Porting guidelines
In addition to the documents outlined above, each Python version comes with a porting guideline: porting to Python 3.2, porting to Python 3.3, etc.
Useful compatibility
PEP 3151 introduced me to the concept of useful compatibility. In my own words, this boils down to the idea that only if code is carefully written language developers need to be careful to maintain compatibility. It doesn't really define useful compatibility, but I think it's similar to the ideas I quoted from the PEP 387 discussion above.
From the programmers' point of view
As a programmer, I know that Python will change in the future and that people — most notably myself — will try to run my code perhaps several years from now in a Python version that is one, two, or perhaps three minor versions up. Not everything will be compatible, and in fact it's easy to come up with code that will fail (I once encountered code stating if sys.version[:3] != '2.3': print 'Wrong version, exiting'
). What I'm looking for is a set of guidelines on what to do and what not to do to enhance the chances that my code will still run unaltered in the future.
Are there any such guidelines? How do I write Python code that will still run in the future?
My question relates both to the Python core, to its standard library, but also to commonly used add-on libraries, in particular numpy
, scipy
, matplotlib
.
EDIT: So far, two of the answers relate to python2 vs. python3. This is not what I mean. I know about tools to migrate from Python2 to Python3. My question relates to language changes yet to come. We can do better than a crystal ball in finding coding guidelines that are more stable. For example:
import module
is more future-proof thanfrom module import *
, because the latter can break code ifmodule
grows one or more new functions/classes.Using undocumented methods may be less future-proof than using documented methods, as something being undocumented may be a sign of something being not stable yet.
It's this kind of practical coding advices that I'm after. Since it's about present→future, we can limit ourselves to Python3, because Python2 is not going to change anymore.