A distributed development setup allowing many different developers to work on the same application.
Software enginering sometimes is, arguably, difficult but has some advantages few other disciplines can offer. One of which is where you work. Granted you are equipped with a notebook and have access to a (stable) internet connection, you can almost work from any place you like.
We recently started to work with remotely located front-end developers. To make the lives of all of us easier, we decided to refactor some parts of our application.
Despite a notebook and an internet connection, another important aspect is the application's architecture and how the application can be accessed while developing new features. With many developers working on the same application, progress and features somehow need to be synchronised between all developers.
We at Lovely Systems build modern web applications and web sites. These web applications comprise of several different parts, with front-end and back-end probably being the roughest distinctions.
Each part may consist again of several smaller parts i.e. in a microservice fashion. But that's not the topic of this post. We want to focus on something different. (By the way, with all the hype around microservices, you may also want to read Martin Folwer's post on MicroservicePremium.)
For the sake of ease, let's agree on some common terminology. When I speak about the back-end, I mean the business logic and it's data (and data store). Usually, the back-end is implemented as one or many REST services.
The front-end (at least the application in mind), is an Angular JS application which uses the REST API (i.e. the back-end) to retrieve all necessary data and manipulate said data. However, it doesn't really matter if it's Anuglar JS or something different.
Before refactoring anything, make sure certain goals are set. This not only allows you to know what you're heading for, but (perhaps even more important so) also when something goes out of hand.
The application consists of HTTP server(s) (nginx) to handle incoming requests and serve static files, an API and the implementing services (including a data store), and the front-end (of course there is more such as caching but I'll neglect that for this post).
Image 1 - Simplified application archtitecture
The application as such is quite common and nothing special. The details are in the deployment process. Each part of the application comes with all it's dependencies on its own (much like microservices, but not quite). For instance, when the RPM package for the REST API service is deployed, it not only installs the Python application (we've implemented the REST API with lovely.pyramid), it also installs e.g. the Nginx configuration files which describe the locations and upstreams necessary for the API.
The same is true for the front-end. The RPM installs the Angular JS app and other static files (HTML, CSS, Images, etc.) and the Nginx configuration files which describe how the front-end shall be served.
The Nginx loads all necessary configuration files when starting up (or when the config is reloaded).
The architecture can only theoretically be compared with microservices (but is pretty close in that sense). For instance, when we implement a new endpoint we treat it (and even call it) as a new service which should only interact with other services (endpoints) on a high level. What still differs the described architecture from microservices is the deployment process and a data store which is used by many services. Microservices would probably install their very own data store (and other requirements) for each service. (However, with rather minimal additional work, we could implement microservices. That is, move the 'high level' access which I've described before to an even higher level e.g. their own HTTP REST endpoints. And also granted, that we want to deal with the increased deployment and coordination overhead introduced by the microservices architecture.)
Developing with the new architecture is much easier and stream-lined. For
instance, the front-end development setup only needs to serve a bunch of static
files. That's it. We still use a rather heavy nginx config (to imitate the
staging/production environment) when developing new features locally but it
could all be replaced by node.js, grunt-serve or even
a simple Python one-liner such as
python -m SimpleHTTPServer 9876.
The API calls the Angular JS application is performing, can be done against a central development environment (i.e. cloud) or against a locally installed instance.
So, our new friend, the remote developer (or any other developer) only needs to setup a light-weight Nginx (which isn't too complicated since we have convenient bootstrap scripts for that) and configure the Angular JS application to use our centrally installed API. After that, one can start to implement new features.
A loosely coupled architecure helps tremendously. Each part of the application can be installed on different machines, for either production or development use.
Of course, a convenient and suitable architecture is only the start. What still needs to be taken care of is e.g. communication, coordination between all involved people, quality assurance, source code management, and a lot more.
But I guess that's it for now and we'll talk about that in later posts.
Written by Lukas Ender
Posted on May 15, 2015