Thus although, with single primitive and target systems, one would nearly always program up from the bottom and down from the top to meet in the middle, in practice the picture is often more complicated. Multiple targets could result in an inversion of this arrangement in the upper layers, and multiple hardware could have the same effect in the lower layers. Thus for example a common strategy in such situations is to define a programming language at some intermediate level, intended for a range of target problems or for several different hardware systems. Having defined the language, one then proceeds to program both upwards and downwards from it.
It is in constructing such an intermediate system, before it has been formally related to either the lower or higher levels, that the greatest demands are made on a programmer’s intuition. Programming is practically never a deductive process, but the depth of induction required can vary greatly according to the situation. The least demanding situation is that in which there is a single primitive system available, and a single clearly defined target system to be implemented, with only a small conceptual gap (i.e. number of software layers) between them. In the early days of programming such situations were common; now they only occur as small components of bigger tasks.
Within the last fifteen years the range of targets spanned by single software projects has increased greatly, and on the whole successfully. The range of hardware has also increased, but to a lesser extent, with greater difficulty and less success. One of the most fruitful areas for improving software productivity lies in devising an effective universal primitive system (such as Iliffe’s ‘Basic Language’).