I had a client ask me this posts's title question recently. It's a good one. In this case, the person asking develops internal Python software, but that's not his primary job so he wasn't familiar with the deep world of enterprise-level development.

How do we explain this to a newcomer? Here's my answer, rooted in Azure DevOps. Perfect? No. Useful? I hope so!

Automated Testing

Any tests that don't require human interaction. These include:

  • Unit tests (testing discrete code elements such as a single function)
  • Integration tests (testing that modules all communicate with each other, external sites, back-end databases, etc)

Automated testing is essential, not optional. When practiced well, developers routinely have more test code than app code. Unit testing improves architecture by encouraging loose coupling. It also dramatically improves confidence when making changes.

Continuous Integration and Continuous Deployment

Typically, when there's team development, the "main" version of the source code is kept on a remote server. In our case, that's the Azure DevOps Git repositories.

That main branch is what's been deployed to production users, and must always be working. So, how do we ensure that our changes don't break the main branch? That is, how do we safely integrate our code to what's already there?

The two parts to the answer are branching and building.

  1. Develop new features in a short-lived branch (short-lived branches exist no more than a day or two)
  2. Pull the main branch code to your local workspace
  3. Rebase your feature code onto the main, so it's as if you just made your changes. You've integrated locally.
  4. Rebuild the application locally and run all tests.
  5. Push your feature branch to the remote Git server and create a pull request, saying "Hey, I want this branch merged into main!"
  6. Someone approves the PR, integrating into main.
  7. The main branch automatically builds and tests the code from scratch. This is key. It's as if a new developer is working on a new computer. We build/test the code once, then deploy it to different environments. What could go wrong?
    1. The code doesn't build.
    2. The tests fail
  8. If anything goes wrong, we're notified and the bad code doesn't get released to users.

It can be counter-intuitive, but the safest development method is to push changes frequently. Why?

  • Fewer changes are easier to fix
  • Rolling back a PR with only a few changes isn't a big deal
  • Integrating multiple times per day means the latest changes are working

When there is high automated test coverage and frequent integration, there can be enough confidence in the system to enable continuous deployment. That's where the code is automatically released to users as long as all environments build and test successfully. If there's a problem in a release deployment, it's easy to either roll that back or, better, rapidly fix the issue since there will have been a small number of changes.