How do you build features on #Azure?
For the past few months my team and I have been using git-flow to build a new REST API for an existing cloud native application. The challenge was impressive, but this is how we did it.
Our team is spread across many geographical locations. We use a mix & match of best practices to help us communicate. For example, we use Skype, it’s on all our devices (phones, tables, laptops and desktops…) and makes collaboration easy! Our code lives in Git and is continuously built and released to our integration environment for validation.
Our team isn’t unique. Many of my colleagues work in distributed teams. Surprisingly, we all share the same common challenges. Time zones, cultural differences and language barriers are now part of each decision we make. Fortunately we all speak a common language, C#.
With this added complexity, how did we go to production on time? We implemented a process that we continuously tweak and adjust to meet our goals.
Our team is composed of 10 individuals. A project manager, a team lead, an architect/ developer, a minimum of 5 developers and 2 testers. Together we produce quite a bit of work for our build master.
Our team grows and shrinks to accommodate schedules, budgets and efforts.
We start by creating a Story for each feature. The story is written from the consumer’s point of view. This allows us to focus on the “what” instead of the “how”.
Each story gets triaged, prioritized and accepted for development. Then stories are added to sprints. At this point, developers pick stories and start to devise their strategy.
As the architect, I communicate the global vision. I propose solutions in order to turn challenges into opportunities. Developers evaluate and decompose stories into bite sized tasks.
During this process, we try to give a rough estimate so that we can set expectations. If we notice that our expectations can’t be met, we course correct, adjust and move on. So far, this practice has paid-off and has helped us refine our design.
Our Development Efforts
To organize efforts, we implemented the following branching strategy.
When we start building a feature, we start by creating a new feature branch. This is where we’re going to collaborate for each feature. The idea here is that we work in isolation. This means that we can deploy the contents of the develop branch at any given moment. I’d like to put some emphasis around this last statement, because it’s really important. Because we isolate development efforts from our integration (develop) branch, we always have a deliverable. It means that we can deploy to a QA (Quality Assurance) or UAT (User Acceptance Test) environments on very short notice.
Then we start coding. But don’t worry, we’ve got a plan. We start by implementing a unit test. This first step is important, because it helps us validate our understanding of the requirements. If the development efforts are hard to test, we try different approaches. In general we strive to use TDD (Test Driven Development), but only when it makes sense.
We implement the feature by writing the code necessary to satisfy our tests. We use tools like R# (ReSharper) and Visual Studio Code Analysis to standardize our code. Before we start working on a new feature, we spend time polishing our code so that our intentions are clear to everyone, including our future selves.
We feel that caring for our code is crucial, and we’ll get to that in a minute.
Once we’re done with the code, we produce internal documentation. We store it in our Visual Studio solution in a folder called “Documentation” (As you can see, I like using meaningful names). This documentation is very important, because it’s used by developers, testers and technical writers.
Our Post Development Efforts
At this point, we have a brand new feature. But we’re not done yet!
We create a pull request in order to merge our code into the develop branch. This is when other developers spend time reviewing code that may potentially make its way to production.
Our code review process is all about verifying that we haven’t forgotten anything. We believe that internal documentation is just as important as our tests and the code itself. We look at standards and logic. We try to identify potential challenges and record our technical dept. Remember, technical debt needs to be addressed early. If you wait too long, it’s probably going to cost you quality and it’s probably going to cost you your future deadlines.
Satisfied with the code, we merge it to the develop branch. This is when our build server takes over for a little while. It validates that our solution builds. It runs our unit tests. Then it deploys to our integration environment (Microsoft Azure) which is then used by our developers to validate that the feature behaves as expected.
When our code behaves, we update our public documentation and deploy to QA.
If all goes well, the QA team performs regression tests, exploratory tests and makes sure that our public documentation matches our API.
Production isn’t far behind, but we make sure that our client’s expectations have been met.
We create a release branch and merge it to the master branch. Simultaneously, we merge the release branch to the develop branch and the master branch is tagged with the release version.
Being a bunch of passionate developers who love to innovate, we kept refining our process. We kept our NuGet packages up-to-date and used bleeding edge technology. Motivated by this challenge we have discovered that it’s possible to be agile, even if your team is distributed across the globe.