Four of a Kind – Azure Certifications for Software Developers

I have finally collected all four Azure credentials that I’ve been seeking. This was my goal for the last 6 months, and I achieved it today 28 November 2025.

Let me tell you a bit on why it was important for me to get certified, and why I selected these four certifications.


An Azure certification is an important tool if you’re a contractor, because it acts as a credential when looking for contracts. For me as a freelance consultant, I don’t have a big firm validating my knowledge, but have to stand completely on my own merit. One way to get someone to vouch for you, is to take an Azure certification. That way, Microsoft vouch that I know these topics that I’m certified on.

If you’re not freelance like me, it can still be helpful to take a certification, to increase your value within the company. These certifications are counted towards the company’s Microsoft partner level, which comes with benefits. The certificates are also personal, so if you plan on looking for a new job, they are a merit in your job search and might land you a better offer.

I’ve chosen to take the following certificates

I will give you my view on why these certificates are the most important for a software developer on the Microsoft stack.

Azure Administrator Associate

As a software developer this is a really cool certification as it helps you learn the things that you don’t come in contact with very often, like setting up an Azure subscription from scratch, Azure networking and how to secure your solution in Azure.

Even if this certificate is more directed at IT Operations, it’s knowledge that’s also very useful to know as a software developer.

Azure Developer Associate

This certification is a must have if you’re writing software that is run on Azure. It helps you understand how to write cloud native solutions, by utilizing the features that are provided in Azure. I have so many times seen developers reinventing the wheel, when there’s already a native Azure solution for the same problem.

This certification will help you learn about all those features, so you don’t have to implement them yourself.

Azure Solutions Architect Expert

If you are going to be consulting on Azure, you need to get this certification. It will help you get a grip on all the service offerings on Azure. You will get the birds eye view on governance, security and how Microsoft intends Azure to be used in an enterprise setting.

After completing this certification you will see Azure as a set of puzzle pieces and know how to fit them together into a working system.

DevOps Engineer Expert

This last certification, that I completed today, teaches you how to deliver software in a cloud environment. How can you shorten the cycle time, and at the same time increase quality and security in your software delivery pipeline.


Once you have these four certifications, you have a pretty good grip on how to develop, deliver and host software in a Microsoft setting.

This article was written without AI.

What’s a devContainer and what is it good for?

This is supposed to become a series of three parts, so I’m writing down the titles of the next parts here to incentivise me to write them

  1. What’s a devContainer and what is it good for?
  2. How to setup a devContainer with Visual Studio Code
  3. Remote development with devContainers

This first article is an introduction to devContainers.

What is a devContainer?

You’ve probably heard about Docker containers and how you can package an application with the operating system to make it run on any hardware.

A devContainer is exactly that but for development environments. You write code, run and debug it inside a Docker container. The devContainer has all the tools you’ll need for your development, Git, dotnet, nodejs, you name it.

What problems does it solve?

Have you ever tried to onboard a new developer to the project and spent a day trying to get the development environment to run on his/hers machine? Was it the wrong version of nodejs installed or did they miss a Windows update?

A devContainer solves this by installing the correct versions of all dependencies from the Dockerfile.

Have you developed an application using the latest technologies, .NET 5 and then a year later when you are just going to fix an issue the application no longer builds because you have .NET 6 installed on your machine and there were some breaking changes between versions?

With devContainers you will stay on .NET 5 until _you_ decide it is time to upgrade the code base. The application will not stop working because you switched machines or the tools got outdated.

Have you ever had your development environment stop working because you share database with the team and someone else ran a database migration that you haven’t got yet?

With devContainers it is easy to setup dependencies like databases in the same Docker instance so everyone on the team has their own local database without any messy installations.

What applications can be devContained?

All applications that are targets for Docker could be using devContainers for development

  • Webservices
  • API’s
  • Databases
  • Expo Apps

Applications that doesn’t work as well with devContainers are Desktop, iOS and Android applications.

What tools are required?

The definition for a devContainer is written in a file called .devcontainer/devcontainer.json. This is usually accompanied with a Dockerfile or docker-compose.yml and various setup scripts.

In order to run the devContainer on your local machine you need to have Docker Desktop 2.0+ setup.

Visual Studio Code has the best integration with devContainers as of yet, and you’ll hardly notice that you’re working inside a Docker container.

Okay, but isn’t it weird?

No, you will hardly notice that you’re working inside a Docker container.

  • A Docker container is not a virtual machine. There is almost no performance penalties of working inside a Docker container.
  • The source tree is shared between the host and the container, so you can work with your code files just as normal.
  • Git credentials are automatically forwarded to the container so you do not need any extra authentication for your devContainer.
  • When starting your application inside a container, vscode will automatically forward the port to the host so you can see the result on your machine. Just open a web browser to localhost:5000 as you usually do and it works like magic.

This was a short introduction to devContainers. Setting one up for your project is very easy and what we’re going to look at in part 2.

Document your Code

I was told this week the code doesn’t need documentation because the developers are good at naming things. So I thought it was time to revisit what kind of documentation should be included in code.

Code Comments

There are 2 common objections to code comments

  1. They are not very useful because the code tells us what the program does
  2. They are often wrong because the code changes but not the comments

This is just the talk of lazy “low effort” developers. I think the agile manifesto “working software over comprehensive documentation” has done more harm than good.

Well written comments are invaluable. I’ve never come across an outdated comment that threw me off in a way that I couldn’t just delete it. 🤷‍♂️

Here are some examples of code comments I find useful

1. Adding context that is not in the code

This code was written because a behaviour in macOS.

// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();

2. Adding intention to the code

