6

The third major curse of computer programming is backward compatibility. You have to fix bugs which will unavoidably be found. Some syntax is not really a good idea, and has to be modified to be less bad. These modifications are evil. Either you fix the bug and the behavior of a command changes, or you keep the bug to ensure backward compatibility. The first possibility can have dramatic consequences, e.g. when you use latex to manage your hotel and that \removeClient is modified so instead of removing a patient from the database, it sends a robot to actually remove him from the room, and you tex an old file. The second one leaves you with a \removeClient macro along with a \deleteClient macro, and you never know which one does what you want. This problem is especially acute in latex, where you are always hacking around suspicious behaviors to get what \expandafter\noexpand\noexpand\noexpand you want : a bug correction can wreck your document at the worse time.

I noticed that several packages deal with this kind of problem by providing a compatibility command. For example, pgfplots has \pgfplotsset{compat=1.7}, siunitx has a version-1-compatibility option, mhchem is invoked with \usepackage[version=3]{mhchem}. Other strategies include designing your package correctly from the beginning and, in the rare cases where a drastic change is needed, well, you can afford both \pgfdeclarearrow and \pgfarrowsdeclare. Alternatively, not caring at all about the problem is an option.

From the point of view of an user, the first solution is extremely convenient, as you don't have to find old versions of the packages and fight to get it to work. My question is, how does this work from the point of view of the latex package author ?

Is it better to have conditionals inside your main code, or is it better to have some compatibility files, loaded when compatibility is sought, that contain the compatible definitions of the relevant macros, which supersede the regular ones ? Or are there other solutions ? And how much difficult it is to maintain a latex code base where you want to take such measures ?

All in all, what are best practices when dealing with backward compatibility in latex ?

  • 2
    Isn't the only answer 'it depends'? That is, it depends on the function of your package, who is using it, how they are using it, whether other packages are using it (and, if so, whether you maintain them or somebody else), the nature of the bug etc. If, say, the bug is that \removeClient sends a robot to execute the guest rather than deleting an entry from the database, then maintaining backward compatibility would seem less than desirable. – cfr Dec 7 '14 at 22:04
  • I agree that the decision of breaking backwards compatibility or not often depends on the specific modification. However, when you do, it is often good to provide the users with the possibility to switch back to the old behavior, at least in widely used codes. How to do so correctly in latex is my main question. – Georg Sievelson Dec 11 '14 at 22:13
1

In the case of my stackengine package (and every developer's case will vary), in version 1.0, I stored default stacking gaps in lengths, rather than in macros. Since lengths are fixed in machine units at the time of their definition, this had the unintended behavior that if I changed font size (e.g., went to \Large, for example) after setting the stacking gap, the gap was still expressed in the former font size units (which matters if the gap was a scalable length like 1ex).

I changed over to defining it in terms of macros, so that the actual length could be set from the stored macro, just prior to building the stack, thus in the current font size.

But the syntax to define those gaps could no longer remain, for example, \Sstackgap=1ex, which is a length assignment, but instead changed to \setstackgap{S}{1ex}, which invoked a macro assignment.

For backward compatibility, I provided an [oldsyntax] package option. In my case, the change in code was small enough that I included both code blocks in the main code, with the use of a conditional, as such:

% PROCESS PACKAGE OPTIONS
\newif\ifstackengine@oldsyntax
\newif\ifstackengine@usestackEOL
\DeclareOption{oldsyntax}{\stackengine@oldsyntaxtrue}
\DeclareOption{usestackEOL}{\stackengine@usestackEOLtrue}
\ProcessOptions\relax
\ifstackengine@oldsyntax%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% THIS VERSION 1.0 SYNTAX WAS PHASED OUT %%%%%%%%%%%%%%%%%%%
%%%%% (but can invoke with \usepackage[oldsyntax]{stackengine} )
\newlength\Sstackgap\newlength\Lstackgap
\newcommand\stackgap{\if S\stacktype\the\Sstackgap\else\the\Lstackgap\fi}
\global\setlength{\Sstackgap}{3pt}
\global\setlength{\Lstackgap}{\baselineskip}
\else%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% AND REPLACED WITH THE FOLLOWING %%%%%%%%%%%%%%%%%%%%%%%%%%
% Again, thanks to Prof. Gregorio for his lucid explanation that helped
% improve this package.  See:
% http://tex.stackexchange.com/questions/123443/
% defining-a-length-that-scales-with-fontsize-changes/123470#123470
\newcommand{\setstackgap}[2]{\@namedef{#1stackgap}{#2}}
\newcommand\stackgap{\@nameuse{\if S\stacktype S\else L\fi stackgap}\relax}
\setstackgap{S}{3pt}% SHORTSTACKING GAP BETWEEN ITEMS (SAME AS \shortstack)
\setstackgap{L}{\baselineskip}% LONGSTACKING GAP BETWEEN ITEMS (def. \baselineskip)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\fi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.