How To Set Up Your Coding Environment
Tags: node, python, ruby
Published: 2024-02-06 16:42:37

Many coding tutorials start with a blanket statement that tells you to set up a project environment. Here are some examples:

- Make sure to have Python available
- Create a Ruby environment
- You need to have Node installed

You’re told what to do but not how to do it. You might know what to do, but oftentimes, it might be foreign to you.

If the latter is true for you, you might be wondering, “, So now what?”

Let’s explore what setting up an environment means, why it is important, and the steps you need to take to set up your environment. In this article, we will show you how to set up an environment for Python, Node, and Ruby since we come across these ourselves most of the time.

## What Is An Environment?

As it relates to programming, the concept of an environment is pretty straightforward. It is the context of the processes you run to compile, interpret, and/or run some code. When you are instructed to set up or have an available environment, there is often an indication of what language and version you need.

One project might require you to have the latest Node 18 available, while another tutorial might indicate that you need Node 20 available. You can’t have both, right? Well, you actually can. It just requires additional setup on your part to make sure that you can have both of these language runtimes installed at the same time. You can go even further and have the same Node version installed multiple times. This is not something you will need for Node, but for a Python runtime, this can actually be beneficial.

Regardless, an environment is about creating a situation where you can decide exactly which language runtime version you are running your code against and make sure this language runtime has a dedicated storage mechanism for library versions to be made available to the code you want to compile or run.

## Why Setting Up An Environment Is Important

There are three reasons why setting up your environment is important:

1. Availability
1. Stability
1. Flexibility

Let’s explore each of these.

### Availability

Your operating system of choice might come with Node, Python, or whatever language runtime you need. But, it might not. On top of that, if you rely on an installed version pre-packaged with your operating system, it might not be the specific version you need. If a runtime version is made available by your operating system, it is most often an older version of the language runtime you actually need for the content you are reading.

### Stability

Often, a codebase has specific version requirements on the dependencies that you use. What if you are dealing with multiple codebases that all need the same runtime and packages, but in different versions? Are you going to uninstall and re-install all these versions whenever you need to switch between projects?

What if you rely on an operating system-packaged version of a language runtime? MacOS comes with Ruby installed. But you shouldn’t really install random Ruby gems (Ruby language-based libraries) into the Ruby installation provided by your operating system. You simply cannot tell what adverse interactions you might cause; let alone update the operating system installed package versions. You could end up with a system that is unstable or might not even boot correctly anymore.

On top of that, after days or weeks of picking just the right packages and working with those, there’s an operating system upgrade, and all your modifications to the system-provided language runtime are gone, making you have to apply them all over again. 

To make your day even worse after suffering such a disaster, your code suddenly won't compile or run anymore because the operating system-provided language runtime received some much-needed version updates bringing breaking changes to your codebase.

### Flexibility

As you might be able to tell, it is very beneficial to have just the right version of a language runtime for your code with all the packages it needs on your machine. 

When you need to switch to a different codebase with different requirements, make sure that the switch between these requirements is as quick and easy as possible. You might even want to be able to run the same language runtime side by side for different projects using different versions and different installed packages.

There are many ways you can get a running Node, Python, or Ruby environment. Something to be aware of is to try and keep your system-installed language runtime untouched. The runtimes supplied by your operating system are important. In fact, often, they are reset and cleaned with a system update. If you do not isolate your system-installed language runtime from installing and updating random packages, there will come a day that parts of your system or your whole system will come down with random issues. If you install packages used for development into your system-installed language runtimes, there’s a good chance a system update will reset parts of your development environment and thus break your workflow.

In general, our advice is to keep it simple by separating these kinds of things. Leave your host OS untouched and try to run as much of your development work in an isolated environment. This also makes it easier to transfer your local work to a server later.

## Get Availability, Stability, and Flexibility with A Project-Specific Language Runtime

Isolating your language runtimes between codebases and ensuring each runtime meets the codebase’s exact version requirements and dependencies gives you all the power you need to deal with many codebases on the same machine simultaneously.

