Consolidating Technologies
To consolidate languages, frameworks, technologies, or not to consolidate them? A roundup of approaches.
Q: At my company, several languages are used on the backend, and the new CTO wants to consolidate them. Engineers are grumbling about this, with some threatening to leave if their language gets cut. What are things to consider with such a consolidation?
I’ve received various forms of this question from readers; one was about a bank using both .NET and Java, where the new engineering leader was set on using only one, with an internal “battle” breaking out as a result.
The topic of how to deal with the problem of consolidating technologies is vast. This is because every situation will be slightly different, depending on the context of your environment, the people, and the history, which led to the problem of too many different technologies being in use. In this issue, I offer observations on the most common approaches I’ve observed, with advice on how to go about consolidating technologies once you’ve decided to go ahead with it. Today, we’ll cover:
Extreme #1: the “anything goes” approach
Extreme #2: the “single technology stack” approach
Supporting specific languages and technologies
A ‘tech radar’ process
The ‘Language Wars’
Steps to consolidate to fewer technologies
Attrition and consolidating technologies
1. Extreme #1: the “anything goes” approach
Let’s assume we’re at a new company, building a new product. We’re a startup which expects to grow fast, and wants to avoid the characteristics of many enterprises: slow moving, autonomy not given to engineers, technology choices dictated from above. So, let’s do the opposite.
Teams and software engineers are free to choose which technologies they want to use, as long as they move fast and get things done. We might even record this principle as a part of the engineering culture summary which is put on the office walls – or virtual collaboration spaces, if we’re a full-remote company.
Initially, things go great! The team is happy, productivity is good, and we see some other benefits.
Initial upsides of this approach, which you’ll likely observe:
Autonomy. We give engineers the choice of which technology to use. There’s no grumbling about being forced to use some legacy technology, as engineers are making the choices.
Hiring. It’s easy to hire engineers, as we don’t fixate on experience with a given stack – at least not at first. Engineers with experience across a variety of technologies join the company as early hires.
Lots of fun and experimentation. Thanks to the policy that any technology goes, some teams try out brand new tools, perhaps becoming some of their earliest adopters. This freedom means exploration and fun for many engineers!
But downsides appear, over time. Once the honeymoon period ends and the company grows, cracks begin to appear in the ‘anything goes’ approach:
Working across services and teams is challenging. Since each team has chosen a different set of languages and technologies to use, it’s hard for engineers to work across teams. On the backend, this is usually especially the case. For example, at Uber – where initially, anything went – my team often needed to contribute to microservices written in Python, Node.JS, Java and Go. Doing so meant engineers had to get up to speed with all these languages.
Hiring becomes more challenging. Software engineers with experience of a specific technology might be hesitant to join a team using a very different set of tools. Similarly, closing specialists with deep expertise in a particular technology is also more challenging.
Onboarding gets more time consuming. New hires need to become familiar with systems written in different languages and frameworks. Before becoming productive, they need to first familiarize themselves with those languages and technologies.
Internal mobility can be more limited. Moving teams is easy when the tech stack is familiar enough for an engineer working on Team A to easily contribute to work on Team B. But with a varied tech stack, an engineer moving to Team B must first become familiar with the stack Team B uses.
Maintaining systems. If working across teams is challenging, then hiring and onboarding are more difficult, and dealing with people coming and going gets tricky.
Challenging to put in place platform teams / developer experience (DevEx) teams. As the organization grows, platform teams and DevEx teams are a natural way to create more leverage for software engineers building products. However, with a long list of technologies, it can be challenging to decide what platform teams or DevEx teams should focus on.
The need to consolidate comes up more frequently. The initial benefits of full autonomy are outweighed by a growing list of pain points. Software engineers and engineering leaders will likely both start to recommend some sort of company-wide consolidation.
CTO Zoë Nolan – whom I worked with at Skype – shares a story about how the ‘anything goes’ approach ended up being a mistake:
“At a previous start-up. Greenfield project. New team. We got started with building version one. We began as a free for all. The mindset was the best tool for the job. Resulting in every service being in a different language. Node, Go, Haskell, Python.
For the minimum viable product (MVP), everything was in containers, so deploying and running the system was not that bad. (...) Maintenance or anything post-demo got zero thought. We needed to ship sometime that vaguely worked.
Post MVP. Maintenance was a problem; only certain people could work on certain services. Hiring was the other major problem and ultimately forced the issue. No one knew all those languages. Even people that knew two or three of them were not at the same level in all the languages. Mentally switching between different languages is tiring.
We took the difficult decision to standardise on Go. The largest and most active service used that.”
2. Extreme #2: the “single technology stack” approach
Let’s look at when a company is at the other extreme of this question of technology stacks; when every team uses the same technology in a given area. This could have come about during the early days; for example, it’s typical for companies which start with Microsoft’s ecosystem to not diverge from the .NET world. A single technology stack could have also come about as a result of a successful past consolidation effort.
Whatever the reasons, we have a decently sized company where all backend engineers use the same language and frameworks, all mobile and web engineers do the same, as do other disciplines.
The benefits of this setup are pretty great and are the polar opposite of the downsides in an ‘anything goes’ approach.
Working across teams is straightforward. All teams use the same stack, so working across services and teams is easy.
Hiring is simple. Companies tend to hire for ‘X’ years of experience in the given technology stack.
Onboarding is easy, tech-wise. Assuming the company follows industry practices, new hires will hit the ground running without much onboarding needed.
Internal mobility is easy to put in place. With the same tech stack across teams, encouraging people to move around is simple.
Maintaining systems is much easier. If a team is low on people, it can ask for help from other engineers.
Easy to make the case for platform teams and DevEx teams. There’s a high chance there’s already a core team making suggestions of practices to follow when using the main technologies. Improving the developer experience is also a better-defined challenge, thanks to improvements to the handful of technologies in use.
There are several downsides to this approach:
Hire from a more homogenous pool. The company is most likely to hire only people who have spent years working on the main technology in use. This means the company doesn’t consider people with more varied experiences, nor does it hire those who could pick up the technology in a matter of weeks, and then utilize their experience in other areas. This approach tends to make hires more homogenous.
Harder to hire entrepreneurial engineers. Because the company mandates the technology used, it’s harder to attract curious software engineers who are looking for more autonomy and want to experiment with cutting-edge approaches. This is especially the case for software engineers who see the tech stack as dated.
The industry can evolve past the company. If a company is fully resistant to trialing new technologies, it can miss onboarding to more productive technologies which get popular for the right reasons. With every new language and framework, there is a risk in both being an early adopter and in only considering it after late adopters finally do so.
The pressure to introduce new technologies. Any company hiring autonomous software engineers will face a push to experiment with new technologies. And this “pressure” is a good thing! However, without an outlet for these engineers to trial new technologies, the same engineers might end up leaving for places less hesitant about trying the latest approaches.
3. Supporting specific languages and technologies
A common approach at more mature tech companies is to provide central support for a limited set of technologies, and to encourage teams to choose these tools. While teams using a different set of technologies is not banned, teams are on their own if they do so. At some companies, teams may have to justify not using the centrally supported set of languages and frameworks.
This is the approach companies with Developer Experience teams typically settle on. Organizations like Meta, Uber or DoorDash all follow it. For example, at Uber the Developer Experience mobile team provided support for Swift and Java on Android, while owning the monorepo and tooling setup for each language. On iOS, teams could still use Objective C, but they needed to take care of their own tooling support. On the backend at Uber, the languages supported were Java and Go, with Developer Experience continually investing in tooling improvements.
Meta follows a similar approach of listing centrally supported languages, as software engineer Nikhil Thomas noted. The company detailed the languages that are formally supported internally:
Hack
C++
Rust
Python
Here’s how Meta defines the concept of ‘supported’ and ‘not supported’ languages:
Supported means a good developer experience. Software engineers can expect to have nice experiences with editing their code, debugging, building, deploying, core libraries and interoperability.
‘Not supported’ means community support. Languages not in the ‘supported’ group don’t come with the above guarantee. Instead, teams adopting them must take on the burden for maintaining them, and finding/using the tools for a good developer experience.
The company does not recommend starting new projects with unsupported languages. Of course, teams still use languages like OCaml or Haskell, taking on the maintenance burden themselves. It’s safe to assume that if a new language gains traction across Meta, it will likely become easier to make a case for adding more formal support for it.
Uber is another case of how such an approach can work when consolidating a large number of technologies. Go and Java becoming officially supported languages was the result of engineering leadership’s aim to consolidate the backend technologies from several languages – Python, Node.JS, Java, Go, and others – to just one language. In the end, both Java and Go won enough support to become officially supported languages. Over time, almost all new backend services were written in one of these two languages.
4. A ‘tech radar’ process
Putting a more formal technology adoption process in place is how some startups approach introducing new technologies: