It’s been awhile since the last post about our development process. Today I'd like to focus on some aspects of our developers’ workflow. Despite writing shiny new code which is the most interesting part, developer's life consists of necessary, but not so interesting routine work. In this post I’ll describe our approach to minimise routine. It has helped us not only to devote more time to other activities, but also to enforce our practices. Let's dive into it.
Here are some tools we use:
- GitHub - source code repository
- HubFlow - git extension that makes it easy to use GitFlow with GitHub
- Jenkins - CI tool
- Targetprocess - Project management tool
- Slack - communication tool
We have several teams each working on a separate feature. It's the responsibility of a team to split each feature into user stories. Every user story is then elaborated by a feature owner and put into the 'Planned' state. A user story is implemented by 1-2 developers from the team. Let's imagine that is my turn to do it. Before I start user story implementation, we arrange a kick-start meeting which is aimed to ensure all team members have shared understanding of the user story. It's also a good time for constructive critics and suggestions. After the meeting I open Targetprocess and move the user story from the 'Planned' to 'In Dev' state. It's time to code now!
According to HubFlow, we create a new branch for each work item (a user story, or a bug). There is ‘develop’ branch which is an integration branch accumulating all the work that has been done lately. So, I start with creating a new branch from ‘develop’ for the user story. Then I open GitHub and create a pull request on the newly created branch.
As functionality is implemented, I run builds on Jenkins on my branch. Our builds have rather complex structure, so it's quite difficult to see what's going on in Jenkins. We have different types of builds: short, full and custom builds.
- Short build includes only a subset of all tests and is the most commonly used one.
- Full build includes all tests and performs some extra actions, i.e. building installation packages. It is usually triggered automatically every night on ‘develop’. Full builds are necessary for release process as well.
- Custom build, as it comes from its name, is used when we want to run only particular test parts for some reason and don't need to run others. Sometimes functional tests fail because of instability. In such case we re-launch failed parts to make sure that everything is OK.
After coding has been finished I have to make sure that the pull request can be merged and the last build is green (all tests passed). It’s a good idea to perform code review now. Another developer reviews all the changes made in the pull request. If everything is good, he leaves 'LGTM' comment in GitHub. Then I move the user story to the 'Coded' state and it becomes ready for testing. If bugs have been found by our QA, the user story goes to the 'Reopen' state and I fix these bugs. After the fixes are verified, the user story finally goes to the 'Tested' state.
Eventually, the user story is in the 'Tested' state, and it’s high time to merge! Before that, I have to wait for the green 'develop', ensure my pull request can still be merged and code has been reviewed. Only then I can merge my branch into 'develop'. Finally merged! I have to remember to move the card in the 'Done' state in Targetprocess. Phew... Actually not yet. After each push, a new build on 'develop' is started automatically. If it fails, I'll have to fix it ASAP, or revert my commit which caused the failure, fix the build and merge again.
As you can see, there are some steps that are nothing but routine (although, useful) and can be automated. Thankfully, we've created a tool for that. We called it BuildBoard. It looks like this:
I see all work items (user stories, bugs, etc.) assigned to me with the corresponding pull requests, statuses of the latest builds, as well as the states of these work items in Targetprocess. Here I can run a new build, open a pull request or, for example, change the state of a work item in Targetprocess. When all conditions are met, the grey 'Merge' button in the right column becomes enabled and I can eventually merge my branch into 'develop'.
If any part of tests fails, BuildBoard automatically re-runs this part to determine whether it is due to instability, or the build fails for a valid reason and has to be fixed. I can also re-run particular part manually.
In BuildBoard, we can also view and manage special branches like 'develop', 'master', 'release' or 'hotfix'.
The status of 'develop' and the status of Transifex integration (the tool we use for localization) are placed in the header, because we want this important information to be always visible.
After a build is complete, BuildBoard pushes a notification to Slack. So, I do not have to periodically check build progress in Jenkins. That means less context switching and, thus, better productivity.
I can even deploy the current 'develop' to the staging server in two clicks.
In a nutshell, BuildBoard allows us to automate our workflow and enforce our practices, spend more time on actually important things, not routine work. Moreover, it helps us to visualise our development process with all its peculiarities.
Is there anything similar in your developer’s life? How do you automate your workflow? Please share in comments.