Azure Functions Continuous Deployment with Azure Pipelines: Part 2 - Getting Started

This is the second part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

In the previous instalment we got an overview  of Azure DevOps and of the app we’ll be building/deploying.

Demo app source code is available on GitHub.

Creating Azure Function Apps

There will be two Function Apps created in Azure. One will be a self-contained test environment (with separate Azure Storage account) and to keep things simple they both belong to the same resource group. The other Function App will be the production version. For simplicity we won’t be using slots, proxies, etc.

The two Function Apps are:

  • InvestFunctionAppDemo (production)
  • InvestFunctionAppDemoTest (test)

The test environment will be used to check the deployment  artifacts and as a target to execute functional end-to-end tests against.

Once these two Function Aps have been created in the Portal, all of the deployments will happen automatically from the release pipeline, including setting test/production specific Function App settings.

If you don’t have an Azure account you can currently sign up for free.

Signing Up or Azure DevOps

Now that there are some Function Apps created in Azure to deploy to, you can deploy to them from an Azure release pipelines. To do this you’ll need to sign up to Azure DevOps.

Azure DevOps contains a number of services, in this series we’ll be using the Azure Pipeline service.

An Overview of Azure Pipelines

Azure DevOps is currently free for open source projects and small teams with some limitations on things such as parallel job execution all the way up to 1,000 users for $7,833.26 USD/month. You can find the latest pricing information as part of the product information and compare features of different plans.

You can just use Azure Pipelines on their own, you don’t have to use all of the DevOps services (Boards, etc.).

Some of the features of Azure Pipelines include:

  • Build, test, and deploy for multiple languages including .NET. Python, Java, iOS, etc.
  • Container build and publish support
  • Deploy to clouds including Azure, AWS, and Google Cloud Platform
  • 10 free parallel jobs and unlimited build minutes for all open source projects
  • Advanced workflow features, testing, reporting, gates, etc.

Azure DevOps Organizations and Projects

Once you’ve signed up for Azure DevOps you’ll need an organization and a project.

“..an organization is a mechanism for organizing and connecting groups of related projects. Examples are business divisions, regional divisions, or other organizational structure. You can choose one organization for your entire company, or separate organizations for specific business units, or an organization just for you.” [Microsoft]

“Each organization contains one or more projects. Each project contains a set of features: boards and backlogs for agile planning, pipelines for continuous integration and deployment, repos for version control and management of source code and artifacts, and continuous test integration throughout the life cycle.” [Microsoft]

Creating a new Azure DevOps Project

Once the project is created, you can navigate to Pipelines section to create and manage build and release pipelines.

In the next part of this series we’ll create an initial build pipeline and get an introduction to defining source-controlled build definitions using YAML.

Azure Functions Continuous Deployment with Azure Pipelines: Part 1 - Overview

This is the first part in a series demonstrating how to setup continuous deployment of an Azure Functions App using Azure DevOps build and release pipelines.

In this series we’ll explore (among other things) how to:

  • Build an Azure Functions App when code is pushed to GitHub
  • Run unit tests as part of the build
  • Create multiple Azure Pipeline artifacts
  • Release to a test Function App environment in Azure
  • Run automated functional end to end tests
  • Release to a production Function App environment in Azure
  • Run smoke tests after a production deployment

…in addition to a whole host of other things such as: YAML build files, Azure Pipeline variables, setting Azure Function app settings from an Azure release pipeline, and using post-deployment gates.

An Overview of Azure DevOps

Azure DevOps is a collection of cloud services to facilitate team DevOps practices and includes:

  • Azure boards
  • Azure Pipelines
  • Azure repos
  • Azure test plans
  • Azure Artifacts

In this series we’ll be focussing on Azure Pipelines to build, test, and deploy the Function App automatically when new changes are pushed to GitHub.

InvestFunctionApp Overview

The demo code for these demos can be found on GitHub.

The InvestFunctionApp contains a number of functions that allow the investing of money in a portfolio. The initial entry point is a HTTP-triggered function that allows the investor id to be specified along with how much to add to the investors portfolio.Depending on the investors target allocations, the amount will be investing in either bonds or shares.

There are 4 functions involved in this workflow:

  1. Portfolio function: HTTP trigger, starts the workflow, creates a queue message
  2. CalculatePortfolioAllocation function: queue trigger from the output of (1), calculates where to invest and writes to either buy-stocks queue (3) or buy-bonds queue (4)
  3. BuyStocks function: triggered from buy-stocks queue, simulates buying stocks and updates the investor’s portfolio in Azure Table storage
  4. BuyBonds function: triggered from buy-bonds queue, simulates buying bonds and updates the investor’s portfolio in Azure Table storage

