emacs.d/clones/abseil.io/resources/swe-book/html/ch15.html

237 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Software Engineering at Google</title>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script>
<link rel="stylesheet" type="text/css" href="theme/html/html.css">
</head>
<body data-type="book">
<section xmlns="http://www.w3.org/1999/xhtml" data-type="chapter" id="deprecation">
<h1>Deprecation</h1>
<p class="byline">Written by Hyrum Wright</p>
<p class="byline">Edited by Tom Manshreck</p>
<blockquote data-type="epigraph">
<p>I love deadlines. I like the whooshing sound they make as they fly by.</p>
<p data-type="attribution">Douglas Adams</p>
</blockquote>
<p>All systems age. <a contenteditable="false" data-primary="deprecation" data-type="indexterm" id="ix_depr">&nbsp;</a>Even though software is a digital asset and the physical bits themselves dont degrade, new technologies, libraries, techniques, languages, and other environmental changes over time render existing systems obsolete. Old systems require continued maintenance, esoteric expertise, and generally more work as they diverge from the surrounding ecosystem. Its often better to invest effort in turning off obsolete systems, rather than letting them lumber along indefinitely alongside the systems that replace them. But the number of obsolete systems still running suggests that, in practice, doing so is not trivial. We refer to the process of orderly migration away from and eventual removal of obsolete systems as <em>deprecation</em>.</p>
<p>Deprecation is yet another topic that more accurately<a contenteditable="false" data-primary="software engineering" data-secondary="deprecation and" data-type="indexterm" id="id-owfkHeSn">&nbsp;</a> belongs to the discipline of software engineering than programming because it requires thinking about how to manage a system over time. For long-running software ecosystems, planning for and executing deprecation correctly reduces resource costs and improves velocity by removing the redundancy and complexity that builds up in a system over time. On the other hand, poorly deprecated systems may cost more than leaving them alone. While deprecating systems requires additional effort, its possible to plan for deprecation during the design of the system so that its easier to eventually decommission and remove it. Deprecations can affect systems ranging from individual function calls to entire software stacks. For concreteness, much of what follows focuses on code-level deprecations.</p>
<p>Unlike with most of the other topics we have discussed in this book, Google is still learning how best to deprecate and remove software systems. This chapter describes the lessons weve learned as weve deprecated large and heavily used internal systems. Sometimes, it works as expected, and sometimes it doesnt, but the general problem of removing obsolete systems remains a difficult and evolving concern in the industry.</p>
<p>This chapter primarily deals with deprecating technical systems, not end-user products. The distinction is somewhat arbitrary given that an external-facing API is just another sort of product, and an internal API may have consumers that consider themselves end users. Although many of the principles apply to turning down a public product, we concern ourselves here with the technical and policy aspects of deprecating and removing obsolete systems where the system owner has visibility into its use.</p>
<section data-type="sect1" id="why_deprecatequestion_mark">
<h1>Why Deprecate?</h1>
<p>Our discussion of deprecation <a contenteditable="false" data-primary="deprecation" data-secondary="reasons for" data-type="indexterm" id="id-AEfLHXCvsB">&nbsp;</a>begins from the fundamental premise that <em>code is a liability, not an asset</em>. After all, if code were an asset, why should we even bother spending time trying to turn down and remove obsolete systems? Code has costs, some of which are borne in the process of creating a system, but many other costs are borne as a system is maintained across its lifetime. These ongoing costs, such as the operational resources required to keep a system running or the effort to continually update its codebase as surrounding ecosystems evolve, mean that its worth evaluating the trade-offs between keeping an aging system running or working to turn it down.</p>
<p>The age of a system alone doesnt justify its deprecation. A system could be finely crafted over several years to be the epitome of software form and function. Some software systems, such as the LaTeX typesetting system, have been improved over the course of decades, and even though changes still happen, they are few and far between. Just because something is old, it does not follow that it is obsolete.</p>
<p>Deprecation is best suited for systems that are demonstrably obsolete and a replacement exists that provides comparable functionality. The new system might use resources more efficiently, have better security properties, be built in a more sustainable fashion, or just fix bugs. Having two systems to accomplish the same thing might not seem like a pressing problem, but over time, the costs of maintaining them both can grow substantially. Users may need to use the new system, but still have dependencies that use the obsolete one.</p>
<p>The two systems might need to interface with each other, requiring complicated transformation code. As both systems evolve, they may come to depend on each other, making eventual removal of either more difficult. In the long run, weve discovered that having multiple systems performing the same function also impedes the evolution of the newer system because it is still expected to maintain compatibility with the old one. Spending the effort to remove the old system can pay off as the replacement system can now evolve more quickly.</p>
<aside data-type="sidebar" id="earlier_we_made_the_assertion_that_quot">
<p>Earlier we made <a contenteditable="false" data-primary="code" data-secondary="code as a liability, not an asset" data-type="indexterm" id="id-kPfwHGH6SwsY">&nbsp;</a>the assertion that “code is a liability, not an asset.” If that is true, why have we spent most of this book discussing the most efficient way to build software systems that can live for decades? Why put all that effort into creating more code when its simply going to end up on the liability side of the balance sheet?</p>
<p>Code <em>itself</em> doesnt bring value: it is the <em>functionality</em> that it provides that brings value. That functionality is an asset if it meets a user need: the code that implements this functionality is simply a means to that end. If we could get the same functionality from a single line of maintainable, understandable code as 10,000 lines of convoluted spaghetti code, we would prefer the former. Code itself carries a cost—the simpler the code is, while maintaining the same amount of functionality, the better.</p>
<p>Instead of focusing on how much code we can produce, or how large is our codebase, we should instead focus on how much functionality it can deliver per unit of code and try to maximize that metric. One of the easiest ways to do so isnt writing more code and hoping to get more functionality; its removing excess code and systems that are no longer needed. Deprecation policies and procedures make this possible.</p>
</aside>
<p>Even though deprecation is useful, weve learned at Google that organizations have a limit on the amount of deprecation work that is reasonable to undergo simultaneously, from the aspect of the teams doing the deprecation as well as the customers of those teams. For example, although everybody appreciates having freshly paved roads, if the public works department decided to close down <em>every</em> road for paving simultaneously, nobody would go anywhere. By focusing their efforts, paving crews can get specific jobs done faster while also allowing other traffic to make progress. Likewise, its important to choose deprecation projects with care and then commit to following through on finishing them.</p>
</section>
<section data-type="sect1" id="why_is_deprecation_so_hardquestion_mark">
<h1>Why Is Deprecation So Hard?</h1>
<p>Weve mentioned Hyrums Law <a contenteditable="false" data-primary="deprecation" data-secondary="difficulty of" data-type="indexterm" id="ix_deprdiff">&nbsp;</a>elsewhere in this book, but its worth repeating its applicability here: the more users of a system, the higher <a contenteditable="false" data-primary="Hyrum's Law" data-secondary="deprecation and" data-type="indexterm" id="id-NmfeCGCytx">&nbsp;</a>the probability that users are using it in unexpected and unforeseen ways, and the harder it will be to deprecate and remove such a system. Their usage just “happens to work” instead of being “guaranteed to work.” In this context, removing a system can be thought of as the ultimate change: we arent just changing behavior, we are removing that behavior completely! This kind of radical alteration will shake loose a number of unexpected dependents.</p>
<p>To further complicate matters, deprecation usually isnt an option until a newer system is available that provides the same (or better!) functionality. The new system might be better, but it is also different: after all, if it were exactly the same as the obsolete system, it wouldnt provide any benefit to users who migrate to it (though it might benefit the team operating it). This functional difference means a one-to-one match between the old system and the new system is rare, and every use of the old system must be evaluated in the context of the new one.</p>
<p>Another surprising reluctance to deprecate is emotional attachment to old systems, particularly those that the deprecator had a hand in helping to create. An example of this change aversion happens when systematically removing old code at Google: weve occasionally encountered resistance of the form “I like this code!” It can be difficult to convince engineers to tear down something theyve spent years building. This is an understandable response, but ultimately self-defeating: if a system is obsolete, it has a net cost on the organization and should be removed. One of the ways weve addressed concerns about keeping old code within Google is by ensuring that the source code repository isnt just searchable at trunk, but also historically. Even code that has been removed can be found again (see <a data-type="xref" href="ch17.html#code_search">Code Search</a>).</p>
<aside data-type="sidebar" id="thereapostrophes_an_old_joke_within_goo">
<p>Theres an old joke within Google that there are two ways of doing things: the one thats deprecated, and the one thats not-yet-ready. This is usually the result of a new solution being “almost” done and is the unfortunate reality of working in a technological environment that is complex and fast-paced.</p>
<p>Google engineers have become used to working in this environment, but it can still be disconcerting. Good documentation, plenty of signposts, and teams of experts helping with the deprecation and migration process all make it easier to know whether you should be using the old thing, with all its warts, or the new one, with all its <span class="keep-together">uncertainties.</span></p>
</aside>
<p>Finally, funding and executing deprecation efforts can be difficult politically; staffing a team and spending time removing obsolete systems costs real money, whereas the costs of doing nothing and letting the system lumber along unattended are not readily observable. It can be difficult to convince the relevant stakeholders that deprecation efforts are worthwhile, particularly if they negatively impact new feature development. Research techniques, such as those described in <a data-type="xref" href="ch07.html#measuring_engineering_productivity">Measuring Engineering Productivity</a>, can provide concrete evidence that a deprecation is worthwhile.</p>
<p>Given the difficulty in deprecating and removing obsolete software systems, it is often easier for users to evolve a system <em>in situ</em>, rather than completely replacing it. Incrementality doesnt avoid the deprecation process altogether, but it does break it down into smaller, more manageable chunks that can yield incremental benefits. Within Google, weve observed that migrating to entirely new systems is <em>extremely</em> expensive, and the costs are frequently underestimated. Incremental deprecation efforts <span class="keep-together">accomplished</span> by in-place refactoring can keep existing systems running while making it easier to deliver value to users.<a contenteditable="false" data-primary="deprecation" data-secondary="difficulty of" data-startref="ix_deprdiff" data-type="indexterm" id="id-LrfQIwTyt0">&nbsp;</a></p>
<section data-type="sect2" id="deprecation_during_design">
<h2>Deprecation During Design</h2>
<p>Like many engineering activities, deprecation of a software system can be planned as those systems are first built.<a contenteditable="false" data-primary="designing systems to eventually be deprecated" data-type="indexterm" id="id-EPf0HQCecntR">&nbsp;</a><a contenteditable="false" data-primary="deprecation" data-secondary="during design" data-type="indexterm" id="id-LrfBCKCbckt3">&nbsp;</a> Choices of programming language, software architecture, team composition, and even company policy and culture all impact how easy it will be to eventually remove a system after it has reached the end of its useful life.</p>
<p>The concept of designing systems so that they can eventually be deprecated might be radical in software engineering, but it is common in other engineering disciplines. Consider the example of a nuclear power plant, which is an extremely complex piece of engineering. As part of the design of a nuclear power station, its eventual decommissioning after a lifetime of productive service must be taken into account, even going so far as to allocate funds for this purpose.<sup><a data-type="noteref" id="ch01fn146-marker" href="ch15.html#ch01fn146">1</a></sup> Many of the design choices in building a nuclear power plant are affected when engineers know that it will eventually need to be decommissioned.</p>
<p>Unfortunately, software systems are rarely so thoughtfully designed. Many software engineers are attracted to the task of building and launching new systems, not maintaining existing ones. The corporate culture of many companies, including Google, emphasizes building and shipping new products quickly, which often provides a disincentive for designing with deprecation in mind from the beginning. And in spite of the popular notion of software engineers as data-driven automata, it can be psychologically difficult to plan for the eventual demise of the creations we are working so hard to build.</p>
<p>So, what kinds of considerations should we think about when designing systems that we can more easily deprecate in the future? Here are a couple of the questions we encourage engineering teams at Google to ask:</p>
<ul>
<li>
<p>How easy will it be for my consumers to migrate from my product to a potential replacement?</p>
</li>
<li>
<p>How can I replace parts of my system incrementally?</p>
</li>
</ul>
<p>Many of these questions relate to how a system provides and consumes dependencies. For a more thorough discussion of how we manage these dependencies, see <span class="keep-together"><a data-type="xref" href="ch16.html#version_control_and_branch_management">Version Control and Branch Management</a></span>.</p>
<p>Finally, we should point out that the decision as to whether to support a project long term is made when an organization first decides to build the project. After a software system exists, the only remaining options are support it, carefully deprecate it, or let it stop functioning when some external event causes it to break. These are all valid options, and the trade-offs between them will be organization specific. A new startup with a single project will unceremoniously kill it when the company goes bankrupt, but a large company will need to think more closely about the impact across its portfolio and reputation as they consider removing old projects. As mentioned earlier, Google is still learning how best to make these trade-offs with our own internal and external products.</p>
<p>In short, dont start projects that your organization isnt committed to support for the expected lifespan of the organization. Even if the organization chooses to deprecate and remove the project, there will still be costs, but they can be mitigated through planning and investments in tools and policy.</p>
</section>
</section>
<section data-type="sect1" id="types_of_deprecation">
<h1>Types of Deprecation</h1>
<p>Deprecation isnt a<a contenteditable="false" data-primary="deprecation" data-secondary="types of" data-type="indexterm" id="ix_deprtyp">&nbsp;</a> single kind of process, but a continuum of them, ranging from “well turn this off someday, we hope” to “this system is going away tomorrow, customers better be ready for that.” Broadly speaking, we divide this continuum into two separate areas: advisory and compulsory.</p>
<section data-type="sect2" id="advisory_deprecation">
<h2>Advisory Deprecation</h2>
<p><em>Advisory</em> deprecations are those that dont <a contenteditable="false" data-primary="deprecation" data-secondary="types of" data-tertiary="advisory deprecation" data-type="indexterm" id="id-JgfeCjCRUgf8">&nbsp;</a>have a deadline<a contenteditable="false" data-primary="advisory deprecations" data-type="indexterm" id="id-bafmUJC5UpfO">&nbsp;</a> and arent high priority for the organization (and for which the company isnt willing to dedicate resources). These could also be labeled <em>aspirational</em> deprecations: the team knows the system has been replaced, and although they hope clients will eventually migrate to the new system, they dont have imminent plans to either provide support to help move clients or delete the old system. This kind of deprecation often lacks enforcement: we hope that clients move, but cant force them to. As our friends in SRE will readily tell you: “Hope is not a strategy.”</p>
<p>Advisory deprecations are a good tool for advertising the existence of a new system and encouraging early adopting users to start trying it out. Such a new system should <em>not</em> be considered in a beta period: it should be ready for production uses and loads and should be prepared to support new users indefinitely. Of course, any new system is going to experience growing pains, but after the old system has been deprecated in any way, the new system will become a critical piece of the organizations <span class="keep-together">infrastructure.</span></p>
<p>One scenario weve seen at Google in which advisory deprecations have strong benefits is when the new system offers compelling benefits to its users. In these cases, <span class="keep-together">simply</span> notifying users of this new system and providing them self-service tools to migrate to it often encourages adoption. However, the benefits cannot be simply incremental: they must be transformative. Users will be hesitant to migrate on their own for marginal benefits, and even new systems with vast improvements will not gain full adoption using only advisory deprecation efforts.</p>
<p>Advisory deprecation allows system authors to nudge users in the desired direction, but they should not be counted on to do the majority of migration work. It is often tempting to simply put a deprecation warning on an old system and walk away without any further effort. Our experience at Google has been that this can lead to (slightly) fewer new uses of an obsolete system, but it rarely leads to teams actively migrating away from it. Existing uses of the old system exert a sort of conceptual (or technical) pull toward it: comparatively many uses of the old system will tend to pick up a large share of new uses, no matter how much we say, “Please use the new system.” The old system will continue to require maintenance and other resources unless its users are more actively encouraged to migrate.</p>
</section>
<section data-type="sect2" id="compulsory_deprecation">
<h2>Compulsory Deprecation</h2>
<p>This active encouragement comes in the form of <em>compulsory</em> deprecation. <a contenteditable="false" data-primary="deprecation" data-secondary="types of" data-tertiary="compulsory deprecation" data-type="indexterm" id="id-bafwCJCaIpfO">&nbsp;</a><a contenteditable="false" data-primary="compulsory deprecation" data-type="indexterm" id="id-y9feUlCaIlfz">&nbsp;</a>This kind of deprecation usually comes with a deadline for removal of the obsolete system: if users continue to depend on it beyond that date, they will find their own systems no longer work.</p>
<p>Counterintuitively, the best way for compulsory deprecation efforts to scale is by localizing the expertise of migrating <a contenteditable="false" data-primary="migrations" data-secondary="migrating users from an obsolete system" data-type="indexterm" id="id-bafEHOUaIpfO">&nbsp;</a>users to within a single team of experts—usually the team responsible for removing the old system entirely. This team has incentives to help others migrate from the obsolete system and can develop experience and tools that can then be used across the organization. Many of these migrations can be effected using the same tools discussed in <a data-type="xref" href="ch22.html#large-scale_changes">Large-Scale Changes</a>.</p>
<p>For compulsory deprecation to actually work, its schedule needs to have an enforcement mechanism. This does not imply that the schedule cant change, but empower the team running the deprecation process to break noncompliant users after they have been sufficiently warned through efforts to migrate them. Without this power, it becomes easy for customer teams to ignore deprecation work in favor of features or other more pressing work.</p>
<p>At the same time, compulsory deprecations without staffing to do the work can come across to customer teams as mean spirited, which usually impedes completing the deprecation. Customers simply see such deprecation work as an unfunded mandate, requiring them to push aside their own priorities to do work just to keep their services running. This feels much like the “running to stay in place” phenomenon and creates friction between infrastructure maintainers and their customers. Its for this reason that we strongly advocate that compulsory deprecations are actively staffed by a specialized team through completion.</p>
<p>Its also worth noting that even with the force of policy behind them, compulsory deprecations can still face political hurdles. Imagine trying to enforce a compulsory deprecation effort when the last remaining user of the old system is a critical piece of infrastructure your entire organization depends on. How willing would you be to break that infrastructure—and, transitively, everybody that depends on it—just for the sake of making an arbitrary deadline? It is hard to believe the deprecation is really compulsory if that team can veto its progress.</p>
<p>Googles monolithic repository and dependency graph gives us tremendous insight into how systems are used across our ecosystem. Even so, some teams might not even know they have a dependency on an obsolete system, and it can be difficult to discover these dependencies analytically. Its also possible to find them dynamically through tests of increasing frequency and duration during which the old system is turned off temporarily. These intentional changes provide a mechanism for discovering unintended dependencies by seeing what breaks, thus alerting teams to a need to prepare for the upcoming deadline. Within Google, we occasionally change the name of implementation-only symbols to see which users are depending on them unaware.</p>
<p>Frequently at Google, when a system is slated for deprecation and removal, the team will announce planned outages of increasing duration in the months and weeks prior to the turndown. Similar to Googles Disaster Recovery Testing (DiRT) exercises, these events often discover unknown dependencies between running systems. <a contenteditable="false" data-primary="dependencies" data-secondary="unknown, discovering during deprecation" data-type="indexterm" id="id-Qef9HqcDI3fz">&nbsp;</a>This incremental approach allows those dependent teams to discover and then plan for the systems eventual removal, or even work with the deprecating team to adjust their timeline. (The same principles also apply for static code dependencies, but the semantic information provided by static analysis tools is often sufficient to detect all the dependencies of the obsolete system.)</p>
</section>
<section data-type="sect2" id="deprecation_warnings">
<h2>Deprecation Warnings</h2>
<p>For both advisory and compulsory deprecations, it is often useful to have a programmatic way <a contenteditable="false" data-primary="deprecation" data-secondary="types of" data-tertiary="deprecation warnings" data-type="indexterm" id="id-bafEHJCAhpfO">&nbsp;</a>of marking systems as deprecated so that users are warned about their use and encouraged to move away. Its often tempting to just mark something as deprecated and hope its uses eventually disappear, but remember: “hope is not a strategy.” Deprecation warnings can help prevent new uses, but rarely lead to migration of existing systems.</p>
<p>What usually happens in practice is that these warnings accumulate over time. If they are used in a transitive context (for example, library A depends on library B, which depends on library C, and C issues a warning, which shows up when A is built), these warnings can soon overwhelm users of a system to the point where they ignore them altogether.<a contenteditable="false" data-primary="alert fatigue" data-type="indexterm" id="id-y9f8HAUlhlfz">&nbsp;</a> In health care, this phenomenon is known as “<a href="https://oreil.ly/uYYef">alert fatigue</a>.”</p>
<p>Any deprecation warning issued to a user needs to have two properties: actionability and relevance. A warning is <em>actionable</em> if the user can use the warning to actually perform some relevant action, not just in theory, but in practical terms, given the expertise in that problem area that we expect for an average engineer. For example, a tool might warn that a call to a given function should be replaced with a call to its updated counterpart, or an email might outline the steps required to move data from an old system to a new one. In each case, the warning provided the next steps that an engineer can perform to no longer depend on the deprecated system.<sup><a data-type="noteref" id="ch01fn147-marker" href="ch15.html#ch01fn147">2</a></sup></p>
<p>A warning can be actionable, but still be annoying. To be useful, a deprecation warning should also be <em>relevant</em>. A warning is relevant if it surfaces at a time when a user actually performs the indicated action. Warning about the use of a deprecated function is best done while the engineer is writing code that uses that function, not after it has been checked into the repository for several weeks. Likewise, an email for data migration is best sent several months before the old system is removed rather than as an afterthought a weekend before the removal occurs.</p>
<p>Its important to resist the urge to put deprecation warnings on everything possible. Warnings themselves are not bad, but naive tooling often produces a quantity of warning messages that can overwhelm the unsuspecting engineer. Within Google, we are very liberal with marking old functions as deprecated but leverage tooling such as <a href="https://errorprone.info">ErrorProne</a> or clang-tidy to ensure that warnings are surfaced in targeted ways. As discussed in <a data-type="xref" href="ch20.html#static_analysis-id00082">Static Analysis</a>, we limit these warnings to newly changed lines as a way to warn people about new uses of the deprecated symbol. Much more intrusive warnings, such as for deprecated targets in the dependency graph, are added only for compulsory deprecations, and the team is actively moving users away. In either case, tooling plays an important role in surfacing the appropriate information to the appropriate people at the proper time, allowing more warnings to be added without fatiguing the user.<a contenteditable="false" data-primary="deprecation" data-secondary="types of" data-startref="ix_deprtyp" data-type="indexterm" id="id-PqfLU3SmhVfm">&nbsp;</a></p>
</section>
</section>
<section data-type="sect1" id="managing_the_deprecation_process">
<h1>Managing the Deprecation Process</h1>
<p>Although they can feel like different kinds of projects<a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-type="indexterm" id="ix_deprmg">&nbsp;</a> because were deconstructing a system rather than building it, deprecation projects are similar to other software engineering projects in the way they are managed and run. We wont spend too much effort going over similarities between those management efforts, but its worth pointing out the ways in which they differ.</p>
<section data-type="sect2" id="process_owners">
<h2>Process Owners</h2>
<p>Weve learned at Google that <a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-tertiary="process owners" data-type="indexterm" id="id-JgfAHjCRURu8">&nbsp;</a>without explicit <a contenteditable="false" data-primary="ownership of code" data-secondary="deprecation process owners" data-type="indexterm" id="id-bafwCJC5URuO">&nbsp;</a>owners, a deprecation process is unlikely to make meaningful progress, no matter how many warnings and alerts a system might generate. Having explicit project owners who are tasked with managing and running the deprecation process might seem like a poor use of resources, but the alternatives are even worse: dont ever deprecate anything, or delegate deprecation efforts to the users of the system. The second case becomes simply an advisory deprecation, which will never organically finish, and the first is a commitment to maintain every old system ad infinitum. Centralizing deprecation efforts helps better assure that expertise actually <em>reduces</em> costs by making them more transparent.</p>
<p>Abandoned projects often present a problem when establishing ownership and aligning incentives. Every organization of reasonable size has projects that are still actively used but that nobody clearly owns or maintains, and Google is no exception. Projects sometimes enter this state because they are deprecated: the original owners have moved on to a successor project, leaving the obsolete one chugging along in the basement, still a dependency of a critical project, and hoping it just fades away eventually.</p>
<p>Such projects are unlikely to fade away on their own. In spite of our best hopes, weve found that these projects still require deprecation experts to remove them and prevent their failure at inopportune times. These teams should have removal as their primary goal, not just a side project of some other work. In the case of competing priorities, deprecation work will almost always be perceived as having a lower priority and rarely receive the attention it needs. These sorts of important-not-urgent cleanup tasks are a great use of 20% time and provide engineers exposure to other parts of the codebase.</p>
</section>
<section data-type="sect2" id="milestones">
<h2>Milestones</h2>
<p>When building<a contenteditable="false" data-primary="milestones of a deprecation process" data-type="indexterm" id="id-bafEHJCaIRuO">&nbsp;</a> a new system, project milestones<a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-tertiary="milestones" data-type="indexterm" id="id-y9fEClCaI7uz">&nbsp;</a> are generally pretty clear: “Launch the frobnazzer features by next quarter.” Following incremental development practices, teams build and deliver functionality incrementally to users, who get a win whenever they take advantage of a new feature. The end goal might be to launch the entire system, but incremental milestones help give the team a sense of progress and ensure they dont need to wait until the end of the process to generate value for the <span class="keep-together">organization.</span></p>
<p>In contrast, it can often feel that the only milestone of a deprecation process is removing the obsolete system entirely. The team can feel they havent made any progress until theyve turned out the lights and gone home. Although this might be the most meaningful step for the team, if it has done its job correctly, its often the least noticed by anyone external to the team, because by that point, the obsolete system no longer has any users. Deprecation project managers should resist the temptation to make this the only measurable milestone, particularly given that it might not even happen in all deprecation projects.</p>
<p>Similar to building a new system, managing a team working on deprecation should involve concrete incremental milestones, which are measurable and deliver value to users. The metrics used to evaluate the progress of the deprecation will be different, but it is still good for morale to celebrate incremental achievements in the deprecation process. We have found it useful to recognize appropriate incremental milestones, such as deleting a key subcomponent, just as wed recognize accomplishments in building a new product.</p>
</section>
<section data-type="sect2" id="deprecation_tooling">
<h2>Deprecation Tooling</h2>
<p>Much of the tooling used<a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-tertiary="deprecation tooling" data-type="indexterm" id="ix_deprmgtool">&nbsp;</a> to manage the deprecation process is discussed in depth elsewhere in this book, such as the large-scale change (LSC) process (<a data-type="xref" href="ch22.html#large-scale_changes">Large-Scale Changes</a>) or our code review tools (<a data-type="xref" href="ch19.html#critique_googleapostrophes_code_review">Critique: Googles Code Review Tool</a>). Rather than talk about the specifics of the tools, well briefly outline how those tools are useful when managing the deprecation of an obsolete system. These tools can be categorized as discovery, migration, and backsliding prevention tooling.</p>
<section data-type="sect3" id="discovery">
<h3>Discovery</h3>
<p>During the<a contenteditable="false" data-primary="discovery (in deprecation)" data-type="indexterm" id="id-LrfNHKCaUnhDuD">&nbsp;</a> early stages of a deprecation process, and in fact during the entire process, it is useful to know <em>how</em> and <em>by whom</em> an obsolete system is being used. Much of the initial work of deprecation is determining who is using the old system—and in which unanticipated ways. Depending on the kinds of use, this process may require revisiting the deprecation decision once new information is learned. We also use these tools throughout the deprecation process to understand how the effort is progressing.</p>
<p>Within Google, we use tools like Code Search (see <a data-type="xref" href="ch17.html#code_search">Code Search</a>) and Kythe (see <a data-type="xref" href="ch23.html#continuous_integration">Continuous Integration</a>) to statically determine which customers use a given library, and often to sample existing usage to see what sorts of behaviors customers are unexpectedly depending on. Because runtime dependencies generally require some static library or thin client use, this technique yields much of the information needed to start and run a deprecation process. Logging and runtime sampling in production help discover issues with dynamic dependencies.</p>
<p>Finally, we treat our global test suite as an oracle to determine whether all references to an old symbol have been removed. As discussed in <a data-type="xref" href="ch11.html#testing_overview">Testing Overview</a>, tests are a mechanism of preventing unwanted behavioral changes to a system as the ecosystem evolves. Deprecation is a large part of that evolution, and customers are responsible for having sufficient testing to ensure that the removal of an obsolete system will not harm them.</p>
</section>
<section data-type="sect3" id="migration">
<h3>Migration</h3>
<p>Much of the work of <a contenteditable="false" data-primary="migrations" data-secondary="in the deprecation process" data-type="indexterm" id="id-X3f8H7CQIVhmuK">&nbsp;</a>doing deprecation efforts at Google is achieved by using the same set of code generation and review tooling we mentioned earlier. The LSC process and tooling are particularly useful in managing the large effort of actually updating the codebase to refer to new libraries or runtime services.</p>
</section>
<section data-type="sect3" id="preventing_backsliding">
<h3>Preventing backsliding</h3>
<p>Finally, an often overlooked piece of<a contenteditable="false" data-primary="backsliding, preventing in deprecation process" data-type="indexterm" id="id-Qef9H8CNh8hdua">&nbsp;</a> deprecation infrastructure is tooling for preventing the addition of new uses of the very thing being actively removed. Even for advisory deprecations, it is useful to warn users to shy away from a deprecated system in favor of a new one when they are writing new code. Without backsliding prevention, deprecation can become a game of whack-a-mole in which users constantly add new uses of a system with which they are familiar (or find examples of elsewhere in the codebase), and the deprecation team constantly migrates these new uses. This process is both counterproductive and demoralizing.</p>
<p>To prevent deprecation backsliding on a micro level, we use the Tricorder static analysis framework <a contenteditable="false" data-primary="Tricorder static analysis platform" data-type="indexterm" id="id-PqfOHjUmhphzuz">&nbsp;</a>to notify users that they are adding calls into a deprecated system and give them feedback on the appropriate replacement. <a contenteditable="false" data-primary="@deprecated annotation" data-type="indexterm" id="id-DQfVCEULhKhBur">&nbsp;</a>Owners of deprecated systems can add compiler annotations to deprecated symbols (such as the <code>@deprecated</code> Java annotation), and Tricorder surfaces new uses of these symbols at review time. These annotations give control over messaging to the teams that own the deprecated system, while at the same time automatically alerting the change author. In limited cases, the tooling also suggests a push-button fix to migrate to the suggested replacement.</p>
<p>On a macro level, &nbsp;we use visibility whitelists in our build system to ensure that new dependencies <a contenteditable="false" data-primary="dependencies" data-secondary="new, preventing introduction into deprecated system" data-type="indexterm" id="id-DQfJH8ILhKhBur">&nbsp;</a>are not introduced to the deprecated system. Automated tooling periodically examines these whitelists and prunes them as dependent systems are migrated away from<a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-startref="ix_deprmgtool" data-tertiary="deprecation tooling" data-type="indexterm" id="id-KofQCdIdh7hBuk">&nbsp;</a> the obsolete system.<a contenteditable="false" data-primary="deprecation" data-secondary="managing the process" data-startref="ix_deprmg" data-type="indexterm" id="id-5nf8UlIah8hJud">&nbsp;</a></p>
</section>
</section>
</section>
<section data-type="sect1" id="conclusion-id00019">
<h1>Conclusion</h1>
<p>Deprecation can feel like the dirty work of cleaning up the street after the circus parade has just passed through town, yet these efforts improve the overall software ecosystem by reducing maintenance overhead and cognitive burden of engineers. Scalably maintaining complex software systems over time is more than just building and running software: we must also be able to remove systems that are obsolete or otherwise unused.</p>
<p>A complete deprecation process involves successfully managing social and technical challenges through policy and tooling. Deprecating in an organized and well-managed fashion is often overlooked as a source of benefit to an organization, but is essential for its long-term sustainability.</p>
</section>
<section data-type="sect1" id="tlsemicolondrs-id00120">
<h1>TL;DRs</h1>
<ul>
<li>
<p>Software systems have continuing maintenance costs that should be weighed against the costs of removing them.</p>
</li>
<li>
<p>Removing things is often more difficult than building them to begin with because existing users are often using the system beyond its original design.</p>
</li>
<li>
<p>Evolving a system in place is usually cheaper than replacing it with a new one, when turndown costs are included.</p>
</li>
<li>
<p>It is difficult to honestly evaluate the costs involved in deciding whether to deprecate: aside from the direct maintenance costs involved in keeping the old system around, there are ecosystem costs involved in having multiple similar systems to choose between and that might need to interoperate. The old system might implicitly be a drag on feature development for the new. These ecosystem costs are diffuse and difficult to measure. Deprecation and removal costs are often similarly diffuse.<a contenteditable="false" data-primary="deprecation" data-startref="ix_depr" data-type="indexterm" id="id-EPf0H5HMIYCPir">&nbsp;</a></p>
</li>
</ul>
</section>
<div data-type="footnotes"><p data-type="footnote" id="ch01fn146"><sup><a href="ch15.html#ch01fn146-marker">1</a></sup><a href="https://oreil.ly/heo5Q">"Design and Construction of Nuclear Power Plants to Facilitate Decommissioning,"</a> Technical Reports Series No. 382, IAEA, Vienna (1997).</p><p data-type="footnote" id="ch01fn147"><sup><a href="ch15.html#ch01fn147-marker">2</a></sup>See <a href="https://abseil.io/docs/cpp/tools/api-upgrades"><em class="hyperlink">https://abseil.io/docs/cpp/tools/api-upgrades</em></a> for an example.</p></div></section>
</body>
</html>