There are some things that only work in this order.

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow();
});

3. Rabbit holes you went down and want to warn others of

Warning, here be dragons. 🐉

// THE OBJECT POLYFILL WILL NOT WORK ON THE WEBKIT 1.0.3 PLATFORM
// import "core-js/es/object";

4. Explaining what is going on that the code doesn’t communicate clearly

Why must public url be the same as window location?

if (publicUrl.origin !== window.location.origin) {
  // Our service worker won't work if PUBLIC_URL is on a different origin
  // from what our page is served on. This might happen if a CDN is used to
  // serve assets; see https://github.com/facebook/create-react-app/issues/2374
  return;
}

5. Add a reference to the bug or issue that prompted the change

Go check the bug description to find more information why the code looks like this.

Sentry.init({
  // BUG AB#3133 Decrease sample rate in production
  // Decreasing sample rate to keep costs down.
  tracesSampleRate: 0.1,
});

6. Description of public modules and functions

In order to get nice intellisense when using this module or function from elsewhere in the code.

/**
 * A button that let's you copy the current value to clipboard.
 *
 * @param {object} props
 * @param {string} props.text - The text to display on the button.
 * @param {string} props.value - The value to copy to clipboard.
 * @param {boolean} [props.isDisabled] - Whether the button should be disabled.
 */
function CopyButton({ text, value, isDisabled = false }) {
}

7. Source of Information

Not going to explain all this crap here. Go read up!

/**
 * The source for these abbreviations is here.
 * https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations
 */
 let abbreviations = ["aks", "appcs", "ase", "plan", "appi", "apim", ....];

8. Source of Copy/Pasted Code

(we all do it sometimes)

// source: https://stackoverflow.com/a/15289883
function dateDiffInDays(a, b) {
  // Discard the time and time-zone information.
  const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
  const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());

  return Math.floor((utc2 - utc1) / _MS_PER_DAY);
}

9. In order to understand this code you’ll need to know more about this special topic

We are not making up the rules, they are!

// Official DCC Schema documentation
// https://github.com/ehn-dcc-development/ehn-dcc-schema
function parseDccSchema(dcc) {
}

10. What kind of result you can expect from a module or function

/**
 * Calculator screen. It is divided into a left and right part, where the left part 
 * is the input form and the right part presents the result. If the screen width is
 * less than 768px the left part becomes top and the right part becomes the bottom.
 */
function Calculator() {
  /** implementation.. */
}

Summary

Anyone can write code that computers understand. The challenge is writing code that also humans understand.

If you want to know more about how I document code, check out the convention on my wiki.

Rigor Mortis

Disclaimer.

This blog post is about the overuse of code quality methods. If you’re not using good practices for code quality, the advice against overuse doesn’t apply to you. Never stop doing what you never did, because that someone on the internet has overdone it and believe that they should not do it as much anymore.

What is Code Quality

Software Quality is a very wide concept, but when it comes to code quality I find it quite easy to pin down. The following aspects are often mentioned in regards to code quality

  • Low cyclomatic complexity
  • Easy to read
  • Easy to test
  • Can be reused
  • No side effects

These aspects comes down to making the code easy to change. That is why my definition of high quality code is, code that is easy to change.

Code Smell: Rigor Mortis

Code can be easy to read, well tested, documented, reviewed and still be hard to change. Code quality practices can work in a way that locks down the code and makes it harder to change.

If you strive for your code to be perfect, it will also become hard to change. Once you try to change it, tests will break, code quality tools will complain about loose ends and the compiler wants you to fix 20 compilation errors.

I call this code smell Rigor Mortis.

Too Much Quality Slows you Down

If you apply too much quality methods, the code will become harder to change. It will look great, but if it cannot be changed it is dead. Businesses that depends on software being easy to change, will be impeded by code too rigid to change.

Unit Testing

The practice of writing unit tests is a quick way to increase quality. Done the right way it will help you refactor a program and introduce changes while keeping track of unintended side effects.

Too many tests, or tests written in the wrong way, will make the program harder to change.

  • Tests fail when they have high coupling to the implementation of the system under test. Those tests are brittle.
  • Tests fail when you alter the behavior of a program. These tests were intended to fail.

Tests that fail require work to fix them. Each failing test makes it harder to change the code. Good tests only fail when you break your program, and all other tests makes your code smell like rigor mortis.

Static Typing

In compiled languages like C# and Typescript the compiler will check your program for errors. This provides quick feedback of syntax errors, spelling errors or logical errors. It helps you code faster.

Static Typing will also slow you down. The compiler can be so strict that you spend more time trying to satisfy it than making the desired change of your program.

Making a change in a statically typed program, may require you to update code and models in 10 different places to satisfy the compiler. If this helps you prevent errors, that is a good practice, but if it only slows you down it smells of rigor mortis. 

Linting & Static Code Analysis

Tools that automatically review your code may help greatly in avoiding common mistakes that could take hours to troubleshoot. They are very helpful in teaching us quirks of the language that we should watch out for.

Linting tools also works the other way around. It will complain and stop you from making unsafe changes that are needed in troubleshooting. Commenting out a line of code will lead you down a rabbit hole of making sure no references are unused, just to satisfy the linter.

Watch out when your linter makes your code harder to change. It smells like teen spirit, .. I mean rigor mortis.

Summary

I’ve introduced a code smell, that is the smell of too much quality, and I’ve given you some examples of when this smell applies.

All these are good code practices by today’s standards, and you should apply them to your projects. But you also need to beware so your quality methods doesn’t impede with your ability to change your code.

You don’t want your code to reach a state of rigor mortis.