The app is designed to demonstrate the  facilities of Azure Pipelines and is simplified in many ways including minimal error checking, auditing, transactions, security, etc.

Azure DevOps Build Pipeline

The build Azure Pipeline will be automatically triggered from changes pushed to the GitHub repository.

The build pipeline will have the following steps:

  1. Build the solution
  2. Run unit tests
  3. Publish test results
  4. Package the Function App
  5. Publish the packaged Function App as a build artifact
  6. Copy the functional end-to-end tests
  7. Publish the functional end-to-end tests as a separate build artifact

Assuming all these step run without error, the two build artifacts can be passed to/used by the release pipeline.

Azure DevOps Release Pipeline

The release pipeline will be triggered automatically when the build pipeline completes without error.

The following diagram shows what the resulting release pipeline will look like:

Azure DevOps Release Pipeline example

The release pipeline will:

  1. Deploy the Function App to a test Function App in Azure
  2. Run functional end-to-end tests (written in xUnit.net) against this test deployment
  3. If the tests in (2) pass, deploy to production
  4. After deploying to production, call a smoke test function to verify the deployment was successful

In the next part of this series we’ll create Function Apps, signup to Azure DevOps, create a new DevOps project, and take a closer look at the demo function app.

Mocking HttpRequest Body Content When Testing Azure Function HTTP Trigger Functions

When creating Azure Functions that are triggered by an HTTP request, you may want to write unit tests for the function Run method. These unit tests can be executed outside of the Azure Functions runtime, just like testing regular methods.

If your HTTP-triggered function takes as the function input an HttpRequest (as opposed to an automatically JSON-deserialized class) you may need to provide request data in your test.

As an example, consider the following code snippet that defines an HTTP-triggered function.

[FunctionName("Portfolio")]
[return: Queue("deposit-requests")]
public static async Task<DepositRequest> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "portfolio/{investorId}")]HttpRequest req,
    [Table("Portfolio", InvestorType.Individual, "{investorId}")] Investor investor,
    string investorId,
    ILogger log)
{
    log.LogInformation($"C# HTTP trigger function processed a request.");

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

    log.LogInformation($"Request body: {requestBody}");

    var deposit = JsonConvert.DeserializeObject<Deposit>(requestBody);

    // etc.
}

If the  the preceding code is executed in a test, some content needs to be provided to be used when accessing req.Body. To do this using Moq a mock HttpRequest can be created that returns a specified Stream instance for req.Body.

If you want to create a request body that contains a JSON payload, you can use the following helper method in your tests:

private static Mock<HttpRequest> CreateMockRequest(object body)
{            
    var ms = new MemoryStream();
    var sw = new StreamWriter(ms);

    var json = JsonConvert.SerializeObject(body);

    sw.Write(json);
    sw.Flush();

    ms.Position = 0;

    var mockRequest = new Mock<HttpRequest>();
    mockRequest.Setup(x => x.Body).Returns(ms);

    return mockRequest;
}

As an example of using this method in a test:

[Fact]
public async Task ReturnCorrectDepositInformation()
{
    var deposit = new Deposit { Amount = 42 };
    var investor = new Investor { };

    Mock<HttpRequest> mockRequest = CreateMockRequest(deposit);

    DepositRequest result = await Portfolio.Run(mockRequest.Object, investor, "42", new Mock<ILogger>().Object);

    Assert.Equal(42, result.Amount);
    Assert.Same(investor, result.Investor);
}

When the preceding test is run, the function run method will get the contents of the memory stream that contains the JSON.

To learn more about using Moq to create/configure/use mock objects check out my Mocking in .NET Core Unit Tests with Moq: Getting Started Pluralsight course. or to learn more about MemoryStream and how to work with streams in C# check out my Working with Files and Streams course.

Remote Debugging Azure Functions V2 "The breakpoint will not currently be hit. No symbols have been loaded for this document"

Sometimes it can be tricky to attach the Visual Studio debugger to a deployed Azure Functions app. For example if you use Cloud Explorer you can right click on the deployed Azure Function and choose Attach Debugger as the following screenshot shows:

Using Cloud Explorer to debug an Azure Function

