Traditionnellement, le logiciel est apparu pour être exécuté sur une machine physique. Mais on se heurte rapidement à un mur lorsque nous suivons cette voie. Un logiciel peut uniquement être exécuté sur certaines machines, par exemple, car il a besoin d’un certain type de processeur.
Par ailleurs, les logiciels les plus complexes ne sont en général pas exécutés de manière complètement autonome, mais sont intégrés au sein d’un écosystème logiciel. Ces derniers comprennent un système d’exploitation, des bibliothèques, et des dépendances. La bonne version de tous ces composants doit être disponible pour leur permettre d’interagir correctement. Une configuration entre également en jeu, laquelle décrit de quelle manière les composants individuels sont liés les uns aux autres.
Si vous souhaitez exécuter plusieurs applications sur une seule et même machine en parallèle, des conflits de versions émergeront sans tarder. Il se peut qu’une application nécessite une version d’un composant qui se trouve être incompatible avec une autre application. Dans le pire des cas, chaque application devrait être exécutée sur sa propre machine physique. Ce qui est vrai est le fait que les machines physiques sont chères et ne peuvent pas s’adapter facilement à la demande. Dès lors, si les besoins en ressources d’une application croissent, il pourra être nécessaire de migrer cette dernière vers une nouvelle machine physique.
Le fait qu’un logiciel en cours de développement soit utilisé dans différents environnements soulève un autre problème. Un développeur écrit du code sur le système local et l’y exécute à titre de test. L’application passe par différents stades de test avant de passer en phase de production, y compris un test d’environnement pour la certification de qualité ou un environnement de simulation pour être testé par l’équipe produit.