talks: add 2016/refactor.article, based on my GothamGo talk Change-Id: I62653a352773429c4517355d6c280289badd213f Reviewed-on: https://go-review.googlesource.com/33723 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/2016/refactor.article b/2016/refactor.article new file mode 100644 index 0000000..3639a7d --- /dev/null +++ b/2016/refactor.article
@@ -0,0 +1,698 @@ +Codebase Refactoring (with help from Go) + +Russ Cox +rsc@golang.org + +* Abstract + +Go should add the ability to create alternate equivalent names for types, +in order to enable gradual code repair during codebase refactoring. +This article explains the need for that ability and the implications of not having it +for today’s large Go codebases. +This article also examines some potential solutions, +including the alias feature proposed during the development of +(but not included in) Go 1.8. +However, this article is _not_ a proposal of any specific solution. +Instead, it is intended as the start of a discussion by the Go community +about what solution should be included in Go 1.9. + +This article is an extended version of a talk given at +GothamGo in New York on November 18, 2016. + +* Introduction + +Go’s goal is to make it easy to build software that scales. +There are two kinds of scale that we care about. +One kind of scale is the size of the systems that you can build with Go, +meaning how easy it is to use large numbers of computers, +process large amounts of data, and so on. +That’s an important focus for Go but not for this article. +Instead, this article focuses on another kind of scale, +the size of Go programs, +meaning how easy it is to work in large codebases +with large numbers of engineers +making large numbers of changes independently. + +One such codebase is +[[http://m.cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/pdf][Google’s single repository]] +that nearly all engineers work in on a daily basis. +As of January 2015, +that repository was seeing 40,000 commits per day +across 9 million source files +and 2 billion lines of code. +Of course, there is more in the repository than just Go code. + +Another large codebase is the set of all the open source Go code +that people have made available on GitHub +and other code hosting sites. +You might think of this as `go` `get`’s codebase. +In contrast to Google’s codebase, +`go` `get`’s codebase is completely decentralized, +so it’s more difficult to get exact numbers. +In November 2016, there were 140,000 packages known to [[https://godoc.org/][godoc.org]], +and over 160,000 +[[https://github.com/search?utf8=%E2%9C%93&q=language%3AGo&type=Repositories&ref=searchresults][GitHub repos written in Go]]. + +Supporting software development at this scale was in our +minds from the very beginning of Go. +We paid a lot of attention to implementing imports efficiently. +We made sure that it was difficult to import code but forget to use it, to avoid code bloat. +We made sure that there weren’t unnecessary dependencies +between packages, both to simplify programs and to make it +easier to test and refactor them. +For more detail about these considerations, see Rob Pike’s 2012 article +“[[https://talks.golang.org/2012/splash.article][Go at Google: Language Design in the Service of Software Engineering]].” + +Over the past few years we’ve come to realize that there’s +more that can and should be done to make it easier +to refactor whole codebases, +especially at the broad package structure level, +to help Go scale to ever-larger programs. + +* Codebase refactoring + +Most programs start with one package. +As you add code, occasionally you recognize +a coherent section of code that could stand on its own, +so you move that section into its own package. +Codebase refactoring is the process of rethinking +and revising decisions about both the grouping of code +into packages and the relationships between those packages. +There are a few reasons you might want to change the way +a codebase is organized into packages. + +The first reason is to split a package into more manageable pieces for users. +For example, most users of [[https://golang.org/pkg/regexp/][package regexp]] don’t need access to the +regular expression parser, although [[https://godoc.org/github.com/google/codesearch/regexp][advanced uses may]], +so the parser is exported in [[https://golang.org/pkg/regexp/syntax][a separate regexp/syntax package]]. + +The second reason is to [[https://blog.golang.org/package-names][improve naming]]. +For example, early versions of Go had an `io.ByteBuffer`, +but we decided `bytes.Buffer` was a better name and package bytes a better place for the code. + +The third reason is to lighten dependencies. +For example, we moved `os.EOF` to `io.EOF` so that code not using the operating system +can avoid importing the fairly heavyweight [[https://golang.org/pkg/os][package os]]. + +The fourth reason is to change the dependency graph +so that one package can import another. +For example, as part of the preparation for Go 1, we looked at the explicit dependencies +between packages and how they constrained the APIs. +Then we changed the dependency graph to make the APIs better. + +Before Go 1, the `os.FileInfo` struct contained these fields: + + type FileInfo struct { + Dev uint64 // device number + Ino uint64 // inode number + ... + Atime_ns int64 // access time; ns since epoch + Mtime_ns int64 // modified time; ns since epoch + Ctime_ns int64 // change time; ns since epoch + Name string // name of file + } + +Notice the times `Atime_ns`, `Mtime_ns`, `Ctime_ns` have type int64, +an `_ns` suffix, and are commented as “nanoseconds since epoch.” +These fields would clearly be nicer using [[https://golang.org/pkg/time/#Time][`time.Time`]], +but mistakes in the design of the package structure of the codebase +prevented that. +To be able to use `time.Time` here, we refactored the codebase. + +This graph shows eight packages from the standard library +before Go 1, with an arrow from P to Q indicating that P imports Q. + +.html refactor/import1.html + +Nearly every package has to consider errors, +so nearly every package, including package time, imported package os for `os.Error`. +To avoid cycles, anything that imports package os cannot itself be used by package os. +As a result, operating system APIs could not use `time.Time`. + +This kind of problem convinced us that +`os.Error` and its constructor `os.NewError` were so fundamental +that they should be moved out of package os. +In the end, we moved `os.Error` into the language as [[https://golang.org/ref/spec/#Errors][`error`]] +and `os.NewError` into the new +[[https://golang.org/pkg/errors][package errors]] +as `errors.New`. +After this and other refactoring, the import graph in Go 1 looked like: + +.html refactor/import2.html + +Package io and package time had few enough dependencies +to be used by package os, and +the Go 1 definition of [[https://golang.org/pkg/os/#FileInfo][`os.FileInfo`]] does use `time.Time`. + +(As a side note, our first idea was to move `os.Error` and `os.NewError` +to a new package named error (singular) as `error.Value` and `error.New`. +Feedback from Roger Peppe and others in the Go community helped us +see that making the error type predefined in the language would +allow its use even in low-level contexts like the specification of +[[https://golang.org/ref/spec#Run_time_panics][run-time panics]]. +Since the type was named `error`, the package became errors (plural) +and the constructor `errors.New`. +Andrew Gerrand’s 2015 talk +“[[https://talks.golang.org/2015/how-go-was-made.slide#37][How Go was Made]]” has more detail.) + +* Gradual code repair + +The benefits of a codebase refactoring apply throughout the codebase. +Unfortunately, so do the costs: +often a large number of repairs must be made as a result of the refactoring. +As codebases grow, it becomes infeasible to do all the repairs at one time. +The repairs must be done gradually, +and the programming language must make that possible. + +As a simple example, +when we moved `io.ByteBuffer` to `bytes.Buffer` in 2009, the [[https://go.googlesource.com/go/+/d3a412a5abf1ee8815b2e70a18ee092154af7672][initial commit]] +moved two files, adjusted three makefiles, and repaired 43 other Go source files. +The repairs outweighed the actual API change by a factor of twenty, +and the entire codebase was only 250 files. +As codebases grow, so does the repair multiplier. +Similar changes in large Go codebases, +such as Docker, and Juju, and Kubernetes, +can have repair multipliers ranging from 10X to 100X. +Inside Google we’ve seen repair multipliers well over 1000X. + +The conventional wisdom is that when making a codebase-wide API change, +the API change and the associated code repairs should be committed +together in one big commit: + +.html refactor/atomic.html + +The argument in favor of this approach, +which we will call “atomic code repair,” +is that it is conceptually simple: +by updating the API and the code repairs in the same commit, +the codebase transitions in one step from the old API to the new API, +without ever breaking the codebase. +The atomic step avoids the need to plan for a transition +during which both old and new API must coexist. +In large codebases, however, the conceptual simplicity +is quickly outweighed by a practical complexity: +the one big commit can be very big. +Big commits are hard to prepare, hard to review, +and are fundamentally racing against other work in the tree. +It’s easy to start doing a conversion, prepare your one big commit, +finally get it submitted, and only then find out that another developer added +a use of the old API while you were working. +There were no merge conflicts, +so you missed that use, and despite all your effort +the one big commit broke the codebase. +As codebases get larger, +atomic code repairs become more difficult +and more likely to break the codebase inadvertently. + +In our experience, +an approach that scales better is to plan for a transition period +during which the code repair proceeds gradually, +across as many commits as needed: + +.html refactor/gradual.html + +Typically this means the overall process runs in three stages. +First, introduce the new API. +The old and new API must be _interchangeable_, +meaning that it must be possible to convert individual uses +from the old to the new API without changing the overall +behavior of the program, +and uses of the old and new APIs must be able to coexist +in a single program. +Second, across as many commits as you need, +convert all the uses of the old API to the new API. +Third, remove the old API. + +“Gradual code repair” is usually more work +than the atomic code repair, +but the work itself is easier: +you don’t have to get everything right in one try. +Also, the individual commits are much smaller, +making them easier to review and submit +and, if needed, roll back. +Maybe most important of all, a gradual code repair +works in situations when one big commit would be impossible, +for example when code that needs repairs +is spread across multiple repositories. + +The `bytes.Buffer` change looks like an atomic code repair, but it wasn’t. +Even though the commit updated 43 source files, +the commit message says, +“left io.ByteBuffer stub around for now, for protocol compiler.” +That stub was in a new file named `io/xxx.go` that read: + + // This file defines the type io.ByteBuffer + // so that the protocol compiler's output + // still works. Once the protocol compiler + // gets fixed, this goes away. + + package io + + import "bytes" + + type ByteBuffer struct { + bytes.Buffer; + } + +Back then, just like today, +Go was developed in a separate source repository +from the rest of Google’s source code. +The protocol compiler in Google’s main repository was +responsible for generating Go source files from protocol buffer definitions; +the generated code used `io.ByteBuffer`. +This stub was enough to keep the generated code working +until the protocol compiler could be updated. +Then [[https://go.googlesource.com/go/+/832e72beff62e4fe4897699e9b40a2b228e8503b][a later commit]] removed `xxx.go`. + +Even though there were many fixes included in the original commit, +this change was still a gradual code repair, not an atomic one, +because the old API was only removed in a separate stage +after the existing code was converted. + +In this specific case the gradual repair did succeed, but +the old and new API were not completely interchangeable: +if there had been a function taking an `*io.ByteBuffer` argument +and code calling that function with an `*io.ByteBuffer`, +those two pieces of code could not have been updated independently: +code that passed an `*io.ByteBuffer` to a function expecting a `*bytes.Buffer`, +or vice versa, would not compile. + +Again, a gradual code repair consists of three stages: + +.html refactor/template.html + +These stages apply to a gradual code repair for any API change. +In the specific case of codebase refactoring—moving +an API from one package to another, changing its full name in the process—making the old and new API +interchangeable means making the old and new names interchangeable, +so that code using the old name has exactly the same behavior +as if it used the new name. + +Let’s look at examples of how Go makes that possible (or not). + +** Constants + +Let’s start with a simple example of moving a constant. + +Package io defines the [[https://golang.org/pkg/io/#Seeker][Seeker interface]], +but the named constants that developers prefer to use +when invoking the `Seek` method came from package os. +Go 1.7 moved the constants to package io and gave them more idiomatic names; +for example, `os.SEEK_SET` is now available as `io.SeekStart`. + +For a constant, one name is interchangeable with another +when the definitions use the same type and value: + + package io + const SeekStart int = 0 + + package os + const SEEK_SET int = 0 + +Due to [[https://golang.org/doc/go1compat][Go 1 compatibility]], +we’re blocked in stage 2 of this gradual code change. +We can’t delete the old constants, +but making the new ones available in package io allows +developers to avoid importing package os in code that +does not actually depend on operating system functionality. + +This is also an example of a gradual code repair being done +across many repositories. +Go 1.7 introduced the new API, +and now it’s up to everyone with Go code to update their code +as they see fit. +There’s no rush, no forced breakage of existing code. + +** Functions + +Now let’s look at moving a function from one package to another. + +As mentioned above, +in 2011 we replaced `os.Error` with the predefined type `error` +and moved the constructor `os.NewError` to a new package as +[[https://golang.org/pkg/errors/#New][`errors.New`]]. + +For a function, one name is interchangeable with another +when the definitions use the same signature and implementation. +In this case, we can define the old function as a wrapper calling +the new function: + + package errors + func New(msg string) error { ... } + + package os + func NewError(msg string) os.Error { + return errors.New(msg) + } + +Since Go does not allow comparing functions for equality, +there is no way to tell these two functions apart. +The old and new API are interchangeable, +so we can proceed to stages 2 and 3. + +(We are ignoring a small detail here: the original +`os.NewError` returned an `os.Error`, not an `error`, +and two functions with different signatures _are_ distinguishable. +To really make these functions indistinguishable, +we would also need to make `os.Error` and `error` indistinguishable. +We will return to that detail in the discussion of types below.) + +** Variables + +Now let’s look at moving a variable from one package to another. + +We are discussing exported package-level API, so the variable +in question must be an exported global variable. +Such variables are almost always set at init time +and then only intended to be read from, never written again, +to avoid races between reading and writing goroutines. +For exported global variables that follow this pattern, +one name is nearly interchangeable with another when the two have +the same type and value. +The simplest way to arrange that is to initialize one from the other: + + package io + var EOF = ... + + package os + var EOF = io.EOF + +In this example, io.EOF and os.EOF are the same value. +The variable values are completely interchangeable. + +There is one small problem. +Although the variable values are interchangeable, +the variable addresses are not. +In this example, `&io.EOF` and `&os.EOF` are different pointers. +However, it is rare to export a read-only variable +from a package and expect clients to take its address: +it would be better for clients if the package exported a variable set to the address instead, +and then the pattern works. + +** Types + +Finally let’s look at moving a type from one package to another. +This is much harder to do in Go today, as the following three examples demonstrate. + +*** Go’s os.Error + +Consider once more the conversion from `os.Error` to `error`. +There’s no way in Go to make two names of types interchangeable. +The closest we can come in Go is to give `os.Error` and `error` the same underlying definition: + + package os + type Error error + +Even with this definition, and even though these are interface types, +Go still considers these two types [[https://golang.org/ref/spec#Type_identity][different]], +so that a function returning an os.Error +is not the same as a function returning an error. +Consider the [[https://golang.org/pkg/io/#Reader][`io.Reader`]] interface: + + package io + type Reader interface { + Read(b []byte) (n int, err error) + } + +If `io.Reader` is defined using `error`, as above, then a `Read` method +returning `os.Error` will not satisfy the interface. + +If there’s no way to make two names for a type interchangeable, +that raises two questions. +First, how do we enable a gradual code repair for a moved or renamed type? +Second, what did we do for `os.Error` in 2011? + +To answer the second question, we can look at the source control history. +It turns out that to aid the conversion, we +[[https://go.googlesource.com/go/+/47f4bf763dcb120d3b005974fec848eefe0858f0][added a temporary hack to the compiler]] +to make code written using `os.Error` be interpreted as if it had written `error` instead. + +*** Kubernetes + +This problem with moving types is not limited to fundamental changes like `os.Error`, +nor is it limited to the Go repository. +Here’s a change from the [[https://kubernetes.io/][Kubernetes project]]. +Kubernetes has a package util, and at some point the developers +decided to split out that package’s `IntOrString` type into its own +[[https://godoc.org/k8s.io/kubernetes/pkg/util/intstr][package intstr]]. + +Applying the pattern for a gradual code repair, +the first stage is to establish a way for the two types to be interchangeable. +We can’t do that, +because the `IntOrString` type is used in struct fields, +and code can’t assign to that field unless the value being +assigned has the correct type: + + package util + type IntOrString intstr.IntOrString + + // Not good enough for: + + // IngressBackend describes ... + type IngressBackend struct { + ServiceName string `json:"serviceName"` + ServicePort intstr.IntOrString `json:"servicePort"` + } + +If this use were the only problem, then you could imagine +writing a getter and setter using the old type +and doing a gradual code repair to change all existing code +to use the getter and setter, +then modifying the field to use the new type +and doing a gradual code repair to change all existing code +to access the field directly using the new type, +then finally deleting the getter and setter that mention the old type. +That required two gradual code repairs instead of one, +and there are many uses of the type other than this one struct field. + +In practice, the only option here is an atomic code repair, +or else breaking all code using `IntOrString`. + +*** Docker + +As another example, +here’s a change from the [[https://www.docker.com/][Docker project]]. +Docker has a package utils, and at some point the developers +decided to split out that package’s `JSONError` type into a separate +[[https://godoc.org/github.com/docker/docker/pkg/jsonmessage#JSONError][jsonmessage package]]. + +Again we have the problem that the old and new types are not interchangeable, +but it shows up in a different way, namely [[https://golang.org/ref/spec#Type_assertions][type assertions]]: + + package utils + type JSONError jsonmessage.JSONError + + // Not good enough for: + + jsonError, ok := err.(*jsonmessage.JSONError) + if !ok { + jsonError = &jsonmessage.JSONError{ + Message: err.Error(), + } + } + +If the error `err` not already a `JSONError`, this code wraps it in one, +but during a gradual repair, this code handles `utils.JSONError` and `jsonmessage.JSONError` differently. +The two types are not interchangeable. +(A [[https://golang.org/ref/spec#Type_switches][type switch]] would expose the same problem.) + +If this line were the only problem, then you could imagine +adding a type assertion for `*utils.JSONError`, +then doing a gradual code repair to remove other uses of `utils.JSONError`, +and finally removing the additional type guard just before removing the old type. +But this line is not the only problem. +The type is also used elsewhere in the API and has all the +problems of the Kubernetes example. + +In practice, again the only option here is an atomic code repair +or else breaking all code using `JSONError`. + +* Solutions? + +We’ve now seen examples of how we can and cannot move +constants, functions, variables, and types from one package to another. +The patterns for establishing interchangeable old and new API are: + + const OldAPI = NewPackage.API + + func OldAPI() { NewPackage.API() } + + var OldAPI = NewPackage.API + + type OldAPI ... ??? modify compiler or ... ??? + +For constants and functions, the setup for a gradual code repair is trivial. +For variables, the trivial setup is incomplete but only in ways that are not likely to arise often in practice. + +For types, there is no way to set up a gradual code repair in essentially any real example. +The most common option is to force an atomic code repair, +or else to break all code using the moved type and leave clients +to fix their code at the next update. +In the case of moving os.Error, we resorted to modifying the compiler. +None of these options is reasonable. +Developers should be able to do refactorings +that involve moving a type from one package to another +without needing an atomic code repair, +without resorting to intermediate code and multiple rounds of repair, +without forcing all client packages to update their own code immediately, +and without even thinking about modifying the compiler. + +But how? What should these refactorings look like tomorrow? + +We don’t know. +The goal of this article is to define the problem well enough +to discuss the possible answers. + +** Aliases + +As explained above, the fundamental problem with moving types is that +while Go provides ways to create an alternate name +for a constant or a function or (most of the time) a variable, +there is no way to create an alternate name for a type. + +For Go 1.8 we experimented with introducing first-class support +for these alternate names, called [[https://golang.org/design/16339-alias-decls][_aliases_]]. +A new declaration syntax, the alias form, would have provided a uniform way +to create an alternate name for any kind of identifier: + + const OldAPI => NewPackage.API + func OldAPI => NewPackage.API + var OldAPI => NewPackage.API + type OldAPI => NewPackage.API + +Instead of four different mechanisms, the refactoring of package os we considered above +would have used a single mechanism: + + package os + const SEEK_SET => io.SeekStart + func NewError => errors.New + var EOF => io.EOF + type Error => error + +During the Go 1.8 release freeze, we found two small but important unresolved technical details +in the alias support (issues [[https://golang.org/issue/17746][17746]] and [[https://golang.org/issue/17784][17784]]), +and we decided that it was not possible to resolve them confidently +in the time remaining before the Go 1.8 release, +so we held aliases back from Go 1.8. + +** Versioning + +An obvious question is whether to rely on versioning and +dependency management for code repair, +instead of focusing on strategies that enable gradual code repair. + +Versioning and gradual code repair strategies are complementary. +A versioning system’s job is to identify a compatible set of +versions of all the packages needed in a program, or else to +explain why no such set can be constructed. +Gradual code repair creates additional compatible combinations, +making it more likely that a versioning system can find a way +to build a particular program. + +Consider again the various updates to Go’s standard library +that we discussed above. +Suppose that the old API +corresponded in a versioning system +to standard library version 5.1.3. +In the usual atomic code repair approach, +the new API would be introduced and the old API removed at the same time, +resulting in version 6.0.0; +following [[http://semver.org/][semantic versioning]], +the major version number is incremented to indicate the incompatibility +caused by removing the old API. + +Now suppose that your larger program depends on two packages, Foo and Bar. +Foo still uses the old standard library API. +Bar has been updated to use the new standard library API, +and there have been important changes since then that your +program needs: you can’t use an older version of Bar from +before the standard library changes. + +.html refactor/version1.html + +There is no compatible set of libraries to build your program: +you want the latest version of Bar, which requires +standard library 6.0.0, +but you also need Foo, which is incompatible with standard library 6.0.0. +The best a versioning system can do in this case is report the failure clearly. +(If you are sufficiently motivated, you might then resort to updating your own copy of Foo.) + +In contrast, with better support for gradual code repair, +we can add the new, interchangeable API in version 5.2.0, +and then remove the old API in version 6.0.0. + +.html refactor/version2.html + +The intermediate version 5.2.0 is backwards compatible with 5.1.3, +indicated by the shared major version number 5. +However, because the change from 5.2.0 to 6.0.0 only removed API, +5.2.0 is also, perhaps surprisingly, backwards compatible with 6.0.0. +Assuming that Bar declares its requirements precisely—it is +compatible with both 5.2.0 and 6.0.0—a version system can see that +both Foo and Bar are compatible with 5.2.0 and use that version +of the standard library to build the program. + +Good support for and adoption of gradual code repair reduces incompatibility, +giving versioning systems a better chance to find a way to build your program. + +** Type aliases + +To enable gradual code repair during codebase refactorings, +it must be possible to create alternate names for a +constant, function, variable, or type. +Go already allows introducing alternate names for +all constants, all functions, and nearly all variables, but no types. +Put another way, +the general alias form is never necessary for constants, +never necessary for functions, +only rarely necessary for variables, +but always necessary for types. + +The relative importance to the specific declarations +suggests that perhaps the Go 1.8 aliases were an overgeneralization, +and that we should instead focus on a solution limited to types. +The obvious solution is type-only aliases, +for which no new operator is required. +Following +[[http://www.freepascal.org/docs-html/ref/refse19.html][Pascal]] +(or, if you prefer, [[https://doc.rust-lang.org/book/type-aliases.html][Rust]]), +a Go program could introduce a type alias using the assignment operator: + + type OldAPI = NewPackage.API + +The idea of limiting aliases to types was +[[https://golang.org/issue/16339#issuecomment-233644777][raised during the Go 1.8 alias discussion]], +but it seemed worth trying the more general approach, which we did, unsuccessfully. +In retrospect, the fact that `=` and `=>` have identical meanings for constants +while they have nearly identical but subtly different meanings for variables +suggests that the general approach is not worth its complications. + +In fact, the idea of adding Pascal-style type aliases +was [[https://golang.org/issue/16339#issuecomment-233759255][considered in the early design of Go]], +but until now we didn’t have a strong use case for them. + +Type aliases seem like a promising approach to explore, +but, at least to me, generalized aliases seemed equally promising +before the discussion and experimentation during the Go 1.8 cycle. +Rather than prejudge the outcome, the goal of this article is to +explain the problem in detail and examine a few possible solutions, +to enable a productive discussion and evaluation of ideas for next time. + +* Challenge + +Go aims to be ideal for large codebases. + +In large codebases, it’s important to be able to refactor codebase structure, +which means moving APIs between packages and updating client code. + +In such large refactorings, it’s important to be able to use a gradual transition from the old API to the new API. + +Go does not support the specific case of gradual code repair when moving types between packages at all. It should. + +I hope we the Go community can fix this together in Go 1.9. Maybe type aliases are a good starting point. Maybe not. Time will tell. + +* Acknowledgements + +Thanks to the many people who helped us [[https://golang.org/issue/16339][think through the design questions]] +that got us this far and led to the alias trial during Go 1.8 development. +I look forward to the Go community helping us again when we revisit this problem for Go 1.9.
diff --git a/2016/refactor/atomic.graffle b/2016/refactor/atomic.graffle new file mode 100644 index 0000000..a4d9fa4 --- /dev/null +++ b/2016/refactor/atomic.graffle Binary files differ
diff --git a/2016/refactor/atomic.html b/2016/refactor/atomic.html new file mode 100644 index 0000000..7d15ce3 --- /dev/null +++ b/2016/refactor/atomic.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/atomic.svg" alt="Atomic Code Repair"></img></center></div>
diff --git a/2016/refactor/atomic.svg b/2016/refactor/atomic.svg new file mode 100644 index 0000000..244fe37 --- /dev/null +++ b/2016/refactor/atomic.svg
@@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-1 -1 124 82" width="124pt" height="82pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 03:28:54 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <text transform="translate(17.125 4.411133)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="9.143555" y="11" textLength="50.033203">Add new </tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" x="58.51465" y="11" textLength="19.341797">API</tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" x="9.149414" y="39.39258" textLength="68.70117">Code repairs</tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" x=".47753906" y="67.78516" textLength="67.365234">Remove old </tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" x="67.180664" y="67.78516" textLength="19.341797">API</tspan> + </text> + <path d="M 18.1875 7105427e-21 L 103.0625 7105427e-21 C 113.102 7105427e-21 121.25 17.92 121.25 40 C 121.25 62.08 113.102 80 103.0625 80 L 18.1875 80 C 8.148 80 0 62.08 0 40 C 0 17.92 8.148 7105427e-21 18.1875 7105427e-21" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + </g> + </g> +</svg>
diff --git a/2016/refactor/gradual.graffle b/2016/refactor/gradual.graffle new file mode 100644 index 0000000..65b6e84 --- /dev/null +++ b/2016/refactor/gradual.graffle Binary files differ
diff --git a/2016/refactor/gradual.html b/2016/refactor/gradual.html new file mode 100644 index 0000000..24c5612 --- /dev/null +++ b/2016/refactor/gradual.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/gradual.svg" alt="Gradual Code Repair"></img></center></div>
diff --git a/2016/refactor/gradual.svg b/2016/refactor/gradual.svg new file mode 100644 index 0000000..d0caccb --- /dev/null +++ b/2016/refactor/gradual.svg
@@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-2 -1 305 178" width="305pt" height="178pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 03:27:43 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + <font-face font-family="Arial" font-size="10" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <path d="M 107.86806 0 L 192.9375 0 C 203 0 211.16667 5.0524444 211.16667 11.277778 C 211.16667 17.503111 203 22.555556 192.9375 22.555556 L 107.86806 22.555556 C 97.80556 22.555556 89.63889 17.503111 89.63889 11.277778 C 89.63889 5.0524444 97.80556 0 107.86806 0" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(106.79167 4.0814887)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="9.254666" y="11" textLength="50.033203">Add new </tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="58.62576" y="11" textLength="19.341797">API</tspan> + </text> + <path d="M 107.86806 38.11111 L 192.9375 38.11111 C 203 38.11111 211.16667 43.163556 211.16667 49.38889 C 211.16667 55.61422 203 60.66667 192.9375 60.66667 L 107.86806 60.66667 C 97.80556 60.66667 89.63889 55.61422 89.63889 49.38889 C 89.63889 43.163556 97.80556 38.11111 107.86806 38.11111" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(106.79167 42.1926)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="9.260525" y="11" textLength="68.70117">Code repairs</tspan> + </text> + <path d="M 184.09028 76.22222 L 269.15972 76.22222 C 279.22222 76.22222 287.3889 81.27467 287.3889 87.5 C 287.3889 93.72533 279.22222 98.77778 269.15972 98.77778 L 184.09028 98.77778 C 174.02778 98.77778 165.86111 93.72533 165.86111 87.5 C 165.86111 81.27467 174.02778 76.22222 184.09028 76.22222" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(183.01389 80.30371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="9.260525" y="11" textLength="68.70117">Code repairs</tspan> + </text> + <path d="M 30.868056 76.22222 L 115.9375 76.22222 C 126 76.22222 134.16667 81.27467 134.16667 87.5 C 134.16667 93.72533 126 98.77778 115.9375 98.77778 L 30.868056 98.77778 C 20.805556 98.77778 12.638889 93.72533 12.638889 87.5 C 12.638889 81.27467 20.805556 76.22222 30.868056 76.22222" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(29.791667 80.30371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="9.260525" y="11" textLength="68.70117">Code repairs</tspan> + </text> + <path d="M 107.86806 114.33333 L 192.9375 114.33333 C 203 114.33333 211.16667 119.38578 211.16667 125.61111 C 211.16667 131.83644 203 136.88889 192.9375 136.88889 L 107.86806 136.88889 C 97.80556 136.88889 89.63889 131.83644 89.63889 125.61111 C 89.63889 119.38578 97.80556 114.33333 107.86806 114.33333" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(106.79167 118.41482)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="9.260525" y="11" textLength="68.70117">Code repairs</tspan> + </text> + <path d="M 102.67639 152.66667 L 198.12917 152.66667 C 209.41987 152.66667 218.58333 157.71911 218.58333 163.94444 C 218.58333 170.16978 209.41987 175.22222 198.12917 175.22222 L 102.67639 175.22222 C 91.38569 175.22222 82.22222 170.16978 82.22222 163.94444 C 82.22222 157.71911 91.38569 152.66667 102.67639 152.66667" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> + <text transform="translate(100.85833 156.74816)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="6.5219835" y="11" textLength="67.365234">Remove old </tspan> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="73.22511" y="11" textLength="19.341797">API</tspan> + </text> + <line x1="36948222e-20" y1="29.555556" x2="300.41667" y2="29.555556" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="36948222e-20" y1="144.27778" x2="300.41667" y2="144.27778" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(5 2.8919813)" fill="black"> + <tspan font-family="Arial" font-size="10" font-weight="500" x="11.985894" y="9" textLength="34.472656">Stage 1</tspan> + </text> + <text transform="translate(5 40.225315)" fill="black"> + <tspan font-family="Arial" font-size="10" font-weight="500" x="11.985894" y="9" textLength="34.472656">Stage 2</tspan> + </text> + <text transform="translate(5 155.33643)" fill="black"> + <tspan font-family="Arial" font-size="10" font-weight="500" x="11.985894" y="9" textLength="34.472656">Stage 3</tspan> + </text> + </g> + </g> +</svg>
diff --git a/2016/refactor/import1.graffle b/2016/refactor/import1.graffle new file mode 100644 index 0000000..020014a --- /dev/null +++ b/2016/refactor/import1.graffle Binary files differ
diff --git a/2016/refactor/import1.html b/2016/refactor/import1.html new file mode 100644 index 0000000..72177fd --- /dev/null +++ b/2016/refactor/import1.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/import1.svg" alt="Import graph before Go 1"></img></center></div>
diff --git a/2016/refactor/import1.svg b/2016/refactor/import1.svg new file mode 100644 index 0000000..5019904 --- /dev/null +++ b/2016/refactor/import1.svg
@@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 -1 150 284" width="150pt" height="284pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 04:39:54 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"> + <g> + <path d="M 4 0 L 0 -1.5 L 0 1.5 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <rect x="30.776487" y="0" width="76" height="23.5" fill="#0432ff" fill-opacity=".29794784"/> + <rect x="30.776487" y="0" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 4.553711)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="21.66504" y="11" textLength="22.669922">time</tspan> + </text> + <rect x="30.776487" y="37" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 41.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="14.323242" y="11" textLength="37.353516">io/ioutil</tspan> + </text> + <rect x="30.776487" y="74" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 78.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="13.661133" y="11" textLength="38.677734">strconv</tspan> + </text> + <rect x="30.776487" y="111" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 115.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="18.65918" y="11" textLength="28.68164">bytes</tspan> + </text> + <rect x="30.776487" y="148" width="76" height="23.5" fill="#ff2600" fill-opacity=".30133143"/> + <rect x="30.776487" y="148" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 152.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="28.330078" y="11" textLength="9.339844">io</tspan> + </text> + <rect x="30.776487" y="185" width="76" height="23.5" fill="#fffc00" fill-opacity=".30303697"/> + <rect x="30.776487" y="185" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 189.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="26.663086" y="11" textLength="12.673828">os</tspan> + </text> + <rect x="30.776487" y="222" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 226.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="14.99707" y="11" textLength="36.00586">syscall</tspan> + </text> + <rect x="30.776487" y="259" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(35.776487 263.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="19.655273" y="11" textLength="26.689453">utf16</tspan> + </text> + <line x1="68.77649" y1="23.5" x2="68.77649" y2="31.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="60.5" x2="68.77649" y2="68.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="97.5" x2="68.77649" y2="105.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="134.5" x2="68.77649" y2="142.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="171.5" x2="68.77649" y2="179.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="208.5" x2="68.77649" y2="216.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="68.77649" y1="245.5" x2="68.77649" y2="253.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 93.17678 23.500002 C 105.04188 30.545757 116.77649 40.134275 116.77649 50.5 C 116.77649 58.39145 109.97533 65.336525 101.49318 70.92955" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 103.57523 23.5 C 111.59158 28.577334 118.62773 35.76719 121.54406 45.89844 C 128.6898 70.72245 118.68265 88.35867 106.49728 106.14751" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 106.77649 20.676139 C 117.07421 26.626272 127.1848 36.423313 133.77649 52.5 C 152.33296 97.75797 152.69131 130.63325 134.27649 171.5 C 128.80609 183.64007 120.80561 190.79561 112.26099 194.85676" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 46.69024 60.5 C 32.14596 69.32664 15.776487 81.84894 15.776487 93.5 C 15.776487 99.29801 19.83028 104.12623 25.695615 108.07761" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 44.78756 60.5 C 27.89426 70.32656 8.276487 85.59294 8.276487 103.5 C 8.276487 119.9993 24.931005 134.66971 40.763704 144.88843" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 40.13828 60.499996 C 24.193 69.34241 6.925629 83.65174 3.7764867 105 C -1.1995027 138.73258 18.695969 159.73378 36.18698 180.46417" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 106.77649 94.66501 C 113.31042 98.37008 119.27522 103.60549 123.27649 111 C 137.72504 137.7015 135.21661 155.7066 119.77649 180 C 117.47504 183.62108 114.72658 186.53483 111.71549 188.87164" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 95.43459 134.5 C 107.53044 141.217 119.17688 150.30111 119.17688 160.3789 C 119.17688 168.6678 111.29816 176.08786 101.77084 182.03036" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 96.65663 208.5 C 109.00063 215.10652 120.77649 224.02344 120.77649 234 C 120.77649 242.434 112.36065 250.02977 102.3003 256.09678" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + </g> + </g> +</svg>
diff --git a/2016/refactor/import2.graffle b/2016/refactor/import2.graffle new file mode 100644 index 0000000..16fe79e --- /dev/null +++ b/2016/refactor/import2.graffle Binary files differ
diff --git a/2016/refactor/import2.html b/2016/refactor/import2.html new file mode 100644 index 0000000..c88de12 --- /dev/null +++ b/2016/refactor/import2.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/import2.svg" alt="Import graph for Go 1"></img></center></div>
diff --git a/2016/refactor/import2.svg b/2016/refactor/import2.svg new file mode 100644 index 0000000..9e569fd --- /dev/null +++ b/2016/refactor/import2.svg
@@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-1 -1 250 247" width="250pt" height="247pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 04:34:01 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"> + <g> + <path d="M 4 0 L 0 -1.5 L 0 1.5 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <rect x="122.97266" y="148" width="76" height="23.5" fill="#0432ff" fill-opacity=".295307"/> + <rect x="122.97266" y="148" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.97266 152.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="21.66504" y="11" textLength="22.669922">time</tspan> + </text> + <rect x="122.21255" y="0" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.21255 4.553711)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="14.323242" y="11" textLength="37.353516">io/ioutil</tspan> + </text> + <rect x="122.21255" y="74" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.21255 78.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="13.661133" y="11" textLength="38.677734">strconv</tspan> + </text> + <rect x="122.21255" y="37" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.21255 41.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="18.65918" y="11" textLength="28.68164">bytes</tspan> + </text> + <rect x="0" y="185" width="76" height="23.5" fill="#ffc0c0"/> + <rect x="0" y="185" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(5 189.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="28.330078" y="11" textLength="9.339844">io</tspan> + </text> + <rect x="122.97266" y="111" width="76" height="23.5" fill="#ffffc0"/> + <rect x="122.97266" y="111" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.97266 115.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="26.663086" y="11" textLength="12.673828">os</tspan> + </text> + <rect x="122.97266" y="185" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.97266 189.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="14.99707" y="11" textLength="36.00586">syscall</tspan> + </text> + <rect x="122.97266" y="222" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(127.97266 226.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="19.655273" y="11" textLength="26.689453">utf16</tspan> + </text> + <rect x="0" y="222" width="76" height="23.5" stroke="#666" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(5 226.5537)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="17.332031" y="11" textLength="31.335938">errors</tspan> + </text> + <line x1="160.21255" y1="23.5" x2="160.21255" y2="31.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 188.37477 23.500003 C 200.08476 29.83677 211 38.35015 211 48.06921 C 211 56.7416 202.30916 64.687 192.11774 71.02284" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 197.74767 23.500009 C 218.19313 31.76049 239.38865 44.239423 239.5 60.5 C 239.6234 78.51898 213.82277 96.32661 191.76642 108.24899" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 198.21255 17.560787 C 216.79686 22.535277 236.5144 31.720686 242.85742 48.63171 C 255.90117 83.4074 229.45227 103.01963 206.2539 134.5 C 203.58735 138.11854 200.93553 141.23882 198.3303 143.92759" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="160.21255" y1="60.5" x2="160.21255" y2="68.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 122.21254 50.13559 C 113.00153 54.213294 102.8189 61.503755 92.5 74 C 67.89765 103.79357 50.741114 153.22393 42.870807 179.34142" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 122.21254 91.98431 C 118.5502 95.44879 114.92645 100.0403 111.5 106.06921 C 90.75852 142.56434 107.82292 178.3966 87 214.5692 C 84.77209 218.43943 82.45999 221.70105 80.10723 224.44" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="38" y1="208.5" x2="38" y2="216.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="160.97266" y1="134.5" x2="160.97266" y2="142.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="160.97266" y1="171.5" x2="160.97266" y2="179.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="160.97266" y1="208.5" x2="160.97266" y2="216.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 191.02245 134.5 C 204.52862 141.17393 217.48633 150.19272 217.48633 160.23132 C 217.48633 168.6864 208.29419 176.26144 197.32434 182.29105" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 195.85316 134.50001 C 214.84078 142.7605 234.5 155.23942 234.5 171.5 C 234.5 189.44295 210.56231 207.17633 190.00368 219.09776" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 122.97265 158.83137 C 119.283 160.33365 115.72009 162.50762 112.5 165.56921 C 93.95127 183.20495 116.60622 202.68404 95.5 222 C 91.32614 225.81983 86.55442 228.58839 81.58454 230.57208" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="141.4466" y1="134.5" x2="62.58134" y2="181.95794" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 124.9436 60.5 C 118.85351 66.18221 112.66408 74.35976 107 86.06921 C 84.52826 132.52539 100.44062 170.89066 80.89258 212.72937 C 80.21104 214.18806 79.52489 215.5706 78.83527 216.8805" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + </g> + </g> +</svg>
diff --git a/2016/refactor/template.html b/2016/refactor/template.html new file mode 100644 index 0000000..f7d3708 --- /dev/null +++ b/2016/refactor/template.html
@@ -0,0 +1,5 @@ +<ol> +<li>Add new API, <i>interchangeable</i> with old API. +<li>Convert uses of old API to new API. +<li>Remove old API. +</ol>
diff --git a/2016/refactor/version1.graffle b/2016/refactor/version1.graffle new file mode 100644 index 0000000..d76d813 --- /dev/null +++ b/2016/refactor/version1.graffle Binary files differ
diff --git a/2016/refactor/version1.html b/2016/refactor/version1.html new file mode 100644 index 0000000..bd290ec --- /dev/null +++ b/2016/refactor/version1.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/version1.svg" alt="Standard Library Incompatibility"></img></center></div>
diff --git a/2016/refactor/version1.svg b/2016/refactor/version1.svg new file mode 100644 index 0000000..2e5d5d1 --- /dev/null +++ b/2016/refactor/version1.svg
@@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-1 -1 296 187" width="296pt" height="187pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 03:51:49 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#0432ff"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff2600"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_4" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff2600"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <font-face font-family="Helvetica" font-size="8" units-per-em="1000" underline-position="-75.68359" underline-thickness="49.316406" slope="0" x-height="522.9492" cap-height="717.28516" ascent="770.0195" descent="-229.98047" font-weight="500"> + <font-face-src> + <font-face-name name="Helvetica"/> + </font-face-src> + </font-face> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <rect x="78" y="0" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(83 4.553711)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="19.995117" y="11" textLength="26.009766">Main</tspan> + </text> + <rect x="0" y="48" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(5 52.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="22.661133" y="11" textLength="20.677734">Foo</tspan> + </text> + <rect x="156" y="46" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(161 50.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="23.663086" y="11" textLength="18.673828">Bar</tspan> + </text> + <line x1="135.92391" y1="23.5" x2="165.54856" y2="40.97095" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="96.90625" y1="23.5" x2="65.52517" y2="42.811435" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <rect x="48" y="117" width="136" height="68" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(53 166.10742)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="21.64746" y="11" textLength="82.70508">standard library</tspan> + </text> + <rect x="58.5" y="133.5" width="47.5" height="23.5" stroke="#a5a5a5" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(63.5 138.05371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="5.4052734" y="11" textLength="26.689453">5.1.3</tspan> + </text> + <rect x="126" y="133.5" width="47.5" height="23.5" stroke="#a5a5a5" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(131 138.05371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="5.4052734" y="11" textLength="26.689453">6.0.0</tspan> + </text> + <line x1="44.08114" y1="71.5" x2="71.618475" y2="124.70773" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="188.05786" y1="69.5" x2="160.1599" y2="124.66546" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="53.357456" y1="71.5" x2="126.5299" y2="127.48429" marker-end="url(#FilledArrow_Marker_3)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/> + <line x1="178.99357" y1="69.5" x2="105.05126" y2="127.39667" marker-end="url(#FilledArrow_Marker_4)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/> + <line x1="199.5" y1="130" x2="275.95" y2="130" marker-end="url(#FilledArrow_Marker_4)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75" stroke-dasharray="1,3"/> + <text transform="translate(204.5 120)" fill="#ff2600"> + <tspan font-family="Helvetica" font-size="8" font-weight="500" fill="#ff2600" x="14.542969" y="8" textLength="44.91406">incompatible</tspan> + </text> + <line x1="199.5" y1="150" x2="275.95" y2="150" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75"/> + <text transform="translate(204.5 140)" fill="#0432ff"> + <tspan font-family="Helvetica" font-size="8" font-weight="500" fill="#0432ff" x="17.65625" y="8" textLength="38.6875">compatible</tspan> + </text> + </g> + </g> +</svg>
diff --git a/2016/refactor/version2.graffle b/2016/refactor/version2.graffle new file mode 100644 index 0000000..122ec2b --- /dev/null +++ b/2016/refactor/version2.graffle Binary files differ
diff --git a/2016/refactor/version2.html b/2016/refactor/version2.html new file mode 100644 index 0000000..ea6442e --- /dev/null +++ b/2016/refactor/version2.html
@@ -0,0 +1 @@ +<div class="image"><center><img src="refactor/version2.svg" alt="Standard Library Compatibility"></img></center></div>
diff --git a/2016/refactor/version2.svg b/2016/refactor/version2.svg new file mode 100644 index 0000000..698fd14 --- /dev/null +++ b/2016/refactor/version2.svg
@@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-1 -1 296 219" width="296pt" height="219pt" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata> Produced by OmniGraffle 7.2.1 + <dc:date>2016-12-01 03:54:17 +0000</dc:date> + </metadata> + <defs> + <font-face font-family="Arial" font-size="12" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-105.95703" underline-thickness="73.24219" slope="0" x-height="518.5547" cap-height="716.3086" ascent="905.2734" descent="-211.91406" font-weight="500"> + <font-face-src> + <font-face-name name="ArialMT"/> + </font-face-src> + </font-face> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#0432ff"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff2600"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_4" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff2600"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + <font-face font-family="Helvetica" font-size="8" units-per-em="1000" underline-position="-75.68359" underline-thickness="49.316406" slope="0" x-height="522.9492" cap-height="717.28516" ascent="770.0195" descent="-229.98047" font-weight="500"> + <font-face-src> + <font-face-name name="Helvetica"/> + </font-face-src> + </font-face> + <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_5" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#0432ff"> + <g> + <path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/> + </g> + </marker> + </defs> + <g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"> + <title>Canvas 1</title> + <g> + <title>Layer 1</title> + <rect x="78" y="0" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(83 4.553711)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="19.995117" y="11" textLength="26.009766">Main</tspan> + </text> + <rect x="0" y="48" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(5 52.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="22.661133" y="11" textLength="20.677734">Foo</tspan> + </text> + <rect x="156" y="46" width="76" height="23.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(161 50.55371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" x="23.663086" y="11" textLength="18.673828">Bar</tspan> + </text> + <line x1="135.92391" y1="23.5" x2="165.54856" y2="40.97095" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="96.90625" y1="23.5" x2="65.52517" y2="42.811435" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <rect x="48" y="117" width="136" height="100.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(53 198.60742)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="21.64746" y="11" textLength="82.70508">standard library</tspan> + </text> + <rect x="58.5" y="133.5" width="47.5" height="23.5" stroke="#a5a5a5" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(63.5 138.05371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="5.4052734" y="11" textLength="26.689453">5.1.3</tspan> + </text> + <rect x="126" y="133.5" width="47.5" height="23.5" stroke="#a5a5a5" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(131 138.05371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="5.4052734" y="11" textLength="26.689453">6.0.0</tspan> + </text> + <line x1="44.08114" y1="71.5" x2="71.618475" y2="124.70773" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="188.05786" y1="69.5" x2="160.1599" y2="124.66546" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <line x1="53.357456" y1="71.5" x2="126.5299" y2="127.48429" marker-end="url(#FilledArrow_Marker_3)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/> + <line x1="178.99357" y1="69.5" x2="105.05126" y2="127.39667" marker-end="url(#FilledArrow_Marker_4)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/> + <line x1="199.5" y1="130" x2="275.95" y2="130" marker-end="url(#FilledArrow_Marker_4)" stroke="#ff2600" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75" stroke-dasharray="1,3"/> + <text transform="translate(204.5 120)" fill="#ff2600"> + <tspan font-family="Helvetica" font-size="8" font-weight="500" fill="#ff2600" x="14.542969" y="8" textLength="44.91406">incompatible</tspan> + </text> + <line x1="199.5" y1="150" x2="275.95" y2="150" marker-end="url(#FilledArrow_Marker_2)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width=".75"/> + <text transform="translate(204.5 140)" fill="#0432ff"> + <tspan font-family="Helvetica" font-size="8" font-weight="500" fill="#0432ff" x="17.65625" y="8" textLength="38.6875">compatible</tspan> + </text> + <rect x="58.5" y="164.5" width="47.5" height="23.5" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <text transform="translate(63.5 169.05371)" fill="black"> + <tspan font-family="Arial" font-size="12" font-weight="500" fill="black" x="5.4052734" y="11" textLength="26.689453">5.2.0</tspan> + </text> + <path d="M 38.299127 71.49998 C 39.23227 79.43687 41.73237 91.49878 48 108 C 54.4882 125.08193 63.43472 142.76385 70.55697 155.84909" marker-end="url(#FilledArrow_Marker_5)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + <path d="M 186.36683 69.5 C 174.03483 88.13288 148.80312 124.81951 126 149 C 121.50411 153.76745 117.40286 157.7304 113.67481 161.0212" marker-end="url(#FilledArrow_Marker_5)" stroke="#0432ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/> + </g> + </g> +</svg>