While this may seem to work at first, you may experience a problem with your breakpoints actually being hit when function app code is executing with the message “The breakpoint will not currently be hit. No symbols have been loaded for this document”.

The breakpoint will not currently be hit. No symbols have been loaded for this document

As an alternative to attaching via Cloud Explorer, you can try the following approach:

1 Log in to Azure Portal and navigate to your deployed function app.

2 Download the publish profile

Downloading publish profile for Azure Function app

3 Open the downloaded file

Make a note of the userName, userPWD and destinationAppUrl values.

4 Attach Visual Studio Debugger to Azure Function App

  1. Make sure your function app project is open in Visual Studio
  2. Make sure that you have deployed a debug version of the function app to Azure
  3. On the Debug menu choose Attach to Process..
  4. For the Attach to value, click the Select.. button and un-tick everything except Managed (CoreCLR) code
  5. In the Connection target enter the  destinationAppUrl (without the preceding http) followed by :4022 – for example: investfunctionappdemotest.azurewebsites.net:4022 – and hit Enter
  6. You should now see an Enter Your Credentials popup box, use the userName and userPWD from step 3 and click Ok
  7. Wait a few seconds for Visual Studio to do its thing
  8. Click the w3wp.exe process and the click the Attach button (see screenshot below) Be patient after clicking as it may take quite a while for all the debug symbols to load.

Attaching to the Azure Functions w3wp.exe process

5 Set your breakpoints as desired

Breakpoint set in function run method

6 Invoke your function code and you should see your breakpoint hit

Once again this may take a while so be patient, you may also see “a remote operation is taking longer than expected”.

Azure Function breakpoint being hit in Visual Studio

Azure Functions Dependency Injection with Autofac

This post refers specifically to Azure Function V2.

If you want to write automated tests for Azure Functions methods and want to be able to control dependencies (e.g. to inject mock versions of things) you can set up dependency injection.

One way to do this is to install the AzureFunctions.Autofac NuGet package into your functions project.

Once installed, this package allows you to inject dependencies into your function methods at runtime.

Step 1: Create DI Mappings

The first step (after package installation) is to create a class that configures the dependencies. As an example suppose there was a function method that needed to make use of an implementation of an IInvestementAllocator. The following class can be added to the functions project:

using Autofac;
using AzureFunctions.Autofac.Configuration;

namespace InvestFunctionApp
{
    public class DIConfig
    {
        public DIConfig(string functionName)
        {
            DependencyInjection.Initialize(builder =>
            {
                builder.RegisterType<NaiveInvestementAllocator>().As<IInvestementAllocator>(); // Naive

            }, functionName);
        }
    }
}

In the preceding code, a constructor is defined that receives the name of the function that’s being injected into. Inside the constructor, types can be registered for dependency injection. In the preceding code the IInvestementAllocator interface is being mapped to the concrete class NaiveInvestementAllocator.

Step 2: Decorate Function Method Parameters

Now the DI registrations have been configured, the registered types can be injected in function methods. To do this the [Inject] attribute is applied to one or more parameters as the following code demonstrates:

[FunctionName("CalculatePortfolioAllocation")]
public static void Run(
    [QueueTrigger("deposit-requests")]DepositRequest depositRequest,
    [Inject] IInvestementAllocator investementAllocator,
    ILogger log)
    {
        log.LogInformation($"C# Queue trigger function processed: {depositRequest}");

        InvestementAllocation r = investementAllocator.Calculate(depositRequest.Amount, depositRequest.Investor);
    }

Notice in the preceding code the [Inject] attribute is applied to the IInvestementAllocator investementAllocator parameter. This IInvestementAllocator is the same interface that was registered earlier in the DIConfig class.

Step 3: Select DI Configuration

The final step to make all this work is to add an attribute to the class that contains the function method (that uses [Inject]). The attribute used is the DependencyInjectionConfig attribute that takes the type containing the DI configuration as a parameter, for example: [DependencyInjectionConfig(typeof(DIConfig))]

The full function code is as follows:

using AzureFunctions.Autofac;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace InvestFunctionApp
{
    [DependencyInjectionConfig(typeof(DIConfig))]
    public static class CalculatePortfolioAllocation
    {
        [FunctionName("CalculatePortfolioAllocation")]
        public static void Run(
            [QueueTrigger("deposit-requests")]DepositRequest depositRequest,
            [Inject] IInvestementAllocator investementAllocator,
            ILogger log)
        {
            log.LogInformation($"C# Queue trigger function processed: {depositRequest}");

            InvestementAllocation r = investementAllocator.Calculate(depositRequest.Amount, depositRequest.Investor);
        }
    }
}

