From idea to production in under 60 minutes — 8 techniques to help you ship code faster.
As an Engineering manager, I spend a lot of my time thinking of ways to reduce cycle time for new feature developments. I’ve always tried to find ways to ship features as quickly as possible to customers. Here are a few practices/techniques that Engineering managers can adopt to reduce cycle time, and improve quality. I’m assuming your team is already using a modern collaborative Git based source code control system like Github or Gitlab, and the pull request (merge request in Gitlab) workflow for code review and merge.
- Small feature branches: Break out features into individual, small feature branches, that can be merged into “master” branch via a pull request. Small feature branches are simpler to test and deploy, de-risking deployments. This increases efficiency, and is crucial for faster code deployments.
- Use pull requests to collaborate: A developer can open a Work in Progress pull request to get early feedback and code review from the team. She can add a “[WIP]” label at the start of the pull request to indicate this pull request isn’t ready to merge yet. This moves feedback to quite early in the development process, increasing efficiency. Once ready for merge, she simply removes the [WIP] label and asks the reviewer to merge.
- Run automated tests on the pull request before merging: Your continuous integration system (like Jenkins or Gitlab CI) can be configured to run your automated test suite on the updated code in your pull request. Before accepting the pull request, the reviewer should ensure the automated tests are passing. This also helps the developer get feedback early on about code that could break the tests. This avoids CI builds failing in your deployment pipeline. The automated tests should ideally be run on a production-like environment, which brings me to the next point. Github and Gitlab both show you the status of your automated test runs within the pull request itself, so it’s quite easy to check before merging.
- Create a production-like environment for every pull request: Infrastructure as code tools like Puppet can help create a new, on-the-fly, production-like environment to automatically test every pull request. These environments can also be used as places to stage and demo features to stakeholders even before merging the pull request. Any feedback from stakeholders can be incorporated even before merge, increasing efficiency. Demos also ensure the developer properly unit tests her code, as it’s her reputation is at stake if the demo is buggy. This practice also gets the developer talking with the DevOps team early on during development to provision the right environment. An automated CI/CD pipeline will also help you create such production-line environments for a pull request, but is not mandatory. All this reduces cycle time.
- Use an automated CI/CD pipeline: A proper continuous delivery pipeline helps you automatically test post-merge changes before deployment. A good pipeline will deploy & test the change on a Staging environment, then on a Performance testing environment before finally deploying on Production. A manual final approval gate is also a good idea, giving the reviewer the opportunity to review the test results and metrics before deploying into Production. Most good pipeline tools will allow you to automatically stop code deployments that don’t meet your test or performance thresholds. This avoids bad code from getting deployed into production, and helps your engineers waste time fixing outages or production issues. This increases efficiency. The pipeline can be also used to create a production like environment for pull requests as mentioned above. Engineers saying their company uses a “Deploy button” are referring to such a pipeline.
- Remove end-to-end automated tests: A good automated testing strategy is likely 70% unit test coverage, 20% integration tests and the remaining performance tests. Most experienced engineering managers know end-to-end automated tests, especially UI tests, break in every sprint or major change. In addition, end-to-end tests don’t help you nail down the root cause of the problem. A developer has to spend time debugging the end-to-end test failure and finding root cause. More often than not, software teams start disregarding the failure notifications. End-to-end tests will likely be marked as “ignored” rather than being fixed to get the build to pass. Unit tests, on the other hand, tell you the root cause upfront as they’re only meant to test a single unit of code. In addition, a reviewer usually ensures all unit tests are made to pass before merging. This increases efficiency tremendously. If a team has the resources and the time, they can choose to run end-to-end tests on their Pre-production environment continuously. This would help catch any remaining bugs. See Martin Fowler’s TestPyramid.
- Use a hosted IDE: Having a hosted development environment for your developers takes away bugs caused by environmental issues with their local development machines, and also catches a few bugs early in the process. Infrastructure as code tools like Puppet will help you setup such environments, or use Koding. Not a biggie though.
- Use Chatops to document and execute every production change: Executing every change from a Chatops console like Slack will allow everyone to see what production changes are being made, and automatically document all changes. Most deployment engines have Slack integration. Chatops avoids downtime as it allows the DevOps team to weigh in on changes before executing them. This increases efficiency.
Subsequent articles here will go deeper into each point, and provide real-world implementations and challenges.