The Dev Environment
Each developer has their own development environment, hosted on their local development computer. Nothing is shared between individual developers. There is no shared development server, nor are there any shared databases. Everything is local.
For complete instructions on setting up your local development environment, see this page.
Linux (Ubuntu 16.04 LTS Precise Pangolin) and Mac OS X (10.8+) are used for development. Windows is not supported as a development environment. (Front-end testing of browser code running under Windows is done on dedicated AWS instances.)
Test- and Behaviour-Driven Development
Full test suites (TDD/BDD) are developed along with each separate application/service and hosted, as well as run, as an integral part of their source code. Test coverage should be very high - with a framework such as Rails, 100% is well within the bounds of reach.
Live External Services
The development environment must never access live external services. All such calls must be stubbed or mocked away. This is a prerequisite for automated testing and Continuous Integration, which in its turn is a prerequisite for such things as Continuous Deployment.
Local Back-end Tests
When developing and testing a service, it runs in full isolation: other services called by the service under development are mocked/stubbed away. Development and testing may not at any time affect the state of production databases - all environments must at all times be kept fully apart. Rails manages local development and test databases automatically.
Back-end services run their tests only against themselves, not against other services. All accesses to other services, local or external, are stubbed out or mocked away using pre-recorded responses.
Local Front-end Tests
Front-end applications (clients) test against the full SOA stack. Care must be taken to allow tests to run in parallel in multiple copies: in order to avoid collisions, values entered in forms, such as names, email adresses, credit card numbers and so forth, must not be constant where database uniqueness is required, and all tests must clean up after themselves. Rails handles this automatically.
It should be pointed out that the tests run locally are the same tests subsequently run by the Continuous Integration server. Only the context varies. This applies to both front-end and back-end tests.
To the developer, the development cycle essentially is as follows:
- Prerequisite software is installed (compilers, interpreters, basic tools, etc).
- The developer checks out their current project and/or navigates to its home directory.
- The latest changes are pulled from the source control server.
- If there were any changes, the developer runs the local test suite - a one-line terminal command - to see that the changes don’t break code being worked on locally.
- If there were errors, these are corrected and tests are run again until they pass.
- The developer proceeds to code the task at hand in small steps; new tests are written and run continuously.
- During this process, the developer periodically - typically several times a day - returns to step 1 to keep up with any changes submitted by other developers.
- As often as is possible - preferrably several times a day - and given that all local tests pass, the developer pushes their changes to source control.
- The Continuous Integration server picks up on the changes and runs its test suites. These are the same tests written by the developers as part of the TDD development cycle.
- The developer awaits the results of the C.I. tests. If they all pass, which they should, since nothing must be pushed to the server unless it passes all tests, the developer continues development, typically from step 1 or 4.
- If there are errors, these must be corrected at once. If this proves to take more than a couple of minutes, the changes should be reverted and resubmitted: all branches (development, staging, and production) must always be correct and deployable. Under no circumstances whatsoever must the master branch be left in a broken state for more than a few minutes. Automatic deployment in master and staging halts for an environment if errors are detected during Continuous integration. Furthermore, a broken master or staging branch may never be propagated to the next stage, as this also would propagate the errors. Correctness of code in master, staging and production is therefore essential.
What we see here is a tight development loop, the key property of which is “pull often, run tests at every step, push when tests pass”.
There are excellent IDEs available, fully integrated with the Continuous Integration server we’re using. However, should you prefer working with your usual tools, this is of course also a perfectly viable option. Most Ruby programmers use only an editor and a shell.
One of the strongest features of Git (the source control system used as the basis of the pipeline) is its excellent branching facilities. Thus it is possible to work on features in so-called feature branches which can be shared between developers.