At runtime, when the CalculatePortfolioAllocation runs, an instance of an NaiveInvestementAllocator will be supplied to the function.

The library also supports features such as named dependencies and multiple DI configurations, to read more check out GitHub.

Setting Up Mock ref Return Values in Moq

I recently received a message related to my Mocking in .NET Core Unit Tests with Moq: Getting Started Pluralsight course asking how to set the values of ref parameters.

As a (somewhat contrived) example, consider the following code:

public interface IParser
{
    bool TryParse(string value, ref int output);
}

public class Thing
{
    private readonly IParser _parser;

    public Thing(IParser parser)
    {
        _parser = parser;
    }

    public string ConvertStringIntToHex(string number)
    {
        int i = 0;

        if (_parser.TryParse(number, ref i))
        {
            return i.ToString("X");
        }

        throw new ArgumentException("The value supplied cannot be parsed into an int.", nameof(number));
    }
}

The Thing class requires an IParser to be able to work. In a test, a mocked version of an IParser can be created by Moq as the following initial test demonstrates:

[Fact]
public void ReturnHex_Fail_NoSetup()
{
    var mockParser = new Mock<IParser>();

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255"); // fails with ArgumentException

    Assert.Equal("FF", result);
}

The preceding test will fail however because the mocked TryParse has not been configured correctly, for example specifying that the method should return true.

The following modified test attempts to fix this:

[Fact]
public void ReturnHex_Fail_NoRefValueSetup()
{
    var mockParser = new Mock<IParser>();
    mockParser.Setup(x => x.TryParse(It.IsAny<string>(), ref It.Ref<int>.IsAny))
              .Returns(true);

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255");

    Assert.Equal("FF", result); // Fails, actual result == 0
}

In the preceding code, the return value is being set, but nowhere is the ref int output “return value” being configured.

In the following test the Callback method is used to set the ref value. To be able to do this, a delegate must first be defined that matches the signature of the mocked method that contains the ref parameter. Once this delegate is defined it can be used in the Callback method as the following code demonstrates:

// Define a delegate that can be used to set the ref value in the mocked TryParse method 
delegate void MockTryParseCallback(string number, ref int output);

[Fact]
public void ReturnHex()
{
    var mockParser = new Mock<IParser>();
    mockParser.Setup(x => x.TryParse("255", ref It.Ref<int>.IsAny)) // When the TryParse method is called with 255
              .Callback(new MockTryParseCallback((string s, ref int output) => output = 255)) // Execute callback delegate and set the ref value
              .Returns(true); // Return true as the result of the TryParse method

    var sut = new Thing(mockParser.Object);

    var result = sut.ConvertStringIntToHex("255");

    Assert.Equal("FF", result);
}

If you’ve never used Moq or want to learn more about it check out the official Moq quickstart  or head over to my Pluralsight course.

Unit Testing C# File Access Code with System.IO.Abstractions

It can be difficult  to write unit tests for code that accesses the file system.

It’s possible to write integration tests that read in an actual file from the file system, do some processing, and check the resultant output file (or result) for correctness. There are a number of potential problems with these types of integration tests including the potential for them to more run slowly (real IO access overheads), additional test file management/setup code, etc. (this does not mean that some integration tests wouldn’t be useful however).

The System.IO.Abstractions NuGet package can help to make file access code more testable. This package provides a layer of abstraction over the file system that is API-compatible with existing code.

Take the following code as an example:

using System.IO;
namespace ConsoleApp1
{
    public class FileProcessorNotTestable
    {
        public void ConvertFirstLineToUpper(string inputFilePath)
        {
            string outputFilePath = Path.ChangeExtension(inputFilePath, ".out.txt");

            using (StreamReader inputReader = File.OpenText(inputFilePath))
            using (StreamWriter outputWriter = File.CreateText(outputFilePath))
            {
                bool isFirstLine = true;

                while (!inputReader.EndOfStream)
                {
                    string line = inputReader.ReadLine();

                    if (isFirstLine)
                    {
                        line = line.ToUpperInvariant();
                        isFirstLine = false;
                    }

                    outputWriter.WriteLine(line);
                }
            }
        }
    }
}

The preceding code opens a text file, and writes it to a new output file, but with the first line converted to uppercase.