Each language runtime has different ways to set up such an environment. Often there are even multiple ways you can achieve the same result.

In this article, we will share what environment setup strategies we like to use at Stream for:

- Node
- Python
- Ruby

## Setting Up a Node Environment

For Node, internally here at Stream we tend to use [NVM](https://github.com/nvm-sh/nvm). It can be installed on a Mac with:

```shell 
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
```

Make sure to check the current version of that NVM installation command at the [GitHub page of NVM](https://github.com/nvm-sh/nvm). There you can also find instructions specific to other operating systems beyond MacOS.

On a Mac, do not use [HomeBrew](https://brew.sh/) to install NVM. The NVM project recommends using their script. We had a few issues using NVM installed through HomeBrew over the years — issues we did not have when using their installer script.

The README of NVM specifically states the following:

> Homebrew installation is not supported. If you have issues with homebrew-installed nvm, please brew uninstall it, and install it using the instructions below before filing an issue.

Once you have installed NVM, you also need to install a Node version. Running this command without a version will install the latest version of Node.

```shell
nvm install node
```

If you specifically want to install Node version 21.1.0, you could run the following command:

```shell
nvm install node 21.1.0
```

To list installed Node versions, run the following command.

```shell
nvm use 21
```

This command will pick the latest installed version of Node 21.

And that’s it. 

You are now ready to start using Node on your machine in isolation. Each installed Node version has its own storage for Node packages. With the way Node handles packages, that’s all you need to do. Node and NPM are quite powerful in dealing with dependencies correctly, and this fact allows you to safely share a specific node version across multiple codebases without issues.

## Setting Up a Python Environment

For this setup instruction we are assuming Python 3.9. But you can pick any reasonable Python version you need, these instructions should work on lower versions of Python as well. You can discover which version you have installed by running the `python --version` command.

Also, we are using [venv](https://docs.python.org/3/library/venv.html) to create and manage our so-called virtual environments. A virtual environment is defined at the file system level by having a folder with specific content at a specific location in your file system. Usually, this virtual environment folder lives at the root level of the codebase you are working with.

So at the root level of a specific codebase, you need to run the following command:

```bash
python -m venv my-new-venv
```

What actions does this command perform?

- create a directory in our project folder (called  `my-new-venv` - we could also name it any other way)
- places a `pyvenv.cfg` file inside of the directory (which points to the Python installation from which the command was run)
- create a `bin` sub-directory, which contains the Python binaries and another one for the packages that’ll be installed

<aside>
💡 Note: you can add the `llm-env` folder to the `.gitignore` file.
</aside>

When working with virtual environments in Python, you need to activate it first. You do that by running the following command at the same location as when you created your environment

```bash
// macOS, Linux
source my-new-venv/bin/activate
```

We have a working separate Python environment now and can install the required packages for the project without making those packages available to any other Python code running on your system.

## Ruby

Setting up a Ruby environment is, yet again, different.

To run a Ruby environment in isolation, you need to install some prerequisites first using Homebrew.
```
brew install rbenv ruby-build
```

Next we need to add a bit of configuration to the terminal configuration. We could do this manually, but rbenv has a convenient command for just that.

```
rbenv init
```

You can install specific Ruby versions using the `rbenv install` command.

```
# Install Ruby
rbenv install 2.3.3
rbenv global 2.3.3
```

More details on how to pick a specific version to install is available on [the RBEnv website](https://github.com/rbenv/rbenv).

## Conclusion

By setting up your environment in isolation, you can prevent yourself from a lot of issues when experimenting with code. It makes your code behave more predictable due to the defined state of the runtime environment you are working with. This article should provide you with enough information to get started, but obviously, there is a lot more power embedded in [NVM](https://github.com/nvm-sh/nvm), [Virtual Environment](https://docs.python.org/3/library/venv.html) and [RBEnv](https://github.com/rbenv/rbenv). So make sure to check their documentation.

As always, if there are any questions, feel free to reach out to us over [X](https://twitter.com/getstream_io).