Supporting Teams with Different Maturity Levels

Sven Hans Knecht
7 min readJun 26, 2023

--

Pyramid in water color

Introduction

If you’ve worked on a platform team, or as a DevOps engineer, or as a developer experience/tools engineer, you’ll quickly discover that engineering teams have various levels of engineering maturity. This can be for a variety of reasons. Gergely from the Pragmatic Engineer describes the various levels really well in an article from 2022. Summarized, there’s at least 6 different teams to think about.

The stable senior team.

The newly formed team.

The junior team.

The overworked team.

The toxic team.

“The manager’s about to be fired” team.

In light of these various levels of maturity, building tooling and developer platforms can be really difficult that matches each use case.

Target 80% of the use cases

When you first start writing a tool/building a platform, you will have a use-case in mind. You probably have a grand vision for how it will solve all of X, Y, and/or Z problems! The reality will be very different. When building tooling/platforms, it is easy to get caught in solving all the features. It is easy to target 100% migrations. That’s a mistake. I shoot for 80% of a known use case, inevitably discovering that about 15% comes along, and the last 5% will never happen — so don’t worry about it. This also helps when talking to teams of different maturity levels.

For the stable senior team, they can just migrate onto your tool with minimal help, and they can provide immediate feedback as power users.

For the newly formed and junior teams, they just want to get features and products out the door and don’t have a ton of context on what decisions they should be making — so target the base use-case and build from there

The overworked and “manager’s about to be fired” teams don’t have time to migrate onto your tool. With a team like this you’ll have to do it for them if you want adoption. This means you need to make it as easy on yourself to understand how to translate their configuration into your tool/setup.

The toxic team will almost certainly be part of the 5% and only migrate to your tool/platform when forced.

Targeting 80% of use cases is what lead to great success with the ML-Service helm chart at Mission Lane. It’s also what our DevSecOps sister team did when migrating teams from Jenkins to Github Actions. It’s what worked really well when the SRE team wrote a Database Configuration Analyzer.

Over and over, I’ve seen migrations and new tools fail because they tried to do too much. Even if you have the power of an internal mandate to use your tool, targeting 100% of use cases makes your tool overall worse as you have to build too much complexity and logic into it.

Ensure the happy path is easy

Make the most common things easy. Don’t get focused on small edge cases or power users. Make the happy path — most common actions easy.

This focus helps every single team. However, implementation isn’t as simple as it sounds. You need to have a defined happy path. You need to understand the workflow of the users well enough to know the happy path.

Let’s take a helm chart as an example. The happy path is: I want to install a piece of software. But there’s some nuance. Should you make the installation production ready? Should you add reliability/security settings from the beginning? How about networking? Storage?

This choice depends strongly on your users and audience. But regardless of the end determination, make your happy path easy and seamless to use.

Ensure that customization is possible

Now that you’ve made the happy path easy to use and you’ve only solved for 80% of use cases (both of which make low maturity teams happy), you need to make sure your power users can build on top of your platform or tools.

If you are abstracting other APIs, make sure to follow the well-known spec for them. Don’t force power users to rewrite the spec from the conventional one to a new custom one. Build on what individuals already know and use.

Ensure that options aren’t hidden or unable to be changed — usually. While there are some options that should be prevented from being changed by hard coding a value (perhaps a security setting somewhere), even those should be seriously thought through as defaults rather than hard coded values. At some point, you will almost certainly want to test a configuration change, modify a setting, or disable/enable something for troubleshooting purposes, and having to release a whole new version or having to change foundational configurations in your platform to do so is bad.

Further, ensuring options aren’t hidden or unchangeable will force you to build a more resilient tool/platform. AWS doesn’t prevent you from changing things. They give you nearly full access into your accounts. GCP is the same way. You can have an opinionated, easy to use happy path, and still allow teams the flexibility to build on top of it, to use it in interesting and new ways.

Be opinionated

Most of the teams and most developers don’t care about how things are configured. They are overworked, or too junior, too new, etc. Your tooling/platform should tell them how their service should run. It should enforce certain standards. Remove the cognitive complexity and decision/analysis paralysis and make it easy for teams. They should be able to customize if/when they figure out they need to make a change to a setting, but the defaults should work and should work in your environment to the standards of the platform team/company.