This class is not easy to unit test however, it is tightly coupled to the physical file system with the calls to File.OpenText and File.CreateText.

Once the System.IO.Abstractions NuGet package is installed, the class can be refactored as follows:

using System.IO;
using System.IO.Abstractions;

namespace ConsoleApp1
{
    public class FileProcessorTestable
    {
        private readonly IFileSystem _fileSystem;

        public FileProcessorTestable() : this (new FileSystem()) {}

        public FileProcessorTestable(IFileSystem fileSystem)
        {
            _fileSystem = fileSystem;
        }

        public void ConvertFirstLineToUpper(string inputFilePath)
        {
            string outputFilePath = Path.ChangeExtension(inputFilePath, ".out.txt");

            using (StreamReader inputReader = _fileSystem.File.OpenText(inputFilePath))
            using (StreamWriter outputWriter = _fileSystem.File.CreateText(outputFilePath))
            {
                bool isFirstLine = true;

                while (!inputReader.EndOfStream)
                {
                    string line = inputReader.ReadLine();

                    if (isFirstLine)
                    {
                        line = line.ToUpperInvariant();
                        isFirstLine = false;
                    }

                    outputWriter.WriteLine(line);
                }
            }
        }
    }
}

The key things to notice in the preceding code is the ability to pass in an IFileSystem as a constructor parameter. The calls to File.OpenText and File.CreateText are now redirected to _fileSystem.File.OpenText and _fileSystem.File.CreateText  respectively.

If the parameterless constructor is used (e.g. in production at runtime) an instance of FileSystem will be used, however at test time, a mock IFileSystem can be supplied.

Handily, the System.IO.Abstractions.TestingHelpers NuGet package provides a pre-built mock file system that can be used in unit tests, as the following simple test demonstrates:

using System.IO.Abstractions.TestingHelpers;
using Xunit;

namespace XUnitTestProject1
{
    public class FileProcessorTestableShould
    {
        [Fact]
        public void ConvertFirstLine()
        {
            var mockFileSystem = new MockFileSystem();

            var mockInputFile = new MockFileData("line1\nline2\nline3");

            mockFileSystem.AddFile(@"C:\temp\in.txt", mockInputFile);

            var sut = new FileProcessorTestable(mockFileSystem);
            sut.ConvertFirstLineToUpper(@"C:\temp\in.txt");

            MockFileData mockOutputFile = mockFileSystem.GetFile(@"C:\temp\in.out.txt");

            string[] outputLines = mockOutputFile.TextContents.SplitLines();

            Assert.Equal("LINE1", outputLines[0]);
            Assert.Equal("line2", outputLines[1]);
            Assert.Equal("line3", outputLines[2]);
        }
    }
}

To see this in action or to learn more about file access, check out my Working with Files and Streams in C# Pluralsight course.

Testing for Thrown Exceptions in MSTest V2

In previous posts we looked at testing for thrown exceptions in xUnit.net and NUnit. In this post we’re going to see how to test in MSTest V2.

As with the previous posts, the class being tested is as follows:

public class TemperatureSensor
{
    bool _isInitialized;

    public void Initialize()
    {
        // Initialize hardware interface
        _isInitialized = true;
    }

    public int ReadCurrentTemperature()
    {
        if (!_isInitialized)
        {
            throw new InvalidOperationException("Cannot read temperature before initializing.");
        }

        // Read hardware temp
        return 42; // Simulate for demo code purposes
    }
}

And the first test to check the normal execution:

[TestMethod]
public void ReadTemperature()
{
    var sut = new TemperatureSensor();

    sut.Initialize();

    var temperature = sut.ReadCurrentTemperature();

    Assert.AreEqual(42, temperature);
}

Next, a test can be written to check that the expected exception is thrown:

[TestMethod]
public void ErrorIfReadingBeforeInitialized()
{
    var sut = new TemperatureSensor();

    Assert.ThrowsException<InvalidOperationException>(() => sut.ReadCurrentTemperature());
}

The preceding code using the Assert.ThrowsException method, this method takes the type of the expected exception as the generic type parameter (in this case InvalidOperationException). As the method parameter an action/function can be specified – this is the code that is supposed to cause the exception to be thrown.

The thrown exception can also be captured if you need to test the exception property values:

[TestMethod]
public void ErrorIfReadingBeforeInitialized_CaptureExDemo()
{
    var sut = new TemperatureSensor();

    var ex = Assert.ThrowsException<InvalidOperationException>(() => sut.ReadCurrentTemperature());

    Assert.AreEqual("Cannot read temperature before initializing.", ex.Message);
}

