La portabilité d'un programme peut se faire à plusieurs niveaux. Pour l'instant, les bibliothèques logicielles telle que POSIX ou Qt se limitent à la portabilité au niveau du code source. Mais il existe d'autre niveaux, comme celui au niveau du bytecode ou encore, mais plus rare, au niveau de l'exécutable.
Du code source
La portabilité au niveau du code source nécessite la disponibilité de l'interface de programmation sur tous les environnements d'exécution cibles. À partir de cela, il suffit de recompiler le code source avec les bonnes bibliothèques.
La portabilité au niveau du code source est également valable avec les langages interprétés, où cette fois-ci, c'est la disponibilité de l'interpréteur et de sa bibliothèque standard qui est nécessaire.
Du bytecode
Le bytecode est une sorte de code intermédiaire, que l'on peut considérer comme « à moitié compilé » ou « pseudo-compilé ». Ce code n'est pas destiné à être exécuté directement par le système d'exploitation comme tout programme compilé. C'est une machine virtuelle qui est chargée de cela. Son but est de servir de couche entre le programme et le système d'exploitation, rendant ainsi parfaitement portable tout programme, à condition qu'il existe une machine virtuelle sur le système cible.
Aujourd'hui, le principal représentant est Java, dont la machine virtuelle est disponible, sur les principaux systèmes d'exploitation (Windows, Linux, Mac OS, FreeBSD ou encore Solaris).
Le langage de programmation de Microsoft, C# dispose également d'une machine virtuelle, mais il est destiné à fonctionner uniquement sur Windows. Cependant, le projet Mono a pour objectif d'implémenter un telle machine virtuelle et ses bibliothèques logicielles.
De l'exécutable
Ce niveau de portabilité est notamment offert par le format d'exécutable Universal binaries d'Apple, ou des applications s'exécutent indifféremment sur architecture PowerPC et x86, mais uniquement sur Mac OS X