Starting a new development project in an unknown environment
Surya Ambrose9 min read
When I first arrived at Theodo, my whole developer world was shaken. I used to work in a robotics company, developing in C++ and Python with a Ubuntu machine, deploying on a Debian environment. And within a week, I was asked to develop new features in Javascript, on a MacBook, that needed to be deployed on an iPhone 8 and a Nexus 5. I had no clue what tools I needed to use. I was not even able to install a simple thing on that stupid Mac that did not know my dear apt-get
. But I still had to work, so I followed the guidelines of my new colleagues, installing dozens of things whose purpose were unknown to me.
Only a few days later did I realise that, actually, I had already used each one of the tools I was told to install. But different ones, for different platforms, and different languages. The principles were the same, the purposes were the same, but the names were not. And when I understood that, everything became much simpler.
So today, I offer to take you on a trip. A trip through a development project. And for each tool that we will encounter on that journey, we will see which one is used depending on the platform, the programming language, or the project nature.
Are you ready?
Then buckle up!
Getting your environment ready
All right, turn on your computer, and let’s get started. You just arrived at your new job, and you got your machine, but have no idea how to install on it the stuff you are supposed to use.
It usually comes in two flavours.
Compiled executables
Those executables are binary files, specifically made to be read by a specific system (OS and CPU architecture). In other words, you cannot use the same binary files on different machines, which is why you will always need to use something specific to YOURS. The consequence is that a developer must find a way to make binary files available for every supported system.
And you need to find where they are. To make this easier, each platform provides a package manager, a tool made to install so called “packages”. A package is an archive containing all the files needed for installation. The package manager knows where to look and automatically downloads the package upon request.
Those binaries are generally installed “globally”, in the “system” part of the filesystem (/usr/lib, /Library, C://, …). Any project on your machine will have access to it (that’s cool, right?). The negative part is that you usually can have only one version installed (so if you need two different versions for two different projects, it might be painful).
Package manager | apt-get | Homebrew | Chocolatey |
Executables go in | /usr/bin | <PKG-ROOT>/bin | |
Libraries go in | /usr/lib | <PKG-ROOT>/lib | |
Headers go in | /usr/include | <PKG-ROOT>/include | |
Resources go in | /usr/share | <PKG-ROOT>/share | |
Natively installed | Yes | No | No |
Comments | PKG-ROOT is /usr/local/Cellar/<package> Symbolic links are then added in /usr/local/bin /usr/local/lib /usr/local/include /usr/local/share for each package | There are many different installation rules. You can find more details here. |
Uncompiled code
Code can sometimes be directly executed without being compiled, using a program called an interpreter. Thanks to that, this code is not dependent of the machine it is run on, only of the language it is written into, and the interpreter version. It is then much more interesting for the developers to distribute their product via package managers related to the language they use rather than the platform you are using. Another difference is that these package managers use a repository containing the packages which can be installed, instead of storing them somewhere and pointing them out by a entry in a text file. Those packages are usually installed locally (directly in the project folder), so that only this project can use it. The good part is that you can use different versions of the same package for different projects.
language | Package manager | Main registry |
---|---|---|
pip | https://pypi.python.org/pypi | |
yarn | https://registry.yarnpkg.com | |
composer | https://packagist.org/packages/ | |
maven | https://mvnrepository.com/artifact/ | |
gem | https://rubygems.org/gems/ |
language | Packages go in | Comments |
---|---|---|
A global folder on your system :( | pip stores packages in the root filesystem by default, in the user filesystem upon request. To store packages within the project, you need to use the virtualenv package | |
./node_modules | For NodeJS also exists npm , which actually is the official one. Both are similar and works the same way, although yarn seems to be way faster | |
./vendor | ||
./target/dependency/BOOT-INF/lib | ||
A global folder on your system :( | gem also installs packages in a folder unrelated to your project, but you can choose which one it will be. To install packages in a folder specific to your project, you can use bundler |
Handling cross-platform development
Now, let’s say you are working on a project where your target is different from your development environment.
This is actually extremely common. You can be working on a website for instance (you probably don’t have the exact same configuration as the server machine), or you could be developing a mobile application, or a library for a robot. You could also be working on a program that should be runnable on Mac AND Windows, etc..
To test that your product actually works on the target environment, you will need to use emulation tools (or have an equal number of devices and targets, which is not always practical). For mobile development, there are powerful simulators which allow the testing of several shapes of mobiles and several OS versions. They also allow the emulation of mobile-specific inputs, like GPS.
The ones I know about (there might be others):
- XCode’s Simulator (iOS)
- Android Studio’s emulator (Android)
- GenyMotion (Android)
For “computer” environments, you can use virtual environments. They can be created by:
- Vagrant (virtual machine handler)
- Docker (virtual environment running on your real machine)
Docker is lighter than a virtual machine, but provides less insulation (although this is not important in most projets). Also, only a Linux environment can be emulated. Vagrant is actually more a virtual machine manager. It is based on programs like VMWare and VirtualBox to build virtual machines from a script.
To know more about the difference between Docker and Virtual machines, you can have a look at this blog article which gives a generic explanation of the difference or this StackOverflow’s answer giving more technical details.
Testing your feature
You now have everything you need to install the dependencies of your project. You are ready to go, ready to develop on any platform, in any language. But after finishing your first feature, a new problem arises: tests.
What are you supposed to use to test your code on this alien environment? Well thankfully, as you would expect, there is always at least one unit test library for each language.
language | Framework |
---|---|
pytest | |
jest, mocha, chai | |
junit | |
gtest (among many others) | |
phpunit | |
(built-in) |
There are of course several other kinds of tests (end to end, integration, api, stress, static type) but they are not as widely represented as unit tests, because their value relies more on the language, and what it is used for.
For instance, static type tests on c++ or java is useless (because proper typing is assured by the language itself). If you develop a graphical interface in python, you will need to use a specific framework, which will (or will not) provide end to end testing capabilities. But Python won’t.
Another thing you might want to do is to make sure your project works under different versions of the language you use. To test that, you can use a language’s version manager if it exists (so you don’t need to use several VMs/docker images with different languages versions).
Language | Version manager |
---|---|
nvm | |
rvm | |
pyenv |
This feature is also widely used for solely development purposes, if different projects you are working on need different language versions. This helps you to keep a clean environment and is very handy.
Deploying the feature
Alright, your tests are fine, your code has been approved by a peer, now you need to push it into production.
Until now, we did not really care about the type of project you were working on (installing dependencies and testing your code is something to do in every project). But the deployment will be much more tied to the nature of the project. Besides, not only do we need to deploy our new feature, we also need to make sure our dependencies are available on the target machine.
I am working on a library for others to use
Here, “deploying to production” means “making the newest version available for download and installation”. Do you remember what we said about package managers? That they help you install software made by others? Well, now you are on the other side of the mirror. So you need to ask yourself one simple question:
- Am I trying to distribute compiled code, which is therefore platform specific?
Whichever the answer, you will need to look for how to build a package for one or more specific package managers.
The answer will only guide you in your selection. Platform-wise package managers, or language-related package managers?
I am working on a web application
In this case, deploying to production means sending the newest version of your product on the server hosting it. The number of possible tools’ combinations is too huge to be listed here, so only the main ones will be described.
- Pack your application in a docker, send it to a docker host using
docker
api, then start your application with- Docker Compose (starts one or more containers on one host)
- Docker Swarm (starts one or more containers on several hosts)
- Kubernetes (very close to docker swarm, developed by Google)
- Or one of these
To know more, you can find a pretty good comparison of Kubernetes and Docker Swarm here.
- Configure a remote machine to download your application (from github for instance) and its dependencies (also called “provisioning”)
- Chef
- Capistrano
- Shipit (Alternative to capistrano in JS)
- Ansible
- and many others
By the way, although Chef and Capistrano are perfectly capable of deploying your app AND configuring the host server by themselves, it seems better to actually use both, as explained in this article.
I am working on a mobile application
In this situation, you need to package your app and send it to a storing server. It is actually pretty similar to providing a library, except the tools use to package and upload the app are quite different. The main one I encounter is Fastlane
And that’s it
Hey, you made it. You actually managed to install your dependencies, on your machine and on the target system. You developed your feature using an emulated environment, and tested it using an appropriate unit test framework. Finally, you deployed your newly added feature to prod.
Congratulations!
You managed to work efficiently, even in an alien environment.
You can be proud!