To learn more about using exceptions to handle errors in C#, check out my Error Handling in C# with Exceptions Pluralsight course or to learn more about MS Test V2 check out my Automated Testing with MSTest V2 Pluralsight course.

Testing for Thrown Exceptions in NUnit

In a previous post, testing for thrown exceptions using xUnit.net was demonstrated. In this post we’ll see how to do the same with NUnit.

Once again the class being tested is as follows:

public class TemperatureSensor
{
    bool _isInitialized;

    public void Initialize()
    {
        // Initialize hardware interface
        _isInitialized = true;
    }

    public int ReadCurrentTemperature()
    {
        if (!_isInitialized)
        {
            throw new InvalidOperationException("Cannot read temperature before initializing.");
        }

        // Read hardware temp
        return 42; // Simulate for demo code purposes
    }
}

The first test can be to test the happy path:

[Test]
public void ReadTemperature()
{
    var sut = new TemperatureSensor();

    sut.Initialize();

    var temperature = sut.ReadCurrentTemperature();

    Assert.AreEqual(42, temperature);
}

Next, a test can be written to check that the expected exception is thrown:

[Test]
public void ErrorIfReadingBeforeInitialized()
{
    var sut = new TemperatureSensor();

    Assert.Throws<InvalidOperationException>(() => sut.ReadCurrentTemperature());
}

Notice in the preceding code that any InvalidOperationException thrown will pass the test. To ensure that the thrown exception is correct, it can be captured and further asserts performed against it:

[Test]
public void ErrorIfReadingBeforeInitialized_CaptureExDemo()
{
    var sut = new TemperatureSensor();

    var ex = Assert.Throws<InvalidOperationException>(() => sut.ReadCurrentTemperature());

    Assert.AreEqual("Cannot read temperature before initializing.", ex.Message);
    // or:
    Assert.That(ex.Message, Is.EqualTo("Cannot read temperature before initializing."));
}

There’s also other ways to assert against expected exceptions such as the following:

Assert.Throws(Is.TypeOf<InvalidOperationException>()
                .And.Message.EqualTo("Cannot read temperature before initializing."), 
              () => sut.ReadCurrentTemperature());

There’s some personal preference involved when choosing a style, for example the preceding code could be considered more verbose by some and may muddle the distinction between the Act and Assert phases of a test.

To learn more about using exceptions to handle errors in C#, check out my Error Handling in C# with Exceptions Pluralsight course.

Software Development Wheel

An example of a rendered software development wheel showing what areas are in need of attention.

In business/personal coaching there is an idea of a “wheel of life”. The idea is that you evaluate different dimensions of your life and plot them on a wheel. Ideally the wheel should be balanced (and as large) as possible. If the wheel is crooked, you know what areas need to be improved to create a better wheel that turns more easily. When the wheel turns more easily, you get better results.

In a similar vein, the Software Development Wheel allows you to evaluate your overall software development  experience.

The tool below can be used by individual developers, the software development team as a whole, or team leaders/managers.

To generate your own wheel fill out the form below.

Each dimension is rated from 1 (“very little or none”) to 5 (“awesome, doing everything possible”) and descriptions for the dimensions are included below.

Be sure to share this page with your fellow developers, team leaders, and management as it’s a great way to start a conversation about how to improve things.

More than one person looking at code before it is released. For example: pair programming, code reviews mob programming, etc.

Tests than can be (and are) run periodically to check that code is working as expected. For example: unit tests, integrations tests, UI tests, performance tests, security tests, etc.

Code from developers is regularly combined together to check that it all works together. Usually automated with a build system/server such as TeamCity, Jenkins, VSTS, etc.

Codebase and development teams evolve over time. For example: incremental development, shorter feedback loops, self-organizing/cross-functional teams, etc.

Code that is easier to change. For example: readable code, good object oriented practices (SOLID), YAGNI, use of appropriate design patterns, well-named variables/functions/etc.

Management exists primarily to remove things that stop/slow down the team delivering the software. For example: shielding the team from management politics, empowering individuals and teams to make decisions, caring about people, providing training, etc.

The team has the best tools that are available to do their work. For example, powerful hardware, latest versions of IDEs, multiple monitors, etc.

Comfortable work environment. Includes: comfortable ergonomic chairs/desks, standing desks, peace and quiet, air conditioning, plants, cleanliness, etc.