In a traditional organization roles were clear: there was the software development group and the server administration group. The software development group would build the applications which would then get deployed to the target servers in specific locations with specific service contracts. Even within organizations I’ve witnessed these being as stringent as a highly scrutinized legal agreement between the two organizations. The server administration group would be responsible for keeping all servers, networks, and systems running. Up until the application processes in most cases.

These two groups have a mixed level of success working together. In some instances the two groups were unable to establish trust and compassion. In others they worked really well together, optimizing the systems and deploying new technologies at a fast pace. Ecosystems like Java Application Servers provided facilities to ease some of the natural conflict between keeping a system reliable and deploying new revisions.

With traditional divides execution environments provided the seam between these two conflicting goals. In a sense scoped to modern computation an execution environment is a place where application code any run to provide a user interface or a network service.

Exploring Execution Environments

My journeys tell me there are many types of execution environments, most are application specific. Let us start with one as old as time.

There is a decent chance for all native languages we begin programming in a stateless terminal application: just a simple terminal connected to stdin and stdout. The application is not interested in the details of storage beyond ensuring the user sees the output. Many of these applications make various assumptions about the execution environment:

  • The entry point is invoked with a single fiber or thread. From a logical perspective this means a single unit of execution trundling forward through the instructions and following the branches as prescribed by the construct of the language.
  • For data input the application may read from stdin. For simple programs this is expected to be a human or under more complicated sytems (*nix) it’s expected to be a pipe.
  • For data output the application may sink data to stdout, which again might be human or another application.
  • An application of this class generally does not consider persistence. A user will retain the information needed or may construct a series of pipes which will provide the data in an appropriate format.

I feel like we can put another point at the traditional 3-tier web application. As a general rule I would consider the underlying web framework as apart of the execution environment since it changes the model of computation.

  • There are multiple entry points based on the specific request made by the user.
  • Web frameworks dictate the model of computation. For example in NodeJS we use cooperative multitasking with implicit yields within an event reactor. On the other side of the computational coin we’ve got the Java Servlet specification which projects the conceptual model a thread-pre-request model. In the interest of rounding: many Ruby and Python web frameworks operate in a single-threaded single-process model where only a single thing is happening at any given time. Once in production those frameworks are typically configured to run as single-threaded multi-process setups.
  • Persistence is handled in two different ways: one as state which is included in the request and the other as a trusted store mechanism. The state carried in the request is usually just a handle to trusted state, however in the case of cookie based sessions or in some uses of cryptographic web tokens may contain all the state. For traditional 3-teir web applications trusted state is stored through a network connected relational database and serves as the primary persistent memory of the application.