What’s the downside of being un-opinionated?

  • You’ll have a lot of configuration sprawl.
  • You’ll have a lot of duplicated configuration.
  • You’ll have non-standard configuration for each application preventing applications and services from being the same shape.
  • Developers have many more decisions to make, most of which they may not have context for yet.

Being opinionated doesn’t mean being inflexible. It doesn’t mean that teams can’t make changes. It means ensuring that the defaults match the most common use cases and don’t add to the cognitive complexity of standing up and managing an application.

Don’t be too complex/abstract

It’s easy to want to hide everything from the developers. This instinct is especially tempting if you have a majority of teams not being “stable seniors.” When most of your teams are low maturity teams, it’s easy to want to completely remove options. This is also a problem.

Think about kubernetes. If your platform team writes a custom operator where an application team only needs to submit an application name/image and everything else is created for them, you run into a cultural problem quickly where things are thrown over the wall. When something goes wrong, it’s then the platform’s problem. This doesn’t scale.

What a developer submits should map to the underlying concepts. They should be able to look at and recognize the APIs that they are interacting with.

I’ve made this mistake twice before. When we first wrote the ML-Service helm chart, we abstracted too many things. We changed the spec of APIs and made it “easier” to interact with. What we quickly discovered was that developers want to solve problems on their own, so when they googled a query and the API didn’t match our spec, it spawned a ton of extra support questions.

The other time I made this mistake was at Capital One. At Capital One, we were writing a lot of terraform and abstracting ECS services. We had gotten it to the point where only application name and image was required. But that resulted in a lot more burden on a platform team that wasn’t healthy, so we actually pared it back and gave ownership back to developers over a lot more of their application.

Provide immediate and significant value

A rule of thumb,that I picked up from Will Larson:

Reuse common technologies unless you see 10x improvement

This means that your tooling, your platform, or your service should represent a significant improvement (not just a little bit) over the status quo. It needs to deliver real value to improve the experience of the developers.

With every team, regardless of maturity, you are competing for oxygen in their sprint and workload. With “stable seniors,” they’ll be able to recognize more abstract value, but they won’t want to migrate unless there is significant improvement because they know the current system. Why change it?

For immature teams, they don’t have time to migrate. So if you are going to convince them to migrate, if you are going to convince leadership to allocate time and resources to migration, you need to ensure it delivers value.

This can come from more efficient usage of resources, improved automation, improved developer experience (don’t undervalue this!), improved visibility and observability, and/or lowering the amount of developer time spent on activities.

Ensure when you are going into the project that you have a vision for what this value will be.

Conclusion

Working with teams that have different maturity levels is nuanced and sometimes complex. Paved path choices are not always easy.

Building something for high maturity teams carries different challenges around justification, customization, and support. It means not removing their self-service ability. It requires an understanding of the problems that they are experiencing and demonstrating value.

Building for teams that are under water or have just been stood up means making things simple, accessible, and easy. It means minimizing the decision tree and the cognitive complexity, making documentation easy and accessible, and enabling them to move quickly.

To do so, follow these principles:

  • Target 80% of the use cases. Don’t get bogged down in edge cases. Work on applications and data with the same shape
  • Ensure the happy path is easy. Make it easy to start using and simple to configure for developers.
  • Ensure customization is possible. Stable senior teams and those with edge cases are going to want to customize your tool/platform to meet their needs. Don’t block them from doing so.
  • Be Opinionated. Ensure the defaults configured have weight behind them. Have opinions on how services should be configured on your platform. Make recommendations. Defaults rarely get changed so ensure you have the right ones.
  • Don’t be too complex/abstract. Don’t reinvent the wheel. Ensure that if you abstract an API or an object you follow the same schema. Follow convention.
  • Provide immediate and significant value. Try to ensure that your tool/platform has a 10x improvement over the alternatives. Drive investment and buy-in internally by ensuring your tooling has real value to the developers.

--

--

Sven Hans Knecht

SRE/Platform Engineer Professional. Amateur Analytics and Sports Enthusiast